Hangman in Python: Logic and Validation

Updated on: August 21, 2026
Reading time: 5 minutes
Jogo da forca para iniciantes desenvolvido com Python

Hangman is a classic terminal project for practicing strings, sets, loops, conditions, random selection, input validation, and functions. The computer chooses a secret word, the player guesses one letter at a time, correct letters are revealed, and incorrect guesses reduce the remaining lives.

This tutorial builds a complete Hangman game in Python. The final program prevents repeated guesses, validates letters, supports whole-word guesses, displays simple ASCII stages, gives hints, separates game logic into testable functions, and lets the player start another round.

The secret word is selected with the Python random module. Lists and sets hold words and guesses; review the Python lists guide when necessary.

How Hangman Works

  1. Choose a secret word.
  2. Display an underscore for every hidden letter.
  3. Read one letter or a complete-word guess.
  4. Reveal every matching letter.
  5. Reduce lives after a wrong new guess.
  6. Continue until the word is complete or lives reach zero.

The official random.choice() documentation defines the selection function. The Python string methods documentation covers methods such as lower(), strip(), and isalpha().

Choose a Secret Word

import random

WORDS = ["python", "variable", "function", "iterator", "package"]
secret_word = random.choice(WORDS)

The standard random module is appropriate here because the game does not protect a secret with security consequences.

Represent Guessed Letters with a Set

A set stores each letter once and makes membership checks clear:

guessed_letters = set()

guessed_letters.add("p")
print("p" in guessed_letters)  # True

Using a set prevents duplicate letters from appearing in the history and lets the program detect repeated input without scanning a list manually.

Build the Masked Word

def masked_word(secret_word, guessed_letters):
    return " ".join(
        letter if letter in guessed_letters else "_"
        for letter in secret_word
    )


print(masked_word("python", {"p", "o"}))
# p _ _ _ o _

The function returns display text and does not print it directly. This makes it reusable and easy to test.

Check Whether the Word Is Complete

def word_is_complete(secret_word, guessed_letters):
    return all(letter in guessed_letters for letter in secret_word)

all() returns true only when every letter satisfies the condition. Repeated letters do not need special handling because checking membership reveals every occurrence.

Validate the Player’s Input

Accept a single alphabetic letter or a full alphabetic word:

def read_guess():
    while True:
        guess = input("Guess a letter or the whole word: ").strip().lower()

        if not guess:
            print("Enter a letter or word.")
            continue

        if not guess.isalpha():
            print("Use letters only.")
            continue

        return guess

isalpha() accepts letters beyond plain English ASCII. If a word list uses accents, normalize words and guesses consistently.

Handle Repeated Guesses

Do not remove a life for a letter the player already tried:

if guess in guessed_letters:
    print("You already tried that letter.")
    continue


guessed_letters.add(guess)

A whole-word guess can be tracked in a separate set when repeated word attempts should also be ignored.

Create Simple Hangman Stages

Store one drawing for each number of wrong guesses. The first stage is empty and the last is complete.

HANGMAN_STAGES = [
    """
     +---+
     |   |
         |
         |
         |
         |
    =========
    """,
    """
     +---+
     |   |
     O   |
         |
         |
         |
    =========
    """,
    """
     +---+
     |   |
     O   |
     |   |
         |
         |
    =========
    """,
    """
     +---+
     |   |
     O   |
    /|   |
         |
         |
    =========
    """,
    """
     +---+
     |   |
     O   |
    /|\  |
         |
         |
    =========
    """,
    """
     +---+
     |   |
     O   |
    /|\  |
    /    |
         |
    =========
    """,
    """
     +---+
     |   |
     O   |
    /|\  |
    / \  |
         |
    =========
    """,
]

With seven stages, the player can make six incorrect guesses. The current drawing is HANGMAN_STAGES[wrong_guesses].

Add Words and Hints

A list of dictionaries keeps each word beside a hint:

WORD_BANK = [
    {"word": "python", "hint": "A popular programming language"},
    {"word": "iterator", "hint": "Produces values one at a time"},
    {"word": "function", "hint": "Reusable block of behavior"},
    {"word": "variable", "hint": "A name that refers to a value"},
    {"word": "package", "hint": "Distributable collection of modules"},
]

