Related
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')
I want to print if the word appears, as well as how many times the word appears in the file. I can't get it to say anything other than this word appears 1 or 0 times in the file.
This problem occurs on line 26, print("It appears " + str(wordcount[word]) + " times")
specifically str(wordcount[word]). This probably simple question, but this is my first week of python so if anyone has an idea please share. Thanks!
I've tried putting wordcount[word], word_counter.__getitem__(wordcount[word]), and word_counter.__getitem__(wordcount)
import collections
file = open(r"C:\Users\Patrick Wu\Documents\1wordfreqtest.txt", "r")
if file.mode == "r":
contents = file.read()
word = input("Word to check for :")
wordcount = {}
"""this below is to remove difference between upper and lower cases as
well as punctuation"""
for word in contents.lower().split():
word = word.replace(".","")
word = word.replace(",","")
word = word.replace(":","")
word = word.replace("\"","")
word = word.replace("!","")
word = word.replace("“","")
word = word.replace("‘","")
word = word.replace("*","")
if word not in wordcount:
wordcount[word] = 1
else:
wordcount[word] += 1
word_counter = collections.Counter(wordcount)
if word in str(contents):
print("This word is in the file :)")
print("It appears " + str(wordcount[word]) + " times")
else:
print("This word isn't in the file")
The variable word is overwritten in the local scope, by the loop. So your input word is overwritten by the loop and you end up checking the count of the last word of the input file. Change the input word to be a different variable name than the word you're iterating through in the file.
You have a scoping problem, by using the same name "word" both in the input and in the for-loop.
I would suggest doing something like this:
word = input("Word to check for :")
with open('your_file.txt') as f:
raw = f.read()
num_appearences = raw.count(word)
print(f"The word {word} appears {num_appearences} times in the file")
You can use this code:
import collections
file = open("wordfreqtest.txt", "r")
if file.mode == "r":
contents = file.read().lower()
word = input("Word to check for :").lower()
times = 0
finish = 0
while finish==0:
if word in contents:
contents = contents[contents.find(word) + len(word):]
times += 1
else:
break
if times > 0:
print("This word is in the file :)")
print("It appears " + str(times) + " times")
else:
print("This word isn't in the file")
I have to count the number of times a given word appears in a given text file, this one being the Gettysburg Address. For some reason, it is not counting my input of 'nation' so the output looks as such:
'nation' is found 0 times in the file gettysburg.txt
Here is the code I have currently, could someone point out what I am doing incorrectly?
fname = input("Enter a file name to process:")
find = input("Enter a word to search for:")
text = open(fname, 'r').read()
def processone():
if text is not None:
words = text.lower().split()
return words
else:
return None
def count_word(tokens, token):
count = 0
for element in tokens:
word = element.replace(",", " ")
word = word.replace("."," ")
if word == token:
count += 1
return count
words = processone()
word = find
frequency = count_word(words, word)
print("'"+find+"'", "is found", str(frequency), "times in the file", fname)
My first function splits the file into a string and turns all letters in it lower case. The second one removes the punctuation and is supposed to count the word given in the input.
Taking my first coding class, if you see more flaws in my coding or improvements that could be made, as well as helping find the solution to my problem, feel free.
In the for loop in the count_word() function, you have a return statement at the end of the loop, which exits the function immediately, after only one loop iteration.
You probably want to move the return statement to be outside of the for loop.
as a starter I would suggest you to use print statements and see what variables are printing, that helps to breakdown the problem. For example, print word was showing only first word from the file, which would have explained the problem in your code.
def count_word(tokens, token):
count = 0
for element in tokens:
word = element.replace(",", " ")
word = word.replace("."," ")
print (word)
if word == token:
count += 1
return count
Enter a file name to process:gettysburg.txt
Enter a word to search for:nation
fourscore
'nation' is found 0 times in the file gettysburg.txt
Use code below:
fname = input("Enter a file name to process:")
find = input("Enter a word to search for:")
text = open(fname, 'r').read()
def processone():
if text is not None:
words = text.lower().split()
return words
else:
return None
def count_word(tokens, token):
count = 0
for element in tokens:
word = element.replace(",", " ")
word = word.replace("."," ")
if word == token:
count += 1
return count
words = processone()
word = find
frequency = count_word(words, word)
print("'"+find+"'", "is found", str(frequency), "times in the file", fname)
statement "return" go out statement "for"
I'm designing a program that looks through a list of words, and counts how many words only have the letters p, y, t, h, o and n in them.
So far, my code is:
def find_python(string, python):
"""searches for the letters 'python' in the word."""
for eachLetter in python:
if eachLetter not in string:
return False
return True
def main():
python = 'python'
how_many = 0
try:
fin = open('words.txt')#open the file
except:
print("No, no, file no here") #if file is not found
for eachLine in fin:
string = eachLine
find_python(string, python)
if find_python(string, python) == True:
how_many = how_many + 1#increment count if word found
print how_many#print out count
fin.close()#close the file
if __name__ == '__main__':
main()
However, my code is returning the incorrect number of words, for example, it will return the word 'xylophonist' if I put in the print statement for it because it has the letters python in it. What should I do so it will reject any word that has forbidden letters?
Correct your test function:
def find_python(string, python):
"""searches for the letters 'python' in the word.
return True, if string contains only letters from python.
"""
for eachLetter in string:
if eachLetter not in python:
return False
return True
Welcome to regular expressions:
import re
line = "hello python said the xylophonist in the ythoonp"
words = re.findall(r'\b[python]+\b',line)
print words
returns
['python', 'ythoonp']
If what you want is to find how many times the actual word python appears, then you should issue a re.findall(r'\bpython\b')
If you don't want to go this route, I suggest you return false if any of the letters of the string is NOT p,y,t,h,o or n.
from os import listdir
def diagy(letters,li):
return sum( any(c in letters for c in word) for word in li )
def main():
dir_search = 'the_dir_in_which\\to_find\\the_file\\'
filename = 'words.txt'
if filename in listdir(dir_search):
with open(dir_search + 'words.txt',) as f:
li = f.read().split()
for what in ('pythona','pyth','py','ame'):
print '%s %d' % (what, diagy(what,li))
else:
print("No, no, filename %r is not in %s" % (filename,dir_search))
if __name__ == '__main__':
main()