This program implements a three-dimensional tic-tac-toe game on a 3×3×3 grid, offering 49 possible winning lines across horizontal, vertical, and diagonal orientations in all three spatial dimensions. Two custom UDG characters (at positions 144 and 145) are defined via POKE USR to render the player and computer tokens on the board. The computer AI operates at three skill levels: level 1 uses random moves, level 2 skips blocking and winning checks, and level 3 plays a full strategic sequence — center first, then blocking human threats, then seeking its own winning moves. Game state is tracked using two arrays: a(27) stores piece placement and b(49) records the sum of each of the 49 lines, allowing win detection by checking for sums of 3 (computer) or 12 (human, using value 4). The board is rendered with PLOT/DRAW commands to create an isometric-style 3D visual effect with three labeled levels (A, B, C).
Program Analysis
Program Structure
The program is organized into clearly delineated sections separated by REM statements. Execution begins at line 1 but immediately jumps to subroutine 9900 for the instructions and skill-level selection screen. After returning, the board is initialized and drawn before entering the main game loop.
- Lines 1–95: Setup — UDG definition, array initialization, screen configuration
- Lines 100–175: Board drawing using
PRINT ATandPLOT/DRAW - Lines 185–350: Human move input and validation
- Lines 1000–2055: Computer AI move selection
- Lines 7000–7030: Random fallback move
- Lines 7500–7800: Gap-finding subroutine for line completion
- Lines 8000–8830: Move display, line state recording, win/draw detection, turn switching
- Lines 9000–9480: End-of-game scoring and replay prompt
- Lines 9500–9540: Reusable
INKEY$keypress subroutine - Lines 9600–9640: Line state storage subroutine
- Lines 9900–9995: Instructions screen and level selection
Data Representation
The 3×3×3 grid is flattened into a one-dimensional array a(27), where positions 1–9 are level A (bottom), 10–18 are level B (middle), and 19–27 are level C (top). Cell values use 1 for the computer and 4 for the human, chosen deliberately so that line sums are unambiguous: a sum of 3 in b(f) means the computer owns all three cells in that line, while a sum of 12 means the human does. The array b(49) stores the running sum of all 49 lines and is updated via the GO SUB 9600 subroutine each time a move is made.
UDG Character Definition
Two UDG characters are defined at startup using a FOR loop (lines 20–50) with POKE USR CHR$ g+f, a. UDG \a (char 144) holds the computer’s token and \b (char 145) the human’s token, based on the 8-byte bitmaps in the DATA statements at lines 60–70. These are rendered on the board at line 8080 using the expression ("\a" AND m=1)+("\b" AND m=0), a compact Boolean string-selection idiom common in Sinclair BASIC.
AI Strategy and Skill Levels
The computer’s move logic (lines 1000–2055) implements a priority-ordered strategy that is curtailed at lower skill levels:
- Level 1: Only takes center (position 14) if empty; otherwise picks randomly (line 7000).
- Level 2: Takes center, then tries to create its own two-in-a-row, then falls back to strategic positions.
- Level 3: Full strategy — center, block human’s winning threats, create computer winning threats, strategic corners/faces, then random.
The DATA blocks at lines 1060–1080 list the 49 line indices in a priority order. The subroutine at line 7500 receives a line index t and uses a chain of conditional FOR loops to locate the empty cell in that line, storing it in a (repurposed as a temporary variable).
49-Line Enumeration
The win-check section (lines 8500–8745) iterates over all 49 lines using multiple FOR loops that call GO SUB 9600 to accumulate sums into b(). The line categories covered are:
| Line Type | Count | Lines |
|---|---|---|
| Vertical columns (through levels) | 9 | 8510–8525 |
| Horizontal rows within levels (one axis) | 9 | 8530–8555 |
| Horizontal rows within levels (other axis) | 9 | 8560–8575 |
| Level-spanning face diagonals | 3+3+3+3 | 8580–8695 |
| Space diagonals (corner to corner) | 4 | 8710–8745 |
Board Rendering
The 3D board is drawn in two passes. First, PRINT AT statements (lines 105–140) place the digit characters 1–9 as position labels within each of the three levels, using CHR$ a with a starting at 49 (ASCII “1”). Then PLOT/DRAW commands (lines 145–170) draw isometric lines to visually suggest the three-dimensional stacked layers. The POKE 23658,8 at line 95 enables CAPS LOCK for uppercase input throughout the game.
Move Coordinate Mapping
The conversion from a flat position index posn to screen coordinates is performed in lines 8030–8070. The level offset is computed first, then the row and column within the level are derived using modular arithmetic. This mapping must account for the staggered isometric offsets applied during board drawing.
Notable Techniques and Idioms
- Boolean arithmetic strings:
("\a" AND m=1)+("\b" AND m=0)selects between two single-character strings without anIFstatement. - Plural suffix:
"line"+("s" AND b<>1)correctly pluralizes the score message. - INKEY$ handshake: The subroutine at 9500 first waits for any held key to be released (line 9510), then waits for a new keypress (9520), preventing repeated key detection.
- RESTORE targeting:
RESTORE 1060andRESTORE 2055rewind the DATA pointer to specific lines, allowing the same DATA blocks to be re-read multiple times during AI processing. - Variable reuse:
aandbare heavily reused as loop variables, counters, and temporary values in addition to being array namesa()andb(); Sinclair BASIC permits scalar and array variables to share a name. - GO SUB via variable:
LET a=9600followed byGO SUB aat lines 8505–8520 uses a variable as the subroutine target, allowing the same dispatch loop to call the line-recording routine without repeating the literal line number.
Potential Anomalies
The gap-finding subroutine at lines 7510–7620 uses a chain of IF ... FOR constructs without explicit ELSE branching — all conditions are evaluated sequentially. This is safe only because the ranges of t are mutually exclusive. However, if t falls outside all defined ranges (e.g., due to unexpected DATA), no FOR loop is opened and the subsequent NEXT p at line 7800 would cause an error. The DATA priority list at lines 1060–1080 contains exactly 49 values matching the b(49) array, so in normal play this situation should not arise.
At line 8830, the expression 1000-(800 AND m) evaluates to 200 when m=1 (human’s turn) and 1000 when m=0 (computer’s turn), providing a single-line branch to alternate turns without an explicit IF statement.
Content
Source Code
1 REM 3d tic-tac-toe
5 GO SUB 9900
10 REM define characters
20 FOR g=144 TO 145: FOR f=0 TO 7
30 READ a
40 POKE USR CHR$ g+f,a
50 NEXT f: NEXT g
60 DATA 0,120,132,130,65,33,30,0
70 DATA 0,136,72,56,24,20,18,17
80 REM initialise
85 DIM a(27): DIM b(49): LET go=0
90 BORDER 1: PAPER 1: INK 4: CLS
95 POKE 23658,8: POKE 23659,1
100 REM print board
105 FOR f=1 TO 15 STEP 7
110 LET b=8: LET a=49
115 FOR p=f TO f+5 STEP 2
120 FOR q=b TO b+9 STEP 4
125 PRINT AT p,q; INK 5;CHR$ a
130 LET a=a+1: NEXT q
135 LET b=b+2
140 NEXT p: NEXT f
145 FOR f=14 TO 127 STEP 56
150 PLOT 122,f: DRAW -44,44
155 PLOT 154,f: DRAW -44,44
160 PLOT 80,f+14: DRAW 84,0
165 PLOT 64,f+30: DRAW 84,0
170 NEXT f
175 INK 6: PRINT AT 0,0;"LEVEL";AT 3,2;"A";AT 10,2;"B";AT 17,2;"C"
180 REM decide who goes first
185 PRINT AT 22,2;"Do you want to go first?": BEEP .05,40
190 GO SUB 9500
192 IF a$<>"Y" AND a$<>"N" THEN BEEP .1,-24: GO TO 190
194 PRINT AT 22,2,,
196 IF a$="N" THEN LET m=0: GO TO 1000
198 LET m=1
200 REM human's move
210 PRINT AT 0,25;"Your";AT 2,25;"move?"
220 PRINT AT 8,25;"Press";AT 10,25;"level";AT 12,24;"(A,B,C)"
230 PRINT AT 16,25;"then";AT 18,25;"number";AT 20,24;"(1 - 9)"
235 BEEP .05,40
240 GO SUB 9500
245 IF a$<>"A" AND a$<>"B" AND a$<>"C" THEN BEEP .1,-24: GO TO 240
250 BEEP .08,31
260 LET a=CODE a$-64
270 PRINT AT 22,0;"YOUR MOVE IS ";a$;"- ";: PRINT AT 22,15;
280 GO SUB 9500
290 IF a$<"1" OR a$>"9" THEN BEEP .1,-24: PRINT "? Start again.": GO TO 240
300 PRINT a$: BEEP .1,31
310 LET posn=VAL a$+(9 AND a=2)+(18 AND a=3)
320 IF a(posn)<>0 THEN BEEP .1,-24: PRINT AT 22,0;"That place is full: try again.": GO TO 240
330 LET a(posn)=4
340 FOR p=0 TO 21 STEP 2: PRINT AT p,24;" ": NEXT p
350 GO TO 8000
1000 REM computer's move
1005 PRINT AT 22,0;"I'M THINKING...";: BEEP .005,12
1010 REM is center filled?
1020 IF a(14)=0 THEN LET a(14)=1: LET posn=14: GO TO 8000
1025 PRINT ".";: BEEP .005,12
1030 IF level=1 THEN GO TO 7000
1050 REM check for 2 in a row
1060 DATA 14,23,5,48,46,49,47,33,28,39,36,45,43,40,37,30,31,34,42
1070 DATA 25,1,12,9,10,19,7,3,18,21,27,16,35,44,32,29,41,38
1080 DATA 11,20,15,17,2,8,4,26,22,6,13,24
1100 REM -first, human's lines
1105 IF level=2 THEN GO TO 1180
1110 RESTORE 1060
1115 FOR f=1 TO 49
1120 READ t
1125 IF b(t)=8 THEN GO TO 7500
1130 NEXT f
1135 PRINT ".";: BEEP .005,12
1140 REM then, computer's lines
1145 RESTORE 1060
1150 FOR f=1 TO 49
1155 READ t
1160 IF b(t)=2 THEN GO TO 7500
1170 NEXT f
1175 PRINT ".";: BEEP .005,12
1180 REM can computer line 2 up?
1185 RESTORE 1060
1190 FOR f=1 TO 49
1195 READ t
2000 IF b(t)=1 THEN GO TO 7500
2005 NEXT f
2010 PRINT ".";: BEEP .005,12
2015 REM If not, look for empty faces and corners
2020 RESTORE 2055
2025 FOR f=1 TO 14
2030 READ t
2035 IF a(t)=0 THEN LET posn=t: LET a(t)=1: GO TO 8000
2040 NEXT f
2050 PRINT ".";: BEEP .005,12
2055 DATA 15,23,5,13,23,11,27,19,3,7,1,21,25,9
7000 REM if none, random move
7010 LET posn=INT (RND*27)+1
7020 IF a(posn)=0 THEN LET a(posn)=1: GO TO 8000
7030 BEEP .005,12: GO TO 7000
7500 REM Find gap in line
7505 LET a=0
7510 IF t<10 THEN FOR p=t TO t+18 STEP 9
7515 IF t>9 AND t<19 THEN LET q=((t-9) AND t<13)+((t-3) AND (t<16 AND t>12))+((t+3) AND t>15): FOR p=q TO q+7 STEP 3
7520 IF t>18 AND t<28 THEN LET q=(t-19)*3+1: FOR p=q TO q+2
7530 IF t>27 AND t<31 THEN LET q=t-27: FOR p=q TO q+24 STEP 12
7540 IF t>30 AND t<34 THEN LET q=(t-31)*3+1: FOR p=q TO q+20 STEP 10
7550 IF t>33 AND t<37 THEN LET q=t-27: FOR p=q TO q+12 STEP 6
7560 IF t>36 AND t<40 THEN LET q=(t-37)*3+3: FOR p=q TO q+16 STEP 8
7570 IF t>39 AND t<43 THEN LET q=(t-40)*9+1: FOR p=q TO q+8 STEP 4
7580 IF t>42 AND t<46 THEN LET q=(t-43)*9+3: FOR p=q TO q+4 STEP 2
7590 IF t=46 THEN FOR p=1 TO 27 STEP 13
7600 IF t=47 THEN FOR p=3 TO 25 STEP 11
7610 IF t=48 THEN FOR p=7 TO 21 STEP 7
7620 IF t=49 THEN FOR p=9 TO 19 STEP 5
7800 LET a=a+(p AND (a=0 AND a(p)=0)): NEXT p
7900 REM record computer's move
7910 LET posn=a: LET a(a)=1
8000 REM print move
8010 IF m=0 THEN PRINT " ";CHR$ (CODE "B"-(1 AND posn<10)+(1 AND posn>18));"-";posn-(9 AND posn>9)-(9 AND posn>18)
8030 LET a=8-(7 AND posn<10)+(7 AND posn>18)
8040 LET no=posn-(18 AND a=15)-(9 AND a=8)
8050 LET p=a+(2 AND no>3)+(2 AND no>6)
8060 LET q=p-a+8
8070 LET q=q+(4 AND (no=2 OR no=5 OR no=8))+(8 AND (no=3 OR no=6 OR no=9))
8080 BEEP .01,36: PRINT INK 7; BRIGHT 1;AT p,q;("\a" AND m=1)+("\b" AND m=0)
8500 REM search through lines
8505 LET a=9600: LET b=1
8510 FOR p=1 TO 9
8515 LET q=a(p)+a(p+9)+a(p+18)
8520 GO SUB a
8525 NEXT p
8530 FOR p=1 TO 21
8535 IF p=4 THEN LET p=10
8540 IF p=13 THEN LET p=19
8545 LET q=a(p)+a(p+3)+a(p+6)
8550 GO SUB a
8555 NEXT p
8560 FOR p=1 TO 25 STEP 3
8565 LET q=a(p)+a(p+1)+a(p+2)
8570 GO SUB a
8575 NEXT p
8580 FOR p=1 TO 3
8585 LET q=a(p)+a(p+12)+a(p+24)
8590 GO SUB a
8595 NEXT p
8600 FOR p=1 TO 7 STEP 3
8605 LET q=a(p)+a(p+10)+a(p+20)
8610 GO SUB a
8615 NEXT p
8620 FOR p=7 TO 9
8625 LET q=a(p)+a(p+6)+a(p+12)
8630 GO SUB a
8635 NEXT p
8640 FOR p=3 TO 9 STEP 3
8645 LET q=a(p)+a(p+8)+a(p+16)
8650 GO SUB a
8655 NEXT p
8660 FOR p=1 TO 19 STEP 9
8665 LET q=a(p)+a(p+4)+a(p+8)
8670 GO SUB a
8675 NEXT p
8680 FOR p=3 TO 21 STEP 9
8685 LET q=a(p)+a(p+2)+a(p+4)
8690 GO SUB a
8695 NEXT p
8710 LET q=a(1)+a(14)+a(27)
8715 GO SUB a
8720 LET q=a(3)+a(14)+a(25)
8725 GO SUB a
8730 LET q=a(7)+a(14)+a(21)
8735 GO SUB a
8740 LET q=a(9)+a(14)+a(19)
8745 GO SUB a
8750 REM Is game over?
8760 LET go=go+1
8770 IF go=27 THEN GO TO 9000
8800 REM go to next move
8810 PRINT AT 22,0,,
8820 LET m=ABS (m-1)
8830 GO TO 1000-(800 AND m)
9000 REM Count completed lines
9010 LET a=0: LET b=0
9020 FOR f=1 TO 49
9030 IF b(f)=3 THEN LET a=a+1
9040 IF b(f)=12 THEN LET b=b+1
9050 NEXT f
9200 REM Print final score
9210 PRINT AT 1,25;"You";AT 3,25;"have";AT 5,25;b;AT 7,25;"line"+("s" AND b<>1);":"
9220 PRINT AT 11,25;"I have";AT 13,25;a;AT 15,25;"line"+("s" AND a<>1);"."
9230 IF a>b THEN GO TO 9300
9240 IF b=a THEN GO TO 9400
9250 PRINT AT 22,0;"You win, darn it. Another game?"
9260 BEEP 1,-12
9270 GO TO 9430
9300 PRINT AT 22,0; FLASH 1;"I win again!";
9310 PRINT " Another try?"
9320 FOR g=1 TO 5: FOR f=12 TO 36 STEP 10: BEEP .05,f: NEXT f: NEXT g
9330 GO TO 9430
9400 PRINT AT 22,0;" "; INVERSE 1;"DRAW";
9410 PRINT " Another game?"
9420 BEEP .5,24
9430 GO SUB 9500
9440 IF a$<>"Y" AND a$<>"N" THEN BEEP .1,-24: GO TO 9430
9450 PRINT AT 22,0,,
9460 POKE 23659,2
9470 IF a$="Y" THEN RUN
9480 FOR f=12 TO 0 STEP -1: BEEP .05,f: NEXT f: STOP
9500 REM INKEY$ routine
9510 IF INKEY$<>"" THEN GO TO 9510
9520 IF INKEY$="" THEN GO TO 9520
9530 LET a$=INKEY$
9540 RETURN
9600 REM store state of lines
9610 LET b(b)=q
9620 IF q=12 OR q=3 THEN BEEP .01,16+(12 AND q=3)
9630 LET b=b+1
9640 RETURN
9900 REM instructions
9910 BORDER 4: PAPER 4: INK 0: FLASH 0: BRIGHT 0: CLS
9920 PRINT AT 0,7; INVERSE 1;"3-D TIC TAC TOE"
9930 PRINT ''"The object of the game is to complete as many lines as you can. The computer will also try!"
9940 PRINT '"All straight lines count,whetherhorizontal,vertical or diagonal.There are 49 possible lines."
9950 PRINT '''"Choose a skill level:-"''TAB 4;"Press: 1 = dead easy",TAB 11;"2 = hard",TAB 11;"3 = even harder"
9960 BEEP .05,40
9970 GO SUB 9500
9980 IF a$<"1" OR a$>"3" THEN BEEP .1,-24: GO TO 9970
9990 LET level=VAL a$
9995 CLS : RETURN
9999 SAVE "3DTICTACTO" LINE 1
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