Hints make uncommon words fairer and let the project teach vocabulary at the same time.

Complete Hangman Game

import random

HANGMAN_STAGES = [
    """
     +---+
     |   |
         |
         |
         |
         |
    =========
    """,
    """
     +---+
     |   |
     O   |
         |
         |
         |
    =========
    """,
    """
     +---+
     |   |
     O   |
     |   |
         |
         |
    =========
    """,
    """
     +---+
     |   |
     O   |
    /|   |
         |
         |
    =========
    """,
    """
     +---+
     |   |
     O   |
    /|\  |
         |
         |
    =========
    """,
    """
     +---+
     |   |
     O   |
    /|\  |
    /    |
         |
    =========
    """,
    """
     +---+
     |   |
     O   |
    /|\  |
    / \  |
         |
    =========
    """,
]

WORD_BANK = [
    {"word": "python", "hint": "A popular programming language"},
    {"word": "iterator", "hint": "Produces values one at a time"},
    {"word": "function", "hint": "Reusable block of behavior"},
    {"word": "variable", "hint": "A name that refers to a value"},
    {"word": "package", "hint": "Distributable collection of modules"},
]


def masked_word(secret_word, guessed_letters):
    return " ".join(
        letter if letter in guessed_letters else "_"
        for letter in secret_word
    )


def word_is_complete(secret_word, guessed_letters):
    return all(letter in guessed_letters for letter in secret_word)


def read_guess():
    while True:
        guess = input("Guess a letter or the whole word: ").strip().lower()

        if not guess:
            print("Enter a letter or word.")
            continue

        if not guess.isalpha():
            print("Use letters only.")
            continue

        return guess


def play_round(generator=None):
    generator = generator or random.Random()
    entry = generator.choice(WORD_BANK)
    secret_word = entry["word"].lower()
    hint = entry["hint"]
    guessed_letters = set()
    guessed_words = set()
    wrong_guesses = 0
    maximum_wrong = len(HANGMAN_STAGES) - 1

    print(f"Hint: {hint}")

    while wrong_guesses < maximum_wrong:
        print(HANGMAN_STAGES[wrong_guesses])
        print(masked_word(secret_word, guessed_letters))
        print(f"Lives: {maximum_wrong - wrong_guesses}")

        if guessed_letters:
            print("Letters:", " ".join(sorted(guessed_letters)))

        guess = read_guess()

        if len(guess) == 1:
            if guess in guessed_letters:
                print("You already tried that letter.")
                continue

            guessed_letters.add(guess)

            if guess in secret_word:
                print("Correct letter!")
            else:
                wrong_guesses += 1
                print("That letter is not in the word.")
        else:
            if guess in guessed_words:
                print("You already tried that word.")
                continue

            guessed_words.add(guess)

            if guess == secret_word:
                guessed_letters.update(secret_word)
            else:
                wrong_guesses += 1
                print("That is not the secret word.")

        if word_is_complete(secret_word, guessed_letters):
            print(masked_word(secret_word, guessed_letters))
            print("You won!")
            return True

    print(HANGMAN_STAGES[wrong_guesses])
    print(f"You lost. The word was: {secret_word}")
    return False


def wants_to_play_again():
    while True:
        answer = input("Play again? [y/n]: ").strip().lower()

        if answer in {"y", "yes"}:
            return True

        if answer in {"n", "no"}:
            return False

        print("Enter y or n.")


def main():
    print("Python Hangman")

    while True:
        play_round()

        if not wants_to_play_again():
            print("Thanks for playing!")
            break


if __name__ == "__main__":
    main()

Save the program as hangman.py and run:

python hangman.py

Test the Core Functions

from hangman import masked_word, word_is_complete


def test_masked_word():
    result = masked_word("letter", {"l", "e", "r"})
    assert result == "l e _ _ e r"


def test_word_is_complete():
    assert word_is_complete("cat", {"c", "a", "t"}) is True
    assert word_is_complete("cat", {"c", "a"}) is False

The Python unit testing guide explains assertions and mocks. A seeded random.Random instance can make word selection predictable during a larger test.

