This program implements a 4×4 sliding-tile puzzle called “SPINNER T.M.” where sixteen tiles are arranged in a grid and the player rotates groups of four tiles in a cycle using keyboard keys. The array A$(16,1) stores the current symbol in each of the 16 tile positions, with each cell holding one of four inverse-video characters (X, colon, O, or space) indicating the tile type. The 16 move subroutines (lines 1000–1269) each perform a four-way cyclic rotation among four specific array indices, using a temporary variable B$ for the swap. The display loop redraws the board frame using inverse-video border characters and then plots the current tile symbols into the grid at lines 500–550; PAUSE 60000 inside the loop effectively halts until a keypress is detected in the next iteration. The program auto-saves itself with an auto-run flag via the SAVE command at line 1280.
Program Analysis
Program Structure
The program is divided into four functional regions:
- Initialisation (lines 5–60): Populates a 16-element string array with the starting tile symbols.
- Main game loop (lines 70–570): Scans 16 keys, redraws the board frame and tile grid, then pauses before repeating.
- Move subroutines (lines 1000–1269): One subroutine per key, each performing a four-element cyclic rotation in the tile array.
- Save/restart (lines 1270–1290): Clears memory, saves the program with an auto-run flag, and restarts.
Data Representation
The board is modelled as A$(16,1), a 16-row string array where each row holds exactly one character. The four tile types are initialised in lines 10–60:
| Array indices | Character | Meaning |
|---|---|---|
| 1–4 | %X (inverse X) | Top row tiles |
| 5–8 | %: (inverse colon/block) | Second row tiles |
| 9–12 | %O (inverse O) | Third row tiles |
| 13–16 | % (inverse space) | Bottom row tiles |
The solved state therefore has all tiles of the same type in their respective row, providing a clear visual goal.
Board Layout and Display
The frame is drawn unconditionally on every loop iteration (lines 400–430) using inverse-video border characters including %=, %S, %P etc. to form decorative text and box-drawing lines. The tile symbols are then overlaid in lines 500–550 using PRINT AT 6,(9+2*A) and similar expressions, placing tiles in every other column to give visual spacing. The PAUSE 60000 at line 560 acts as a long delay that is effectively interrupted by the INKEY$ polling on the next loop iteration — since PAUSE does not exit early on keypress in this context, the game relies on the outer FOR T=1 TO 60000 loop cycling rapidly through the INKEY$ checks before reaching the PAUSE.
Move Subroutines and the Rotation Mechanic
Each of the 16 keys (1–4, Q–R, A–F, Z–V) calls a dedicated subroutine that rotates four specific array indices in a cycle. The canonical pattern uses a temporary string B$:
- Save the first element into
B$. - Copy element 2 → position 1, element 3 → position 2, element 4 → position 3.
- Restore
B$into position 4.
This is a standard four-element left-rotation. The indices chosen for each subroutine define which “spinner” on the board is activated, linking adjacent or diagonal cells in a pattern consistent with the game’s name.
Key-to-Subroutine Mapping
| Key | Subroutine | Indices rotated |
|---|---|---|
| 1 | 1000 | 2→13→4→5→2 |
| 2 | 1120 | 3→14→1→6→3 |
| 3 | 1130 | 4→15→2→7→4 |
| 4 | 1140 | 1→16→3→8→1 |
| Q | 1150 | 6→1→8→9→6 |
| W | 1160 | 7→2→5→10→7 |
| E | 1170 | 8→3→6→11→8 |
| R | 1180 | 5→4→7→12→5 |
| A | 1190 | 10→5→12→13→10 |
| S | 1200 | 11→6→9→14→11 |
| D | 1210 | 12→7→10→15→12 |
| F | 1220 | 9→8→11→16→9 |
| Z | 1230 | 14→9→16→1→14 |
| X | 1240 | 15→10→13→2→15 |
| C | 1250 | 16→11→10→3→16 |
| V | 1260 | 13→12→15→4→13 |
Notable Techniques
- Single-character string array:
DIM A$(16,1)exploits the ZX81’s fixed-length string array rows to store exactly one character per tile, making indexing straightforward withA$(N)notation. - Inline INKEY$ polling: Each key check is a separate
IF INKEY$="x" THEN GOSUBline rather than using a variable, avoiding the overhead of storing the key value but requiring 16 consecutive INKEY$ reads per frame, which can miss fast keypresses. - Symmetric keyboard layout: The keys 1–4 occupy a physical row, Q–R the next, A–F the next, and Z–V the bottom — matching the four columns and four rows of the spinner grid in a spatially intuitive way.
- Auto-run save: Line 1280 uses an inverse-video digit in the filename to set the auto-run flag, so the program restarts automatically when loaded.
Anomalies and Notes
- Line 1250 rotates indices 16→11→10→3→16. The middle step goes 11→10 rather than 11→14 as would be expected for a clean 2×2 spinner — this may be an intentional asymmetric move or a transcription error in the original.
- The subroutine numbering is inconsistent: the first subroutine is at 1000 while subsequent ones skip to 1120, leaving a gap of 120 lines that could have accommodated the earlier subroutine’s lines more naturally at 1100.
PAUSE 60000inside the main loop means the screen freezes for a very long time if no key is pressed during the INKEY$ checks at the top of each iteration; the effective frame rate depends entirely on how quickly lines 75–260 are scanned.
Content
Source Code
5 DIM A$(16,1)
10 FOR B=1 TO 4
20 LET A$(B)="%X"
30 LET A$(4+B)="%:"
40 LET A$(8+B)="%O"
50 LET A$(12+B)="% "
60 NEXT B
70 FOR T=1 TO 60000
75 IF INKEY$="1" THEN GOSUB 1000
120 IF INKEY$="2" THEN GOSUB 1120
130 IF INKEY$="3" THEN GOSUB 1130
140 IF INKEY$="4" THEN GOSUB 1140
150 IF INKEY$="Q" THEN GOSUB 1150
160 IF INKEY$="W" THEN GOSUB 1160
170 IF INKEY$="E" THEN GOSUB 1170
180 IF INKEY$="R" THEN GOSUB 1180
190 IF INKEY$="A" THEN GOSUB 1190
200 IF INKEY$="S" THEN GOSUB 1200
210 IF INKEY$="D" THEN GOSUB 1210
220 IF INKEY$="F" THEN GOSUB 1220
230 IF INKEY$="Z" THEN GOSUB 1230
240 IF INKEY$="X" THEN GOSUB 1240
250 IF INKEY$="C" THEN GOSUB 1250
260 IF INKEY$="V" THEN GOSUB 1260
400 PRINT AT 2,7;"%=%=%=%=%=%=%=%=%=%=%=%=%=%=%=%="
405 PRINT AT 3,7;"% %S%P%I%N%N%E%R % T.M."
410 PRINT AT 4,7;"% %=%=%=%=%=%=%=%=%=%=%= % "
411 PRINT AT 5,7;"% % % % "
412 PRINT AT 6,7;"% %X% % %X% "
413 PRINT AT 7,7;"% % % % "
414 PRINT AT 8,7;"% %:% % %:% "
415 PRINT AT 9,7;"% % % % "
416 PRINT AT 10,7;"% %O% % %O% "
417 PRINT AT 11,7;"% % % % "
418 PRINT AT 12,7;"%=%=%= %=%=%="
419 PRINT AT 13,7;"% % % % "
423 PRINT AT 14,7;"% % %*%*%*%*%*%*%*%*%*% % "
425 PRINT AT 15,7;"% % "
430 PRINT AT 16,7;"%=%=%=%=%=%=%=%=%=%=%=%=%=%=%="
500 FOR A=1 TO 4
510 PRINT AT 6,(9+2*A);A$(A);" "
520 PRINT AT 8,(9+2*A);A$(4+A);" "
530 PRINT AT 10,(9+2*A);A$(8+A);" "
540 PRINT AT 12,(9+2*A);A$(12+A);" "
550 NEXT A
560 PAUSE 60000
570 NEXT T
\n1000 LET B$=A$(2)
\n1010 LET A$(2)=A$(13)
\n1020 LET A$(13)=A$(4)
\n1030 LET A$(4)=A$(5)
\n1040 LET A$(5)=B$
\n1050 RETURN
\n1120 LET B$=A$(3)
\n1122 LET A$(3)=A$(14)
\n1124 LET A$(14)=A$(1)
\n1126 LET A$(1)=A$(6)
\n1128 LET A$(6)=B$
\n1129 RETURN
\n1130 LET B$=A$(4)
\n1132 LET A$(4)=A$(15)
\n1134 LET A$(15)=A$(2)
\n1136 LET A$(2)=A$(7)
\n1138 LET A$(7)=B$
\n1139 RETURN
\n1140 LET B$=A$(1)
\n1142 LET A$(1)=A$(16)
\n1144 LET A$(16)=A$(3)
\n1146 LET A$(3)=A$(8)
\n1148 LET A$(8)=B$
\n1149 RETURN
\n1150 LET B$=A$(6)
\n1152 LET A$(6)=A$(1)
\n1154 LET A$(1)=A$(8)
\n1156 LET A$(8)=A$(9)
\n1158 LET A$(9)=B$
\n1159 RETURN
\n1160 LET B$=A$(7)
\n1162 LET A$(7)=A$(2)
\n1164 LET A$(2)=A$(5)
\n1166 LET A$(5)=A$(10)
\n1168 LET A$(10)=B$
\n1169 RETURN
\n1170 LET B$=A$(8)
\n1172 LET A$(8)=A$(3)
\n1174 LET A$(3)=A$(6)
\n1176 LET A$(6)=A$(11)
\n1178 LET A$(11)=B$
\n1179 RETURN
\n1180 LET B$=A$(5)
\n1182 LET A$(5)=A$(4)
\n1184 LET A$(4)=A$(7)
\n1186 LET A$(7)=A$(12)
\n1188 LET A$(12)=B$
\n1189 RETURN
\n1190 LET B$=A$(10)
\n1192 LET A$(10)=A$(5)
\n1194 LET A$(5)=A$(12)
\n1196 LET A$(12)=A$(13)
\n1198 LET A$(13)=B$
\n1199 RETURN
\n1200 LET B$=A$(11)
\n1202 LET A$(11)=A$(6)
\n1204 LET A$(6)=A$(9)
\n1206 LET A$(9)=A$(14)
\n1208 LET A$(14)=B$
\n1209 RETURN
\n1210 LET B$=A$(12)
\n1212 LET A$(12)=A$(7)
\n1214 LET A$(7)=A$(10)
\n1216 LET A$(10)=A$(15)
\n1218 LET A$(15)=B$
\n1219 RETURN
\n1220 LET B$=A$(9)
\n1222 LET A$(9)=A$(8)
\n1224 LET A$(8)=A$(11)
\n1226 LET A$(11)=A$(16)
\n1228 LET A$(16)=B$
\n1229 RETURN
\n1230 LET B$=A$(14)
\n1232 LET A$(14)=A$(9)
\n1234 LET A$(9)=A$(16)
\n1236 LET A$(16)=A$(1)
\n1238 LET A$(1)=B$
\n1239 RETURN
\n1240 LET B$=A$(15)
\n1242 LET A$(15)=A$(10)
\n1244 LET A$(10)=A$(13)
\n1246 LET A$(13)=A$(2)
\n1248 LET A$(2)=B$
\n1249 RETURN
\n1250 LET B$=A$(16)
\n1252 LET A$(16)=A$(11)
\n1254 LET A$(11)=A$(10)
\n1256 LET A$(10)=A$(3)
\n1258 LET A$(3)=B$
\n1259 RETURN
\n1260 LET B$=A$(13)
\n1262 LET A$(13)=A$(12)
\n1264 LET A$(12)=A$(15)
\n1266 LET A$(15)=A$(4)
\n1268 LET A$(4)=B$
\n1269 RETURN
\n1270 CLEAR
\n1280 SAVE "1026%8"
\n1290 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
