HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Hangman implementation for teaching a Python course

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
coursehangmanteachingpythonforimplementation

Problem

I am teaching a Python crash course and thought a Hangman rendition would be fruitful.

I have an end product that I'll work towards with the hour that I have for the course and I was wondering what could be refactored or implemented to help the exchange of information from myself, intermediate level, to absolute beginners.

```
from random import choice
import os

def clear():
os.system('clear')

def play_again():
play_again = input("\nPlay again? Y/n ").lower()

if play_again != 'n':
clear()
game()

else:
print("\n\n\n\nCatch ya later!\n\n\n\n")

def get_secret_word():
words = ['angry', 'beautiful', 'brave', 'careful', 'charismatic', 'clever',
'creative', 'cute', 'dangerous', 'exciting', 'famous', 'friendly',
'happy', 'interesting', 'lucky', 'profound', 'popular', 'rich',
'thin', 'young']

secret_word = choice(words)
return secret_word

def get_guess(bad_guesses, good_guesses, secret_word):
while True:
guess = input("Guess a letter: ").lower()

if guess == '':
print("What was that?")
board(bad_guesses, good_guesses, secret_word)

elif len(guess) != 1:
input("You can only guess a single letter!")
board(bad_guesses, good_guesses, secret_word)

elif guess in bad_guesses or guess in good_guesses:
input("You've already guessed that letter!")
board(bad_guesses, good_guesses, secret_word)

elif not guess.isalpha():
input("You can only guess LETTERS!")
board(bad_guesses, good_guesses, secret_word)

else:
return guess

def board(bad_guesses, good_guesses, secret_word):
clear()
print(' Strikes: {}/8 '.format(len(bad_guesses)))
print('')

for guess in bad_guesses:
print(guess, end=" ")
print('\n\n')

for guess in secret_word:
if guess in good_guesses:
print(guess, end=" ")

Solution

I could come up with a laundry list of improvements. However, I would like to focus on just one issue, which makes your code an unforgivably bad example for your students: you are misusing functions as goto labels.

Note that game() can call play_again(), which in turn may call game(). This mutual recursion is inappropriate. If you play many games, then hit CtrlC, you will see that the deepening stack is keeping track of all the previous calls, uselessly preparing for resuming execution after a return.

If you want to do something repeatedly in Python, use a loop. A reasonable outline would look like this:

def play_again():
    """
    Ask the player whether to play again.
    """
    return 'n' != input("\nPlay again? Y/n ").lower()

def game():
    """
    Play a single game of Hangman.  Return True if the player wins,
    or False if the player loses.
    """
    secret_word = get_secret_word()
    good_guesses = []
    bad_guesses = []

    while True:
        board(bad_guesses, good_guesses, secret_word)
        guess = get_guess(bad_guesses, good_guesses, secret_word)
        if guess in secret_word:
            good_guesses.append(guess)
            if all(letter in good_guesses for letter in secret_word):
                print("\nYou win!\n")
                print("The secret word was {}.".format(secret_word.upper()))
                return True
        else:
            bad_guesses.append(guess)
            if len(bad_guesses) == 8:
                clear()
                print("\n **  ENGHH!  ** ")
                print("\nStrike ! You lost!")
                print("\nThe secret word was {}".format(secret_word.upper()))
                return False

def main():
    """
    Play games of Hangman until the user decides to quit.
    """
    while True:
        game()
        if not play_again():
            print("\n\n\n\nCatch ya later!\n\n\n\n")
            break
        clear()


Additional remarks:

  • Flag variables, like game_done and found, are almost always a bad idea. Here, game_done can be eliminated by using proper looping technique, and found can be determined using the all() function.



  • if guess not in secret_word is redundant. You can just say else.



Oh, and one more thing, since you should be setting a good example for your students: write a docstring for every function.

Code Snippets

def play_again():
    """
    Ask the player whether to play again.
    """
    return 'n' != input("\nPlay again? Y/n ").lower()

def game():
    """
    Play a single game of Hangman.  Return True if the player wins,
    or False if the player loses.
    """
    secret_word = get_secret_word()
    good_guesses = []
    bad_guesses = []

    while True:
        board(bad_guesses, good_guesses, secret_word)
        guess = get_guess(bad_guesses, good_guesses, secret_word)
        if guess in secret_word:
            good_guesses.append(guess)
            if all(letter in good_guesses for letter in secret_word):
                print("\nYou win!\n")
                print("The secret word was {}.".format(secret_word.upper()))
                return True
        else:
            bad_guesses.append(guess)
            if len(bad_guesses) == 8:
                clear()
                print("\n **  ENGHH!  ** ")
                print("\nStrike ! You lost!")
                print("\nThe secret word was {}".format(secret_word.upper()))
                return False

def main():
    """
    Play games of Hangman until the user decides to quit.
    """
    while True:
        game()
        if not play_again():
            print("\n\n\n\nCatch ya later!\n\n\n\n")
            break
        clear()

Context

StackExchange Code Review Q#162198, answer score: 8

Revisions (0)

No revisions yet.