This program implements a two-player Noughts and Crosses (Tic-Tac-Toe) game on a 3×3 grid drawn with inverse-video “%” characters as dividing lines. The grid is rendered at startup using PRINT statements, and player moves are entered via the numeric keypad (keys 1–9 corresponding to grid positions). A lookup string at line 150 encodes the board state and win/draw detection, while line 240 uses two separate lookup strings to map a cell number to its screen coordinates via AT row,col positioning. The game alternates between players, printing “=” (CHR$ 61) for one and “<” (CHR$ 52) for the other, which visually represent O and X tokens.
Program Analysis
Program Structure
The program divides into four logical sections:
- Lines 10–110: Draw the static 3×3 grid using inverse-video “%” characters as horizontal and vertical dividers.
- Lines 120–190: Main game loop — initialises the turn counter, reads a move, places a token, and repeats.
- Lines 200–230: Input subroutine — waits for a key in the range 1–9 to be pressed and converts it to a cell index via
CODE INKEY$-29(ASCII digit minus 29 gives 1–9 for keys “1”–”9″ since ASCII “1”=49, 49−29=20… actually the offset yields a raw CODE value that is used as an index into the lookup strings). - Lines 240–250: Placement subroutine — uses two embedded lookup strings to translate a cell number
Tinto screen row and column, then PRINTs the token character there.
Grid Rendering
Lines 10–110 draw the board with a fixed PRINT layout. Cells are labelled 1–9 using the digit characters, arranged in the expected keypad order (1 top-left, 9 bottom-right). The grid separators are inverse-video “%” characters (% in the source represents an inverse space or block depending on context, here used decoratively as lines).
Key BASIC Idioms
IF INKEY$<>"" THEN GOTO 210followed byIF INKEY$="" THEN GOTO 220— the classic ZX81/TS1000 key-wait idiom: first flush any held key, then wait for a new press.LET T=CODE INKEY$-29— converts the ASCII code of the pressed digit key into a small integer used as a string index. For key “1” (ASCII 49), this gives 20; the lookup strings at lines 150 and 240 are indexed at8*A+BorTrespectively to extract row/column coordinates packed as character codes.PRINT AT row,col;CHR$(X)at line 240 — overwrites a cell on the already-drawn board without redrawing the whole grid.
Lookup String Technique
Line 150 contains a long string of block-graphic characters indexed by 8*A+B where A=T-1 and B=T. The CODE of the extracted character encodes game state information (win detection or next-state transitions). This is a compact alternative to a multi-dimensional array or nested IF statements.
Line 240 similarly uses two short strings indexed by T: the first provides the AT row value and the second provides the AT column value, both stored as character codes in the string data. This maps cell numbers 1–9 directly to their screen positions without any arithmetic.
Token Characters
| CHR$ code | Character | Player |
|---|---|---|
| 52 | 4 | Player 1 (first to move) |
| 61 | = | Player 2 |
The subroutine at line 200 sets X=52 before waiting for input, so the current player’s token is determined by which subroutine path was taken. Line 160 sets X=61 for the second player’s placement. The alternation is handled by the GOSUB ordering in the main loop (lines 120→200, then 160+170→240, then 180→200 again).
Initialisation and Save Block
Lines 260–280 form a standard save/auto-run block: CLEAR resets variables, SAVE "1027%7" saves the program (the inverse characters in the filename act as an auto-run flag on the ZX81/TS1000), and RUN restarts execution. These lines are not part of normal program flow and are only executed if the user manually runs from line 260.
Potential Anomalies
- There is no win-detection displayed to the player — the game loop at lines 130–190 runs indefinitely. The lookup string at line 150 updates
Tbut the result is used only to position the next token; no branch exits the loop on a win condition as visible in the listing. - No bounds checking is performed: if a player presses a key whose
CODEminus 29 falls outside the valid cell range, the string index at line 240 may produce an incorrect or out-of-bounds result. - Overwriting an already-occupied cell is not prevented — the program does not check whether a cell has already been claimed before placing a new token.
Content
Source Code
10 PRINT " % % "
20 PRINT " 1 % 2 % 3"
30 PRINT " % % "
40 PRINT "% % % % % % % % % % % "
50 PRINT " % % "
60 PRINT " 4 % 5 % 6"
70 PRINT " % % "
80 PRINT "% % % % % % % % % % % "
90 PRINT " % % "
100 PRINT " 7 % 8 % 9"
110 PRINT " % % "
120 GOSUB 200
130 LET A=T-1
140 LET B=T
150 LET T=CODE "\. \:'\##\' \.'\: \ '\''\:'\''\ '\.'\##\. \' \: \##\:'\. \''\.'\: \ '\' \:'\.'\: \##\''\ '\' \. \:'\##\. \''\.'\: \' \ '\.'\. \: \ '\''\' \##\:'\. \:'\: \' \''\##\ '\.'\.'\##\. \''\:'\' \: \ '"(8*A+B)
160 LET X=61
170 GOSUB 240
180 GOSUB 200
190 GOTO 140
200 LET X=52
210 IF INKEY$<>"" THEN GOTO 210
220 IF INKEY$="" THEN GOTO 220
230 LET T=CODE INKEY$-29
240 PRINT AT CODE "\' \' \: \: \: \,,\,,\,,"(T),CODE "\: \,,\' \: \,,\' \: \,,"(T);CHR$ (X)
250 RETURN
260 CLEAR
270 SAVE "1027%7"
280 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
