Skip to content

Choose an empty square

Choose an empty square in a readable Sinclair BASIC opponent game.

  • Sinclair ZX Spectrum
  • BASIC

Give O to the computer. Its first rule will be modest: choose the first empty square in numerical order. A predictable opponent makes a useful starting point because we can check every choice.

Replace lines 20, 320, 330, 340, 1060.

Add lines 600, 610, 620, 630, 640, 650, 660, 670, 4000, 4010, 4020, 4030.

Delete lines 7000, 7010.

To delete a BASIC line, enter its number on its own.

changes.bas
  20 DIM b(9): LET moves=0
 320 LET b(n)=1: LET moves=moves+1: GO SUB 2000
 330 GO SUB 3000
 340 IF winner>0 OR moves=9 THEN GO TO 4900
 600 PRINT AT 18,2; INK 6;"O is choosing...             "
 610 GO SUB 4000
 620 LET n=mv: LET b(n)=2: LET moves=moves+1: GO SUB 2000
 630 PRINT AT 19,2; INK 5;"O chose ";mv;": ";a$;"        "
 640 GO SUB 3000
 650 IF winner>0 OR moves=9 THEN GO TO 4900
 660 PRINT AT 18,2; INK 7;"Your move: choose 1-9.        "
 670 GO TO 250
1060 PRINT AT 18,2; INK 7;"Your move: choose 1-9."
4000 LET mv=0
4010 FOR j=1 TO 9
4020 IF mv=0 AND b(j)=0 THEN LET mv=j
4030 NEXT j: LET a$="first empty": RETURN

RUN the changed program.

Complete source at this checkpoint
three.bas
  10 BORDER 0: PAPER 0: INK 7: BRIGHT 1: CLS
  20 DIM b(9): LET moves=0
  30 GO SUB 1000
  40 GO TO 250
 250 GO SUB 9000
 260 LET k$=INKEY$: IF k$="" THEN GO TO 260
 270 IF k$="q" OR k$="Q" THEN GO TO 8000
 280 IF k$="r" OR k$="R" THEN RUN
 290 IF k$<"1" OR k$>"9" THEN GO TO 250
 300 LET n=VAL k$
 310 IF b(n)<>0 THEN PRINT AT 18,2; INK 6;"Taken. Choose an empty square.": GO TO 250
 320 LET b(n)=1: LET moves=moves+1: GO SUB 2000
 330 GO SUB 3000
 340 IF winner>0 OR moves=9 THEN GO TO 4900
 600 PRINT AT 18,2; INK 6;"O is choosing...             "
 610 GO SUB 4000
 620 LET n=mv: LET b(n)=2: LET moves=moves+1: GO SUB 2000
 630 PRINT AT 19,2; INK 5;"O chose ";mv;": ";a$;"        "
 640 GO SUB 3000
 650 IF winner>0 OR moves=9 THEN GO TO 4900
 660 PRINT AT 18,2; INK 7;"Your move: choose 1-9.        "
 670 GO TO 250
