Just added: Algorithms you can run and practice
Updated Jul 16, 2026 Edit on GitHub

The Secret Word and the Blanks

The heart of Hangman is a single visual: a word you can't see yet, shown as a row of blanks, with the letters you've correctly guessed filled in. Get that one thing working and the rest of the game is steps you hang off it.

So that's where we start. A secret word, a set of letters that have been guessed, and a way to print the word with the right letters showing and the rest hidden.

The pieces we need

Two things:

  • The word - a plain string, like "python".
  • The guessed letters - the letters the player has tried and got right (or tried at all; we'll firm that up next phase). For now, think of it as a small collection of single letters.

The display rule is one sentence: for each letter in the word, show the letter if it's been guessed, otherwise show an underscore.

Walking the word one letter at a time

In Python you can loop straight over a string and you get one character at a time:

Before you run this, guess how many lines it'll print. Then check.

word = "python"
for letter in word:
    print(letter)

Run that. Six lines, one letter each. That for letter in word loop is the engine of the whole display - we'll decide, per letter, whether to show it or hide it.

Show it or hide it

For each letter we want a small either/or: the real letter if it's been guessed, an underscore if it hasn't. You already have the tools for that - an if check and the in operator, which asks "is this letter inside the guessed collection?" and hands back True or False. Put those together, loop over the word, and you can build the whole display.

Putting it together

Write a function show(word, guessed) that returns the word as a single display string: each letter of word, in order, separated by spaces - the letter itself if it's in guessed, an underscore if it isn't. p _ t _ _ n reads better than p_t__n, which is why the spaces matter.

Your turn. This function is the point of the phase, so have a go before you read on. Fill it in and hit Run: the checks underneath tell you whether it works. My version is in the next block whenever you want it.

def show(word, guessed):
    # Return `word` as a display string: each letter, in order, separated
    # by single spaces. Show the letter if it's in `guessed`, otherwise
    # show "_".
    pass


# --- checks: fix your function until this prints "All good." ---
assert show("python", {"p", "t", "n"}) == "p _ t _ _ n", f"got: {show('python', {'p','t','n'})!r}"
assert show("python", set()) == "_ _ _ _ _ _", f"got: {show('python', set())!r}"
assert show("python", set("python")) == "p y t h o n", f"got: {show('python', set('python'))!r}"
print("All good.")

Stuck on the per-letter decision? You need one of two values for each letter - think about how to say "this if a condition holds, otherwise that" in a single line, then join the results with a space.

One way to write it

Here's a first pass, glueing the per-letter results into one line by hand. Build a list of the shown characters and join them with spaces:

word = "python"
guessed = {"p", "t", "n"}

shown = []
for letter in word:
    if letter in guessed:
        shown.append(letter)
    else:
        shown.append("_")

display = " ".join(shown)
print("Word:", display)

Run it. You should see Word: p _ t _ _ n. The p, t, and n show because they're in guessed; the y, h, and o are still underscores. Change guessed to {"y", "o"} and run again - different letters reveal. This is the game's whole face.

That curly-brace {"p", "t", "n"} is a set - a collection with no duplicates and fast membership checks. It's exactly the right tool for "which letters have been guessed," and we'll lean on it hard next phase. For now, know that letter in guessed against a set is quick and reads like English.

We'll need this display in every phase, so let's wrap it in a function that takes the word and the guessed letters and hands back the line to print. Python also has a compact way to write "this value if a condition is true, otherwise that value," which flattens the loop above into one line:

shown = letter if letter in guessed else "_"

Read it left to right: letter (use this) if letter in guessed (when it's been guessed) else "_" (otherwise an underscore).

def show(word, guessed):
    return " ".join(letter if letter in guessed else "_" for letter in word)

# A few different guess states, so you can see the masking change:
word = "python"
print("Guessed p, t, n ->", show(word, {"p", "t", "n"}))
print("Guessed y, o    ->", show(word, {"y", "o"}))
print("Guessed nothing ->", show(word, set()))
print("Guessed it all  ->", show(word, set("python")))

Run it and read the four lines. The first two mask different letters. The third passes an empty set (set()) - every letter is hidden, all underscores, exactly what a fresh game looks like. The fourth passes set("python"), which turns the string into a set of its letters {'p','y','t','h','o','n'} - every letter is guessed, so the full word shows. That last line is what winning looks like, and we'll use that same idea to detect a win in Phase 3.

Why a set, not a list

You could store guessed letters in a list. A set is the better fit here for two reasons:

List Set
Duplicate guesses Keeps both Ignores the repeat
"Have I guessed this?" Scans the whole list Instant lookup

A player will fat-finger the same letter twice. A set absorbs that without you writing a line of code to handle it. We'll make use of exactly that next.

Where you are

You have a show(word, guessed) function that turns any word plus any set of guessed letters into a clean masked line. That's the screen of your game. Next we make a guess actually do something - add a letter, tell the player hit or miss, and watch the blanks fill in.