This program implements the classic peg solitaire board game, where the player removes pegs by jumping one over another until ideally only the center peg remains. The board is represented as a 7×7 array P(), with values 1 (empty) and 2 (peg), initialized so all holes are filled except the center at position (4,4). Board graphics are drawn using eight UDGs (characters \\a through \\h) defined via POKEs into UDG memory at line 9940, with the UDG address calculated dynamically using PEEK of system variables 23637–23638. Cursor movement maps both the 5/6/7/8 keys and Spectrum+ cursor keys (detected via CHR$ codes 8–11) to directional movement, with boundary-clamping logic that restricts the cursor to the cross-shaped playable area. Move validation checks that the source has a peg, the destination is empty, exactly one peg lies between them, the jump is orthogonal (not diagonal), and the distance is exactly two squares. Separate musical sequences stored in DATA statements are played on completion or resignation using BEEP with note values read via VAL from string data.
Program Structure
The program is organized into clearly labeled sections separated by REM statements. The flow proceeds as follows:
- Lines 100–140: Variable initialization and board array setup
- Lines 200–360: Multi-page instruction display
- Lines 400–590: Board drawing (UDG tiles, PLOT/DRAW border, status display)
- Lines 600–1040: Cursor movement loop with keyboard input and flashing cursor rendering
- Lines 1100–1310: Jump coordinate collection and move validation
- Lines 1400–1630: Move execution, board update, and completion checks
- Lines 2000–2100: Loss/resignation handler with music
- Lines 3000–3120: Win handler with music and replay prompt
- Lines 9000, 9910–9950: Entry point and UDG definition subroutine
- Line 9999: SAVE with auto-run
UDG Setup
Eight UDGs (\\a through \\h) are used to draw the peg and empty-hole tiles, each occupying a 2×2 character cell. The subroutine at line 9910 defines FN h(x) and FN l(x) for high/low byte extraction. Line 9940 reads the UDG base address dynamically from system variables at addresses 23637–23638 using PEEK, then POKEs the calculated address into 23675–23676. The actual UDG pixel data is embedded as raw bytes inside the REM statement at line 9950, which is a well-known technique for storing binary data inline in a BASIC program without using DATA statements.
Board Representation
The board is stored in a 7×7 array P(Z,Y) initialized to 2 (peg present) throughout, then set to 1 (empty) only at the center position P(4,4). The value 2 means “peg” and 1 means “empty hole.” Positions outside the cross shape are initialized to 2 but never addressed by the cursor logic, so they act as implicit out-of-bounds markers.
The program uses NOT PI (evaluates to 0) and SGN PI (evaluates to 1) extensively instead of literal 0 and 1. This is a common memory-saving technique, since NOT PI and SGN PI tokenize more compactly than numeric literals in some contexts, and also avoids ambiguity with the value 1 used as the “empty” state.
Cursor Movement and Boundary Clamping
Cursor movement is handled at lines 770–900. The board cross-shape is enforced by two conditional clamp steps applied to each axis after movement. For vertical moves (lines 810–830):
- Line 810 clamps Y to [3,5] when X is outside the center column range (columns 3–5), preventing cursor entry into non-existent corner squares.
- Line 830 clamps Y to [1,7] for the full column range. The condition
(FLASHX>2 OR FLASHX<6)is always true (since every integer either exceeds 2 or is less than 6), making line 830 a general wrap-prevention guard for Y.
An analogous pair of clamps applies to horizontal moves at lines 880 and 900. The symmetry of lines 810/830 and 880/900 is slightly redundant — line 830’s condition is a tautology — but the effect is correct in practice.
Movement keys include both the traditional 5/6/7/8 layout and Spectrum+ cursor keys detected via CHR$ 8 (left), CHR$ 9 (right), CHR$ 10 (down), CHR$ 11 (up).
Move Validation
The two-phase “J” key press system at lines 1100–1130 uses KEYM to distinguish the first press (select source) from the second (select destination). Validation at lines 1210–1290 checks in order:
- Source square must have a peg (
P(FROMX,FROMY)=1means empty, so the condition checks for NOT 1, i.e., value 2); destination must be empty (P(TOX,TOY)=2means peg, also rejected). Note: the condition at line 1210 readsP(FROMX,FROMY)\\{20}\\{1}\\{20}\\{0}=1where the token sequence represents the “not equals” operator — source must not be empty. - The intermediate square (jumped peg) is calculated: if X coordinates match, the jumped Y is between FROMY and TOY; otherwise the jumped X is between FROMX and TOX.
- The jumped square must contain a peg (
P(XJUMPED,YJUMPED)=1would be empty — move rejected if so). - Diagonal moves are rejected if both X and Y differ (line 1270).
- Jumps of distance other than 2 are rejected (line 1290 checks
ABS(FROMX-TOX)=2 OR ABS(FROMY-TOY)=2).
An invalid move triggers a low-pitched BEEP and returns to line 655 to restart with KEYM=1.
Screen Coordinate Mapping
Board coordinates (X, Y) in the range 1–7 are mapped to screen rows and columns by the formulas AT 22-3*Y, 3*X-2 and AT 23-3*Y, 3*X-2 for the two rows of each 2-row tile. This gives each cell a 3-row, 2-column footprint on screen, consistent with the UDG tile pairs used.
Music
Both the win and loss sequences store note duration and pitch as string pairs in DATA statements, read back with VAL a$ and VAL b$ to produce BEEP arguments. This avoids storing floating-point numbers directly in DATA, saving memory. The loss tune is played twice via an outer FOR loop at line 2030, then a coda sequence from line 2050’s DATA block is appended.
Notable Anomalies
- Line 1000 is referenced by a GO TO from line 970 but does not appear in the listing. Execution falls through from line 980 to line 1040 normally, so the missing line 1000 is harmless — the fall-through is intentional.
- Line 830’s condition
(FLASHX>2 OR FLASHX<6)is always true for valid integer coordinates 1–7, making it an unconditional clamp. Similarly line 900 for Y. These lines guard against wrap-around but are logically tautological conditions. - The “try again” branch at line 3110 jumps to line 400 (draw board) rather than line 100 (initialize variables), meaning a replay does not reinitialize the array or counters. The board is redrawn but
P(),JUMPS, andPEGSare not reset — this is a bug. - Line 3090 calls
STOPfollowed byCLEARandLOAD ""on “N” response. TheCLEARandLOADafterSTOPare unreachable in normal execution.
Source Code
100 \{20}\{1} REM INITIALISE VARIABLES \{20}\{0}\{20}\{1} \{20}\{0}
110 LET XJUMPED= NOT PI:LET YJUMPED= NOT PI:LET JUMPS= NOT PI:LET PEGS=32
120 DIM P(7,7)
130 FOR Z=1 TO 7:FOR Y=1 TO 7:LET P(Z,Y)=2:NEXT Y:NEXT Z
140 LET P(4,4)= SGN PI
200 \{20}\{1} REM INSTRUCTIONS \{20}\{0}
210 PAPER NOT PI:BORDER NOT PI:CLS :PAPER 2:INK 6
220 PRINT AT NOT PI,9;"*************"; AT SGN PI,9;"* *"; AT 2,9;"* SOLITAIRE *"; AT 3,9;"* *"; AT 4,9;"*************"
240 PAPER NOT PI:PRINT AT 8, NOT PI;"The ancient european game of logic. The game commences with across shaped peg board having pegs in every hole but the centre."
250 PRINT '"The objective of the game is to remove all the pegs barr one, and to leave the last peg in thecentre of the board."
260 PRINT PAPER SGN PI; INK 7; AT 21,3;"PRESS ANY KEY TO CONTINUE.":PAUSE 0
290 PRINT AT 8, NOT PI; TAB 31'"Pegs are removed by 'jumping' one peg over another into an empty hole and removing the jumped peg. Only horizontal and"
300 PRINT "vertical jumps are permitted:no diagonal jumping is allowed. Youmay only jump 1 peg at a time: jumping over holes is not allowed. "
310 PAUSE NOT PI
320 PRINT AT 7, NOT PI;"Use keys 5-8 to position the flashing cursor over the piece you wish to move and press 'J'. Next,position the cursor over"
330 PRINT "the hole you wish to jump into and press 'J'. The spectrum checks jumps and rejects illegalmoves."; TAB NOT PI
350 PRINT TAB 31' TAB 31'"If you cannot move,or simply wish to resign,press 'R'."
360 PAUSE NOT PI
400 \{20}\{1} REM DRAW BOARD \{20}\{0}
410 BORDER 7:PAPER 7:CLS
500 PAPER 6:INK 2:PRINT AT 0,6; TAB 16; AT SGN PI,6;" \a\b \a\b \a\b "; AT 2,6;" \c\d \c\d \c\d "; AT 3,6; TAB 16; AT 4,6;" \a\b \a\b \a\b "; AT 5,6;" \c\d \c\d \c\d "; AT 6,6; TAB 16
510 PRINT AT 6, NOT PI; TAB 22'" \a\b \a\b \a\b \a\b \a\b \a\b \a\b "'" \c\d \c\d \c\d \c\d \c\d \c\d \c\d "' TAB 22
520 PRINT AT 10, NOT PI;" \a\b \a\b \a\b \a\b \a\b \a\b \a\b "'" \c\d \c\d \c\d \c\d \c\d \c\d \c\d "' TAB 22
530 PRINT AT 13, NOT PI;" \a\b \a\b \a\b \a\b \a\b \a\b \a\b "'" \c\d \c\d \c\d \c\d \c\d \c\d \c\d "' TAB 22
540 PRINT AT 16,6;" \a\b \a\b \a\b "; AT 17,6;" \c\d \c\d \c\d "; AT 18,6; TAB 16; AT 19,6;" \a\b \a\b \a\b "; AT 20,6;" \c\d \c\d \c\d "; AT 21,6; TAB 16
550 PLOT 0,127:DRAW 48,0
570 DRAW 0,48:DRAW 80,0:DRAW 0,-48:DRAW 48,0:DRAW 0,-80:DRAW -48,0:DRAW 0,-47:DRAW -80,0:DRAW 0,47:DRAW -48,0:DRAW 0,80
580 PAPER 7:INK 9:PRINT AT 9,23;"MOVES ";JUMPS; AT 12,23;"PEGS LEFT"; AT 13,23;".....=";PEGS
590 BEEP .02,12
600 \{20}\{1} REM MOVE INPUT \{20}\{0}
610 LET KEYM=1:LET FROMX=4:LET FROMY=4:LET TOY=4:LET TOX=4:LET FLASHX=4:LET FLASHY=4:LET NONFLASHX=4:LET NONFLASHY=4
620 PAPER 6:INK P(4,4):FLASH 1
630 IF P(4,4)=1 THEN GO TO 650
640 PRINT AT 10,10;"\a\b"; AT 11,10;"\c\d":FLASH 0:GO TO 660
650 PRINT AT 10,10;"\e\f"; AT 11,10;"\g\h":FLASH 0
655 LET KEYM=1
660 \{20}\{1} REM KEYPRESS VERIFICATION \{20}\{0}
670 LET M$= INKEY$:IF M$="" THEN GO TO 670
680 IF m$="r" OR m$="R" THEN GO TO 2000
690 IF M$="J" OR M$="j" THEN GO TO \{20}\{1}\{20}\{0}1100
710 LET MOVE=5*(M$= CHR$ 8)+8*(M$= CHR$ 9)+7*(M$= CHR$ 11)+6*(M$= CHR$ 10):REM \{20}\{1} SPECTRUM+ CURSORS \{20}\{0}
720 IF M$>"4" AND M$<"9" THEN LET MOVE= VAL M$
750 IF MOVE=0 THEN GO TO 670
770 IF MOVE=5 OR MOVE=8 THEN GO TO \{20}\{1}\{20}\{0}850
780 \{20}\{1} REM VERTICALMOVE \{20}\{0}
790 LET NONFLASHY=FLASHY:LET NONFLASHX=FLASHX
800 LET FLASHY=FLASHY+1*(MOVE=7)-1*(MOVE=6)
810 IF (FLASHX<3 OR FLASHX>5) THEN LET FLASHY=FLASHY-1*(FLASHY=6)+1*(FLASHY=2)
830 IF (FLASHX>2 OR FLASHX<6) THEN LET FLASHY=FLASHY-1*(FLASHY=8)+1*(FLASHY=0)
840 GO TO 910
850 \{20}\{1} REM HORIZONTALMOVE \{20}\{0}
860 LET NONFLASHX=FLASHX:LET NONFLASHY=FLASHY
870 LET FLASHX=FLASHX+1*(MOVE=8)-1*(MOVE=5)
880 IF (FLASHY<3 OR FLASHY>5) THEN LET FLASHX=FLASHX-1*(FLASHX=6)+1*(FLASHX=2)
900 IF (FLASHY>2 OR FLASHY<6) THEN LET FLASHX=FLASHX-1*(FLASHX=8)+1*(FLASHX=0)
910 \{20}\{1} REM "ERASE" OLD CURSOR \{20}\{0}
920 FLASH 0:IF P(NONFLASHX,NONFLASHY)=1 THEN GO TO 940
930 PRINT INK 2; AT 22-3*NONFLASHY,3*NONFLASHX-2;"\a\b"; AT 23-3*NONFLASHY,3*NONFLASHX-2;"\c\d":GO TO 950
940 PRINT INK SGN PI; AT 22-3*NONFLASHY,3*NONFLASHX-2;"\e\f"; AT 23-3*NONFLASHY,3*NONFLASHX-2;"\g\h"
950 \{20}\{1} REM PRINT NEW "CURSOR" \{20}\{0}
960 IF P(FLASHX,FLASHY)=1 THEN GO TO \{20}\{1}\{20}\{0}980
970 PRINT FLASH SGN PI; INK 2; AT 22-3*FLASHY,3*FLASHX-2;"\a\b"; AT 23-3*FLASHY,3*FLASHX-2;"\c\d":GO TO 1000
980 PRINT FLASH SGN PI; INK SGN PI; AT 22-3*FLASHY,3*FLASHX-2;"\e\f"; AT 23-3*FLASHY,3*FLASHX-2;"\g\h"
1040 BEEP .02,12:GO TO 660
1100 \{20}\{1} REM JUMP CO-ORDINATES \{20}\{0}
1110 IF KEYM <>1 THEN GO TO 1130
1120 LET FROMX=FLASHX:LET FROMY=FLASHY:BEEP .25,24:BEEP .25,12:LET KEYM=0:GO TO 660
1130 LET TOX=FLASHX:LET TOY=FLASHY:BEEP .25,24:BEEP .25,12
1200 \{20}\{1} REM VALIDATEMOVE \{20}\{0}
1210 IF P(FROMX,FROMY)\{20}\{1}\{20}\{0}=1 OR P(TOX,TOY)=2 THEN GO TO \{20}\{1}\{20}\{0}1300\{20}\{0}
1230 IF FROMX=TOX THEN LET XJUMPED=FROMX:LET YJUMPED=FROMY+1*(TOY>FROMY)-1*(TOY<FROMY):GO TO 1250
1240 LET YJUMPED=FROMY:LET XJUMPED=FROMX+1*(TOX>FROMX)-1*(TOX<FROMX)
1250 IF P(XJUMPED,YJUMPED)=1 THEN GO TO \{20}\{1}\{20}\{0}1300
1260 \{20}\{1} REM ELIMINATE DIAGONALS \{20}\{0}
1270 IF FROMX <>TOX AND FROMY <>TOY THEN GO TO 1300
1280 \{20}\{1}\{20}\{1} REM ELIMINATE "LONG" JUMPS\{20}\{0}
1290 IF (ABS (FROMX-TOX)=2) OR (ABS (FROMY-TOY)=2) THEN GO TO \{20}\{0}1400
1300 \{20}\{1} REM ERROR DISCOVERED \{20}\{0}
1310 BEEP .5,-12:GO TO 655
1400 \{20}\{1} REM MOVE IS O.K. \{20}\{0}
1410 INK 1:PRINT AT 22-3*FROMY,3*FROMX-2;"\e\f"; AT 23-3*FROMY,3*FROMX-2;"\g\h"
1420 INK 2:PRINT FLASH 1; AT 22-3*TOY,3*TOX-2;"\a\b"; AT 23-3*TOY,3*TOX-2;"\c\d"
1430 INK 1:PRINT AT 22-3*YJUMPED,3*XJUMPED-2;"\e\f"; AT 23-3*YJUMPED,3*XJUMPED-2;"\g\h"
1500 \{20}\{1} REM ALTER VARIABLES \{20}\{0}
1510 LET P(FROMX,FROMY)=1:LET P(XJUMPED,YJUMPED)=1:LET P(TOX,TOY)=2
1520 LET JUMPS=JUMPS+1:LET PEGS=PEGS-1
1530 PRINT PAPER 7; AT 9,29;JUMPS; AT 13,29;PEGS;" "
1600 \{20}\{1} REM CHECK FOR COMPLETION \{20}\{0}
1610 IF PEGS=1 AND P(4,4)=2 THEN GO TO \{20}\{1}\{20}\{0}3000
1620 IF PEGS=1 THEN GO TO \{20}\{1}\{20}\{0}2000\{20}\{0}
1630 GO TO 655
2000 \{20}\{1} REM GAME NOT WON \{20}\{0}\{20}\{0}
2020 PAPER 7:INK 2:PRINT AT 8,23;"YOU MADE"; AT 9,23;JUMPS;" MOVES"; AT 10,23;"LEAVING"; AT 11,23;PEGS;" PEGS"; AT 12,23; TAB NOT PI; AT 13,23; TAB NOT PI
2030 FOR j=1 TO 2:RESTORE 2060:FOR i=1 TO 9:READ a$:READ b$:BEEP VAL a$, VAL b$:NEXT i:NEXT j
2050 RESTORE 2070:FOR i=1 TO 12:READ a$:READ b$:BEEP VAL a$, VAL b$:NEXT i
2060 DATA ".2","4",".2","2",".2","5","1.4","4",".4","11",".2","9",".2","9",".2","7",".2","5"
2070 DATA ".2","7",".2","4",".2","12",".6","9",".2","4",".2","2",".2","7",".6","4",".2","2",".2","0",".2","2","1.4","4"
2100 GO TO 3070
3000 \{20}\{1} REM GAME WON \{20}\{0}
3020 INK 2:PAPER 2:PRINT FLASH 1; AT 8,23;"WELL DONE"
3030 PRINT AT 9,23;"COMPLETED"; AT 10,23;"IN ";JUMPS; AT 11,23;"MOVES "
3040 RESTORE 3050:FOR i=1 TO 28:READ a$:READ b$:BEEP VAL a$, VAL b$:NEXT i
3050 DATA ".3","0",".15","-3",".15","0","1","2",".15","-5",".15","-3",".3","0",".3","2",".3","7",".15","9",".15","2",".3","7",".3","9","1.4","2"
3060 DATA ".3","0",".15"\{18}\{0},"-3",".15","0","1","2",".15","-5",".15","-3",".3","0",".3","2",".3","7",".15","9",".15","2",".3","7",".3","9","1","11"
3070 PRINT AT 12,23;"TRY AGAIN"; AT 13,23;"(Y OR N)"
3090 LET R$= INKEY$:IF R$="n" OR R$="N" THEN STOP :CLEAR :PRINT AT 10,8;"START THE TAPE":LOAD ""
3110 IF R$="y" OR R$="Y" THEN GO TO 400
3120 GO TO 3090
9000 GO SUB 9910:RUN
9910 REM POKE UDGs
9920 DEF FN h(x)= INT (x/256):DEF FN l(x)=x-256* FN h(x)
9930 :
9940 LET x= VAL "(PEEK 23637+ PEEK 23638*256)+6":POKE 23675, FN l(x):POKE 23676, FN h(x):RETURN
9950 REM \{1}\{3}\{7}\{31}\{23};}RETURN \ USR LPRINT SAVE CONTINUE BRIGHT PEEK \*RETURN };\{23}\{31}\{7}\{3}\{1}\*PEEK BRIGHT CONTINUE SAVE LPRINT USR \ \{0}\{0}\{0}\{0}\{3}\{7}\{15}\{15}\{0}\{0}\{0}\{0}USR LPRINT LIST LIST \{15}\{15}\{7}\{3}\{0}\{0}\{0}\{0}LIST LIST LPRINT USR \{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}\{0}
9999 SAVE "solitaire" LINE 9000
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

