This program implements the classic Mastermind code-breaking game, supporting up to 20 guesses to crack a randomly generated 4-digit code where digits range from 1 to 9. The secret code is stored in array B(4) and the player’s guess is decomposed digit by digit using integer arithmetic rather than string parsing. Black pegs (correct digit, correct position) and White pegs (correct digit, wrong position) are determined through a two-pass comparison loop that temporarily modifies array values by adding 10 or 20 to mark already-matched elements. The program mirrors all output to a printer via paired PRINT/LPRINT statements throughout. Randomization is seeded by counting how long the player holds before pressing a key at the start, providing a simple but effective entropy source.
Program Structure
The program is organized into four broad phases:
- Setup and randomization (lines 5–80): Sets colors, displays the title, and seeds the random number generator by counting loop iterations while waiting for a keypress.
- Instructions (lines 100–200): Prints the rules to both screen and printer, then pauses.
- Game initialization (lines 210–260): Allocates arrays and generates the 4-digit secret code.
- Guess loop (lines 270–670): Iterates up to 20 times, accepting input, decomposing digits, scoring Black/White pegs, and checking win/loss conditions.
Randomization Technique
Lines 30–70 implement a timing-based seed. The variable N is incremented each iteration of a busy-wait loop (lines 50–60) that spins until INKEY$ returns a non-empty string. The accumulated count is then passed to RANDOMIZE N, making the seed dependent on human reaction time — a straightforward entropy source requiring no machine code.
Digit Decomposition
Rather than treating the input as a string, the program uses repeated integer division to extract individual digits from the numeric input X:
P = INT(X/1000)— thousands digitQ = INT((X - 1000*P)/100)— hundreds digitR = INT((X - 1000*P - 100*Q)/10)— tens digitS = INT(X - 1000*P - 100*Q - 10*R)— units digit
Input validation at lines 310–320 rejects values outside the range 1000–9999, ensuring exactly four digits are always present.
Black/White Peg Scoring Algorithm
The scoring is a two-pass algorithm that avoids double-counting by mutating array values in place:
Pass 1 (lines 410–470) — Black pegs: For each position E, if D(E) = B(E), a “Black” is printed, B(E) is increased by 10, and D(E) by 20. This marks both as used so they cannot participate in the White-peg pass. The hit counter H is incremented.
Pass 2 (lines 490–570) — White pegs: For each unmatched guess digit D(F) (those not incremented to 20+), the program scans all positions of B for a match. Because matched secret digits were already raised by 10, they won’t spuriously match. On a hit, B(G) is raised by another 10 to prevent it from matching again.
Restoration (lines 580–610): After scoring, any B(G) value ≥ 10 has 10 subtracted to restore the original code for the next guess.
Notable Bugs and Anomalies
- White peg over-counting: The White-peg pass at line 500 copies
D(F)into the scalar variableD, which shadows the arrayD(). However, in Sinclair BASIC a scalar and an array of the same letter can coexist, so this works correctly — but it is a confusing naming choice. - Black-marked guess digits not skipped in White pass: The White-peg loop (line 490) iterates over all
Ffrom 1 to 4, including positions whereD(E)was raised by 20. Since those values are now 20+ above their originals, they will not match any validB(G)(which are at most 1–9 or 11–19 after one Black mark), so the exclusion is implicit but correct. - Secret code digits are 1–9 only: Line 250 uses
INT(RND*9)+1, which produces values 1–9, excluding 0. The rules text does not mention this restriction explicitly. - H not reset between Black and White passes:
His used only as a Black-peg counter to detect a win (line 480). It is reset to 0 at line 620 after each failed guess, which is correct. HoweverHis never used to count White pegs, so the naming is slightly misleading. - Win message missing printer output: Line 680 prints the win message only to the screen via
PRINT; there is no correspondingLPRINT, unlike the rest of the program. - Dual PRINT/LPRINT throughout: Every output statement is duplicated for the printer. This is functional but verbose; a subroutine approach would have been more compact.
Variable Summary
| Variable | Purpose |
|---|---|
N | Timing counter for RANDOMIZE seed |
B(4) | Secret code digits (mutated during scoring) |
D(4) | Player’s guess digits (mutated during scoring) |
D | Scalar copy of current guess digit in White-peg loop |
H | Black peg count; resets each round; win detected at 4 |
C | Guess number (loop counter, 1–20) |
X | Raw numeric input from player |
P,Q,R,S | Extracted digits (thousands through units) |
E,F,G | Loop indices for scoring passes |
A | Loop index for code generation |
Source Code
5 INK 7: BORDER 0: PAPER 0
10 PRINT TAB 10;"MASTERMIND": LPRINT TAB 10;"MASTERMIND"
15 PAUSE 90
20 CLS
30 LET N=1
40 PRINT "Press a key": LPRINT "Press a key"
50 LET N=N+1
60 IF INKEY$="" THEN GO TO 50
70 RANDOMIZE N
80 CLS
100 PRINT : LPRINT
110 PRINT "When told to do so, enter a"'"4-digit number and then press"'"RETURN"''"Digits can be repeated"''"You have 20 tries to break the"'"code"
120 LPRINT "When told to do so, enter a"'"4-digit number and then press"'"RETURN"''"Digits can be repeated"''"You have 20 tries to break the"'"code"
190 PAUSE 600
200 CLS
210 DIM B(4)
220 DIM D(4)
230 LET H=0
240 FOR A=1 TO 4
250 LET B(A)=INT (RND*9)+1
260 NEXT A
270 FOR C=1 TO 20
280 PRINT : LPRINT
290 PRINT "Enter guess number ";C;"=";: LPRINT "Enter guess number ";C;"=";
300 INPUT X: PRINT X: LPRINT X
310 IF X>9999 THEN GO TO 290
320 IF X<1000 THEN GO TO 290
330 LET P=INT (X/1000)
340 LET Q=INT ((X-1000*P)/100)
350 LET R=INT ((X-1000*P-100*Q)/10)
360 LET S=INT (X-1000*P-100*Q-10*R)
370 LET D(1)=P
380 LET D(2)=Q
390 LET D(3)=R
400 LET D(4)=S
410 FOR E=1 TO 4
420 IF D(E)<>B(E) THEN GO TO 470
430 PRINT " Black";
435 LPRINT " Black";
440 LET B(E)=B(E)+10
450 LET D(E)=D(E)+20
460 LET H=H+1
470 NEXT E
480 IF H=4 THEN GO TO 680
490 FOR F=1 TO 4
500 LET D=D(F)
510 FOR G=1 TO 4
520 IF D<>B(G) THEN GO TO 560
530 PRINT " White";
535 LPRINT " White";
540 LET B(G)=B(G)+10
550 GO TO 570
560 NEXT G
570 NEXT F
580 FOR G=1 TO 4
590 IF B(G)<10 THEN GO TO 610
600 LET B(G)=B(G)-10
610 NEXT G
620 LET H=0
630 PRINT : LPRINT
640 NEXT C
650 PRINT : PRINT "You did not get it...."''"The answer is: ";B(1);B(2);B(3);B(4)
660: LPRINT "You did not get it...."''"The answer is: ";B(1);B(2);B(3);B(4)
670 STOP
680 PRINT '''"Well done"'''"You got the answer in"''TAB 5;"just ";C;" tries"
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.