1000 CLS
1010 PRINT AT 0,8; INK 5;"THREE IN A ROW"
1030 INK 1: FOR i=0 TO 3: PLOT 56 +i*48,32: DRAW 0,120: NEXT i
1040 FOR i=0 TO 3: PLOT 56,32 +i*40: DRAW 144,0: NEXT i
1050 FOR n=1 TO 9: GO SUB 2000: NEXT n
1060 PRINT AT 18,2; INK 7;"Your move: choose 1-9."
1090 PRINT AT 21,2; INK 7;"1-9: place  R: reset  Q: quit"
1100 RETURN
2000 LET row=INT ((n-1)/3): LET col=n-1 -row*3
2010 LET x=80 +col*48: LET y=132 -row*40
2020 PRINT AT 5 +row*5,10 +col*6; INK 7;" "
2030 IF b(n)=0 THEN PRINT AT 5 +row*5,10 +col*6; INK 7;n: RETURN
2040 IF b(n)=2 THEN GO TO 2100
2050 INK 5: FOR z=0 TO 1: PLOT x-10 +z,y-10: DRAW 20,20: PLOT x-10 +z,y+10: DRAW 20,-20: NEXT z
2060 RETURN
2100 INK 7: FOR z=0 TO 1: PLOT x-6,y+10 -z: DRAW 12,0: DRAW 4,-4: DRAW 0,-12: DRAW -4,-4: DRAW -12,0: DRAW -4,4: DRAW 0,12: DRAW 4,4: NEXT z
2110 RETURN
3000 LET winner=0: LET wl=0: RESTORE 9500
3010 FOR w=1 TO 8: READ p,q,f
3020 IF b(p)<>0 AND b(p)=b(q) AND b(q)=b(f) THEN LET winner=b(p): LET wl=w
3030 NEXT w: RETURN
4000 LET mv=0
4010 FOR j=1 TO 9
4020 IF mv=0 AND b(j)=0 THEN LET mv=j
4030 NEXT j: LET a$="first empty": RETURN
4900 LET r$="DRAW. No line completed."
4910 IF winner=1 THEN LET r$="X WINS. Three in a row."
4920 IF winner=2 THEN LET r$="O WINS. Three in a row."
4930 IF wl>0 THEN GO SUB 6000
4940 PRINT AT 18,2; INK 6;r$;"      "
5000 GO SUB 9000
5010 LET k$=INKEY$: IF k$="" THEN GO TO 5010
5020 IF k$="q" OR k$="Q" THEN GO TO 8000
5030 IF k$="r" OR k$="R" THEN RUN
5040 GO TO 5010
6000 RESTORE 9500: FOR w=1 TO wl: READ p,q,f: NEXT w
6010 LET row=INT ((p-1)/3): LET col=p-1 -row*3
6020 LET sx=80 +col*48: LET sy=132 -row*40
6030 LET row=INT ((f-1)/3): LET col=f-1 -row*3
6040 INK 6: PLOT sx,sy: DRAW 80 +col*48 -sx,132 -row*40 -sy: RETURN
8000 PAPER 0: INK 7: BRIGHT 0: PRINT AT 21,2;"Three in a Row finished.     ": STOP
9000 IF INKEY$<>"" THEN GO TO 9000
9010 RETURN
9500 DATA 1,2,3,4,5,6,7,8,9
9510 DATA 1,4,7,2,5,8,3,6,9
9520 DATA 1,5,9,3,5,7

Play 1. O should choose 2 and explain first empty. Play 5; O takes 3. Play 9 and X wins the diagonal. There must be no extra O move after that win.

A routine returns a choice

The routine at 4000 starts mv at zero, meaning “no move selected”. It visits squares 1–9. When mv is still zero and b(j) is empty, it stores j in mv. Later empty squares do not replace that first choice. NEXT stays outside the conditional line.

The routine chooses but does not place. On returning, line 620 copies mv to n, stores O in b(n), increases moves and calls the cell drawing routine. Line 630 prints the selected square and a$, the reason supplied by the choosing routine.

The main loop now has two move paths. Human input always stores 1. O’s path always stores 2. Both draw the accepted mark before calling the result scan, and both check whether the round has ended before handing over. We can remove the two-player turn variable and caption routine because these paths establish whose turn it is.

A promise about the board

The choosing routine is called only when there is an empty square and the game has no winner. Under those conditions it must return a legal square. If a full board reached it, mv would remain zero and b(0) would be invalid. The end checks are therefore part of the routine’s calling contract, not optional decoration.

The display says that O is choosing, then keeps its reason on the row below your prompt. A longer message is padded with spaces so a shorter later reason leaves no stale letters behind.

Why not let every empty square replace mv in the loop?

Show the explanation

That would choose the last empty square instead of the first. Either can be a legal policy, but our stated rule and displayed reason must agree with the code. The mv = 0 guard preserves the first choice.

Try an occupied key after O replies, then hold a number through its next turn. Neither should grant another human move without a fresh valid press. R restarts with X; Q quits.

Sources

Steven Vickers, edited by Robin Bradbeer, ZX Spectrum BASIC Programming, second edition (Sinclair Research, 1983), chapter 4, chapter 5, chapter 13.

Run it here

This unit's program, built for you.

Program
three.tap
Machine
ZX Spectrum 48K (PAL)

Download three.tap