How to assign item to a string in python - python

I'm really new to programming in general, but I'm a fast learner. I've been working on a project. I wanted to make a simple hangman game, but I hit a road block and I'd like to figure it out before continuing.
I'm trying to assign correct guesses to an empty variable and print is as they go, but it seems I can't assign "items" to strings. Is there an alternative method I could use?
Here's the code
switch = True
def hangman():
guess_number = 0 # Var that keeps track of the guesses
secret_word = input("What is the secret word?\n>") # Gets the secret word
print("The secret word is %d characters long." % len(secret_word)) # Lenght of secretword
answer = "-" * len(secret_word) # Create empty answer for assigning characters
while switch is True:
guess_number = guess_number + 1 # Counts the guesses
index_num = 0 # Tring to use this to assign correct guesses to answer
user_guess = input("Guess #%d >" % guess_number) # Gets user guess
print("Secret word: " + answer) # prints empty answer as "----"
for each_char in secret_word:
index_num = index_num + 1 # Counting index for assigning to answer variable
print("testing index #" + str(index_num))
if user_guess is each_char:
print("Correct Guess for index #" + str(index_num))
#------> answer[index_num] = each_char <--------
hangman()

Python strings are immutable, they can't be modified.
You can treat your string as a list
answer = list("-" * len(secret_word))
Then join chars together
answer_str="".join(answer)

There are some other ways, that have been suggested. If you're determined to keep the string answer, try this:
answer = answer[:index_num] + each_char + answer[index_num+1:]
This creates a new string by adding (string add is concatenation) together three substrings: first, the substring created by slicing the original string from zero (default: [:) up to index_num, non-inclusive. That is, answer[0] ... answer[index_num-1]. Then the each_char, which is a string (or a char, same difference). Finally, another substring, this one from index_num+1 running up to the end (default: :]).

Related

How do you split duplicate letters with "x" in Python?

I am trying to create a function which is able to detect when two letters back to back are duplicates, for example the ls in "hello", and split the duplicate letters with the letter "x". Here is my code:
plaintext = input("Enter plaintext here: ")
plaintext = plaintext.lower() # makes plaintext lowercase
plaintext = plaintext.replace(" ", "") # removes all spaces
# this separates all duplicate letters
i = 0 # sets i to 0
for letter in plaintext:
if plaintext[-1] == plaintext[-2]: # if the last letter is the same as the second to last
plaintext = plaintext[:-1] + "x" + plaintext[-1:] # separate them with an x
elif plaintext[i] == plaintext [i+1]: # if one letter is the same as the next letter
# the line above makes an error
plaintext = plaintext[:i+1] + "x" + plaintext[i+1:] #separate them with an x
i += 1
else:
i += 1
This code works when I enter hello there as the input; I receive helxlothere. However, when I test another input, such as heythere, IndexError: string index out of range shows up for elif line (line 12). How can I make this code work for all inputs?
You can use regex to achieve this.
For both approaches, it will work for hello there hey there
The difference comes when more than two character repetition happens.
approach 1
import re
string='hello there'
# find Any repeated character and add x in between them
answer = re.sub(r'(.)\1{1}', r'\1x\1', string)
print(answer)
Here for hellllo there text, you will get output helxllxlo there
approach 2
alternatively, you can use this method.
s="hello there"
for match in re.finditer(r"(.)\1+", s):
old=s[match.start():match.end()]
s=s.replace(old,'x'.join(old))
print(s)
here for hellllo there text, you will get output helxlxlxlo there as output.
I think the second approach will be more appropriate.
The IndexError is caused by the fact that you are looking at plaintext[i+1]. As you can see in the word heythere, there are no letters which match back to back, and therefore the code continues until it hits the end, and so you get an IndexError because there is no element i+1.
You can fix this by using this code instead:
plaintext = input("Enter plaintext here: ")
plaintext = plaintext.lower() # makes plaintext lowercase
plaintext = plaintext.replace(" ", "") # removes all spaces
# this separates all duplicate letters
i = 0 # sets i to 0
for letter in plaintext:
if plaintext[-1] == plaintext[-2]: # if the last letter is the same as the second to last
plaintext = plaintext[:-1] + "x" + plaintext[-1:] # separate them with an x
try:
elif plaintext[i] == plaintext [i+1]: # if one letter is the same as the next letter
# the line above makes an error
plaintext = plaintext[:i+1] + "x" + plaintext[i+1:] #separate them with an x
i += 1
else:
i += 1
except IndexError:
pass
This code should stop your code from crashing in the elif statement, while also completing properly.
Hope this helps, have a nice day!
You are receiving the IndexError because during iterating, when the loop reaches the last letter,
elif plaintext[i] == plaintext [i+1]:
this line checks for the letter after the last letter, which does not exist, which causes the program to run into IndexError.
You have to check till the second last letter, for it to work properly.
A simple and easier way to arrive at the same output with a bit easier logic.
Logic
Create a new string and insert all letters which are in the old string, plaintext, to the new string, newPlainText, and check only for one condition that is whether the last letter is same as current or not and if yes then also insert letter 'x' into the newPlainText and that's it!
plaintext = input("Enter plaintext here: ")
plaintext = plaintext.lower() # makes plaintext lowercase
plaintext = plaintext.replace(" ", "") # removes all spaces
# this separates all duplicate letters
newPlainText = plaintext[0] # copy the first letter of plaintext to the new string which will handle the duplicates and insert `x` between them
i = 1 # sets i to 1
while i < len(plaintext):
if plaintext[i] == plaintext[i-1]: # we check that if prev and current letters are same then also append x to the new string
newPlainText += "x"
newPlainText += plaintext[i] # we insert the required current letter to the new string in each iteration
i += 1
print(newPlainText)

