PythonMastery
beginner 25 min read · lesson 2 of 15 in Projects

Project: Number Guessing Game

1 · The lesson

read

The computer picks a secret number between 1 and 100. You guess. It tells you "too high" or "too low" until you nail it. Counts your attempts.

This is the first program with state that changes over time — there's a loop, a counter, a condition that ends the game. It's the smallest meaningful interactive program.

What you'll practice: while loops, conditionals, random, input validation, breaking out of loops.


Step 1 — The Single-Round Version

Forget the loop for a moment. Just compare one guess to the secret.

python
import random

secret = random.randint(1, 100)
guess = 50                          # in a real game: int(input("Guess: "))

if guess == secret:
    print("You got it!")
elif guess < secret:
    print("Too low")
else:
    print("Too high")

print(f"(Secret was {secret})")

Run it a few times. Change guess to see all three branches fire.


Step 2 — Add the Loop

A guessing game is a loop that runs until the guess matches. Use while True + break.

python
import random

secret = random.randint(1, 100)
attempts = 0

# A short list of "guesses" to simulate user input in the browser sandbox
simulated_guesses = [50, 25, 37, 31, 28]

for guess in simulated_guesses:
    attempts += 1
    print(f"Attempt {attempts}: you guess {guess}")

    if guess == secret:
        print(f"✓ Got it in {attempts} attempts!")
        break
    elif guess < secret:
        print("  → too low")
    else:
        print("  → too high")
else:
    # `else` on a for-loop runs if the loop completed without `break`
    print(f"You didn't guess it. The secret was {secret}.")

In a real terminal you'd do:

python
# while True:
#     try:
#         guess = int(input("Your guess: "))
#     except ValueError:
#         print("Numbers only, please.")
#         continue
#
#     attempts += 1
#     if guess == secret:
#         print(f"✓ Got it in {attempts} attempts!")
#         break
#     ...

Two things to notice:


  • break exits the loop early when the user wins.

  • else on the loop runs only if the loop ended without break — perfect for "you didn't guess in time".


Step 3 — Bound the Attempts (Make It Fair)

Six attempts is enough — 2^6 = 64 < 100 < 128 = 2^7, so a perfect binary-searcher can always win in 7 guesses. We give 7.

python
import random

MAX_ATTEMPTS = 7
secret = random.randint(1, 100)

simulated_guesses = [50, 75, 87, 93, 96, 94, 95]  # would-be perfect binary search

for attempt in range(1, MAX_ATTEMPTS + 1):
    guess = simulated_guesses[attempt - 1]
    print(f"Attempt {attempt}/{MAX_ATTEMPTS}: {guess}", end="  ")

    if guess == secret:
        print(f"✓ won in {attempt} attempts!")
        break
    elif guess < secret:
        print("too low")
    else:
        print("too high")
else:
    print(f"Out of guesses. Secret was {secret}.")

The range(1, MAX_ATTEMPTS + 1) is a Pythonism — it gives you attempt numbers 1, 2, 3, ..., 7. The + 1 is because range(a, b) stops before b.


Step 4 — Wrap It in a Function (and Validate Input)

python
import random

def play_game(low=1, high=100, max_attempts=7):
    """Play one round. Returns the number of attempts (or 0 if lost)."""
    secret = random.randint(low, high)
    print(f"I'm thinking of a number between {low} and {high}. You have {max_attempts} guesses.")

    for attempt in range(1, max_attempts + 1):
        # In real life: raw = input(f"Guess #{attempt}: ")
        raw = "50"  # placeholder
        try:
            guess = int(raw)
        except ValueError:
            print(f"  ⚠️  '{raw}' isn't a number — try again")
            continue   # doesn't count against attempts

        if guess < low or guess > high:
            print(f"  ⚠️  out of range — pick between {low} and {high}")
            continue

        if guess == secret:
            return attempt
        elif guess < secret:
            print(f"  Attempt {attempt}: {guess} → too low")
        else:
            print(f"  Attempt {attempt}: {guess} → too high")

    print(f"Out of guesses. Secret was {secret}.")
    return 0


