Implementing a hangman function in Python - 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)

Related

String exercise in Python which detects certain letters

I am trying to create a function in Python which allows me to know if a string contains a letter "y" which appears in the beginning of a word and before a consonant. For example, the sentence "The word yes is correct but the word yntelligent is incorrect" contains the "y" of the word "yncorrect", so the function has to return True. In addition, it has to return true if the "y" is in capital letters and verifies those same conditions.
I have done it in the following way and it appears as if the program works but I was asked to use the method for strings in Python find and I havent't been able to include it. Any hint about how to do it using the method find? Thank you very much.
def function(string):
resultado=False
consonants1="bcdfghjklmnñpqrstvwxyz"
consonants2="BCDFGHJKLMNÑPQRSTVWXYZ"
for i in range(0,len(string)):
if string[i]=="y" and string[i-1]==" " and string[i+1] in consonants1:
resultado=True
break
if string[i]=="Y" and string[i-1]==" " and string[i+1] in consonants2:
resultado=True
break
return resultado
print(function("The word yes is correct but the word yntelligent is incorrect"))
Basically it is better to use re
consonants1="BCDFGHJKLMNÑPQRSTVWXYZ"
for i in consonants1:
if (a:= string.upper().find(f' Y{i}')) != -1:
print(...)
break
I think the function you want isn't find, but finditer from the package 're' (find will only give you the first instance of y, while finditer will return all instances of y)
import re
import string
consonants = string.ascii_lowercase
vowels = ['a', 'e', 'i', 'o', 'u']
for vowel in vowels:
consonants.remove(vowel)
def func(string):
for x in re.finditer('y', string.lower()):
if string[x.start() + 1] in consonants:
return True
return False
The function find returns the index at which the string first begins or is found. So, it returns the first index, else -1. This won't work for your use cases, unless you make it a bit more complicated.
Method One: Check every combination with find.
You have to two results, one to check if its the first word, or if its in any other word. Then return True if they hit. Otherwise return false
def function(string):
consonants1="bcdfghjklmnñpqrstvwxyz"
string = string.lower()
for c in consonants1:
result1 = string.find(" y" + c)
result2 = string.find("y" + c)
if result1 != 1 or result2 == 0:
return True
return False
Method Two: loop through find results.
You can use .find but it will be counter-intuitive. You can use .find and loop through each new substring excluding the past "y/Y", and do a check each time you find one. I would also convert the string to .lower() (convert to lowercase) so that you don't have to worry about case sensitivity.
def function(string):
consonants1="bcdfghjklmnñpqrstvwxyz"
string = string.lower()
start_index = 0
while start_index < len(string):
temp_string = string[start_index+1:end] ## add the 1 so that you don't include the past y
found_index = temp_string.find("y")
if found_index == -1: return False
og_index = start_index + found_index
## check to see if theres a " yConsonants1" combo or its the first word without space
if (string[og_index - 1] == " " and string[og_index+1] in consonants1) or (string[og_index+1] in consonants1 and og_index == 0):
return True
else:
start_index = og_index
return False
Here's how I would go about solving it:
Look up what the find function does. I found this resource online which says that find will return the index of the first occurrence of value (what's passed into the function. If one doesn't exist, it returns -1.
Since we're looking for combinations of y and any consonant, I'd just change the arrays of your consonants to be a list of all the combinations that I'm looking for:
# Note that each one of the strings has a space in the beginning so that
# it always appears in the start of the word
incorrect_strings = [" yb", " yc", ...]
But this won't quite work because it doesn't take into account all the permutations of lowercase and uppercase letters. However, there is a handy trick for handling lowercase vs. uppercase (making the entire string lowercase).
string = string.lower()
Now we just have to see if any of the incorrect strings appear in the string:
string = string.lower()
incorrect_strings = [" yb", " yc", ...]
for incorrect_string in incorrect_strings:
if string.find(incorrect_string) >= 0:
# We can early return here since it contains at least one incorrect string
return True
return False
To be honest, since you're only returning a True/False value, I'm not too sure why you need to use the find function. Doing if incorrect_string in string: would work better in this case.
EDIT
#Barmar mentioned that this wouldn't correctly check for the first word in the string. One way to get around this is to remove the " " from all the incorrect_strings. And then have the if case check for both incorrrect_string and f" {incorrect_string}"
string = string.lower()
incorrect_strings = ["yb", "yc", ...]
for incorrect_string in incorrect_strings:
if string.find(incorrect_string) >= 0 or string.find(f" {incorrect_string}"):
# We can early return here since it contains at least one incorrect string
return True
return False

Issue with analyzing strings in Python for Wordle-esque progam

I've created a function that takes a user-inputted guess, compared it to a hidden word taken randomly from a word doc, and returns a string that indicates if any letters match or are in the word at all. Here is the function:
def wordResults(guess, testGuess):
#guess = user inputted guess
#testGuess = secret word
results = ""
for i in range(5):
#Check if letters at given position match
#in each word, append capital letter if so
if guess[i] == testGuess[i]:
results += guess[i].upper()
#Check if letter at given position is in
#the secret word at all, append lowercase
#letter if so
elif testGuess.find(guess[i]) != -1:
results += guess[i]
#Append underscore if neither condition is met
else:
results += "_"
return results
My issue lies with the elif-statement. I would like it to print a lowercase only if that letter appears in the word, but not if the letter is already in the correct spot. Here is the program running to show what I'm referring to:
(Note: the hidden word is also user-inputted until I get the program working as intended)
For Guess #2, I would like it so that the first 'h' does not show up, since it is indicating the 5th letter in 'conch' that is already confirmed with a capital 'H'. Hope that makes sense.
It's a lot easier to work with a list and then make it a string at the end:
guess = guess.lower()
testGuess = testGuess.lower()
result = []
for i, letter in enumerate(guess):
if letter in testGuess:
if letter == testGuess[i]:
result.append(letter)
else:
result.append(letter.upper())
else:
result.append('_')
for i, letter in enumerate(result):
if letter.upper() in result and result[i] != letter:
result[i] = '_'
return ''.join(result)
For guess two, then the second loop checks if each letter is already in the loop and placed correctly and if it's not in the correct spot, makes it back into an _.

Creating a word scrambler but it won't work, need help as a beginner

Beginner python coder here, keep things simple, please.
So, I need this code below to scramble two letters without scrambling the first or last letters. Everything seems to work right up until the scrambler() function.
from random import randint
def wordScramble(string):
stringArray = string.split()
for word in stringArray:
if len(word) >= 4:
letter = randint(1,len(word)-2)
point = letter
while point == letter:
point = randint(1, len(word)-2)
word = switcher(word,letter,point)
' '.join(stringArray)
return stringArray
def switcher(word,letter,point):
word = list(word)
word[letter],word[point]=word[point],word[letter]
return word
print(wordScramble("I can't wait to see how this turns itself out"))
The outcome is always:
I can't wait to see how this turns itself out
Since you are a beginner, I tried to change your code as little as possible. Mostly you are expecting changes to word to change the contents or your list stringArray. The comments mark the changes and reasons.
from random import randint
def wordScramble(myString): # avoid name clashes with python modules
stringArray = myString.split()
for i, word in enumerate(stringArray): # keep the index so we can update the list
if len(word) >= 4:
letter = randint(1,len(word)-2)
point = letter
while point == letter:
point = randint(1, len(word)-2)
stringArray[i] = switcher(word,letter,point) # update the array
return ' '.join(stringArray) # return the result of the join
def switcher(word,letter,point):
word = list(word)
word[letter],word[point]=word[point],word[letter]
return ''.join(word) # return word back as a string
print(wordScramble("I can't wait to see how this turns itself out"))
Because there had to be a cleaner (and better documented) way to do this:
from random import sample
def wordScramble(sentence):
# Split sentence into words; apply switcher to each; rejoin into a sentence
return ' '.join([switcher(x) for x in sentence.split()])
def switcher(word):
if len(word) <= 3: # Don't bother if not enough letters to scramble
return word
# Pick 2 positions from interior of word
a,b = sorted(sample( xrange(1,len(word)-1), 2 ))
# Re-assemble word with out 2 positions swapped using bits before, between & after them
return word[:a] + word[b] + word[a+1:b] + word[a] + word[b+1:]
print wordScramble("I can't wait to see how this turns itself out")

How to replace characters in a string using iteration and variables? (Python)

I am writing a hangman game in python as part of a college project, and I am trying to use string.replace(old, new) to substitute the blanks (_) with letters. Instead of using actual string characters though, I am trying to use variables for 'old' and 'new'. Here's what I've got so far for this bit:
if validGuess == 'true':
if guess in word:
for letter in word:
if letter == guess:
word.replace(letter, guess)
else:
missNum = (missNum + 1)
else:
tryNum = (tryNum - 1)
However, it isn't working. I don't get any errors, it simply will not replace the blanks.
What am I doing wrong here? Is there a better way to achieve what I am doing?
-EDIT-
I tried to implement #Peter Westlake's solution (which seemed to me the most elegant) but I have run into an issue. I have a section of code which converts a randomly selected word into underscores:
#converting word to underscores
wordLength = len(word)
wordLength = (wordLength - 1)
print(wordLength) #testing
for i in range(0,wordLength):
wordGuess = (wordGuess + '_')
print(wordGuess)
And this seems to work fine. Here is the code for letter substitution:
if validGuess == 'true':
wordGuess = ''.join([letter if guess == letter else wordGuess[pos]
for pos, letter in enumerate(word)])
if guess not in word:
tryNum = (tryNum - 1)
print(wordGuess)
However, here is the output:
Guess a letter: a
test
Traceback (most recent call last):
File "G:\Python\Hangman\hangman.py", line 60, in <module>
for pos, letter in enumerate(word)])
File "G:\Python\Hangman\hangman.py", line 60, in <listcomp>
for pos, letter in enumerate(word)])
IndexError: string index out of range
String index out of range? What does that mean?
str.replace() returns the new string, store the new value:
word = word.replace(letter, guess)
Python strings are immutable and cannot be altered in-place.
However, you are replacing letter with the exact same value; letter == guess is only True if both are the same character.
I'd keep a separate set of correctly guessed letters instead, and rebuild the displayed underscores and correct guesses each time:
correct_guesses = set()
incorrect_guesses = set()
if guess in correct_guesses & incorrect_guesses:
print('You already guessed that letter')
elif guess in word:
# correct guess!
correct_guesses.add(guess)
display_word = ''.join(char if char in correct_guesses else '_' for char in word)
else:
# incorrect guess!
incorrect_guesses.add(guess)
print('Oops, incorrect guess!')
missNum += 1
I think I understand what you're getting at here.
I would probably rebuild the word-so-far on the spot instead of having a persistent string for it, keeping the tested letters separately. When the user tries a new character, make two checks:
See if the guess character has been guessed already: if guess in tried. If so, proceed however you like (penalize or ignore), but don't add the character to the tried-characters list.
If not, see if the character is in the target word: elif guess in word. If not, assess some penalty and add the guess to the tried-characters list.
For any other result: else. Add the guess to the tried-characters list.
To display the user's progress, make a blank string. Go through the target word character-at-a-time: for char in word, like you have been. But instead of trying to modify an extant string, just add the character to the end of the blank string if it's in the tried-characters string, or an underscore if not: show += char if char in tried else "_". Once that for loop is exhausted, display what you've got!
Alternatively, use .join with a slightly different iterator: show = "".join(char if char in tried else '_' for char in word). It'll iterate through word, keeping each letter if it's in your tried-characters string, or substituting an underscore if not, putting whatever is in "" between them (or nothing, if you leave it as ""). It looks like you already know that, though.
At the hazard of completely rewriting your code, this is what it might look like:
## init
word = "mauritius" # the word you're looking for. I chose this one.
tried = str() # initialize a list of tested characters
tryNum = 3 # however many wrong guesses the user gets
...
## in your run loop...
if tryNum: # evaluates 0 as Fasle, if you didn't already know
guess = UserInput() # some means to get the guess from the user; a single-character string.
if guess in tried:
print "Tried that letter already!"
elif guess not in word: # the letter hasn't been tested yet, but isn't in the word, either.
print "Wrong! %d guesses left!" % tryNum
tryNum -= 1
tried += guess
else: # the guess is new (not tried) and it's valid (in the word)
tried += guess
show = str() # this is the string you will display. make a blank one each time.
for char in word:
show += char if char in tried else "_" # if the character has been tried, add it to the output. Otherwise, an underscore.
print show # display the word so far
if show == word:
print "You win!" # Congratulations! You hung a man.
else: # Out of tries; the convict lives another day.
print "Game Over!" # I am not sure how committed to this vocabulary-based execution you really are...
You can swap if tryNum: with while tryNum: and it should work all by itself, after initialization. If you do, there are fun things you can do with continues and breaks, but that goes a bit beyond the scope of your question.
You can swap show = str() and the for char in word: block out with the .join singleton in this next example, too. Change ''.join(..) to ' '.join(..) to add a space between characters/underscores!
This compressed version is probably a bit less Pythonic:
# post-init...
if tryNum:
guess = UserInput()
if guess in tried: pass
elif guess not in word:
print "Wrong! %d guesses left!" % tryNum
tryNum -= 1
tried += guess
else: tried += guess
show = ''.join(char if char in tried else '_' for char in word)
if show == word: print "You win!"
else: print "Game Over!"
This does not answer your first question of "What am I doing wrong?" but I think it might be a better way of going about what you intend? It might be a bit easier to maintain and expand for you, too.
Note: Go ahead and replace UserInput() with something like str(raw_input("Guess a letter!"))[0] if you want to try this thing out on its own.
Replacing a letter with an identical guess isn't going to do anything! I think you want to find the position in the word where the guessed letter appears, and replace the _ in that position with the letter. For that you will need to find every position where the letter occurs, e.g. using the index() method.
For instance, to replace the first occurrence of the guess:
# Illustration of the principle, not the complete answer.
word = 'faq'
display = '___'
# Put in a loop, one iteration for each guess input.
guess = 'a'
display = ''.join([letter if guess == letter else display[pos]
for pos, letter in enumerate(word)])
print display
Which will print _a_.

