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()
Related
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.
Disclaimer
To preface this, I am new to programming and even newer to python, so my knowledge of the mechanics of the interpreter is very limited.
Explanation I am currently writing a pythonic code that simulates a text-based game of cheating hangman, which means the program changes the word to evade the player from guessing the correct word using "word families." The game requires three files: hangman.py, play_hangman.py, and dictionary.txt. In hangman.py, I have created a hangman class that contains the self-instance of the class, several methods for producing the necessary objects, and a play method that uses these methods. Then, in play_hangman.py, it calls the hangman instance, and the play method, and puts the play method into a while loop that repeats as long as the player enters "yes" to keep playing.
The Problem I have called the methods into the play function for executing the game. However, it gives me an error saying:
- "12 positional arguments: 'word_length', 'num_guesses',
'remaining_words', 'remainingWords', 'letters_already_guessed',
'askForWordLength', 'printGameStats', 'askForPlayerGuess',
'wordStatus', 'printCountOfRemainingWords', 'retreiveRemainingWords',
and 'askForNumberOfGuesses' "
These are the twelve variables and methods I have called in the def play(): function. I have researched that I need to call an object of the class before the method, which I attempted to do, but does not work. I am not sure how to avoid the error.
HANGMAN.PY
import re
class Hangman:
# hangman self method
def hangman(self):
self.hangman = Hangman() # object of the Hangman class
def words(self):
with open('dictionary.txt') as file: # opens dictionary text file
file_lines = file.read().splitlines() # reads and splits each line
all_words = [] # empty list to contain all words
valid_words = [] # empty list to contain all valid words
for word in file_lines: # traverses all words in the file lines
if len(word) >= 3: # accepts word if it has at least 3 letters
all_words.append(word) # appends accepted word to list
# list of all invalid characters in python
CHARACTERS = ["~", "`", "!", "#", "#", "$", "%", "^", "&", "*", "(",
")", "-", "_", "=", "+", "[", "]", "{", "}", "|", "\","
"", "'", "?", "/", ">", ".", "<", ",", "", ";", ":"]
for i in CHARACTERS: # traverse list of invalids
for word in all_words:
if i not in word: # if invalid character is not in word
valid_words.append(word) # accept and append to list
return valid_words # return list of valid words
def askForWordLength(self, valid_words):
word_lengths = [] # empty list for possible word lengths
for word in valid_words: # traverse list of valid words
length = word.__len__() # record length of current word
if (length not in word_lengths):
word_lengths.append(length) # accept and append to list
word_lengths.sort()
# inform user of possible word lengths
print('The available word lengths are: ' + str(word_lengths[0]) + '-'
+ str(word_lengths[-1]))
print()
# have user choose from possible word lengths
while(1):
try:
length = int(input('Please enter the word length you want: '))
if (length in word_lengths):
return length
except ValueError:
print('Your input is invalid!. Please use a valid input!')
print()
def askForNumberOfGuesses(self):
while(1):
try:
num_guesses = int(input('Enter number of guesses you want: '))
if (num_guesses >= 3):
return num_guesses
except ValueError:
print('Your input is invalid!. Please use a valid input!')
print()
def wordStatus(self, length):
status = '-'
for i in range(0, length):
status += '-'
return
def remainingWords(self, lines, length):
words = []
for word in lines:
if (word.__len__() == length):
words.append(word)
return words
def printGameStats(self, letters_guessed, status, num_guesses):
print('Game Status: ' + str(status))
print()
print('Attempted Guesses' + str(letters_guessed))
print('Remaining Guesses' + str(num_guesses))
def askPlayerForGuess(self, letters_guessed):
letter = str(input('Guess a letter: ')).lower()
pattern = re.compile("^[a-z]{1}$")
invalid_guess = letter in letters_guessed or re.match(pattern, letter) == None
if (invalid_guess):
while (1):
print()
if (re.match(pattern, letter) == None):
print('Invalid guess. Please enter a correct character!')
if (letter in letters_guessed):
print('\nYou already guessed that letter' + letter)
letter = str(input('Please guess a letter: '))
valid_guess = letter not in letters_guessed and re.match(pattern, letter) != None
if (valid_guess):
return letter
return letter
def retrieveWordStatus(self, word_family, letters_already_guessed):
status = ''
for letter in word_family:
if (letter in letters_already_guessed):
status += letter
else:
status += '-'
return status
def retrieveRemainingWords(self, guess, num_guesses, remaining_words,
wordStatus, guesses_num, word_length,
createWordFamiliesDict,
findHighestCountWordFamily,
generateListOfWords):
word_families = createWordFamiliesDict(remaining_words, guess)
family_return = wordStatus(word_length)
avoid_guess = num_guesses == 0 and family_return in word_families
if (avoid_guess):
family_return = wordStatus(word_length)
else:
family_return = findHighestCountWordFamily(word_families)
words = generateListOfWords(remaining_words, guess, family_return)
return words
def createWordFamiliesDict(self, remainingWords, guess):
wordFamilies = dict()
for word in remainingWords:
status = ''
for letter in word:
if (letter == guess):
status += guess
else:
status += '-'
if (status not in wordFamilies):
wordFamilies[status] = 1
else:
wordFamilies[status] = wordFamilies[status] + 1
return wordFamilies
def generateListOfWords(self, remainingWords, guess, familyToReturn):
words = []
for word in remainingWords:
word_family = ''
for letter in word:
if (letter == guess):
word_family += guess
else:
word_family += '-'
if (word_family == familyToReturn):
words.append(word)
return words
def findHighestCountWordFamily(self, wordFamilies):
familyToReturn = ''
maxCount = 0
for word_family in wordFamilies:
if wordFamilies[word_family] > maxCount:
maxCount = wordFamilies[word_family]
familyToReturn = word_family
return familyToReturn
def printCountOfRemainingWords(self, remainingWords):
show_remain_words = str(input('Want to view the remaining words?: '))
if (show_remain_words == 'yes'):
print('Remaining words: ' + str(len(remainingWords)))
else:
print()
def play(self, askForWordLength, askForNumberOfGuesses, remainingWords,
words, wordStatus, printCountOfRemainingWords, printGameStats,
askPlayerForGuess, retrieveRemainingWords):
MODE = 1
openSession = 1
while (openSession == 1):
word_length = askForWordLength(words)
num_guesses = askForNumberOfGuesses()
wordStatus = wordStatus(word_length)
letters_already_guessed = []
print()
game_over = 0
while (game_over == 0):
if (MODE == 1):
printCountOfRemainingWords(remainingWords)
printGameStats(remainingWords, letters_already_guessed,
num_guesses, wordStatus)
guess = askPlayerForGuess(letters_already_guessed)
letters_already_guessed.append(guess)
num_guesses -= 1
remainingWords = retrieveRemainingWords(guess, remainingWords,
num_guesses, word_length)
wordStatus = wordStatus(remainingWords[0], letters_already_guessed)
print()
if (guess in wordStatus):
num_guesses += 1
if ('-' not in wordStatus):
game_over = 1
print('Congratulations! You won!')
print('Your word was: ' + wordStatus)
if (num_guesses == 0 and game_over == 0):
game_over = 1
print('Haha! You Lose')
print('Your word was: ' + remainingWords[0])
print('Thanks for playing Hangman!')
Self as First Parameter
When creating functions inside classes, the first parameter should be 'self', as you've done in the function askForNumberOfGuesses. This happens because, when calling functions from an object, Python passes the object to 'self' so the method's logic can access its data.
When calling functions such as "play" (in which you haven't used the self parameter in the declaration), the first parameter will be handled as the self, even though it has a different name. So, actually, your code expects the variable 'words' to be a Hangman object.
Static Functions
If the function needs to know information about the object, you need to add the 'self' as the first parameter.
However, if your method does not use any data declared inside the Hangman class (such as self.hangman that you've created inside init) you can just add a "#staticmethod" to the line before the function definition. This way, Python will know that you don't need self and that the first parameter is not the object.
Notice that, if you want to use a static method, you can use the class itself to call the method. You don't even need to create an object:
Hangman.static_function_name(function_parameters) # calls static function
hangman_object = Hangman()
hangman_object.non_static_function_name(function_parameters) # calls non-static function
hangman_object.static_function_name(function_parameters) # calls static function using object (there is no problem)
Design Considerations
Also, I believe you should change your init implementation and use 'self' to store data that you use in more than one function, instead of passing arguments to all the functions and returning them.
Also note that, in order to create a Hangman object, you need to use ():
self.hangman = Hangman # reference to the Hangman class
self.hangman = Hangman() # Object of the Hangman class
However, if you create a Hangman object inside init, it will call init again and this will go on "forever". So, you shouldn't create a Hangman object inside it's init method. I believe you should create the Hangman object in play_hangman.py file and call the play method from there.
In my opinion, init should create all the data you need as self.<var_name>, load should prepare words or whatever you need using self.<functions_you_create> and play should just start the game. play_hangman.py shouldn't need to know all these parameters.
hangman.py
class GameClass:
def __init__(self):
self.words = list()
# TODO: Implement adding other game data
def load(self):
# TODO: Implement logic
self.words.append('country')
pass
def play(self):
# TODO: Implement loop logic
print(self.words)
# while(True):
# break
pass
play_hangman.py
game = GameClass()
game.load()
game.play()
IDE
Use a good IDE so it will remember you to add self if you are not used yet.
None of those methods have self as their first argument.
And that is what is used to define class methods and give access to attributes.
So, adding that "self" keyword is the first step to make your code work.
I would recommend installing a linter, it automatically identifies this kind of error for you:
I am building hangman in python.
The first two functions I have generates random word and its definition from the API which is working just perfectly.
import requests
import json
def generate_random_word():
random_word_url = 'https://random-word-form.herokuapp.com/random/adjective'
random_word_url_response = requests.get(random_word_url)
return json.loads(random_word_url_response.text)[0]
def get_random_word_definition():
random_word = generate_random_word()
definition_api_url = 'https://api.dictionaryapi.dev/api/v2/entries/en/' + random_word
definition_api_url_response = requests.get(definition_api_url)
return json.loads(definition_api_url_response.text)[0]['meanings'][0]['definitions'][0]['definition'],random_word
My next function is this:
def modify_word(word):
new_word = list(word)
for i in range(len(new_word)):
new_word[i] = '_'
return "".join(new_word)
As you see it iterates other listed word and changes its characters to "_".
Now to the issue:
def validate_word(original,guess,hidden_word):
listify_original = list(original)
listify_hidden = list(hidden_word)
for char_place,char in enumerate(listify_original):
if(char == guess):
listify_hidden[char_place] = char
print('found it',"".join(listify_hidden))
This function is accepting 3 parameters: 1.Original word which was generated 2.Players guess 3.Hidden word which is generated through modify_word function above
Whenever user gets correct guess the hidden latter in string is replaced with this letter as expected so if the word is "colour" and user types c the function will print out string like this c_____ as it should. But issue comes when user guesses another latter. in this case whole string is being re-generated and letter guessed before last one just disappears. so for example if user guessed "l" function will print out __l___ instead of c__l___.
This is the main function in which I call validate_word:
def hang_man_guess_game():
generate_word = get_random_word_definition()
definition = generate_word[0]
word = generate_word[1]
amount_of_tries = 6
print('Welcome to the hang man game!')
print('The length of the word is: ',len(word))
print('The defition of word is: ',definition)
print('Amount of health: ',amount_of_tries)
print('Health gets lesser on each wrong guess!')
print('You can write as many latters as you want!')
print('Good luck player!')
hidden_word = modify_word(word)
while(amount_of_tries != 0):
print('Word: ', hidden_word)
user_guess = input('Enter your guess: ')
validate_word(word,user_guess,hidden_word)
# print('Good job')
hang_man_guess_game()
Sample input for word is: hopeful
Any suggestions please?
You are not updating hidden_word as the user guesses a letter right. You can do this by returning the updated value in validate_word()
def validate_word(original,guess,hidden_word):
listify_original = list(original)
listify_hidden = list(hidden_word)
current_word = hidden_word
for char_place,char in enumerate(listify_original):
if(char == guess):
listify_hidden[char_place] = char
current_word = "".join(listify_hidden)
print('Found it', current_word)
return current_word
Similarly in the while loop in hang_man_guess_game(), the returned value now becomes the updated hidden_word
while(amount_of_tries != 0):
print('Word: ', hidden_word)
user_guess = input('Enter your guess: ')
hidden_word = validate_word(word,user_guess,hidden_word)
Output
Enter your guess: q
Word: _____
Enter your guess: c
Found it c____
Word: c____
Enter your guess: r
Found it c___r
Word: c___r
Enter your guess: o
Found it co__r
Found it co_or
Word: co_or
Enter your guess: l
Found it color
Word: color
You should update your hidden_word with based on the function validate_word. You should return "".join(listify_hidden). You can then move you print function in the main, this is in my opinion good practice to make sure each function only has one task (i.e. validate the word and printing is another task).
In your validate_word function, the hidden_word variable is never modified. At the beginning, you create a new list, called listify_hidden, but when you change a letter in this variable, it does not propagate to the original hidden_word string, outside of the function.
One way to fix this would be to make your validate_word return the new hidden string, with the discovered letters.
So you'll have to make the function return the updated hidden word:
def validate_word(original,guess,hidden_word):
listify_original = list(original)
listify_hidden = list(hidden_word)
for char_place,char in enumerate(listify_original):
if(char == guess):
listify_hidden[char_place] = char
print('found it',"".join(listify_hidden)
)
return "".join(listify_hidden)
And then in your while loop:
while(amount_of_tries != 0):
print('Word: ', hidden_word)
user_guess = input('Enter your guess: ')
hidden_word = validate_word(word,user_guess,hidden_word) <-- update the hidden word
# print('Good job')
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
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!