This program implements a two-player diagonal-move board game played on a 9×9 grid using block graphics for the border and “X” versus “O” pieces. The board is stored in a 12×13 string array S$, with an 11-character-wide border representation built from a separate 10×11 string array B$ using Spectrum block graphic characters (▄, █, ▟, ▘). The human player inputs moves as two-digit coordinates (row×10+column), and the computer’s AI subroutine at line 1000 scans the board for an X piece that has a valid diagonal capture of an O piece, setting flag FL and direction offsets G and H. Score tracking variables HS (human score) and CS (computer score) count captured pieces, with a win condition at 6 captures.
Program Analysis
Program Structure
The program is divided into three logical sections: board initialization (lines 6–132), the main game loop (lines 135–320), the computer AI subroutine (lines 1000–1129), and the display subroutine (lines 1130–1180). A SAVE command is placed at line 2000 for archiving purposes.
- Lines 6–132: Build the graphical border in
B$, then copy each character into the game state arrayS$. - Lines 135–320: Main game loop — display board, accept player move, validate it, update state, invoke AI.
- Lines 1000–1129: Computer move subroutine — scans for an X piece with an adjacent A$ neighbor and sets direction offsets.
- Lines 1130–1180: Display subroutine — redraws the full board and score line.
Board Representation
The playing field is encoded using two string arrays. B$(10,11) stores the visual rows of the border/grid using Spectrum block graphic characters: \.' (▖), \:: (█), \:. (▙), \':. (▟), \'. (▚), and \.' (▖). The border rows use characters like 1–9 as column labels, and rows 1 and 10 serve as top and bottom decorative borders. The actual game state array S$(12,13) is slightly oversized to allow safe ±1 index arithmetic without boundary errors.
At line 125, the copy loop uses C$(B TO )(1) — a slice-then-index idiom — to extract a single character from position B of the cached row string C$, avoiding repeated indexing into the 2D array B$ inside the inner loop.
Move Input and Validation
The player encodes a board position as a two-digit number: tens digit = row, units digit = column. Lines 167–175 decode this with integer division and modulo arithmetic. The move is validated at line 180: both the row and column differences between source and destination must be exactly 1 (diagonal adjacency), enforced with ABS(A-C)<>1 OR ABS(B-D)<>1. There is no check that the source square actually contains the player’s piece (“O”), so the player could technically move from any non-border square.
Computer AI Subroutine (Lines 1000–1120)
The AI scans the board sequentially, column by column within each row, looking for an “X” piece. For each X found, it checks all four diagonal neighbors for the target piece type stored in A$. The direction variables G and H are set according to which diagonal contains the match. Flag FL=1 is raised and the routine returns as soon as a valid move is found.
The subroutine is called twice in the main loop: first with A$="O" to find a capture move (line 255), and if none is found (FL=0), again with A$=" " to find any move to an empty square (line 265). This gives the computer a simple capture-first priority heuristic.
| Variable | Role |
|---|---|
E, F | Row/column of the X piece found by AI |
G, H | Row/column direction offsets (+1 or -1) for the move |
FL | Flag: 1 if a valid move was found |
HS | Human score (captures) |
CS | Computer score (captures) |
Notable Techniques
- The
S$array is dimensionedDIM S$(12,13)rather than(10,11), providing a one-cell buffer on all sides so the AI’sE±1/F±1accesses never go out of bounds. - Line 135 uses
RND>0.5to randomly decide whether to pre-place an X at position (5,5) and clear (5,7), adding slight positional variety at the start. - The display subroutine uses
PRINT AT 0,0;to home the cursor without clearing the screen, then overwrites the previous board in place — a flicker-free redraw technique. - Line 125’s
C$(B TO )(1)is a character extraction idiom:C$(B TO )produces a substring from position B to the end, and(1)takes its first character — equivalent toC$(B)on some BASICs but here used as a workaround for single-character extraction from a slice.
Bugs and Anomalies
- Line 1145 uses lowercase
aandbinPRINT S$(a,b);— on the Spectrum/TS2068, variable names are case-sensitive in the token stream; these should be uppercaseAandBto match the loop variables declared at lines 1132 and 1140. This is likely a transcription artifact. - The win condition checks at lines 157 and 245 print a win message but do not halt the game — they fall through to the next input prompt. The variable
SWreferenced in both win messages is never assigned, so it would print as 0. - Line 1085 returns from within the scan loop with
EandFpointing to a found piece, but does not resetE,F,G,Hon entry — lines 1000–1003 do this explicitly each call, which is correct. - No check is made that the destination square is empty before placing the player’s piece at line 221, allowing overwriting of border characters or the opponent’s pieces.
Content
Source Code
6 DIM B$(10,11)
10 LET B$(1)="\.'123456789\'."
20 LET B$(2)="1X\::X\::X\::X\::X1"
30 LET B$(3)="2\::X\::X\::X\::X\::2"
40 LET B$(4)="3X\::X\::X\::X\::X3"
50 LET B$(5)="4\::X\::X\::X\::X\::4"
60 LET B$(6)="5 \:: \:: \:: \:: 5"
70 LET B$(7)="6\::O\::O\::O\::O\::6"
80 LET B$(8)="7O\::O\::O\::O\::O7"
90 LET B$(9)="8\::O\::O\::O\::O\::8"
100 LET B$(10)="\'.123456789\.'"
102 LET HS=0
105 LET CS=0
110 DIM S$(12,13)
115 FOR A=1 TO 10
117 LET C$=B$(A)
120 FOR B=1 TO 11
125 LET S$(A,B)=C$(B TO )(1)
130 NEXT B
132 NEXT A
135 IF RND>.5 THEN GO TO 150
140 LET S$(5,5)="X"
141 LET S$(5,7)=" "
150 GO SUB 1130
155 PRINT
157 IF CS=6 THEN PRINT "I WIN ";SW
160 PRINT AT 15,0;"FROM?"
165 INPUT MOVE
166 PRINT AT 15,4;" ";MOVE;" TO?"
167 LET A=INT (MOVE/10)
168 LET B=MOVE-10*A
170 INPUT MOVE
171 PRINT AT 15,0;" "
172 LET C=INT (MOVE/10)
175 LET D=MOVE-10*C
180 IF ABS (A-C)<>1 OR ABS (B-D)<>1 THEN GO TO 160
190 IF S$(C+1)(D+1)="X" THEN LET HS=HS+1
210 LET S$(A+1)(B+1)=" "
221 LET S$(C+1)(D+1)="O"
240 GO SUB 1130
245 IF HS=6 THEN PRINT "YOU WIN ";SW
250 LET A$="O"
255 GO SUB 1000
257 IF FL=1 THEN GO TO 300
260 LET A$=" "
265 GO SUB 1000
300 LET S$(E)(F)=" "
305 IF S$(E+G)(F+H)="O" THEN LET CS=CS+1
310 LET S$(E+G)(F+H)="X"
320 GO TO 150
1000 LET E=2
1001 LET F=2
1002 LET G=0
1003 LET H=0
1010 LET FL=0
1020 IF S$(E)(F)<>"X" THEN GO TO 1100
1040 IF S$(E+1)(F+1)=A$ OR S$(E+1)(F-1)=A$ THEN LET G=1
1050 IF S$(E+1)(F+1)=A$ OR S$(E-1)(F+1)=A$ THEN LET H=1
1060 IF S$(E-1)(F+1)=A$ OR S$(E-1)(F-1)=A$ THEN LET G=-1
1070 IF S$(E+1)(F-1)=A$ OR S$(E-1)(F-1)=A$ THEN LET H=-1
1080 IF G<>0 AND H<>0 THEN LET FL=1
1085 IF FL=1 THEN RETURN
1100 LET E=E+1
1101 IF E>10 THEN LET F=F+1
1102 IF E>10 THEN LET E=2
1110 IF F>11 THEN RETURN
1120 GO TO 1010
1129 STOP
1130 PRINT AT 0,0;
1132 FOR A=1 TO 10
1135 PRINT
1140 FOR B=1 TO 11
1145 PRINT S$(a,b);
1150 NEXT B
1155 NEXT A
1160 PRINT
1165 PRINT
1170 PRINT "ME: ";Cs;" YOU: ";HS
1180 RETURN
2000 SAVE "BATTLE"
2010 STOP
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
