Python Tutorial
Python Project: Hangman
Guess letters in a hidden word. Track lives, reveal matches, and stop when the word is complete.
What you are building
Hangman hides a word and accepts one letter at a time. A match reveals every copy of that letter. A miss costs a life. The game ends when the pattern has no blanks, or when lives reach zero.
Run the examples at /try in the Python editor, not HTML, C, or C++. A list of letters plays the whole game so the log prints in one Run. There is no input() in the complete program.
Skills used
- A string for the hidden word
- A
setof guessed letters - A pattern function that prints letters or underscores
- A
whileloop, or aforover a prepared guess list - A lives counter that only drops on a new miss
Reveal the pattern
The player never sees the raw word after the game starts. They see a pattern: a letter if it was guessed, an underscore if it was not. Build that string from the word and the guessed set.
Example
def pattern(word, guessed):
parts = []
for ch in word:
if ch in guessed:
parts.append(ch)
else:
parts.append("_")
return " ".join(parts)
word = "python"
guessed = {"p", "t"}
print(pattern(word, guessed))
print("Lives:", 6)That print is p _ t _ _ _ with spaces so each blank is visible. Without spaces,p____ is harder to count.
Apply one letter
A guess has three outcomes: already tried, in the word, or a miss. Only a miss reduces lives. Add every guess to the set, even misses, so a repeated miss does not punish twice.
Example
word = "python"
guessed = set()
lives = 6
letter = "z"
if letter in guessed:
print("Already tried", letter)
else:
guessed.add(letter)
if letter in word:
print(letter, "is in the word.")
else:
lives -= 1
print(letter, "is a miss. Lives:", lives)
print("Guessed:", sorted(guessed))| Case | Lives | Pattern |
|---|---|---|
| New letter in the word | unchanged | that letter appears |
| New letter not in the word | minus one | unchanged |
| Repeat of an old letter | unchanged | unchanged |
Pattern has no _ | win | full word |
| Lives reach 0 | loss | word is revealed |
Store guesses in a set. Membership tests stay fast to write: letter in guessed. When you print the tried letters, wrap with sorted(...) so the order is stable.
Complete program
The hidden word is python. The letter list is p, y, z, t, h, o, n. One miss onz, then the word fills in. Copy the whole script into /try.
Example
def pattern(word, guessed):
parts = []
for ch in word:
if ch in guessed:
parts.append(ch)
else:
parts.append("_")
return " ".join(parts)
def won(word, guessed):
return all(ch in guessed for ch in word)
WORD = "python"
LIVES = 6
LETTERS = ["p", "y", "z", "t", "h", "o", "n"]
guessed = set()
lives = LIVES
print("Hangman")
print("Word length:", len(WORD))
print("Lives:", lives)
print(pattern(WORD, guessed))
for letter in LETTERS:
print()
print("Guess:", letter)
if letter in guessed:
print("Already tried.")
print(pattern(WORD, guessed))
continue
guessed.add(letter)
if letter in WORD:
print("Hit.")
else:
lives -= 1
print("Miss. Lives left:", lives)
print(pattern(WORD, guessed))
print("Tried:", " ".join(sorted(guessed)))
if won(WORD, guessed):
print()
print("You revealed the word:", WORD)
break
if lives <= 0:
print()
print("Out of lives. The word was:", WORD)
break
else:
if not won(WORD, guessed):
print()
print("No more guesses. The word was:", WORD)The for/else at the bottom runs only if the loop did not break. That covers a letter list that runs out while blanks remain. This demo wins before that happens.
Common mistakes
- Comparing
letter == word. Hangman guesses letters, not the whole word, in this version. - Using a list for
guessedand forgetting to skip duplicates. A secondzwould steal another life. - Revealing with
word.replace("_", letter)on the original word. Build the pattern from the secret and the set each time. The secret string itself should not change. - Mixing case: secret
Pythonand guessp. Lowercase the word once at the start, and lowercase each guess. - Ending the game when
letter in wordis true once. You win only when every character is in the guessed set.
How to extend / Practice tasks
Use the same complete program. Change the word or the letter list.
- Change
WORDtostringand write a letter list that wins with exactly one miss. - Start with
LIVES = 2and a list of three misses. Confirm the game stops at zero lives and prints the secret. - Reject guesses longer than one character: if
len(letter) != 1, print a message and do not add it to the set. Test with"py"in the list.
📘 Real-World Deep Dive
Hangman is a great state-machine exercise: a secret word, a set of guessed letters, a dwindling life count, and a masked display recomputed after every guess. Get those four pieces right and the game just works.
What to build
The player guesses letters in a hidden word; correct letters reveal, wrong letters cost a life, and the game ends on a full reveal or zero lives.
Real-Life Example
def masked(word: str, guessed: set[str]) -> str:
return " ".join(c if c in guessed else "_" for c in word)
def turn(word, guessed, lives, letter):
guessed.add(letter)
if letter not in word:
lives -= 1
return lives
word, guessed, lives = "python", set(), 6
for letter in "pyaton":
lives = turn(word, guessed, lives, letter)
print(f"{masked(word, guessed)} lives={lives}")Using a set for guessed letters makes "already tried?" an O(1) check and quietly ignores repeats.
Expected Output
p _ _ _ _ _ lives=6
p y _ _ _ _ lives=6
p y _ _ _ _ lives=5
p y t _ _ _ lives=5
p y t _ o _ lives=5
p y t _ o n lives=5Common mistakes
- Not tracking guessed letters lets a player "lose" a life twice for the same wrong letter — a set fixes both the repeat and the double penalty.
- Case matters: normalise both the word and the guess with
.lower()or "A" never matches "a". - Recompute the masked display from
word+guessedevery turn; trying to edit the display string in place gets out of sync fast.
🚀 Performance & Best Practices
- The whole game state is (word, guessed set, lives) — small enough to pass around explicitly, which makes it easy to test.
- Win check is one line:
set(word) <= guessed("every letter has been guessed"). - Load words from a file into a list once and
random.choiceper game rather than re-reading.
🧪 Try It Yourself
- Add a win/lose message and detect the win with
set(word) <= guessed. - Reject non-letters and already-guessed letters with a friendly message instead of a wasted life.
- Draw the classic ASCII gallows that grows as lives drop.