~90 minutes. Build a CLI game where the program picks a secret number 1-100; the player guesses; the program responds. Practice while, if/elif/else, break, and the random module.
Goal: ship a working Python CLI game that runs in a loop until the player wins or runs out of tries.
Estimated time: 90 minutes
Prerequisites: Week 2 lecture (if/elif/else, while, break, random.randint). Lab 1 complete.
Setup
mkdir -p ~/fnd-102/lab-2
cd ~/fnd-102/lab-2
Open lab-2-guess.py.
Part A: Minimum viable game (30 min)
Build the simplest version that works:
- Pick a secret integer between 1 and 100 with
random.randint(1, 100) - Loop:
- Ask the player for a guess
- If the guess is the secret, print "you got it!" and exit the loop
- Otherwise, print "too low" or "too high" and loop again
A minimal version:
import random
secret = random.randint(1, 100)
print('I picked a number between 1 and 100. Guess!')
while True:
guess = int(input('Your guess: '))
if guess == secret:
print('You got it!')
break
elif guess < secret:
print('Too low.')
else:
print('Too high.')
Run it. Play through twice. Note any rough edges (typing eight crashes; entering -5 is accepted as a valid guess; the player can loop forever).
Part B: Bounded retries (20 min)
Modify the game so the player gets a fixed number of tries (say, 7). If they run out, the game says "you lost; the number was X" and exits.
You will replace while True: with a counted loop:
secret = random.randint(1, 100)
max_tries = 7
tries_left = max_tries
while tries_left > 0:
guess = int(input(f'Your guess ({tries_left} tries left): '))
if guess == secret:
print(f'You got it in {max_tries - tries_left + 1} tries!')
break
elif guess < secret:
print('Too low.')
else:
print('Too high.')
tries_left -= 1
if tries_left == 0:
print(f'You ran out of tries. The number was {secret}.')
Notice the if tries_left == 0: after the loop. This is one way to detect "loop ended without break." Another way (Python-specific) is the else clause on a while or for loop:
while tries_left > 0:
# ... guess logic with break on win ...
else:
print(f'You ran out of tries. The number was {secret}.')
The else on a loop runs ONLY if the loop completed without hitting break. It is a Python-unique feature. Optional; either form works.
Part C: UX polish (20 min)
- Reject out-of-range guesses. If the player enters 0, 200, or -5, do not count it against their tries. Print "Out of range; try 1-100" and loop again WITHOUT decrementing
tries_left. Usecontinuefor this. - Reject non-numeric input gracefully. If the player enters
seven,int('seven')crashes. Wrap theint(input(...))in atry/except ValueErrorblock to print "Please type a number" and re-prompt without counting the try. (try/exceptis week 9 material; using it here is a forward-stretch but the pattern is small enough to introduce.)
try:
guess = int(input(f'Your guess ({tries_left} tries left): '))
except ValueError:
print('Please type a number.')
continue
if guess < 1 or guess > 100:
print('Out of range; try 1-100.')
continue
The continue jumps to the top of the while loop without decrementing tries_left, which is what you want.
Part D: Clean exit code (10 min)
Conventionally, a CLI program exits with status 0 on success and nonzero on failure. Add the following at the end of your script:
import sys
# at the end of the game logic, after deciding win/loss:
if tries_left == 0:
print(f'You ran out of tries. The number was {secret}.')
sys.exit(1) # nonzero exit: the player lost
else:
sys.exit(0) # zero exit: the player won
Test from the shell:
python3 lab-2-guess.py
echo $? # prints 0 if you won, 1 if you lost (Bash) or $LastExitCode on PowerShell
The exit code is invisible during interactive play but matters when a program is called by another program (week 9 subprocess work depends on this).
Part E: Commit your work (10 min)
cd ~/fnd-102/lab-2
git add lab-2-guess.py
git commit -m "lab-2: guess-the-number game with bounded retries and clean exit codes"
Expected output / artifact
lab-2-guess.py should:
- Pick a random number 1-100 on each run (verify by running 3 times; you should not get the same number each time)
- Accept the win path: guess the number correctly within 7 tries; the program prints "You got it!" and exits with status 0
- Accept the lose path: exhaust 7 tries; the program prints "You ran out of tries. The number was X." and exits with status 1
- Reject out-of-range guesses with a warning, without decrementing tries
- Survive non-numeric input (
seven) without crashing
The file is committed to your Git repo at ~/fnd-102/lab-2/lab-2-guess.py.
What's the failure mode?
This tool's likely failure modes:
int(input())crashes on non-numeric input. You fixed this in Part C withtry/except. Without that fix, the program crashes with aValueErrortraceback the first time the player typeseight.- Off-by-one in the try count. The player might think they get 7 guesses but actually gets 8, or 6. Test deliberately: lose on purpose and count.
random.randint(1, 100)includes both endpoints. Some random-number functions in other languages are half-open (1 to 99 or 0 to 100); Python'srandintis inclusive on both ends. A subtle bug is "the player guessed 100 and the program said too low," which means the secret was 100 and the player needs one more guess. Decide whether your "too low" message is correct here.
Common pitfalls
while tries_left > 0:infinite loop. If you forgottries_left -= 1in the loop body (or wrotecontinuesomewhere that skipped it), the loop never terminates. Ctrl-C to interrupt; check that every code path either decrements orcontinues past the decrement.if guess = secret:(single equals). Assignment in a condition. Python catches this as aSyntaxError. C-family languages do not; some students who came from those languages type it reflexively.elifvs separateifs. A chain of threeifs (noelif) checks all three independently. Withelif, only the matching branch runs. For mutually exclusive cases (less / equal / greater), useif/elif/else.continuedecrement skip. If yourtries_left -= 1is at the END of the loop body and youcontinueBEFORE it, you do not decrement. That is exactly what you want for "invalid input, free re-prompt." Make sure it is what you intend.
Stretch (optional)
- Difficulty levels. Add a CLI argument (parsed manually from
sys.argvfor now;argparseis week 6) for easy / medium / hard, which sets the range and number of tries: easy = 1-50 with 10 tries; hard = 1-1000 with 10 tries. - Persistent high score. After each game, append the win/loss + tries-used to
~/fnd-102/lab-2/scores.txt. At the start of each game, print "Your best (fewest tries to win): N." (File I/O is week 5; the pattern iswith open('scores.txt', 'a') as f: f.write(line + '\n').) - Play-again loop. Wrap the whole game in an outer
while True:and prompt "Play again? (y/n)" at the end. Break out on n.
Lab 2 v0.1.