python string.split() and loops

disclaimer im new to python
i need to split a string input send if to a function that substitutes a character in the string with a different character (like a substitution cipher) but i just dont know how to go about this
print('Welcome to the encryption protocol for top secret governemt cover ups')
string=input('whats your message?')
def encrypt(string):
alpha = "abcdefghijklmnopqrstuvwyz"
sub_alpha = "pokmenliuytrwqazxcvsdfgbhn"
index=0
while index < len(string):
letter=string[index]
im not really sure what im doing im really bad at python, this has had me stumped for 3 days now ive reviewed my course material and tried videos on youtube im probably just really really dumb
I think the key piece of knowledge you're missing is that strings are iterable. So you can do things like:
for c in "FOO":
print(c)
# prints "F\nO\nO\n"
And you can find the index of a character within a string with str.index. So you can build up your cyphertext like this:
alpha = "abcdefghijklmnopqrstuvwyz "
cypher = "pokmenliuytrw qazxcvsdfgbhn"
plaintext = "some string"
cyphertext = ""
for c in plaintext:
char_index = alpha.index(c)
cyphertext += cypher[char_index]
You can also iterate over things inline - this is called a comprehension. So to transform your string you can do this instead of using the for loop:
cyphertext = "".join(cypher[alpha.index(c)] for c in plaintext)
The example above uses the str.join function to concatenate each character of cyphertext.
Here is a solution that asks the question and then iterates through each letter, finding the index in the alpha key, and replacing it with the sub_alpha key equivalent.
Note this example also checks if it should be lowercase or uppercase.
EDIT: if the input character does not have a valid cipher, it doesn't get altered.
EDIT 2: expanded answer to convert both forwards and backwards.
alpha = "abcdefghijklmnopqrstuvwyz"
sub_alpha = "pokmenliuytrwqazxcvsdfgbhn"
def encrypt(in_char):
is_lower_case = in_char.islower()
index = alpha.find(in_char.lower())
if index < 0:
return in_char
elif is_lower_case:
return sub_alpha[index]
else:
return sub_alpha[index].upper()
def decrypt(in_char):
is_lower_case = in_char.islower()
index = sub_alpha.find(in_char.lower())
if index < 0:
return in_char
elif is_lower_case:
return alpha[index]
else:
return alpha[index].upper()
print('Welcome to the encryption protocol for top secret governemt cover ups')
input_str=input('whats your message? ')
output_str=""
for letter in input_str:
output_str += encrypt(letter)
print("Encrypted: ")
print(output_str)
input_str=""
for letter in output_str:
input_str+= decrypt(letter)
print("Decrypted: ")
print(input_str)

