Project: Number Guessing Game
1 · The lesson
readThe 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.
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.
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:
# 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:
breakexits the loop early when the user wins.elseon the loop runs only if the loop ended withoutbreak— 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.
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)
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 ValueErrorso non-numeric input doesn't crash- Range validation — guesses outside the bounds get rejected with a hint
continuestatements 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.
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.
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
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:+breakfor indefinite loopsfor-elseclause (most beginners never discover this)continueto skip an iterationrandom.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.