Learn
02.6 | PROJECT: A NUMBER GUESSING GAME
01 | DEFINE THE GAME BEFORE CODING
The player must guess an INTEGER secret from one to twenty. After an accepted wrong guess, the game says Too low or Too high. An exact match wins.
The finished game allows five accepted in-range guesses. Non-numeric or out-of-range responses do not use an attempt. A response of quit exits voluntarily. If five accepted guesses are wrong, the game ends with a loss and reveals the secret.
These are deliberate design decisions. Stating them first prevents inconsistent counting or messages when you add features. There is one secret per game, not a new target after each guess.
02 | START WITH ONE FIXED GUESS
Use secret = 12 while developing. A fixed target makes outcomes reproducible. Read one suitable integer guess and choose Too low, Too high or Correct using an if/elif/else chain.
This first version assumes valid integer input and has one guess only. It is a small component to verify before adding repetition. Test 7, 16 and 12 in separate runs.
secret = 12
guess = int(input("Guess from 1 to 20: "))
if guess < secret:
print("Too low")
elif guess > secret:
print("Too high")
else:
print("Correct")03 | GIVE THE GAME A STATE
The game needs attempts, max_attempts and won. attempts counts accepted guesses. max_attempts is the allowed limit. won is a Boolean flag that begins False and becomes True when the guess matches.
The condition attempts < max_attempts and not won means continue only while guesses remain and success has not occurred. Initialise these values before the loop, so they are not reset on every iteration.
The secret is also set before the loop. Choosing it inside the body would move the target and make feedback unreliable.
04 | ADD REPEATED ACCEPTED GUESSES
The loop below introduces repetition while still assuming valid in-range integer input. Each input becomes an accepted guess, so attempts increases once before feedback. A correct first guess is attempt one, not zero.
Setting won to True prevents another iteration. Once the limit is reached, the condition becomes false. The result after the loop must distinguish those two outcomes.
secret = 12
max_attempts = 5
attempts = 0
won = False
while attempts < max_attempts and not won:
guess = int(input("Guess from 1 to 20: "))
attempts += 1
if guess < secret:
print("Too low")
elif guess > secret:
print("Too high")
else:
won = True
print(f"Correct in {attempts} accepted guesses!")
if not won:
print(f"No guesses left. The secret was {secret}.")05 | HANDLE TEXT BEFORE COMPARING NUMBERS
Collect the raw response as text first. strip removes surrounding whitespace. Check quit before conversion, because quit is a command rather than integer text.
Use try/except ValueError around int(text). If conversion fails, print a helpful message and continue. The continue skips the accepted-guess counter and feedback for that iteration.
Then check the permitted range. A whole number such as 25 converts successfully but is still out of range. These two checks establish different properties.
text = input("Guess from 1 to 20, or quit: ").strip()
# This fragment is used inside the full game loop below.
try:
guess = int(text)
except ValueError:
print("That response is not a whole number")06 | COUNT ONLY WHAT THE RULE ACCEPTS
The attempt update belongs after successful conversion and range validation. Otherwise a typo or invalid range value could use a guess despite the stated rule.
Use a separate quit_game flag to record voluntary exit. break leaves the loop, but does not itself explain why it ended. The final selection can then distinguish quit, win and exhausted attempts.
Check the final allowed guess carefully. If attempt five is correct, won becomes True and the win message appears rather than an incorrect loss.
07 | THE COMPLETE TESTABLE CORE GAME
This version uses a fixed secret so every test is repeatable. The named limits appear in prompts and range checks. Read the validation routes before running: invalid input continues without increasing attempts.
# Number guessing game: fixed secret for reproducible testing
MIN_NUMBER = 1
MAX_NUMBER = 20
MAX_ATTEMPTS = 5
secret = 12
attempts = 0
won = False
quit_game = False
print("Guess the secret number")
print(f"Range: {MIN_NUMBER} to {MAX_NUMBER}; accepted guesses: {MAX_ATTEMPTS}")
while attempts < MAX_ATTEMPTS and not won:
text = input(f"Guess {MIN_NUMBER} to {MAX_NUMBER}, or quit: ").strip()
if text == "quit":
quit_game = True
break
try:
guess = int(text)
except ValueError:
print("Enter a whole number or the command quit")
continue
if guess < MIN_NUMBER or guess > MAX_NUMBER:
print("That number is outside the allowed range")
continue
attempts += 1
if guess < secret:
print("Too low")
elif guess > secret:
print("Too high")
else:
won = True
if quit_game:
print(f"Game ended after {attempts} accepted guesses.")
elif won:
print(f"Correct! You used {attempts} accepted guesses.")
else:
print(f"No guesses left. The secret was {secret}.")08 | SWITCH TO A RANDOM SECRET AFTER TESTING
Python’s standard-library random module provides pseudo-random values for games and simulations. import random makes its features available through the module name.
random.randint(MIN_NUMBER, MAX_NUMBER) returns an INTEGER including both endpoints. This differs from range(), whose stop is excluded. Replace the fixed-secret assignment with that call, keeping it before the loop.
This is suitable for a casual learning game, not for secrets such as passwords or security tokens. Keep a fixed-secret development version for regression tests, so random outcomes do not hide a broken branch.
import random MIN_NUMBER = 1 MAX_NUMBER = 20 secret = random.randint(MIN_NUMBER, MAX_NUMBER)
09 | PLAY A BROWSER MODEL OF THE RULES
The activity models the game rules with a new browser-generated secret from one to twenty. It does not execute the Python source. Invalid responses do not use attempts; enter quit to end a round.
10 | TEST EVERY ROUTE WITH SECRET TWELVE
First guess 12 should win with one accepted guess. Sequence 7, 16, 12 should give low, high, then win in three. Sequence 1, 2, 3, 4, 12 should win on the last allowed guess.
Five wrong in-range guesses should lose and reveal twelve. Input hello, 25, then 12 should show two validation messages but win with one accepted guess. Immediate quit should exit after zero accepted guesses.
Include endpoints one and twenty, blank text, a decimal such as 12.5, and whitespace around 12. Explain which inputs convert, which meet the range and which count as guesses.
11 | DEBUG WITHOUT MOVING THE TARGET
If feedback contradicts an earlier clue, check whether secret was reassigned inside the loop. If invalid input reduces remaining guesses, inspect the attempt update’s location. If quitting prints a loss, inspect the final outcome selection.
A temporary diagnostic print of the fixed secret and attempts can help during development, but remove it for actual play. Revealing the secret defeats the normal game even if it helps testing.
Use the known tests after every change. A replay extension should initialise a new round’s state rather than carry the previous round’s count or won flag into it.
12 | MAKE IT YOUR OWN WITHOUT LOSING CLARITY
Personalise the title and messages. Add the number of remaining accepted guesses after a wrong response. Then change the limits through the named constants and update the tests.
Extension: make quit case-insensitive by using text.lower() for the command comparison. lower is a string method that returns a lowercase version; it does not change the original text automatically.
Write a short guide describing range, attempt counting, invalid input and exit commands. Give the game to a partner, then record one improvement based on their experience.
PUT YOUR SKILLS TO WORK
Use the Section 2 challenges to practise these ideas independently. Work in your Python editor, predict results and keep a short test record.
Section 2 challenges Python course navigation