Python GUI Word Guessing Game - python

I'm trying to build a GUI word guess game using tkinter. I'm receiving an UnboundLocalError: local variable 'guesses' referenced before assignment for the line 'if guess.get() in guesses:'.
I have this at the top of my code:
global guesses
guesses = []
And this is the function that is throwing the error:
def play():
while remaining.get() > 0:
if guess.get().isalpha() == False or len(guess.get()) != 1:
output.set('Invalid input. Please enter a letter from a-z.')
else:
if guess.get() in guesses:
output.set('That letter has already been guessed!')
else:
if guess.get() not in secret_word:
output.set('That letter does not occur in the secret word.')
else:
output.set('That is a good guess! ' + str(guess.get()) + ' occurs ' + \
str(countOccurences(str(secret_word), guess.get())) + ' time(s) in the secret word')
guesses += guess.get()
remaining.set(remaining.get() - 1)
if '_' not in getHint(secret_word, guesses):
result.set('Congratulations! You guessed the secret word: ' + str(secret_word))
break
if remaining == 0:
result = 'Sorry, the secret word was: ' + str(secret_word)
I've changed where guesses is scope-wise, I've redefined it multiple times and nothing has worked. I'm not sure what else to do to prevent this error.
Any help would be extremely appreciated. Thanks!

Use the global keyword in the methods that need to use the global variable.
That is, put global guesses inside the play() method, instead of outside it.
guesses = []
...
def play():
global guesses
while remaining.get() > 0:
if guess.get().isalpha() == False or len(guess.get()) != 1:
....
Don't get too comfortable using this though. As you get more experienced with Python, at some point you will probably want to use classes to store and access variables that need to be shared between methods.
Finally, for future questions here, please consider using a title that pinpoints the actual problem instead of your broader intentions. You'll be more likely to receive helpful answers that way!

Related

why does my hangman code keep coming back with an error saying " IndexError: list index out of range"

here is my code
my assignment is to create a hangman game that picks word from an outside text file, pick a word, close the file, then start the game. I think the problem is with opening the text file. I'm not sure if I'm saving it in the right spot or whatever. pls help.
import random
#extract a random word from a text file
def word_selected(fname):
word_file = open('hangman list.txt','r+')
secret_word = random.choice(word_file.read().split())
word_file.close()
return secret_word
secret_word = word_selected('hangman list.txt')
print(secret_word)
#Display randomly chosen word in dash:
def word_selected_dashed():
word_selected_dashed = []
for i in range(len(secret_word)):
word_selected_dashed.append('_')
return ''.join(word_selected_dashed)
word_selected_dashed = word_selected_dashed()
print(word_selected_dashed)
trials = 5
gussed_word = list(word_selected_dashed)
while trials > 0:
if ''.join(gussed_word) == secret_word:
print("Congraluation, you have gussed the correct word")
break
print('you have got '+ str(trials)+ ' wrong tries ')
user_guseed_letter = input('Guess a letter >>>>> \n')
if user_guseed_letter in secret_word:
print('Correct!')
for i in range(len(secret_word)):
if list(secret_word)[i] == user_guseed_letter:
gussed_word[i] = user_guseed_letter
print(''.join(gussed_word))
elif user_guseed_letter not in secret_word:
print('wrong!')
trials -= 1
hang = display_hangman(tries=(5-trials))
print(hang)
if trials == 0 :
print('you have ran out of trials')
One issue I notice is in the word_selected function, you have an input of fname, then use a specific file name within the function. Instead of writing out the actual file name, use the variable you used in the function name (ie fname).
Also, make sure the file is in the same directory (folder) of your Python file. If its in a different directory, you can also specify the whole file path as the file name.

Trying to make a hangman game. Can't get my playerLives variable to decrease correctly

