Tick Tack Toe

Developer(s): Butch Weinberg
Date: 1984
Type: Program
Platform(s): TS 2068
Tags: Game

Tick Tack Toe is a two-player tic-tac-toe game where players enter their names, choose X or O, and take turns pressing number keys 1–9 corresponding to grid positions. The board is drawn using PLOT commands to render two horizontal and two vertical lines forming the grid, with cell positions mapped to a 3×3 string array `E$`. Win detection is handled in a single long conditional at line 470 that checks all eight possible winning combinations, using the sentinel value 9999 in variable `W` to signal a win. The program tracks cumulative scores for X wins, O wins, and “cat” (draw) outcomes across multiple games. An animated title sequence uses random TAB positions, colored PAPER attributes, and block graphic characters to fill the screen before play begins.


Program Analysis

Program Structure

The program is organized into a main game loop and several subroutines. Execution flows through an intro sequence, player name entry, board setup, turn-by-turn play, win/draw detection, and score display, with optional replay handling at the end of each round.

Line(s)Purpose
5–7REM header and tape-stop splash screen
10Calls intro animation subroutines and CLEAR
20–48Player name input, score variable initialization
50–130Draw board using PLOT, play title tune
140–175Initialize E$ grid array and set current player to X
180–510Main game loop: display board, read keypresses, update grid, check for win
520–610Win/draw announcement, score update, replay prompt
1000–1070Subroutine: random-position colored title animation
1080–1130Subroutine: block-graphic scrolling title effect
1140–1170Subroutine: “Touch C to continue” splash
2000–2050Subroutine: scrolling “ERROR TRY AGAIN” message
2500–2550Subroutine: display current scores for X, O, and Cat
3000–3040Replay handler: same players or restart with new names

Board Representation

The 3×3 grid is stored in the two-dimensional string array E$(3,3), initialized with the digit characters "1" through "9" at lines 150–170. When a player claims a cell, its character is replaced with "X" or "O". A cell is considered unoccupied as long as it still holds its original digit, which also serves as the key mapping for input. The board is rendered by printing individual E$ elements at fixed AT positions rather than using a loop, making cell placement explicit but verbose.

Input Handling

The game uses a busy-wait INKEY$ polling loop at line 270. The raw key code is converted to a cell number via CODE INKEY$-48 at line 280, exploiting the fact that ASCII digit codes start at 48. Each cell is then validated by a pair of lines: one that redirects to the error subroutine if the cell is already taken, and one that assigns the current player’s mark if it is free (lines 290–460). This pattern is repeated nine times, once per cell, making the input section lengthy but straightforward.

Win Detection

All eight winning combinations — three rows, three columns, and two diagonals — are evaluated in a single IF statement at line 470 joined by OR. Note that line 470 redundantly includes both a check for the third row (E$(3,1)=A$ AND E$(3,2)=A$ AND E$(3,3)=A$) as part of the rows group and again as an explicit condition, giving nine conditions where eight are sufficient. When a win is detected, the sentinel W=9999 is set; lines 200–210 then branch to the appropriate winner announcement based on the current value of A$.

Draw (Cat) Detection

A move counter L is incremented at line 480 each turn. At line 180, after the board is displayed, the condition IF L=9 AND W<>9999 routes to line 560 for a “WINNER CAT” announcement, correctly triggering only when all nine cells are filled and no winner has been declared.

Score Tracking

Three variables maintain cumulative scores across games: G (X wins), O (O wins), and N (cat/draw). These are initialized at line 45 and incremented at lines 520, 540, and 560 respectively. The subroutine at line 2500 prints all three scores in a fixed column on the right side of the screen. Notably, L, W, and A$ are reset at lines 48 and 175 at the start of each round rather than at initialization, so scores persist correctly through replays.

Intro Animation Subroutines