word separator for python coding [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
So the question reads:
Write a program that accepts as input a sentence in which all of the words are run together but the first character of each word is uppercase. Convert the sentence to a string in which the words are separated by spaces and only the first word starts with an uppercase letter. For example the string "StopAndSmellTheRoses." would be converted to " Stop and smell the roses."
I am so confused this my code so far.
def main():
#User enters a sentence
my_string=input('enter a sentence: ')
print(my_string.capitalize())
main()
You can loop through the string and add a character each time to a result:
my_string = "StopAndSmellTheRoses"
i = 0
result = ""
for c in my_string:
if c.isupper() and i > 0:
result += " "
result += c.lower()
else:
result += c
i += 1
print result
We'll use c for each character as we walk through the string and we'll use i to keep track of the position in the string.
There are two possibilities: it's either an uppercase character (excluding the first one) or it's not.
In the first case we'll add a space and that character as lowercase to the result. This ensures a space is inserted before each uppercase character further in the sentence.
In the second case it's a lowercase character or the uppercase character at the beginning of the sentence. We don't have to do anything with these and we'll add it right away.
Lastly we add one to i whenever we're done with a character (i += 1) as this means we correctly know where we are in the sentence.
Welcome to SO!
One way to do this is to loop through your string, checking the chars one by one:
#You've learned how to iterate through something, right?
i = 0 #a counter
for c in my_string: #get the characters of my_string, one by one.
if c.isupper(): #check if it's in upper case
if i == 0: #if it's the first letter
new_string += c #let it be like the original
else:
new_string += ' '+.lower() #it's not the first letter,
#so add space, and lower the letter.
else:
new_string += c #else, only add the letter to the new string
i += 1
Edit added a double-check to see if it's the first letter of the sentence or not. Updated demo.
As an alternative to using a counter, you can also use the built-in function enumerate, which returns a tuple of index and values.
for i,c in enumerate(my_string): #get the characters of my_string, one by one.
if c.isupper(): #check if it's in upper case
if i == 0: #if it's the first letter
new_string += c #let it be like the original
else:
new_string += ' '+c.lower() #it's not the first letter,
#so add space, and lower the letter.
else:
new_string += c #else, only add the letter to the new string
Demo
>>> my_string = 'ImCool'
>>> new_string = ''
>>> i = 0 #a counter
>>> for c in my_string: #get the characters of my_string, one by one.
if c.isupper(): #check if it's in upper case
if i == 0: #if it's the first letter
new_string += c #let it be like the original
else:
new_string += ' '+.lower() #it's not the first letter,
#so add space, and lower the letter.
else:
new_string += c #else, only add the letter to the new string
i += 1
>>> new_string
'Im cool'
Hope this helps!
You'll need a bit of regex.
import re
split = re.findall(r'[A-Z][a-z\.]+', 'HelloThisIsMyString.')
You'll also need to join those together (inserting spaces)
' '.join(...)
and handle case conversions
' '.join(word.lower() for word in split)
(and as you already did, capitalize the first word)
' '.join(word.lower() for word in split).capitalize()
It appears that you are a little confused and this is to be expected if you are new to Python. I'm assuming you take input from the user as opposed to input for a function. Either way I would create a simple function that you could insert the users input into. The function below will accomplish what the problem asks.
def sentenceSplitter(sentence):
result = ""
for i, x in enumerate(sentence): #i is character index, x is the element
if i == 0:
result = result + x
elif x.isupper() == False: #if element is not uppercase, add it to the result
result = result + x
else: # Otherwise, add a space and lowercase the next letter
result = result + " " +x.lower()
return(result)
To reiterate, if you are looking to print out the sentence you would write this after the function:
def main():
#User enters a sentence
my_string=input('enter a sentence: ')
print(sentenceSplitter(my_string))
main()
If you are still confused feel free to ask any further questions.

Categories

Resources