Python hangman without lists

I need a simple Python Hangman program without using Lists - It is just one word
HAPPY - this program works - BUT...
This is what I did, with Lists - but teacher said Lists are not allowed
We do not have to draw hangman - we just prompt for the letters - print the "-" for each letter to show length of word.
def main():
secretword = "HAPPY"
displayword=[]
displayword.extend(secretword)
for I in range (len(displayword)):
displayword[I]="_"
print ('current word
')
print (' '.join(displayword))
count = 0
while count < len(secretword):
guess = input('Please guess a etter: ')
for I in range(len(secretword)):
if secretword[I] == guess:
displayword[I] = guess
countr - count + 1
print (' '.join(displayword))
print (congratulations you guess the word')
main()
If you don't like the code - that's fine. This is how our teacher is requiring us to do this. I can see it is not like others do it. I only left out the comments - that are also required on every line of code
One solution to your problem would be to use two strings, secretword, which is the word you're looking for and displayword which is what the user sees so far, the combination of letters and -. Every time you enter a letter, the program checks if the secretword contains that letter and if it does, it updates the character of the specific index in displayword:
def main():
secretword = "HAPPY"
length = len(secretword)
displayword = '-' * length
count = 0
while count < length:
guess = input("Please guess a letter: ")
for i in range(length):
if secretword[i] == guess:
displayword[i] = guess
count += 1
print(displayword)
print("Congratulations, you guessed the word.")
main()

Implementing a hangman function in Python

Here is the problematic piece of my function:
def hangman1(word):
global guessesMade
global guessesLeft
currentGuess = '_ ' * len(word)
let = print(input('Please guess a letter: '))
for i in range(len(word)):
if word[i] == let:
print('{} is contained in the word.'.format(let))
if i == 0:
currentGuess = word[0] + currentGuess[1:]
else:
currentGuess = currentGuess[:i] + word[i] + currentGuess[i + 1:]
print(currentGuess)
The user enters a letter at the prompt and it checks if the letter is in the randomWord that was generated outside of the function from a list of words. I can get it to print the blanks correctly, but if the user enters a letter that is in the word it prints out a line of the correct letter instead of the blanks with the correct letter mixed in between.
Any help is appreciated.
The main problem you're having right now is two-fold - one, that the replace() method replaces all instances of any given input within a string, not the first one, and two, that you don't currently have any way of telling which letters you've already uncovered. Calling replace("_", let) will always replace every single instance of "_", and given that you're applying that to a string that is only composed of underscores, it'll always overwrite the entire string. It seems like you're also regenerating hidden_let every time hangman() is called with a guess letter, meaning that best-case with your design now you're only going to ever show every letter the user just guessed and a bunch of underscores otherwise.
What you'd want to do is have two values, correct_word and current_guess. correct_word will be the word the player has to guess, and current_guess will be their progress in guessing the word, starting with a string of only underscores of the same length as correct_word.
Here's a short example. I've taken the liberty of removing your global references - globals are generally frowned upon - and encapsulated the behavior in a small class. You'd want to replace the value in hangmanner.play_hangman() with whatever your random word is.
class Hangmanner:
correct_word = ''
current_guess = ''
def play_hangman(self, word):
self.correct_word = word
self.current_guess = '_' * len(self.correct_word)
while self.current_guess != self.correct_word:
self.guess_letter(input("Please guess a letter: "))
def guess_letter(self, guessed_letter):
for i in range(len(self.correct_word)):
if self.correct_word[i] == guessed_letter:
if i == 0:
self.current_guess = self.correct_word[i] + self.current_guess[1:]
else:
self.current_guess = self.current_guess[:i] + self.correct_word[i] + self.current_guess[i + 1:]
print(self.current_guess)
if __name__ == "__main__":
hangmanner = Hangmanner()
hangmanner.play_hangman("test")
This uses the slicing function in python, where you can use the brackets and the [first:last] syntax to access an arbitrary range of any given collection. If either first or last is missing, the slice continues to the beginning or end of the collection, respectively. Above, current_guess[1:] returns current_guess from the second index to the last. current_guess[:i] returns current_guess from the first index up to the index preceding i, given that last is the exclusive end bound.
hiddenLet.replace('_',let) replaces all occurrences of _ with whatever let represents.
newWordList = [x if x==let else '_' for x in randWord]
newWord = ''.join(newWordList)

Replacing every instance of a character in Python string

I have a game where the user guesses letters. They are shown a blank version of the mystery work (_____ for example, the _'s are equal to number of characters in the word). The program knows the word, and needs to replace every index in the blanked out version of the word if the letter they guess is present in the mystery word.
For example, if the player guesses "p" and the word is "hippo" they will be shown __pp_. But, my code will only replace the first instance of "p", giving __p__ instead.
Would this be easier to tackle as a list problem?
mistakes = 0
complete = False
t = False
words = ['cow','horse','deer','elephant','lion','tiger','baboon','donkey','fox','giraffe']
print("\nWelcome to Hangman! Guess the mystery word with less than 6 mistakes!")
# Process to select word
word_num = valid_number()
word = words[word_num]
#print(word)
print("\nThe length of the word is: ", str(len(word)))
attempt = len(word)*"_"
# Guesses
while not (mistakes == 6):
guess = valid_guess()
for letter in word:
if guess == letter:
print("The letter is in the word.")
position = word.index(guess)
attempt = attempt [0:position] + guess + attempt [position + 1:]
print("Letters matched so far: ", attempt)
t = True
while (t == False):
print("The letter is not in the word.")
print("Letters matched so far: ", attempt)
mistakes = mistakes + 1
hangMan = ["------------", "| |", "| O", "| / |", "| |", "| / |\n|\n|"]
hang_man()
t = True
t = False
answer = 'hippo'
fake = '_'*len(answer) #This appears as _____, which is the place to guess
fake = list(fake) #This will convert fake to a list, so that we can access and change it.
guess = raw_input('What is your guess? ') #Takes input
for k in range(0, len(answer)): #For statement to loop over the answer (not really over the answer, but the numerical index of the answer)
if guess == answer[k] #If the guess is in the answer,
fake[k] = guess #change the fake to represent that, EACH TIME IT OCCURS
print ''.join(fake) #converts from list to string
This runs as:
>>> What is your guess?
p
>>> __pp_
To loop over everything, I did not use index, because index only returns the first instance:
>>> var = 'puppy'
>>> var.index('p')
0
So to do that, I analyzed it not by the letter, but by its placement, using a for that does not put k as each letter, but rather as a number so that we can effectively loop over the entire string without it returning only one variable.
One could also use re, but for a beginning programmer, it is better to understand how something works rather than calling a bunch of functions from a module (except in the case of random numbers, nobody wants to make their own pseudo-random equation :D)
Based on Find all occurrences of a substring in Python:
import re
guess = valid_guess()
matches = [m.start() for m in re.finditer(guess, word)]
if matches:
for match in matches:
attempt = attempt[0:match] + guess + attempt[match+1:]
print("Letters matched so far: ", attempt)
else:
.
.
.

Categories

Resources