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
Related
I'm trying to program hangman and I run into a problem. In line 48 you see that I'm trying to copy the word that has been guessed so far. But the problem is when the program asks for the next letter. For example, if the user have guessed the letter 'h', it says h____ and the next turn I guess the letter e it says _e___ but I want it to say in this example he___.
word = 'hello'
guessed = False
guess_word = secret_word(word)
a = '____'
while guessed == False:
letter = ask_letter_from_user()
if is_the_letter_in_the_word(word, letter):
locatie_letter_in_woord = location_of_letter_in_word(word, letter)
a = replace(guess_word, locatie_letter_in_woord, letter)
print(a)
Seems like a and guess_word should actually be one variable.
You are passing guess_word to the replace function after every guessed letter, but you are never updating its value. Instead, you're updating a.
Get rid of a. Next time, give all your variables meaningful names, then you might realized that you have two variables for one purpose :-)
Instead of calling replace(guess_word, locatie_letter_in_woord, letter), simply do a = a[:locatie_letter_in_woord] + letter + a[locatie_letter_in_woord+1:]. This will prevent the previous letter from being overwritten.
Output:
wich letter do you want to try?: h
well done! you guessed the letter
youre guessed letters are: h
a: h___
wich letter do you want to try?: e
well done! you guessed the letter
youre guessed letters are: h,e
a: he__
Try this:
you can turn the word into a list and have each letter checked one by one
word = 'hello'
guessed = False
found = []
guess_word = secret_word(word)
while guessed == False:
guess= ask_letter_from_user()
if is_the_letter_in_the_word(word, letter):
print('well done! you guessed the letter')
word_as_list = list(guess_word)
indices = [i for i, letter in enumerate(word) if letter == guess]
for index in indices:
word_as_list[index] = letter
found.append(letter)
print('youre guessed letters are: ' + list_joining(found))
guess_word = "".join(word_as_list)
print(guess_word)
In line 47 in place of:
a = replace(guess_word, locatie_letter_in_woord, letter)
write
a = replace(a, locatie_letter_in_woord, letter)
wich letter do you want to try?: h
well done! you guessed the letter
youre guessed letters are: h
h___
wich letter do you want to try?: e
well done! you guessed the letter
youre guessed letters are: h,e
he__
wich letter do you want to try?:
Still, this program has a problem. When you are calling the function:
def location_of_letter_in_word(word, letter):
return word.find(letter)
Look carefully for the second l in hello; it will return the location of the first l.
I have been looking around to see if I could find something that could help, but nowhere has an answer for what I'm looking for. I have a Hangman game I'm doing for a final project in one of my classes, and all I need is to make it so if a word has a capital letter, you can input a lowercase letter for it. This is the code.
import random
import urllib.request
wp = "http://svnweb.freebsd.org/csrg/share/dict/words?view=co&content-
type=text/plain"
response = urllib.request.urlopen(wp)
long_txt = response.read().decode()
words = long_txt.splitlines()
###########
# Methods #
###########
def Run():
dashes1 = "-" * len(word)
dashes2 = "-" * len(word2)
used_letter = []
dashes = dashes1 + " " + dashes2
#dashes ="-" * len(secretWord)
guessesLeft = 6
while guessesLeft > -1 and not dashes == secretWord:
print(used_letter)
print(dashes)
print (str(guessesLeft))
guess = input("Guess:")
used_letter.append(guess)
if len(guess) != 1:
print ("Your guess must have exactly one character!")
elif guess in secretWord:
print ("That letter is in the secret word!")
dashes = updateDashes(secretWord, dashes, guess)
else:
print ("That letter is not in the secret word!")
guessesLeft -= 1
if guessesLeft < 0:
print ("You lose. The word was: " + str(secretWord))
print(dashes)
else:
print ("Congrats! You win! The word was: " + str(secretWord))
print(dashes)
def updateDashes(secret, cur_dash, rec_guess):
result = ""
for i in range(len(secret)):
if secret[i] == rec_guess:
result = result + rec_guess
else:
result = result + cur_dash[i]
return result
########
# Main #
########
word = random.choice(words)
word2 = random.choice(words)
#print(word)
#print(word2)
secretWord = word + " " + word2 # can comment out the + word2 to do one
word or add more above to create and combine more words will have to adjust
abouve in Run()
splitw = ([secretWord[i:i+1] for i in range(0, len(secretWord), 1)])
print (splitw)
Run()
any bit of help is appreciated. The website I'm using has a bunch of words that are being used for the words randomly generated. Some are capitalized, and I need to figure out how to let the input of a letter, say a capital A, accept a lowercase a and count for it.
you could compare after you converted everything to lowercase.
e.g. you could do
secretWord = word.lower() + " " + word2.lower() # that should make your secret all lowercase
for the input you should do the same:
guess = input("Guess:").lower()
after that it should not matter if it is upper or lower case. it should always match if the letter is the correct one.
hope that helps
Simply check everything in lowercase:
[...]
elif guess.lower() in secretWord.lower():
[...]
and so on.
I would just change this line:
while guessesLeft > -1 and not dashes == secretWord:
to:
while guessesLeft > -1 and not dashes.lower() == secretWord.lower():
This way you are always comparing lower-case representations of the user's input to the lower-case representation of your secretWord. Since this is the main loop for your game, you want to break out of this as soon as the user's guess matches your word regardless of case. Then later in your code, you will still check whether they had any guesses left, and print out their answer and the secret word as before.
No other changes required, I think.
You could just force all comparisons to be made in the same Case, such as lowercase.
Let’s say that your word is "Bacon". and someone enters "o".
That will be a match because quite obviously “o” equals “o” so you can cross that letter off the list of letters left to guess.
But in the case that someone enters “b” then b is NOT equal to “B”.
So why not just force all letters to be compared using the same case?
So your comparison will be like
elif guess.Lower() in secretWord.Lower()
My python is rusty as hell, but the idea here should do what you want to do
I am trying to create a simple Hangman game using Python. I have faced a problem which I can't solve and would be grateful to receive some tips from you guys.
Okay I will provide you with a sample of my code and below it, I will explain what my issue is:
# Here is my list of words
words = ['eight', 'nine', 'seven'...]
# Choosing a random word from the list
word = random.choice(words)
# Creating an empty list to store the result
secret_word = []
# Creating a count variable which I will use later to determinate if the player wins
count = 1
for letter in word:
secret_word.append('_')
secret_word[0] = word[0]
user_guess = input("Enter a letter: ")
# Here if the user guesses a letter
# We will place that letter on it's spot in our secret_word
while user_guess:
if user_guess in word:
print("Correct letter.")
# adding 1 to count for every guessed letter
count += 1
# replacing the '_' with the letter
for letter in range(len(word)):
if user_guess == word[letter]:
secret_word[letter] = word[letter]
# here I am checking if the user has won
if count == len(word):
print("You Win!")
I gave you only a part of my program because I don't think that there's a need for the entire code.
My problem is in the count variable. As you can see I am adding + 1 to the variable every time when the user guesses a correct letter so when the count variable = len(word) my little program will know that the user had won.
Anyways when one single letters appears twice in the word like for example the word SEVEN haves the letter E two times my count variable still goes up by only 1 so the user cant win in a case like this. I have absolutely no idea how to fix this and would be happy to receive some hints.
Thank you and please excuse my bad English and coding skills
You could increment count whenever you replace an underscore with the correct the letter. That way, count will be equal to the number of letters correct in the word so far.
To be more clear, move count += 1 to be in the if statement when you replace underscores with the actual letter.
One problem I see with this is you're giving the player the first letter and initializing count to 1. I don't know why you're doing this, but if the first letter occurs more than once, it won't be reflected in the word, and the player will still have to guess that letter anyways.
Move the count to where you are replacing the letter.
while user_guess:
if user_guess in word:
print("Correct letter.")
# replacing the '_' with the letter
for letter in range(len(word)):
if user_guess == word[letter]:
# adding 1 to count for every guessed letter
count += 1
secret_word[letter] = word[letter]
# here I am checking if the user has won
if count == len(word):
print("You Win!")
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')
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