Debug the Game

Set breakpoints where guesses are added and where wrong_guesses changes. Inspect secret_word, guessed_letters, and guess. The VS Code debugging guide covers conditional breakpoints and variable inspection.

Load Words from a Text File

from pathlib import Path


def load_words(path):
    lines = Path(path).read_text(encoding="utf-8").splitlines()
    words = [line.strip().lower() for line in lines]
    return [word for word in words if word.isalpha()]

Validate that the returned list is not empty before calling choice(). The Python pathlib guide explains file paths and text methods.

Common Mistakes

  • Reducing a life for a repeated letter.
  • Revealing only one occurrence when a letter appears several times.
  • Mixing uppercase words with lowercase guesses without normalization.
  • Accepting digits or punctuation as guesses.
  • Calling random.choice() on an empty word list.
  • Putting all logic inside one long interactive loop.
  • Using broad exception handling to hide unrelated programming errors.
  • Testing only through manual gameplay instead of testing pure functions.

Ideas to Extend the Game

  • Add categories and difficulty levels.
  • Allow a limited number of hints.
  • Save wins and losses to JSON.
  • Use words loaded from separate category files.
  • Add colors to terminal output.
  • Create a graphical version with Turtle or Pygame.
  • Add command-line options for lives, category, and seed.
  • Record rounds with the Python logging module.
  • Reuse menu and validation ideas from the terminal quiz project.

Frequently Asked Questions

Why use a set for guessed letters?

A set stores unique values and provides direct membership tests.

How are repeated letters revealed?

The masked-word function checks every character against the same set, so every matching position becomes visible.

Can the player guess the complete word?

Yes. The final program treats guesses longer than one letter as complete-word attempts.

How do I use accented words?

Store and compare text consistently. More advanced programs can normalize Unicode and decide whether accented and unaccented letters should be equivalent.

Do I need an external package?

No. This terminal version uses only the standard library.

Conclusion

A Hangman game in Python practices several fundamentals in a single project: random choice, string iteration, sets, input validation, loops, functions, and testing. Separating the mask, win condition, input, and round logic keeps the program understandable.

Once the basic game is reliable, add one improvement at a time. External word files, categories, scores, command-line options, and a graphical interface all fit naturally on top of the same core rules.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Python PDF search with RAG
    Projects
    Foto de perfil de Leandro Hirt da Academify

    Python PDF Search with RAG

    Build Python PDF search with RAG, semantic retrieval, grounded answers, evaluation, and document security.

    Ler mais

    Tempo de leitura: 6 minutos
    23/07/2026
    Criação de chatbot com API da OpenAI usando Python
    Projects
    Foto de perfil de Leandro Hirt da Academify

    OpenAI Chatbot with Python: Responses API

    Build a Python chatbot with the OpenAI Responses API, secure environment variables, conversation memory, model configuration, and practical error handling.

    Ler mais

    Tempo de leitura: 7 minutos
    10/07/2026
    Chatbot simples em Python
    Projects
    Foto de perfil de Leandro Hirt da Academify

    Python Chatbot: Rules, Intents, and Responses

    Build a simple Python chatbot with rules, input normalization, intents, random replies, JSON data, conversation loops, tests, and practical extension

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Criptografia e segurança de dados em Python
    Projects
    Foto de perfil de Leandro Hirt da Academify

    Secure Password Generator in Python with secrets

    Build a secure password generator in Python with secrets, configurable character rules, a CLI, passphrases, validation, and practical tests.

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Quiz interativo no terminal desenvolvido com Python
    Projects
    Foto de perfil de Leandro Hirt da Academify

    Python Terminal Quiz: Questions and Scoring

    Build a terminal quiz game in Python with questions, input validation, scoring, shuffled answers, replay support, JSON loading, and tests.

    Ler mais

    Tempo de leitura: 4 minutos
    10/07/2026
    Jogo de adivinhação de números desenvolvido com Python
    Projects
    Foto de perfil de Leandro Hirt da Academify

    Python Number Guessing Game: Hints and Attempts

    Build a number guessing game in Python with random numbers, input validation, hints, limited attempts, replay support, and clean functions.

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026