I'm trying to write a program where the user has to guess a letter in the goal of unlocking the secret word. If the secret word is guessed correctly before the maximum 8 guesses, the function returns true else the function returns false. For some reason my function just doesn't produce the right output. I would enter the letter 'a' and it would print "Letters guessed so far: ['a']" and then the program would end. I need help in fixing this issue.
secretWord = 'hello'
lettersGuessed = []
def isWordGuessed(secretWord,lettersGuessed):
guess = 0
while guess <= 8:
secretLetters = list(secretWord)
secretWordLen = len(secretLetters)
letter = input('Enter a letter: ')
lettersGuessed.append(letter)
print('Letters guessed so far: ',lettersGuessed)
if letter not in secretLetters:
guess += 1
while letter in secretLetters:
secretLetters.remove(letter)
if secretLetters == []:
return True
else:
return False
isWordGuessed(secretWord,lettersGuessed)
Your first problem is, as kwatford explained, that you are returning every time through the loop. You can fix that by moving the if statement outside the while loop.
Your next problem, as Vorticity explained, is that it will never return early, even if the user guesses the whole word. To fix that, move the if part back inside the loop, but leave the else part outside the loop (meaning you no longer need the else)
After that, it still won't work, because you're doing secretLetters = list(secretWord) each time through the loop, so you can only win if you guess all the letters in one guess (which is impossible, unless the word is, say, "a" or "aaaaa"). To fix that, move that line outside the loop.
Putting it all together:
def isWordGuessed(secretWord,lettersGuessed):
guess = 0
secretLetters = list(secretWord)
while guess <= 8:
secretWordLen = len(secretLetters)
letter = input('Enter a letter: ')
lettersGuessed.append(letter)
print('Letters guessed so far: ',lettersGuessed)
if letter not in secretLetters:
guess += 1
while letter in secretLetters:
secretLetters.remove(letter)
if secretLetters == []:
return True
return False
As a side note, there are a lot of things you can do to simplify this.
First, you really just need a set of all letters in the secret word—you don't need to know the order, or how many copies there are of each, etc. So, instead of a list, use a set. This also means you don't need the loop around secretLetters.remove(letter).
More trivially, you create secretWordLen but never use it.
You also accept and append to a lettersGuessed passed in by the caller, but the caller is just passing you an empty list, and never using it after the fact, so why bother? And if you don't need to mutate it for the caller's benefit, you can just keep it as a string, so the user sees help instead of ['h', 'e', 'l', 'p'], which is a lot nicer.
You've also got a few cases that are being tested even when they can't possibly be true.
Finally, an empty list (or set, or any other sequence) is false, so there's no reason to explicitly compare to the empty list.
While I'm at it, I'm going to PEP8-ify the spacing to make it easier to see the indentation.
So:
def isWordGuessed(secretWord):
guess = 0
lettersGuessed = ''
secretLetters = set(secretWord)
while guess <= 8:
letter = input('Enter a letter: ')
lettersGuessed += letter
print('Letters guessed so far:', lettersGuessed)
if letter not in secretLetters:
guess += 1
else:
secretLetters.remove(letter)
if not secretLetters:
return True
return False
The last if statement is indented too far, causing it to be part of your while loop. Since both branches of the condition cause the function to return, it always returns on the first iteration of the loop.
You just need to move your return for the False case. Basically, the way your code is written right now, you will never go back to the beginning of your loop. Also, as noted by abarnert, you will never exit the loop because you are reinitializing secretLetters every time you loop. You have to initialize it outside the loop. Your code should look like this:
secretWord = 'hello'
lettersGuessed = []
def isWordGuessed(secretWord,lettersGuessed):
guess = 0
secretLetters = list(secretWord)
secretWordLen = len(secretLetters)
while guess <= 8:
letter = input('Enter a letter: ')
lettersGuessed.append(letter)
print('Letters guessed so far: ',lettersGuessed)
if letter not in secretLetters:
guess += 1
while letter in secretLetters:
secretLetters.remove(letter)
if secretLetters == []:
#Return true if all correct letters have been guessed
return True
#Return false if guessed incorrectly eight times
return False
Related
I have using while loop to input value in list, but every time the loops run, the value is replaced by a new value entered. I have pasted the code below, any help would be great
import random
word_list = ["aardvark", "baboon", "camel"]
chosen_word = random.choice(word_list)
print(f'the solution is {chosen_word}.')
empty_word = False
while not empty_word:
guess = input("Guess a letter: ").lower()
for letter in chosen_word:
if letter == guess:
print("Right")
else:
print("Wrong")
display = []
for letter in chosen_word:
if letter == guess:
display.append(letter)
else:
display.append('_')
print(display)
if "_" not in display:
empty_word = True
print("you win")
Since you initialized the list in the loop, the list will be initialized every time the loop runs. If you want to append to the list without clearing it at every iteration, initialize it outside of the loop.
2 things:
You want to initialize display outside of your loop, otherwise you are creating an empty list at every iteration. This also means that you need to replace letters inside the list instead of appending to it, otherwise you will extend the list's length every time.
Your last if block needs to be indented inside your loop, otherwise you are never setting empty_word to True and never exiting your loop.
I was creating a wordle clone but when I enter word it doesn't do anything, help
so how it works is that there is only 29 possible words and the game is suppose to tell you how many of the letters in the code are in the actual wordle.
import random
words=['about','block','chair','dream','eagle','faint','great','homes','ideas','joker','karen','latin','mouth','noise','orion','paint','queen','raven','speed','smart','tooth','truck','unite','venom','venus','where','xerox','youth','zebra']
wordlenum=random.randint(0,28)
wordle=words.pop(wordlenum)
letters1=''
for char1 in wordle:
letters1 += char1
letters2=''
correct=0
while True:
guess=input('guess:')
for char2 in guess:
letters2+=char2
for l1 in letters1:
for l2 in letters2:
if l1==l2:
correct+=1
if correct == 5:
print('you win')
break
else:
print(correct)
You have extra for loops in your code,
for l1 in letters1:
for l2 in letters2:
this part of code will execute 25 times, and will match if any of the letters in both words are same.
Hence give false results,
For this you can use zip method.
Also, After the failed attempt, you need to reset the correct counter and the word also needs to be reset, otherwise it is keep adding on top of the that.
Also, you would not require to explicitly add the words again to a new variable.
This
letters1=''
for char1 in wordle:
letters1 += char1
and
for char2 in guess:
letters2+=char2
are unnecessary.
After modification, here you can use.
import random
words=['about','block','chair','dream','eagle','faint','great','homes','ideas','joker','karen','latin','mouth','noise','orion','paint','queen','raven','speed','smart','tooth','truck','unite','venom','venus','where','xerox','youth','zebra']
wordlenum=random.randint(0,28)
wordle=words.pop(wordlenum)
while True:
correct = 0
guess=input('guess:')
# you can put some validations here on guess, like len(guess) == 5 and only chars
for l1, l2 in zip(wordle, guess):
if l1==l2:
correct+=1
if correct == 5:
print('you win')
break
else:
print(correct)
Hope this helps.
If you want to find out how many letters from one string occur in another string then convert the strings to sets and find the length of their intersection. For example:
s1 = 'chair'
s2 = 'train'
print(len(set.intersection(set(s1), set(s2))))
Output:
3
I am trying to have the below code return True if the letters in secretWord are also in lettersGuessed. Could someone please let me know what is wrong with my code? It is consistently returning False. Thanks so much.
def isWordGuessed(secretWord, lettersGuessed):
current = ' '
for c in secretWord:
if c in lettersGuessed:
current += c
if len(current) == len(secretWord):
return True
else:
return False
print(isWordGuessed(secretWord, lettersGuessed))
You are checking if len(current) == len(secretWord) inside of the loop, before you have completed checking the letters in secretWord. Move it outside.
Otherwise, you may want to address this problem using sets as #stackErr suggested.
Iterate over each letter in the secret word and check if its in the second word. Store all the common letters in a out. Check the length of out and return True if greater than 0.
def isWordGuessed(s1, s2):
out = ""
for c in s1:
if c in s2 and not c in out:
out += c
if len(out) > 0:
return True
return False
Also it looks like your function is doing too many things. Do you also want to check that the length of the guessed word and the length of secret word is the same? What happens if the secret word or guessed word has repeating letters?
Your current variable already has a "space" in it. So You will always have an extra count in your length.
Secondly, whey not use current_length = 0 instead and change the line 5 to current += 1. Then you can compare the len() with this count directly instead of calling len for each word.
Since your code does not consider duplicates, you can write a much more compact function using sets.
guess = set(lettersGuessed)
secret = set(secretWord)
if secret.intersection(guess) == secret:
return True
else:
return False
>>> a = set("abcdefgh")
>>> b = set("efghabcd")
>>> a.intersection(b)
set(['a', 'c', 'b', 'e', 'd', 'g', 'f', 'h'])
>>> a.intersection(b) == a
True
Note that this does not consider duplicate characters etc. For that you should consider using a dictionary and keep track of counts of each character.
There are two issues here. One is to get the code working as is.
Remove the additional space between ''. Now, the if len(..) statement should be indented below the 'for' statement since we want all the letters in the secretword to be checked i.e the entire for loop needs to finish before checking the length.
def isWordGuessed(secretWord, lettersGuessed):
current = ''
for c in secretWord:
if c in lettersGuessed:
current += c
if len(current) == len(secretWord):
return True
else:
return False
The other issue to address is what is required from the code. Is it supposed to return True only when all letters in secretWord appear in lettersGuessed. For example, 'newd' and 'ne4wd'. Are repetitions allowed? Just something to keep in mind.
You can compare the lengths of secretWord and current inside the for loop but once comparison is successful you need to break out of it to then compare if both are indeed the same strings:
def isWordGuessed(secretWord, lettersGuessed):
current = ''
for i in secretWord:
if i in lettersGuessed:
current = current + i
if len(secretWord) == len(current):
break
if secretWord == current:
return True
return False
I wrote the following python code. It takes a list of English words I found on the internet and makes them a list so that I can use them for hangman. Well my problem is that every time I run this program and successfully guess the word, it doesn't break out of the while loop. It just keeps going. I can't figure out why for the life of me. Anyone have any clue as to why it isn't printing the final message to the winner?
import random
words = []
lettersGuessed = []
isGuessed = 0
wordFile = open(r'C:\Users\Sarah\PycharmProjects\hangman\words.txt')
for word in wordFile:
words.append(word.rstrip(wordFile.readline()))
mysteryWord = random.choice(words)
while len(mysteryWord) <= 1:
mysteryWord = random.choice(words)
for letter in mysteryWord:
print("?", end = "")
print("\n")
def isWon():
#win conditions
count = 0
for letter in mysteryWord:
if letter in lettersGuessed:
count += 1
if count == len(mysteryWord):
isGuessed = 1
count = 0
while isGuessed == 0:
guess = input("Guess a letter \n")
if guess.upper() or guess.lower() in mysteryWord:
lettersGuessed.append(guess)
for letter in mysteryWord:
if letter in lettersGuessed:
print(letter, end ='')
else:
print("?", end = '')
print("\n")
count = 0
isWon()
if isGuessed == 1:
break
print("Congratulations, you correctly guessed ", mysteryWord)
isGuessed in your top-level code and isGuessed in isWon function are two different variables. A function is a separate namespace (else a function using a variable with common name like i would wreak havoc in other code).
This can be solved by a global declaration, but it's a very bad style. Same applies to variables like mysteryWord and lettersGuessed.
Instead, you should return the value from the isWon function:
def isWon(mysteryWord, lettersGuessed):
# do your counting...
return isGuessed
# main code
victory = False
while not victory:
# ...
victory = isWon(mysteryWord, lettersGuessed)
# you don't even need the if ... break statement
BTW your check for all letters being guessed can be made a one-liner:
def isWon(mysteryWord, lettersGuessed):
return set(lettersGuessed) == set(mysteryWord)
The immediate issue is that isWon() is not setting isGuessed regardless of the input. If you guess with the string "foo" then
lettersGuessed.append(guess)
will make lettersGuessed a list with one item, which is a string. I think what you were trying to do was
lettersGuessed.extend(list(guess))
Which will add each letter in guess to the lettersGuessed list.
Two points also worth mentioning:
isWon() will consider the game won if you guess an anagram of the word in in questions e.g. "oof" will be considered a correct solution if the word is "foo"
words.append(word.rstrip(wordFile.readline())) is reading every even line of the input file, and adding it to the words list after removing any characters it has in common with the following word. You want to do words.append(word.strip()) instead.
It's all about scope. isGuessed used in isWon() is define in the local scope. If you want to affect isGuessed declared in the global scope you will have to either pass it to isWon() as a parameter or use the global keyword before modifying isGuessed. See below:
def isWon():
#win conditions
count = 0
for letter in mysteryWord:
if letter in lettersGuessed:
count += 1
if count == len(mysteryWord):
global isGuessed
isGuessed = 1
Output with this change:
python3 test.py
?????
Guess a letter
1
?????
Guess a letter
2
?????
Guess a letter
3
????3
Guess a letter
4t
????3
Guess a letter
t
t??t3
Guess a letter
e
te?t3
Guess a letter
s
test3
Congratulations, you correctly guessed test3
You're trying to communicate using the global variable is_guessed, but the isWon function doesn't gave a global is_guessed line, so it is setting a variable local to isWon named is_guessed rather than the global variable.
My suggestion would be, rather than adding global is_guessed to isWon(), to return either True or False from isWon() (based on whether or not the user has won) and use that to end the loop.
Here is an alternative version of your code:
import random
words = []
with open(r'C:\Users\Sarah\PycharmProjects\hangman\words.txt') as wordFile:
for word in wordFile: # assuming one word per line
words.append(word.strip()) # iterating a file reads one line per iteration
mysteryWord = random.choice(words)
mysteryLetters = set(mysteryWord.lower())
lettersGuessed = set()
def isWon():
return mysteryLetters == (lettersGuessed & mysteryLetters)
while not isWon():
for letter in mysteryWord:
if letter in lettersGuessed:
print(letter, end ='')
else:
print("?", end = '')
print()
guess = input("Guess a letter \n")[:1].lower()
if guess in mysteryWord:
lettersGuessed.add(guess)
print("Congratulations, you correctly guessed ", mysteryWord)
Well, I know that my answer is a little late but here's my solution:
#!/usr/bin/env python3
import random
word_file_name = "/usr/share/dict/canadian-english"
with open(word_file_name) as word_file:
# I'm assuming your input file has one word per line and that
# you want to keep only words that has more than one letter
words = [word.rstrip() for word in word_file if len(word) > 1]
mystery_word = random.choice(words)
# Using sets helps to remove duplicate letters and eases membership tests
letters_mystery = set(mystery_word.upper())
letters_guessed = set()
not_guessed = True
while not_guessed:
# We create a list with all the letters found or not
letters_to_show = [letter if letter.upper() in letters_guessed else "?"
for letter in mystery_word]
# We join them before printing them
print("".join(letters_to_show), "\n")
guess_received = input("Guess a letter :")
if guess_received.strip():
# We only keep the first letter received
guess_kept = guess_received[0].upper()
if guess_kept in letters_mystery:
letters_guessed.add(guess_kept)
# We determine if we need to continue
not_guessed = letters_guessed != letters_mystery
print("Congratulations, you correctly guessed", mystery_word)
Key points:
A list comprehension is used to put the words in a list
Sets are used to keep a group of letters without duplicates.
A conditional expression is used to choose if a letter will be shown or or a ?
Truth testing can be used to simplify the verification of certain information
You are using expressions like isGuessed == 1 like in C where True equals 1 and False equals 0. In Python, a variable can be a boolean. You can use it directly in a if statement
How would I go about telling the user when they've got the correct letter in a list? Only way I know of is to insert the index, but that doesn't feel very flexible, especially when the words vary in length.
import random
possibleWords = [["apple"], ["grapefruit"], ["pear"]]
randomWord = random.choice(possibleWords)
anotherWord = ''.join(randomWord)
finalWord = list(anotherWord)
maxTries = list(range(0, 11))
attemptsMade = 0
triesLeft = 10
print("Hangman!")
print("\nYou got {} tries before he dies!".format(maxTries[10]))
print("There's {} possible letters.".format(len(finalWord)))
for tries in maxTries:
userChoice = input("> ")
if userChoice == finalWord[0]:
print("You got the first letter correct! It is {}.".format(finalWord[0]))
else:
print("Ouch! Wrong letter! {} tries remaining.".format(triesLeft))
attemptsMade += 1
triesLeft -= 1
Talking about characters in lists, or - what i think is more likely in your case - chars in words you can just check for
if userChoice in finalWord:
# [...] do stuff here
and further on use the index function to determine the position (or positions if multiple occurence).
finalWord.index(userChoice)
You could sure also go the way using index() function directly and work your way using the return values.
Use Python's "in" keyword to check if something is within a list/iterable.
if userChoice in finalWord:
Though for this, I'd just use regex or list comprehension to get the indexes while you are at it, since you might want them for the game.
char_indexes = [i for (i, l) in enumerate(finalWord) if l == userChoice]
if len(char_indexes):
Use a set for the letters that are in the word, and whenever the player guesses a letter, check if the letter is still in the set. If it’s not, it was a wrong letter; if it is, then remove that letter and just continue. If the set is empty at some point, then the player guessed all letters of the word.
Something to get you started:
def hangman (word):
letters = set(word.lower())
attempts = 5
while attempts > 0:
guess = input('Guess a character ')
if guess[0].lower() in letters:
print('That was correct!')
letters.remove(guess[0])
else:
print('That was not correct!')
attempts -= 1
if not letters:
print('You solved the word:', word)
return
hangman('grapefruit')