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 find the number, and it counts your attempts.

This is your first program with state that changes over time: there is a loop, a counter, and a condition that ends the game. It is a small program, but it has the same parts as much bigger interactive programs.

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


Step 1: The Single-Round Version

Leave out the loop for now. First, compare one guess to the secret number.

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 so that each of the three branches runs.


Step 2: Add the Loop

A guessing game is a loop that runs until the guess matches. The browser sandbox can't wait for you to type, so the code below loops over a fixed list of guesses with for. In a real terminal you would use while True and 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, the loop looks like this:

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. This is the right place for "you didn't guess in time".


Step 3: Limit the Attempts (Make It Fair)

Six attempts are not enough. 2^6 = 64 < 100 < 128 = 2^7, so a player who uses binary search (always guess the middle) needs up to 7 guesses, and with 7 guesses that player always wins. So we give 7.

python
import random

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

low, high = 1, 100  # a perfect binary search: always guess the middle of what's left

for attempt in range(1, MAX_ATTEMPTS + 1):
    guess = (low + high) // 2
    print(f"Attempt {attempt}/{MAX_ATTEMPTS}: {guess}", end="  ")

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

range(1, MAX_ATTEMPTS + 1) is a common Python pattern. It gives you the 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   # skips the rest of this turn (it still uses up an attempt)

        if guess < low or guess > high:
            print(f"  ⚠️  out of range. Pick a number 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 default arguments, so you can test it with different ranges

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

  • Range validation: a guess outside the range is rejected with a message

  • continue statements that skip the rest of a bad turn (in this for loop a bad guess still uses up an attempt)


Step 5: Multi-Round With a Score

Let the player play several rounds, and keep track of 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, and the computer guesses it with binary search. For a number from 1 to 100, binary search always finds it in 7 guesses or fewer.
5. Multiplayer: two players take turns, and 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' means the secret is higher than the guess
      - 'low'  means the secret is lower than the guess
      - 'yes'  means the guess is correct
    """
    secret = 73   # the "user's" secret, fixed for testing (the 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, move low up, and if too high, move high down
    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 are enough for any number from 1 to 100. This is why binary search is the best general strategy for this game.
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
  • The for-else clause: else runs only when the loop ends without break
  • continue to skip an iteration
  • random.randint(a, b) for inclusive integer ranges
  • Wrapping a self-contained game loop in a function

A loop, a condition, a state variable and an early exit: most interactive programs, from a calculator to a game, are built from these four parts.

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

Found this useful? Share it with a friend who’s learning Python.