2
\$\begingroup\$

I've stopped programming in Python for a long time now, someone recommended to me that the best way to get into it is by trying to code a game of Hangman, and that's what I did. Or tried to, at least.

import random


class Game:
    def __init__(self, game_word):
        self.game_word = game_word


class Player:
    def __init__(self, guessed_word, guess_count, used_letters):
        self.guess_count = guess_count
        self.guessed_word = guessed_word
        self.used_letters = used_letters


def get_word():  # getting a word from a txt file with a list of single words
    word_file = open("words.txt")
    game_word = random.choice(word_file.readlines())
    game_word = game_word.strip().lower()
    word_file.close()
    return game_word


def user_guess(game, player):
    print("Enter a letter: ")
    letter = input().lower()
    if check_input(letter, player.used_letters) == 0:  # if check_input is 0 then there are no errors in player input
        if letter in game.game_word:  # determining if the guessed letter in the word
            for i in range(0, len(game.game_word)):
                if letter == game.game_word[i]:
                    player.guessed_word[i] = letter  # replacing the '-' in the player's "guessed_word" with the letter
            player.used_letters.append(letter)
        else:
            player.guess_count -= 1  # letter is not in the word, so player loses a life
            player.used_letters.append(letter)


def check_input(letter, used_letters):  # Just some error checking
    if letter in used_letters:
        print("Already guessed using this letter, try again with a different one!")
        return 1
    elif not letter.isalpha():
        print("Only alphabets allowed!")
        return 1
    elif len(letter) > 1:
        print("Only one character allowed per guess")
        return 1
    return 0


def game_round(game, player):
    print("".join(player.guessed_word))
    print("Letters used: " + " ".join(player.used_letters))
    print("Number of tries left: " + str(player.guess_count))
    user_guess(game, player)


def game_init():
    guessed_word = list()  # what the user guesses so far
    used_letters = list()  # used_letters will be used to store all the letters the player inputs throughout the game
    game_word = get_word()  # get a random word from a text file
    for i in range(len(game_word)):  # Print '-' for each letter in the word by default
        guessed_word.append("-")
    guess_count = int(input("Set the maximum number of guesses that you want\n"
                            "Number of guesses(lives): "))

    return Game(game_word), Player(guessed_word, guess_count, used_letters)


def end_condition(game, player):
    if "-" not in player.guessed_word:  # if there are no more '-' then it means the user guessed the whole word
        print("".join(player.guessed_word))
        print("Game won!")
        return True

    elif player.guess_count == 0:  # no more guesses remain
        print("The correct word is \'" + game.game_word + "\'")
        print("Game lost!")
        return True
    return False


def retry():
    choice = input("Would you like to play again? Press \'y\' to start over\nNew round: ")
    if choice == "y":
        return True
    return False


def main():
    input("Press \"Enter\" to start the game\n")
    while True:
        game, player = game_init()
        while True:
            game_round(game, player)  # player makes a guess
            if not end_condition(game, player):  # if game not over, player makes another guess
                continue
            break
        if retry():  # start new game
            continue
        break
    print("Game Over!")


if __name__ == "__main__":
    main()

I feel like this could be improved more since I've done a similar version where everything was in the main function, and it was half as long.

import random


def main():
    input("Press \"Enter\" to start the game\n")
    while True:
        word_file = open("words.txt")
        game_word = random.choice(word_file.readlines())
        game_word = game_word.strip().lower()
        word_file.close()
        guessed = list()
        used_letters = list()
        for i in range(len(game_word)):
            guessed.append("-")
        count = 10
        while True:
            print("".join(guessed))
            print("Letters used: " + " ".join(used_letters))
            print("Number of tries left: " + str(count))
            print("Enter a letter: ")
            letter = input().lower()
            if letter in used_letters:
                print("Already guessed using this letter, try again with a different one!")
                continue
            elif not letter.isalpha():
                print("Only alphabets allowed!")
                continue
            elif len(letter) > 1:
                print("Only one character allowed per guess")
                continue
            elif letter in game_word:
                for i in range(0, len(game_word)):
                    if letter == game_word[i]:
                        guessed[i] = letter
            else:
                count -= 1
            used_letters.append(letter)
            if "-" not in guessed:
                print("".join(guessed))
                print("Game won!")
                game_over = True
                break
            elif count == 0:
                print("The correct word is \'" + game_word + "\'")
                print("Game lost!")
                game_over = True
                break
        if game_over:
            choice = input("Would you like to play again? Press \'y\' to start over\nNew round: ")
            if choice == "y":
                continue
            else:
                break
    print("Game Over!")


if __name__ == "__main__":
    main()

The words text file. In case the link doesn't work, the text file is just single-words separated by a new line.

\$\endgroup\$
3
\$\begingroup\$

In this code:

def __init__(self, guessed_word, guess_count, used_letters):
    self.guess_count = guess_count
    self.guessed_word = guessed_word
    self.used_letters = used_letters

used_letters should not be passed as an argument, nor should it be constructed as a list in game_init. Just construct it in __init__. Also, it should be a set instead of a list for efficiency.

Similarly, rather than guessed_word being initialized in game_init, it should be initialized in Player.__init__. You can pass game_word to __init__, and then in there, you can write

guessed_word = '-' * len(game_word)

As for guess_count, the name is a little confusing, because it isn't strictly "guess count", but rather "remaining guesses".

This:

if choice == "y":
    return True
return False

can be abbreviated to

return choice == 'y'

but you should also convert choice to lowercase before doing that comparison.

This loop:

    while True:
        game_round(game, player)  # player makes a guess
        if not end_condition(game, player):  # if game not over, player makes another guess
            continue
        break

can be abbreviated to

while not end_condition(game, player):
    game_round(game, player)

These lines:

print("".join(player.guessed_word))
print("Letters used: " + " ".join(player.used_letters))
print("Number of tries left: " + str(player.guess_count))

should be moved to a method of Player, perhaps called print_round.

This:

word_file = open("words.txt")
game_word = random.choice(word_file.readlines())
game_word = game_word.strip().lower()
word_file.close()

should have word_file in a with statement, rather than an explicit close.

More generally, you're using Game and Player as C struct-like objects with no methods. You should make an attempt to convert them to true classes with methods on the objects, rather than global methods that operate on the class member variables.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.