The subroutine at line 1000 uses nested loops over paper colors (P 1–7) and repetition count (L 1–3) to print the title at a random TAB position, with a BEEP pitched to the random column value for a randomized audio effect. The subroutine at line 1080 prints the title surrounded by block graphic left-half characters (\: = ▌) in each of the seven paper colors, creating a colored stripe effect across the screen.

Error Message Scrolling

When an invalid cell is chosen, the subroutine at line 2000 implements a simple text ticker. It stores the message in B$, then uses a FOR loop printing a substring B$(I TO Z) that advances one character per iteration, with a BEEP pitched to the loop index. The upper bound Z is clamped to LEN B$ to avoid out-of-range errors.

Notable Bugs and Anomalies

  • Line 610 has a logically impossible guard: IF INKEY$<>"Y" OR INKEY$<>"y" OR INKEY$<>"N" OR INKEY$<>"n" is always true because any single character must differ from at least one of those four. The same pattern appears at lines 1170 and 3040. In practice this condition never filters anything meaningful, but control still reaches these lines only for unrecognized keys, so behavior is acceptable.
  • Line 470’s win check contains a duplicate third-row condition, giving nine OR clauses instead of eight. This is harmless but redundant.
  • The variable O is used both as the O-win score counter (line 45) and as a loop variable name conflict risk; however, since no loop uses O as its loop variable, no actual collision occurs.
  • Line 10 calls POKE 23692,22, a system variable poke specific to the ZX Spectrum/TS2068 that controls the number of lines before a scroll prompt — it has no effect on the TS1000 target mentioned in the REM. This is consistent with the July 1984 modification note for the TS2068.
  • The variable S is computed from player name lengths at lines 40 and 45, then modified at line 600, but is never read for any game logic purpose visible in the listing.

Content

Appears On

The biggest LIST tape yet — play poker accompanied by chip-tune renditions of classic songs, diagnose illnesses with a Bayesian expert system, master Z80 opcodes with a quiz, or unscramble anagrams before the cauldron boils over. Four custom fonts included.

Related Products

Related Articles

Related Content

Image Gallery

Tick Tack Toe

