Case Study 2: The Number Guessing Game
Overview
The number guessing game is a classic programming exercise that showcases the REPEAT..UNTIL loop at its best. The computer picks a secret number, the player guesses, and the program provides "too high" or "too low" hints until the player finds the answer. After each round, the player can choose to play again — another REPEAT..UNTIL in action.
This case study builds a complete, polished game with attempt tracking, hint giving, difficulty levels, and replay functionality.
Problem Statement
Write a number guessing game with the following features:
- The player chooses a difficulty level: Easy (1-50), Medium (1-100), or Hard (1-500).
- The computer generates a random secret number within the chosen range.
- The player guesses until they find the number. After each guess, the program says "Too high," "Too low," or "Correct!"
- The program tracks the number of attempts and provides a rating at the end.
- After each round, the player can choose to play again.
- When the player quits, the program displays statistics for the session (games played, best score, average attempts).
Design
Loop Analysis
This game requires multiple layers of loops:
- Outer loop (
REPEAT..UNTIL): Play again loop. The player always plays at least one game. - Inner loop (
REPEAT..UNTIL): Guessing loop. The player always makes at least one guess. - Menu input (
REPEAT..UNTIL): Difficulty selection, validated until a legal choice is made.
All three are REPEAT..UNTIL because each situation requires at least one execution before checking whether to stop.
Pseudocode
Initialize session stats
REPEAT (play again loop)
REPEAT (difficulty selection)
Show difficulty menu
Read choice
UNTIL choice is valid
Generate secret number based on difficulty
REPEAT (guessing loop)
Read guess
Increment attempts
Give hint (too high / too low / correct)
UNTIL guess = secret
Display results and rating
Update session stats
Ask "Play again?"
UNTIL player says no
Display session statistics
Implementation
program NumberGuessingGame;
uses
SysUtils; { For IntToStr }
var
{ Game state }
Secret: Integer;
Guess: Integer;
Attempts: Integer;
MaxNumber: Integer;
Difficulty: Integer;
OptimalGuesses: Integer;
{ Session statistics }
GamesPlayed: Integer;
TotalAttempts: Integer;
BestScore: Integer;
BestDifficulty: string;
{ Control variables }
PlayAgain: Char;
begin
Randomize; { Seed the random number generator }
WriteLn('==========================================');
WriteLn(' THE NUMBER GUESSING GAME');
WriteLn(' Written in Pascal');
WriteLn('==========================================');
WriteLn;
{ Initialize session statistics }
GamesPlayed := 0;
TotalAttempts := 0;
BestScore := MaxInt; { Start with worst possible }
BestDifficulty := '';
{ ===== OUTER LOOP: Play Again ===== }
repeat
GamesPlayed := GamesPlayed + 1;
WriteLn('--- Game ', GamesPlayed, ' ---');
WriteLn;
{ ===== DIFFICULTY SELECTION (validated input) ===== }
repeat
WriteLn('Choose difficulty:');
WriteLn(' 1. Easy (1 to 50)');
WriteLn(' 2. Medium (1 to 100)');
WriteLn(' 3. Hard (1 to 500)');
Write('Your choice (1-3): ');
ReadLn(Difficulty);
if (Difficulty < 1) or (Difficulty > 3) then
WriteLn('Invalid choice. Please enter 1, 2, or 3.');
until (Difficulty >= 1) and (Difficulty <= 3);
{ Set range based on difficulty }
case Difficulty of
1: begin MaxNumber := 50; OptimalGuesses := 6; end;
2: begin MaxNumber := 100; OptimalGuesses := 7; end;
3: begin MaxNumber := 500; OptimalGuesses := 9; end;
end;
{ Generate secret number }
Secret := Random(MaxNumber) + 1; { Random(N) gives 0..N-1 }
WriteLn;
WriteLn('I am thinking of a number between 1 and ', MaxNumber, '.');
WriteLn('Can you guess it?');
WriteLn;
Attempts := 0;
{ ===== GUESSING LOOP ===== }
repeat
Write('Your guess: ');
ReadLn(Guess);
Attempts := Attempts + 1;
if Guess < 1 then
WriteLn(' Please guess a number >= 1.')
else if Guess > MaxNumber then
WriteLn(' Please guess a number <= ', MaxNumber, '.')
else if Guess < Secret then
begin
Write(' Too low!');
if Secret - Guess > MaxNumber div 4 then
WriteLn(' (Way too low...)')
else if Secret - Guess <= 5 then
WriteLn(' (But very close!)')
else
WriteLn;
end
else if Guess > Secret then
begin
Write(' Too high!');
if Guess - Secret > MaxNumber div 4 then
WriteLn(' (Way too high...)')
else if Guess - Secret <= 5 then
WriteLn(' (But very close!)')
else
WriteLn;
end;
until Guess = Secret;
{ ===== RESULTS ===== }
WriteLn;
WriteLn('*** CORRECT! The number was ', Secret, '! ***');
WriteLn('You got it in ', Attempts, ' attempt(s).');
WriteLn;
{ Rating }
Write('Rating: ');
if Attempts <= OptimalGuesses then
WriteLn('PERFECT! You used binary search thinking!')
else if Attempts <= OptimalGuesses + 3 then
WriteLn('Excellent! Very efficient guessing.')
else if Attempts <= OptimalGuesses * 2 then
WriteLn('Good job! Room for improvement.')
else
WriteLn('Keep practicing! Try narrowing the range by half each guess.');
WriteLn('(Optimal for this range: ', OptimalGuesses, ' guesses using binary search)');
{ Update session statistics }
TotalAttempts := TotalAttempts + Attempts;
if Attempts < BestScore then
begin
BestScore := Attempts;
case Difficulty of
1: BestDifficulty := 'Easy';
2: BestDifficulty := 'Medium';
3: BestDifficulty := 'Hard';
end;
end;
{ ===== PLAY AGAIN? ===== }
WriteLn;
repeat
Write('Play again? (Y/N): ');
ReadLn(PlayAgain);
until (PlayAgain = 'Y') or (PlayAgain = 'y') or
(PlayAgain = 'N') or (PlayAgain = 'n');
WriteLn;
until (PlayAgain = 'N') or (PlayAgain = 'n');
{ ===== SESSION STATISTICS ===== }
WriteLn('==========================================');
WriteLn(' SESSION STATISTICS');
WriteLn('==========================================');
WriteLn(' Games played: ', GamesPlayed);
WriteLn(' Total guesses: ', TotalAttempts);
WriteLn(' Average guesses: ', (TotalAttempts / GamesPlayed):0:1);
WriteLn(' Best score: ', BestScore, ' (on ', BestDifficulty, ')');
WriteLn('==========================================');
WriteLn;
WriteLn('Thanks for playing! Goodbye.');
end.
Sample Run
==========================================
THE NUMBER GUESSING GAME
Written in Pascal
==========================================
--- Game 1 ---
Choose difficulty:
1. Easy (1 to 50)
2. Medium (1 to 100)
3. Hard (1 to 500)
Your choice (1-3): 2
I am thinking of a number between 1 and 100.
Can you guess it?
Your guess: 50
Too low!
Your guess: 75
Too high! (But very close!)
Your guess: 63
Too low!
Your guess: 70
Too high! (But very close!)
Your guess: 67
Too low! (But very close!)
Your guess: 68
Too low! (But very close!)
Your guess: 69
*** CORRECT! The number was 69! ***
You got it in 7 attempt(s).
Rating: PERFECT! You used binary search thinking!
(Optimal for this range: 7 guesses using binary search)
Play again? (Y/N): Y
--- Game 2 ---
Choose difficulty:
1. Easy (1 to 50)
2. Medium (1 to 100)
3. Hard (1 to 500)
Your choice (1-3): 1
I am thinking of a number between 1 and 50.
Can you guess it?
Your guess: 25
Too high!
Your guess: 12
Too low! (But very close!)
Your guess: 17
*** CORRECT! The number was 17! ***
You got it in 3 attempt(s).
Rating: PERFECT! You used binary search thinking!
(Optimal for this range: 6 guesses using binary search)
Play again? (Y/N): N
==========================================
SESSION STATISTICS
==========================================
Games played: 2
Total guesses: 10
Average guesses: 5.0
Best score: 3 (on Easy)
==========================================
Thanks for playing! Goodbye.
Analysis
Loop Structure Map
This program contains four REPEAT..UNTIL loops, each serving a distinct purpose:
| Loop | Purpose | Terminates When |
|---|---|---|
| Outer play-again loop | Controls the entire session | Player enters 'N' |
| Difficulty selection | Input validation | Valid choice (1-3) entered |
| Guessing loop | Core gameplay | Guess equals secret |
| Play-again input | Input validation | Valid response (Y/N) entered |
Every one of these is a post-test situation — the body must execute at least once before the condition can be evaluated.
Could We Use Different Loop Types?
- The guessing loop could theoretically be a
WHILEloop, but we would need to initializeGuessto some dummy value (like -1) to ensure the condition is initially True. TheREPEAT..UNTILversion is cleaner because it avoids this artificial initialization. - The play-again loop could not easily be a
FORloop because we do not know how many games the player wants to play. - The difficulty selection is the poster child for
REPEAT..UNTIL: read input, validate, repeat if invalid.
The Binary Search Connection
The "optimal guesses" rating teaches an important algorithmic lesson. The most efficient strategy is binary search: always guess the midpoint of the remaining range. This halves the search space each time, giving a worst case of ceil(log2(N)) guesses. For 100 numbers, that is 7 guesses. This foreshadows the binary search algorithm we will study in later chapters — and it is a lesson the player learns by playing.
Possible Enhancements
- Guess history: Store all guesses in an array and display them with the results. Use a
FORloop to print the history. - Hint system: Allow the player to type "hint" for a clue (e.g., "The number is odd" or "The number is greater than 40"). Limit hints to 2 per game.
- Timed mode: Use Pascal's time functions to measure how long each game takes.
- High score persistence: Save the best score to a file (Chapter 14 material) so it persists across sessions.
- Two-player mode: One player enters the secret number, the other guesses. Use a loop to clear the screen between players.
Exercises
-
Modify the program to limit the number of guesses. On Easy, allow 8 guesses; on Medium, 10; on Hard, 12. If the player runs out of guesses, reveal the answer and end the round. Which part of the
REPEAT..UNTILcondition needs to change? -
Add a fourth difficulty level: "Extreme" (1 to 1000). What is the optimal number of guesses for this range?
-
Add input validation to the guessing loop: if the player enters a number outside the valid range, do not count it as an attempt. (Hint: only increment
Attemptswhen the guess is within range.) -
Track the player's guessing strategy by recording whether each guess was "too high" or "too low." At the end, if the player frequently guessed in the same direction consecutively, suggest they try a binary search approach.
-
Add a "computer plays" mode where the computer guesses using binary search, and the player provides "higher" or "lower" feedback. This demonstrates the optimal strategy. Which loop type would you use for the computer's guessing loop?