I'm trying to make a hangman game. So far, I've got it working pretty effectively when it is cycling through the secret word and returning the result. I had my playerLives variable decreasing correctly every time an incorrect answer was submitted too, but when trying to tidy my code up it has stopped working and I can't for the life of me get it to reduce in value at all.
def checkSecretWord(secretWord, guess, playerLives):
letterConfirm = False
letterDeny = False
for i in range(0, len(secretWord)):
if secretWord[i] == guess:
letterConfirm = True
if guess not in secretWord:
letterDeny = True
if letterConfirm:
print("Correct Guess!")
if letterDeny:
print("Incorrect Guess! Try again!")
playerLives -= 1
return playerLives
def gameRunning():
gameActive = True
secretWord = getSecretWord()
guessed_letters = []
playerLives = 6
while gameActive:
makeBoard(playerLives, secretWord, guessed_letters)
guess = checkLetter(guessed_letters)
checkSecretWord(secretWord, guess, playerLives)
print("You have {} lives reminaing".format(playerLives))
the checkSecretWord() function searches through the secret word for the letter that has been input by the user ('guess'). It correctly returns "Incorrect Guess! Try again!" when letterConfirm is true (that is to say, the letter does not match one in the secret word), but it doesn't seem to decrease the value of the variable 'playerLives' by 1 like I want it to.
EDIT: Just to cover my own back - I know "return playerLives == playerLives - 1" makes zero sense and is just awfully written but it was my last desperate attempt before I resorted to asking for help.
EDIT 2: Thanks for the help everyone. Problem sorted and lessons learnt. Cheers.
you've got a few issues here
your first function is empty. let's fix the tabs on that
def checkSecretWord(secretWord, guess, playerLives):
letterConfirm = False
letterDeny = False
for i in range(0, len(secretWord)):
if secretWord[i] == guess:
letterConfirm = True
if guess not in secretWord:
letterDeny = True
if letterConfirm:
print("Correct Guess!")
return playerLives #THIS IS THE BIG CHANGE
if letterDeny:
print("Incorrect Guess! Try again!")
return playerLives - 1 #THIS IS THE BIG CHANGE
you're treating playerlives like a global variable, you need to rewrite this so that we can increment playerlives properly
def gameRunning():
gameActive = True
secretWord = getSecretWord()
guessed_letters = []
playerLives = 6
while gameActive:
makeBoard(playerLives, secretWord, guessed_letters)
guess = checkLetter(guessed_letters)
playerLives = checkSecretWord(secretWord, guess, playerLives) # see "the big changes" I made earlier
print("You have {} lives reminaing".format(playerLives))

How would I write a program that can call a variable from another def?