# Demo run
result = play_game()
if result:
    print(f"\n🎉 Won in {result} guesses")

Notes on what this version adds:


  • A play_game() function with sensible defaults — easy to test with different ranges

  • try/except ValueError so non-numeric input doesn't crash

  • Range validation — guesses outside the bounds get rejected with a hint

  • continue statements that skip bad attempts without consuming an attempt count


Step 5 — Multi-Round With a Score

Let the player play multiple rounds; track the best score.

python
import random

def play_round(secret, max_attempts=7):
    """Return number of attempts used, or 0 if lost. Returns immediately for this demo."""
    # ... (same logic as above, simplified for demo)
    return random.randint(1, max_attempts)

def play_session():
    best = None
    rounds_played = 0

    for round_num in range(1, 4):                # 3 demo rounds
        print(f"\n=== Round {round_num} ===")
        secret = random.randint(1, 100)
        attempts = play_round(secret)
        rounds_played += 1
        if attempts and (best is None or attempts < best):
            best = attempts
            print(f"  → new best: {attempts} attempts!")

    print(f"\n🏁 Played {rounds_played} rounds. Best: {best} attempts.")

play_session()

Stretch Goals

1. Difficulty levels: easy = 1-50 with 10 attempts; medium = 1-100 with 7; hard = 1-500 with 9.
2. High-score persistence: save the best score to highscore.txt using File I/O. On the next session, load and show it.
3. Hint system: after attempt 3, print "warmer" or "colder" based on whether the new guess is closer than the previous one.
4. Reverse mode: YOU pick the secret, computer guesses (using binary search). A 100-line guess always finds it in 7 moves max.
5. Multiplayer: two players take turns; whoever guesses in fewer attempts wins.


🎯 Your Turn — Reverse the Game

Instead of YOU guessing, write a function where the computer guesses your secret number using binary search. It should win in at most 7 guesses for any number 1-100.

python
def computer_guess(low=1, high=100):
    """Computer narrows down a secret you're thinking of (1..100).

    For each guess, simulate the user's response:
      - 'high' → secret is higher than guess
      - 'low'  → secret is lower than guess
      - 'yes'  → guess is correct
    """
    secret = 73   # the "user's" secret (for testing — real version uses input)
    attempts = 0
    # TODO 1: while low <= high:
    # TODO 2:   pick the middle: guess = (low + high) // 2
    # TODO 3:   compare to secret; print the guess + verdict
    # TODO 4:   if correct, return attempts
    # TODO 5:   if too low, narrow low side; if too high, narrow high side
    pass

# Test
print(f"Won in {computer_guess()} attempts")
Hint 1 — Binary search shape Each iteration: guess = (low + high) // 2. If too low, set low = guess + 1. If too high, set high = guess - 1. Repeat.
Hint 2 — Why max 7 Each guess halves the range. log₂(100) ≈ 6.6 — so 7 guesses is enough for any number 1-100. This is why "binary search" is the most efficient general guess strategy.
Show full solution
python
def computer_guess(low=1, high=100):
    secret = 73   # in real life: input from user as they think of one
    attempts = 0
    while low <= high:
        attempts += 1
        guess = (low + high) // 2
        if guess == secret:
            print(f"Got it: {guess} in {attempts} attempts")
            return attempts
        elif guess < secret:
            print(f"  Guess {guess} → too low")
            low = guess + 1
        else:
            print(f"  Guess {guess} → too high")
            high = guess - 1
    return attempts

print(f"Won in {computer_guess()} attempts")

A real interactive version would prompt the user after each guess for "high"/"low"/"correct" instead of comparing against a hidden secret.


What You Learned

  • while True: + break for indefinite loops
  • for-else clause (most beginners never discover this)
  • continue to skip an iteration
  • random.randint(a, b) for inclusive integer ranges
  • Wrapping a self-contained game loop in a function

The combination of loop + condition + state variable + early-exit is the core shape of every interactive program — from a calculator to a game engine. You just wrote it.

Next: TODO List CLI — your first program that remembers data between runs.