Source Code

    5 REM TICK TACK TOE \* Written by S.J.Weinberg 1983 For TS1000  MOD. Jul. 1984 For TS2068
    7 BORDER 7: PAPER 7: CLS : PRINT AT 10,9; PAPER 2; INK 7; FLASH 1;"STOP THE TAPE"; FLASH 0: PAUSE 300
   10 GO SUB 1000: POKE 23692,22: GO SUB 1080: CLEAR : GO SUB 1140
   20 CLS 
   30 PRINT AT 1,10; INK 1;"TICK TACK TOE"'' INK 2;" \* By S.J.(BUTCH) Weinberg 7/84": BORDER 6
   40 PRINT AT 6,3; INK 3;"Who will be X ?": INPUT M$ : PRINT TAB 10;M$: LET S=331: BEEP .5,1: BEEP .5,9: BEEP .5,5: BEEP .5,9
   45 PRINT AT 10,3; INK 3;"Who will be O ?": INPUT N$: PRINT TAB 10;N$: LET S=S+30: BEEP .5,1: BEEP .5,9: BEEP .5,5: BEEP .5,9: LET G=0: LET O=0: LET N=0: PAUSE 100
   47 CLS 
   48 LET W=0: LET L=0
   50 PRINT AT 1,10; INK 1; FLASH 1;"TICK TACK TOE"; FLASH 0
   60 FOR X=100 TO 160
   70 PLOT X,84: PLOT X,85: PLOT X,98: PLOT X,99: NEXT X
  110 FOR Y=70 TO 114
  120 PLOT 118,Y: PLOT 119,Y: PLOT 141,Y: PLOT 142,Y: NEXT Y
  130 PRINT AT 1,10; PAPER 1; INK 7;"TICK TACK TOE": BEEP .05,6: BEEP .05,6: BEEP .05,6: BEEP .05,4: BEEP .05,4: BEEP .05,4: BEEP .05,2: BEEP .05,2: BEEP .05,2
  140 DIM E$(3,3)
  150 LET E$(1)="123"
  160 LET E$(2)="456"
  170 LET E$(3)="789"
  175 LET A$="X"
  180 PRINT AT  8,13;E$(1,1);AT 8,16;E$(1,2);AT 8,19;E$(1,3);AT 10,13;E$(2,1);AT 10,16;E$(2,2);AT 10,19;E$(2,3);AT 12,13;E$(3,1);AT 12,16;E$(3,2);AT 12,19;E$(3,3): BEEP .05,4: BEEP .05,0: GO SUB 2500: IF L=9 AND W<>9999 THEN GO TO 560
  190 PRINT AT 3,12;"                  "
  200 IF W=9999 AND A$="O" THEN GO TO 520
  210 IF W=9999 AND A$="X" THEN GO TO 540
  220 IF A$="X" THEN PRINT AT 3,4; INK 4;"PLAYER X  "; INK 0;M$
  230 IF A$="O" THEN PRINT AT 3,4; INK 4;"PLAYER O  "; INK 0;N$
  235 GO TO 270
  250 GO SUB 2000
  270 IF INKEY$="" THEN GO TO 270
  280 LET C=CODE INKEY$-48
  290 IF C=1 AND E$(1,1)<>"1" THEN GO TO 250
  300 IF C=1 AND E$(1,1)="1" THEN LET E$(1,1)=A$
  310 IF C=2 AND E$(1,2)<>"2" THEN GO TO 250
  320 IF C=2 AND E$(1,2)="2" THEN LET E$(1,2)=A$
  330 IF C=3 AND E$(1,3)<>"3" THEN GO TO 250
  340 IF C=3 AND E$(1,3)="3" THEN LET E$(1,3)=A$
  350 IF C=4 AND E$(2,1)<>"4" THEN GO TO 250
  360 IF C=4 AND E$(2,1)="4" THEN LET E$(2,1)=A$
  370 IF C=5 AND E$(2,2)<>"5" THEN GO TO 250
  380 IF C=5 AND E$(2,2)="5" THEN LET E$(2,2)=A$
  390 IF C=6 AND E$(2,3)<>"6" THEN GO TO 250
  400 IF C=6 AND E$(2,3)="6" THEN LET E$(2,3)=A$
  410 IF C=7 AND E$(3,1)<>"7" THEN GO TO 250
  420 IF C=7 AND E$(3,1)="7" THEN LET E$(3,1)=A$
  430 IF C=8 AND E$(3,2)<>"8" THEN GO TO 250
  440 IF C=8 AND E$(3,2)="8" THEN LET E$(3,2)=A$
  450 IF C=9 AND E$(3,3)<>"9" THEN GO TO 250
  460 IF C=9 AND E$(3,3)="9" THEN LET E$(3,3)=A$
  470 IF E$(1,1)=A$ AND E$(1,2)=A$ AND E$(1,3)=A$ OR E$(2,1)=A$ AND E$(2,2)=A$ AND E$(2,3)=A$ OR E$(3,1)=A$ AND E$(3,2)=A$ AND E$(3,3)=A$ OR E$(1,1)=A$ AND E$(2,1)=A$ AND E$(3,1)=A$ OR E$(1,2)=A$ AND E$(2,2)=A$ AND E$(3,2)=A$ OR E$(1,3)=A$ AND E$(2,3)=A$ AND E$(3,3)=A$ OR E$(3,1)=A$ AND E$(3,2)=A$ AND E$(3,3)=A$ OR E$(1,1)=A$ AND E$(2,2)=A$ AND E$(3,3)=A$ OR E$(1,3)=A$ AND E$(2,2)=A$ AND E$(3,1)=A$ THEN LET W=9999
  480 LET L=L+1
  490 IF A$<>"X" THEN GO TO 175
  500 LET A$="O"
  510 GO TO 180
  520 PRINT AT 18,5; INK 5; FLASH 1;"WINNER X ";M$; FLASH 0: BEEP .2,10: BEEP .2,10: BEEP .2,10: BEEP 2,-10: LET G=G+1: GO SUB 2500
  530 GO TO 570
  540 PRINT AT 18,5; INK 5; FLASH 1;"WINNER O ";N$; FLASH 0: BEEP .2,10: BEEP .2,10: BEEP .2,10: BEEP 2,-10: LET O=O+1: GO SUB 2500
  550 GO TO 570
  560 PRINT AT 18,5; INK 5; FLASH 1;"WINNER CAT "; FLASH 0;: BEEP 1,-30: BEEP 2,-50: LET N=N+1: GO SUB 2500
  570 PRINT AT 19,1;"Try again? (Y or N)"
  580 IF INKEY$="" THEN GO TO 580
  590 IF INKEY$="Y" OR INKEY$="y" THEN PAUSE 10: GO TO 3000
  600 IF INKEY$="N" OR INKEY$="n" THEN LET S=S-4881: PRINT AT 5,6; INK 2;"\* By (BUTCH) 7/84": BEEP .20,17: BEEP .20,17: BEEP .20,17: BEEP .15,5: BEEP .20,14: BEEP .20,10: BORDER 7: STOP 
  610 IF INKEY$<>"Y" OR INKEY$<>"y" OR INKEY$<>"N" OR INKEY$<>"n" THEN GO TO 580
 1000 CLS 
 1010 FOR L=1 TO 3
 1020 FOR P=1 TO 7
 1030 LET J=INT 19*RND
 1040 PRINT TAB J; PAPER P; INK 9;"TICK TACK TOE": BEEP .25,J
 1050 NEXT P
 1060 NEXT L
 1070 RETURN 
 1080 FOR L=1 TO 3
 1090 FOR P=1 TO 7
 1100 PRINT PAPER P; INK 9;"\: \: \: \: \: \: \: \: \: TICK TACK TOE\: \: \: \: \: \: \: \: \: ": BEEP .05,P*L
 1110 NEXT P
 1120 NEXT L
 1130 RETURN 
 1140 BORDER 6: PRINT INK 2; FLASH 1;AT 11,9;"TICK TACK TOE"; FLASH 0,,,: PRINT TAB 5; INK 4; FLASH 1;"Touch C to continue!!"; FLASH 0
 1150 IF INKEY$="" THEN GO TO 1150
 1160 IF INKEY$="C" OR INKEY$="c" THEN RETURN 
 1170 IF INKEY$<>"C" OR INKEY$<>"c" THEN GO TO 1150
 2000 LET B$="                ERROR TRY AGAIN "
 2010 LET P=LEN B$
 2020 FOR I=1 TO P
 2025 LET Z=I+29: IF Z>=P THEN LET Z=P
 2030 PRINT AT 5,0; INK 2;" ";B$(I TO Z);" ": BEEP .05,I
 2040 NEXT I
 2050 RETURN 
 2500 PRINT AT 14,20; INK 4;"SCORES";AT 15,20;"X= ";G;AT 16,20;"O= ";O;AT 17,20;"CAT= ";N
 2550 RETURN 
 3000 PRINT AT 20,1; INK 2;"Same players? (Y or N)"
 3010 IF INKEY$="" THEN GO TO 3010
 3020 IF INKEY$="Y" OR INKEY$="y" THEN GO TO 47
 3030 IF INKEY$="N" OR INKEY$="n" THEN RUN 20
 3040 IF INKEY$<>"Y" OR INKEY$<>"y" OR INKEY$<>"N" OR INKEY$<>"n" THEN GO TO 3010
 9997 STOP 
 9998 SAVE "TIC TAC TO" LINE 1

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

Scroll to Top