So I set out to make a simple game of hangman and everything worked fine, the whole code worked but it lacked the ability to allow the user to replay when the game is over. Thus I set out to put all the code I have written in various functions. So that I can call the functions when they are required (I thought it was the most logical way to allow replay-ability). Various problems followed but one stood out.
The main culprit (I think) is that I could not successfully get a value to update globally. I've read similar questions on the site but could not successfully adapt it to my case. I have a sample code to show what exactly I mean:
def GameMode():
choice = input('Play alone or play with friends? A F : ')
choice = choice.upper()
if choice == 'A':
wordslotmachine = ['stand','emerald','splash']
word = random.choice(wordslotmachine)
word = word.upper()
Rules()
elif choice == 'F':
word = input('Enter your word for your friends to guess: ')
word = word.upper()
Rules()
else:
choice = input('Please enter A or F: ')
choice = choice.upper()
I would need the program to remember what the value of "word" is and use this word in another method (this method is ran by another method showed below "Rules()"):
def MainGame():
guesses = ''
turns = 10
underscore = 0
seconds = 1
checker = 0
cheaterchance = 5
while turns > 0: #check if the turns are more than zero
for char in word: # for every character in secret_word
if char in guesses: # see if the character is in the players guess
print(char+' ', end='')
else:
print('_ ', end='')# if not found, print a dash
underscore += 1
if underscore == 0:
print(': You got it!')
Wait()
NewGame()
break
#A block of if's to check for cheating
if guess not in word:
print('Your guesses so far: '+guesses)
turns -= 1
if turns == 0:
break
else:
print('')
print('Try again. You have',turns,'more guesses')
print('Delayed chance to answer by',seconds,'seconds')
counter = 1
print(0,'.. ', end='')
while counter < seconds:
time.sleep(1)
print(counter,'.. ', end='')
counter += 1
if counter == seconds:
time.sleep(1)
print(counter,'.. done!', end='')
print('')
print('')
seconds += 1
underscore = 0
else:
print('Your guesses so far: '+guesses)
underscore = 0
#The else portion of the code to check for cheating
I have tried defining "word" outside of the function. Doing this doesn't fix the problem, GameMode() will not successfully update the value of "word". And whatever the value of "word" defined outside of the function will be called and used by MainGame(). However doing this shows another problem.
That being, the code that previously worked (it successfully read the input and correctly updated the game status) now does not work. Even if the correct letter is entered by the user, the program reads the input as incorrect.
These are the two problems I have faced so far and have yet to find a way to overcome them.
Note: I have successfully created a way to make the game replay-able by putting the entire original code (without the functions) inside a while loop. However I would still very much like to know how I can get the code to work using functions.
Edit: This is the function for Rules():
def Rules():
#Bunch of prints to explain the rules
MainGame()
print('Start guessing...')
Wait() is just a delay function with a countdown.
Global vs. Local variables.
You can reference and use a global variable from within a function, but you cannot change it.
It's bad practice, but you CAN declare a variable within your function to be global and then changes to it inside your function will apply to the variable of the same name globally.
HOWEVER, what I suggest is to return the word at the end of your function.
def whatever_function(thisword):
do some stuff
return word
new_word = whatever_function(thisword)
Functions can, and usually should, return values. Make GameMode() return the word to the caller;
def GameMode():
choice = input('Play alone or play with friends? A F : ')
choice = choice.upper()
if choice == 'A':
wordslotmachine = ['stand','emerald','splash']
word = random.choice(wordslotmachine)
word = word.upper()
Rules() #ignore this
elif choice == 'F':
word = input('Enter your word for your friends to guess: ')
word = word.upper()
Rules() #ignore this
else:
choice = input('Please enter A or F: ')
choice = choice.upper()
return word
From the main call GameMode and save the word;
def MainGame():
guesses = ''
turns = 10
underscore = 0
seconds = 1
checker = 0
cheaterchance = 5
word = GameMode() # add this here
You almost certainly want to use a class with instance variables
Contrived example:
class Hangman:
def __init__(self):
print("Starting hangman")
def mode(self):
# ...
self.word = 'whatever'
def play(self):
print("Look i have access to word", self.word)
if __name__ == '__main__':
hm = Hangman()
hm.mode()
hm.play() # may be what you want to put in a while loop
To access a global variable from inside a function, you have to tell python that it is global:
my_global_var = 1
def some_func():
global my_global_var
You have to use global keyword in every method that the global variable is being used or python will think you are defining/ using a local variable
Having said that, you should avoid globals as a coding practise.
global word #probably define this after imports.
def GameMode():
global word #add this
choice = input('Play alone or play with friends? A F : ')
choice = choice.upper()
if choice == 'A':
wordslotmachine = ['stand','emerald','splash']
word = random.choice(wordslotmachine)
word = word.upper()
Rules() #ignore this
elif choice == 'F':
word = input('Enter your word for your friends to guess: ')
word = word.upper()
Rules() #ignore this
else:
choice = input('Please enter A or F: ')
choice = choice.upper()
def MainGame():
guesses = ''
turns = 10
underscore = 0
seconds = 1
checker = 0
cheaterchance = 5
global word # add this here
use the code
global word
above the def or let the def return the value of word so it is stored in a variable outside the def

Python: Variable scope and parameters issue

