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 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.
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.
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:
# 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. 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.
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)
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 ValueErrorso non-numeric input doesn't crash- Range validation: a guess outside the range is rejected with a message
continuestatements that skip the rest of a bad turn (in thisforloop 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.
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.
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
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 loops- The
for-elseclause:elseruns only when the loop ends withoutbreak continueto skip an iterationrandom.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.