Mastermind

Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Game

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:

  1. 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.
  2. Instructions (lines 100–200): Prints the rules to both screen and printer, then pauses.
  3. Game initialization (lines 210–260): Allocates arrays and generates the 4-digit secret code.
  4. 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 digit
  • Q = INT((X - 1000*P)/100) — hundreds digit
  • R = INT((X - 1000*P - 100*Q)/10) — tens digit
  • S = 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 variable D, which shadows the array D(). 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 F from 1 to 4, including positions where D(E) was raised by 20. Since those values are now 20+ above their originals, they will not match any valid B(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: H is 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. However H is 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 corresponding LPRINT, 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

VariablePurpose
NTiming counter for RANDOMIZE seed
B(4)Secret code digits (mutated during scoring)
D(4)Player’s guess digits (mutated during scoring)
DScalar copy of current guess digit in White-peg loop
HBlack peg count; resets each round; win detected at 4
CGuess number (loop counter, 1–20)
XRaw numeric input from player
P,Q,R,SExtracted digits (thousands through units)
E,F,GLoop indices for scoring passes
ALoop 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.