No global please.
Here is my current code:
import random
def getSecretPhrase():
secretPhrase = "I like trains,Drop the bass,YouTube is funny,Ebola is dangerous,Python is cool,PHS is 116 years old,I am a person,Sleep is overrated,Programming is fun".split(",")
x = random.randint(1,10)
correctPhrase = secretPhrase[x-1]
print("Please guess a letter.")
correctLetters = input().lower()
return correctPhrase
def createPhrase():
blanks = '_' * len(correctPhrase)
print(blanks)
print(correctPhrase)
def main():
getSecretPhrase()
createPhrase()
for i in range(len(secretPhrase)):
if correctPhrase[i] in correctLetters:
blanks = blanks[:i]+correctPhrase[i] + blanks[i+1:]
for letter in blanks:
print(letter, end = ' ')
main()
The intended output should be:
Please guess a letter.
>>> e
#assuming the phrase is I like trains
_ - _ _ _ e - _ _ _ _ _ _
However, I get
Traceback (most recent call last):
File "C:\Documents and Settings\Alex\My Documents\Downloads\az_wheeloffortune12.py", line 27, in <module>
main()
File "C:\Documents and Settings\Alex\My Documents\Downloads\az_wheeloffortune12.py", line 21, in main
createPhrase()
File "C:\Documents and Settings\Alex\My Documents\Downloads\az_wheeloffortune12.py", line 16, in createPhrase
blanks = '_' * len(correctPhrase)
NameError: name 'correctPhrase' is not defined
I am trying to use the variable correctPhrase in multiple places. This may be a scope issue.
I made alot of comments here, hopefully you can follow:
import random
def get_secret_phrase():
# Instead of using one long string and creating a list from it, just make a list!
phrases = ["I like trains","Drop the bass","YouTube is funny","Ebola is dangerous","Python is cool","PHS is 116 years old","I am a person","Sleep is overrated","Programming is fun"]
# We have a list, we can get its length by using `len()` so there is no need to hardcode a value of 10
# We also know that lists are 0-indexed, so we can tell `randint()` to get an int between 0 and the length of the list - 1
index = random.randint(0, len(phrases) - 1)
# Now we get the phrase at the random `index` we just created
secret_phrase = phrases[index]
# An alternative, more pythonic way to do the previous 2 lines is to use `random.choice()` which does the same logic
#secret_phrase = random.choice(phrases)
# Finally, return `secret_phrase` and make it lowercase so we can use it in another method
return secret_phrase.lower()
def get_user_guess():
# We want to make this separate from `get_secret_phrase()` because this will be called every time we want the user to guess
print("What is your guess? ")
# Assign the user input to a variable
guess = input().lower()
print("You guessed: {}".format(guess))
return guess
def get_phrase_progress(secret_phrase, guessed_letters):
# We want to generate a string that will represent how many letters the user has gotten correctly
# We will call this every time so that it is always accurate based on what was guessed
blanks = ''
for letter in secret_phrase:
if letter == ' ' or letter in guessed_letters:
blanks = blanks + letter
else:
blanks = blanks + '_'
return blanks
def get_phrase_progress_pythonic(secret_phrase, guessed_letters):
# Here's a really pythonic way to do this
return ''.join(letter if letter == ' ' or letter in guessed_letters else '_' for letter in secret_phrase)
def main():
guessed_letters = list() # First, create a `list` to store the guessed letters
secret_phrase = get_secret_phrase() # Next, call `get_secret_phrase()` and assign it's value to a variable in the scope of `main()`
# Uncomment the following to see that the pythonic version returns the same thing
# print(get_phrase_progress(secret_phrase, guessed_letters))
# print(get_phrase_progress_pythonic(secret_phrase, guessed_letters))
guessed_phrase = get_phrase_progress(secret_phrase, guessed_letters)
# We want to loop until the user guesses the whole phrase
while guessed_phrase != secret_phrase:
print(guessed_phrase)
# We call `get_user_guess()` to prompt the user to enter a letter
letter = get_user_guess()
# Next we want to make sure they entered just 1 letter
# We can use the built in `str.isalpha()` to check if the string entered by the user is alphabetical and at least 1 character long
if not letter.isalpha():
# However, we only want them to guess a single letter, so we also check that the length == 1
if len(letter) != 1:
print('You need to guess something...')
else:
print('You can only guess a single letter, you tried to guess: {}'.format(letter))
# Next we check if the user already guessed the letter, if so, tell them!
elif letter in guessed_letters:
print('You already guessed {}'.format(letter))
else:
guessed_letters.append(letter)
# Here we calculate `guessed_phrase` every loop to make sure it always takes into account what letters have been guessed
guessed_phrase = get_phrase_progress(secret_phrase, guessed_letters)
# If the user escapes the while loop, they won, they guessed the secret phrase
# We can calculate a score based on how many letters they guessed, this isn't great but it works for a simple scoring method
print('You won! It took you {} guesses to guess {}'.format(len(guessed_letters), secret_phrase))
main()
I made a few assumptions writing this. The first is that you want to make this some sort of hangman like game where they keep guessing until they get it. To that end I've made a class that does it for you. The code is commented with explanations
import random
import string
class WordGuesser(object):
## We store the possible secret phrases as a class variable and make sure they're lower case
secretPhrase = map(string.lower, ['I like trains', 'Drop the bass',
'YouTube is funny', 'Ebola is dangerous',
'Python is cool', 'PHS is 116 years old',
'I am a person', 'Sleep is overrated',
'Programming is fun'])
def __init__(self):
## We pick a secret phrase
self.correctPhrase = WordGuesser.secretPhrase[random.randint(1,10)-1]
self.length = len(self.correctPhrase)
## We pick our 'blank' phrase
self.blankPhrase = ''.join(('_' if letter != ' ' else ' ')
for letter in self.correctPhrase)
def gameLoop(self):
print self.blankPhrase
## as long as our blank phrase is wrong we continue
while self.blankPhrase != self.correctPhrase:
guess = raw_input("Please guess a letter.").lower() ## Their guess
temp = ""
for i in range(self.length): ## we fill in the word
if self.correctPhrase[i] == guess:
temp += guess
else:
temp += self.blankPhrase[i]
self.blankPhrase = temp
print self.blankPhrase ## display what they have so far
def main():
game = WordGuesser()
game.gameLoop()
if __name__ == '__main__':
main()
When run it'll look like this
>>>
___________ __ ___
Please guess a letter.a
_____a_____ __ ___
Please guess a letter.e
_____a_____ __ ___
Please guess a letter.i
_____a__i__ i_ ___
Please guess a letter.o
__o__a__i__ i_ ___
Please guess a letter.u
__o__a__i__ i_ _u_
Please guess a letter.p
p_o__a__i__ i_ _u_
Please guess a letter.m
p_o__ammi__ i_ _u_
Please guess a letter.y
p_o__ammi__ i_ _u_
Please guess a letter.e
p_o__ammi__ i_ _u_
Please guess a letter.l
p_o__ammi__ i_ _u_
Please guess a letter.s
p_o__ammi__ is _u_
Please guess a letter.r
pro_rammi__ is _u_
Please guess a letter.g
programmi_g is _u_
Please guess a letter.n
programming is _un
Please guess a letter.f
programming is fun
>>>
Since this is clearly homework, I decided to try and implement it in as few lines of code as possible, just for fun. Got it down to 4...
import random
phrase, board, guesses, tries = random.choice(["I like trains", "Drop the bass", "YouTube is funny", "Ebola is dangerous", "Python is cool", "PHS is 116 years old", "I am a person", "Sleep is overrated", "Programming is fun"]).lower(), lambda guesses: ' '.join(letter if letter in guesses else "-" if letter == " " else "_" for letter in phrase), set(), 0
while board(guesses) != board(phrase.replace(" ", "-")): print board(guesses); guesses.add(raw_input("What is your guess? ").lower()); tries += 1
print "YOU WIN! It took you {} tries and {} unique guesses to guess {}".format(tries, len(guesses), board(guesses))
You can try it out here: http://repl.it/4cg
So to answer your original question...
import random
def getSecretPhrase():
secretPhrase = "I like trains,Drop the bass,YouTube is funny,Ebola is dangerous,Python is cool,PHS is 116 years old,I am a person,Sleep is overrated,Programming is fun".split(",")
x = random.randint(1,10)
correctPhrase = secretPhrase[x-1]
print("Please guess a letter.")
correctLetters = input().lower()
return correctPhrase
def createPhrase(correctPhraseArgument): # this method accepts an argument and stores it in a variable named `correctPhraseArgument`
blanks = '_' * len(correctPhraseArgument)
print(blanks)
print(correctPhraseArgument)
def main():
secretPhrase = getSecretPhrase() # assign the returned value to a variable in this scope
createPhrase(secretPhrase) # pass the variable to this method
for i in range(len(secretPhrase)):
if correctPhrase[i] in correctLetters:
blanks = blanks[:i]+correctPhrase[i] + blanks[i+1:]
for letter in blanks:
print(letter, end = ' ')
main()
This will still have issues, but I want to show you what is causing the error you mentioned...
The variables that are declared inside of your methods live inside those methods. To access them from other methods WITHOUT using global variables, you need to pass them as parameters and return the values. Then, you need to assign those returned values to new variables.
how about:
import random
def getSecretPhrase():
secretPhrase = "I like trains,Drop the bass,YouTube is funny,Ebola is dangerous,Python is cool,PHS is 116 years old,I am a person,Sleep is overrated,Programming is fun".split(",")
x = random.randint(1,10)
correctPhrase = secretPhrase[x-1]
print("Please guess a letter.")
correctLetters = input().lower()
return correctPhrase
def createPhrase(correctPhrase):
blanks = '_' * len(correctPhrase)
print(blanks)
print(correctPhrase)
def main():
secretPhrase = getSecretPhrase()
createPhrase(secretPhrase)
for i in range(len(secretPhrase)):
if correctPhrase[i] in correctLetters:
blanks = blanks[:i]+correctPhrase[i] + blanks[i+1:]
for letter in blanks:
print(letter, end = ' ')
main()
Well, not only "correctLetters", there are a lot of variable scope issue in your code.
correctLetters is defined in getSecretPhrase() and only alive in this method. So, the method() doesn't know what is correctLetters, that's why you see this exception.
There are a lot of ways to solve this issue
First is define the "correctLetters" outside the method, and using keyword global
Second you can return the variable, such as:
def createPhrase():
blanks = '_' * len(correctPhrase)
return blanks
so in main(), you can get the return value as:
blanks = createPhrase()

