Editor

Hangman Game in Python

Beginner · Games · about 45 minutes to build · 140 lines

The computer hides a word, and you guess it one letter at a time while a stick figure appears on the gallows with every wrong letter.

The program

Press Run and answer its questions in the box that appears under the code.

hangman.py
"""Hangman: guess the hidden word one letter at a time before the gallows is complete."""
import random
import string
WORDS = [
"python", "variable", "function", "keyboard", "library", "printer",
"integer", "boolean", "string", "module", "puzzle", "laptop",
"network", "monitor", "browser", "program", "compiler", "rocket",
]
# One drawing per wrong guess: GALLOWS[0] is empty, GALLOWS[6] is the full figure.
GALLOWS = [
r"""
+---+
| |
|
|
|
=======""",
r"""
+---+
| |
O |
|
|
=======""",
r"""
+---+
| |
O |
| |
|
=======""",
r"""
+---+
| |
O |
/| |
|
=======""",
r"""
+---+
| |
O |
/|\ |
|
=======""",
r"""
+---+
| |
O |
/|\ |
/ |
=======""",
r"""
+---+
| |
O |
/|\ |
/ \ |
=======""",
]
MAX_MISSES = len(GALLOWS) - 1
def masked_word(word, guessed):
"""Show the word with unguessed letters as underscores: p _ t h _ n."""
return " ".join(letter if letter in guessed else "_" for letter in word)
def ask_letter(guessed):
"""Ask until the player types one new letter."""
while True:
text = input("Guess a letter: ").strip().lower()
if len(text) != 1 or text not in string.ascii_lowercase:
print(" Please type a single letter from a to z.")
elif text in guessed:
print(f" You already tried {text}.")
else:
return text
def play_round(word):
"""Play one game with the given word and return True if the player wins."""
guessed = set()
misses = []
print(GALLOWS[0].strip("\n"))
while True:
missed = ", ".join(misses) if misses else "none"
print(f"\nWord: {masked_word(word, guessed)} Missed: {missed}")
letter = ask_letter(guessed)
guessed.add(letter)
if letter in word:
print(f" Yes, {letter} is in the word.")
if all(ch in guessed for ch in word):
print(f"\nYou saved him. The word was {word.upper()}.")
return True
else:
misses.append(letter)
left = MAX_MISSES - len(misses)
print(f" No {letter}. Wrong guesses left: {left}")
print(GALLOWS[len(misses)].strip("\n"))
if left == 0:
print(f"\nThe gallows is complete. The word was {word.upper()}.")
return False
def ask_yes_no(prompt):
"""Ask a yes/no question and return True for yes."""
while True:
answer = input(prompt).strip().lower()
if answer in ("y", "yes"):
return True
if answer in ("n", "no"):
return False
print(" Please type y or n.")
def main():
"""Play rounds with random words until the player wants to stop."""
print("=== Hangman ===")
print(f"Guess the word before you make {MAX_MISSES} wrong guesses.\n")
wins = losses = 0
while True:
if play_round(random.choice(WORDS)):
wins += 1
else:
losses += 1
print(f"Wins: {wins} Losses: {losses}\n")
if not ask_yes_no("Play again? (y/n) "):
break
print()
print("Thanks for playing.")
if __name__ == "__main__":
main()

Open it in the full editor to save it, share it and keep building.

Sample run

What the program printed, and what was typed (in yellow).

=== Hangman ===
Guess the word before you make 6 wrong guesses.

  +---+
  |   |
      |
      |
      |
=======

Word: _ _ _ _ _ _    Missed: none
Guess a letter: e
  Yes, e is in the word.

Word: _ _ _ _ _ e    Missed: none
Guess a letter: a
  No a. Wrong guesses left: 5
  +---+
  |   |
  O   |
      |
      |
=======

Word: _ _ _ _ _ e    Missed: a
Guess a letter: ab
  Please type a single letter from a to z.
Guess a letter: l
  Yes, l is in the word.

Word: _ _ _ _ l e    Missed: a
Guess a letter: s
  No s. Wrong guesses left: 4
  +---+
  |   |
  O   |
  |   |
      |
=======

Word: _ _ _ _ l e    Missed: a, s
Guess a letter: e
  You already tried e.
Guess a letter: u
  Yes, u is in the word.

Word: _ u _ _ l e    Missed: a, s
Guess a letter: p
  Yes, p is in the word.

Word: p u _ _ l e    Missed: a, s
Guess a letter: z
  Yes, z is in the word.

You saved him. The word was PUZZLE.
Wins: 1  Losses: 0

Play again? (y/n) n
Thanks for playing.

How it is built

1. The words and the drawings

GALLOWS is a list of seven drawings, from the empty gallows to the full figure, so GALLOWS[len(misses)] is always the right picture and MAX_MISSES is one less than its length. The r before each string keeps the backslashes in the arms and legs as they are.

WORDS = [
    "python", "variable", "function", "keyboard", "library", "printer",
    "integer", "boolean", "string", "module", "puzzle", "laptop",
    "network", "monitor", "browser", "program", "compiler", "rocket",
]

# One drawing per wrong guess: GALLOWS[0] is empty, GALLOWS[6] is the full figure.
GALLOWS = [
    r"""
  +---+
  |   |
      |
      |
      |
=======""",

2. Showing the hidden word

The function keeps each letter the player has found and puts _ in place of the rest. " ".join(...) adds the spaces, so python with p and n found shows as p _ _ _ _ n.

def masked_word(word, guessed):
    """Show the word with unguessed letters as underscores: p _ t h _ n."""
    return " ".join(letter if letter in guessed else "_" for letter in word)

3. Asking for a letter

A guess must be one letter from string.ascii_lowercase. A letter already tried is refused without costing the player a wrong guess.

def ask_letter(guessed):
    """Ask until the player types one new letter."""
    while True:
        text = input("Guess a letter: ").strip().lower()
        if len(text) != 1 or text not in string.ascii_lowercase:
            print("  Please type a single letter from a to z.")
        elif text in guessed:
            print(f"  You already tried {text}.")
        else:
            return text

4. One game

guessed is a set of every letter tried and misses is a list of the wrong ones, in order. The player wins when all() the letters of the word are in guessed, and loses when the misses reach MAX_MISSES.

def play_round(word):
    """Play one game with the given word and return True if the player wins."""
    guessed = set()
    misses = []
    print(GALLOWS[0].strip("\n"))

    while True:
        missed = ", ".join(misses) if misses else "none"
        print(f"\nWord: {masked_word(word, guessed)}    Missed: {missed}")
        letter = ask_letter(guessed)
        guessed.add(letter)

        if letter in word:
            print(f"  Yes, {letter} is in the word.")
            if all(ch in guessed for ch in word):
                print(f"\nYou saved him. The word was {word.upper()}.")
                return True
        else:
            misses.append(letter)
            left = MAX_MISSES - len(misses)
            print(f"  No {letter}. Wrong guesses left: {left}")
            print(GALLOWS[len(misses)].strip("\n"))
            if left == 0:
                print(f"\nThe gallows is complete. The word was {word.upper()}.")
                return False

5. Wins and losses

Each round gets a new word from random.choice(WORDS), and main() counts wins and losses until the player stops.

def main():
    """Play rounds with random words until the player wants to stop."""
    print("=== Hangman ===")
    print(f"Guess the word before you make {MAX_MISSES} wrong guesses.\n")
    wins = losses = 0
    while True:
        if play_round(random.choice(WORDS)):
            wins += 1
        else:
            losses += 1
        print(f"Wins: {wins}  Losses: {losses}\n")
        if not ask_yes_no("Play again? (y/n) "):
            break
        print()
    print("Thanks for playing.")

Make it bigger

  • Group the words into categories such as animals, countries and food, and show the category as a clue.
  • Let the player guess the whole word at once, counting a wrong word as a miss.
  • Add a hint command that reveals one letter but costs a wrong guess.
  • Read the words from a text file so you can add new ones without changing the code.

What it practises