I get an error that says that the beginning is not defined. how do I fix it?

I am trying to create a hangman game.
Python keeps telling me that get_word is not defined, but I'm not sure really as to why it says that.
get_word():
dictionary = ["number","one","hyper","active","knuckle","head","ninja"]
import random
process_guess():
while keep_playing:
dictionary=["number","one","hyper","active","knuckle","head","ninja"]
word=choice(dictionary)
word_len=len(word)
guesses=word_len * ['_']
max_incorrect=7
alphabet="abcdefghijklmnopqrstuvxyz"
letters_tried=""
number_guesses=0
letters_correct=0
incorrect_guesses=0
print_game_rules(max_incorrect,word_len)
while (incorrect_guesses != max_incorrect) and (letters_correct != word_len):
clues()
letter=get_letter()
if len(letter)==1 and letter.isalpha():
if letters_tried.find(letter) != -1:
print ("letter has already been used", letter)
else:
letters_tried = letters_tried + letter
first_index=word.find(letter)
if first_index == -1:
incorrect_guesses= incorrect_guesses +1
print ("The",letter,"is not the unknown word.")
else:
print("The",letter,"is in the unknown word.")
letters_correct=letters_correct+1
for i in range(word_len):
if letter == word[i]:
guesses[i] = letter
else:
print ("Please guess a single letter in the alphabet.")
print("victory:",no guesses ')
play():
1 = yes
0 = no
print(("play again? (1-yes, 0-no")):
print("get the current guess letter:", current)
main()
In Python, new functions are defined using the def keyword. So do:
def get_word():
and everywhere else you want to define a function.

Categories

Resources