The four-cell body works by copying coordinates along the arrays. We can preserve that same body without moving every stored coordinate: remember which slots are currently live.
Replace lines 100, 390, 3500, 3510, 3530.
Add lines 350, 360, 3520, 3540.
100 DIM a(12): DIM b(12)
350 LET t=t+1: IF t=13 THEN LET t=1
360 GO TO 390
390 LET h=h+1: IF h=13 THEN LET h=1
3500 LET hit=0: LET slot=t
3510 FOR j=1 TO n
3520 IF j=1 AND grow=0 THEN GO TO 3540
3530 IF a(slot)=nr AND b(slot)=nx THEN LET hit=1
3540 LET slot=slot+1: IF slot=13 THEN LET slot=1RUN the changed program.
Complete source at this checkpoint
10 GO SUB 7000
100 DIM a(12): DIM b(12)
110 LET n=4: LET eaten=0: LET h=4: LET t=1: LET dr=0: LET dc=1: LET steps=0
120 FOR j=1 TO 4: LET a(j)=8: LET b(j)=j+5: NEXT j
130 GO SUB 1000
140 LET nd=dr: LET nc=dc: LET turn=0: LET tick=PEEK 23672
150 LET k$=INKEY$
160 IF k$="q" OR k$="Q" THEN GO TO 9000
170 IF k$="r" OR k$="R" THEN GO TO 100
180 IF turn=1 THEN GO TO 240
190 IF (k$="i" OR k$="I") AND dr=0 THEN LET nd=-1: LET nc=0: LET turn=1
200 IF (k$="k" OR k$="K") AND dr=0 THEN LET nd=1: LET nc=0: LET turn=1
210 IF (k$="j" OR k$="J") AND dc=0 THEN LET nd=0: LET nc=-1: LET turn=1
220 IF (k$="l" OR k$="L") AND dc=0 THEN LET nd=0: LET nc=1: LET turn=1
240 LET elapsed=PEEK 23672 -tick
250 IF elapsed<0 THEN LET elapsed=elapsed+256
260 IF elapsed<18 THEN GO TO 150
270 LET tick=PEEK 23672: LET dr=nd: LET dc=nc
280 LET nr=a(h)+dr: LET nx=b(h)+dc
290 IF nr<1 OR nr>16 OR nx<1 OR nx>24 THEN LET e$="Mind the wall!": GO TO 4000
300 LET grow=0
310 GO SUB 3500: IF hit=1 THEN LET e$="You caught your tail!": GO TO 4000
320 PRINT AT a(h)+3,b(h)+3; INK 5;CHR$ 149
340 PRINT AT a(t)+3,b(t)+3;" "
350 LET t=t+1: IF t=13 THEN LET t=1
360 GO TO 390
390 LET h=h+1: IF h=13 THEN LET h=1
400 LET a(h)=nr: LET b(h)=nx: LET steps=steps+1
410 GO SUB 3000
460 LET nd=dr: LET nc=dc: LET turn=0: GO TO 150
1000 BORDER 0: PAPER 0: INK 7: CLS
1010 PRINT AT 0,11; INK 5;"TAIL CHASE"
1020 PRINT AT 2,5; INK 7;"Keep some room to turn."
1030 FOR x=3 TO 28: PRINT AT 3,x; INK 1;CHR$ 144;AT 20,x;CHR$ 144: NEXT x
1040 FOR y=4 TO 19: PRINT AT y,3; INK 1;CHR$ 144;AT y,28;CHR$ 144: NEXT y
1050 FOR j=1 TO 4: PRINT AT a(j)+3,b(j)+3; INK 5;CHR$ 149: NEXT j
1060 GO SUB 3000
1070 PRINT AT 21,1; INK 5;"I J K L steer R retry Q quit";
1080 RETURN
3000 LET z=146
3010 IF dr=-1 THEN LET z=145
3020 IF dr=1 THEN LET z=147
3030 IF dc=-1 THEN LET z=148
3040 PRINT AT a(h)+3,b(h)+3; INK 7;CHR$ z
3050 RETURN
3500 LET hit=0: LET slot=t
3510 FOR j=1 TO n
3520 IF j=1 AND grow=0 THEN GO TO 3540
3530 IF a(slot)=nr AND b(slot)=nx THEN LET hit=1
3540 LET slot=slot+1: IF slot=13 THEN LET slot=1
3550 NEXT j: RETURN
4000 PRINT AT 2,0;" ";AT 2,2; INK 6;e$
4010 PRINT AT 21,1; INK 7;"R for another go Q to quit ";
4020 IF INKEY$<>"" THEN GO TO 4020
4030 LET k$=INKEY$
4040 IF k$="q" OR k$="Q" THEN GO TO 9000
4050 IF k$="r" OR k$="R" THEN GO TO 100
4060 GO TO 4030
7000 RESTORE 7200: FOR j=0 TO 47: READ v: POKE USR "a"+j,v: NEXT j: RETURN
7200 DATA 255,129,129,129,255,129,129,255
7210 DATA 60,126,90,90,126,126,60,24
7220 DATA 60,126,252,204,204,252,126,60
7230 DATA 24,60,126,126,90,90,126,60
7240 DATA 60,126,63,51,51,63,126,60
7250 DATA 0,60,126,126,126,126,60,0
9000 PAPER 0: INK 7: PRINT AT 21,0;"Finished. RUN to try again. ";: STOPCapacity is not length
The arrays now have twelve slots, but only four live cells. t identifies the tail slot and h the head slot. Start with t=1 and h=4. On an ordinary move, advance t to 2 and h to 5, then write the new head in slot 5. Slots 2–4 keep their coordinates.
The old values in slot 1 remain in memory. They are no longer part of the body. Likewise, a zero in an unused slot does not describe a snake cell at world (0,0). The live body starts at t and contains exactly n positions.
After slot 12, the next slot is 1. The checks at lines 350 and 390 implement that wrap. This is a circular buffer: the storage is reused in a circle while the snake can move in any legal direction.
| Before | Ordinary move | Growing move, introduced next |
|---|---|---|
| Live slots 9,10,11,12 | Live slots 10,11,12,1 | Live slots 9,10,11,12,1 |
| Tail 9, head 12, length 4 | Tail 10, head 1, length 4 | Tail 9, head 1, length 5 |
Wrapping an array index never wraps a world coordinate. The wall rule is unchanged.
Find the live cells
The collision scan starts slot at t and visits n positions. It increments slot and wraps it after 12. j counts how many live positions have been visited; it is no longer necessarily the array index. On an ordinary move the first live position is excluded because that tail leaves.
The old shifting version copied three row/column pairs per move before writing the head. This version changes indices and writes one new pair. That counts one kind of work; it does not measure the whole BASIC loop, which still scans the body and draws characters.
A stale coordinate in slot 3 matches the next head position, but the live slots are 8,9,10,11. Is that a collision?
Show the explanation
No. Slot 3 is outside the four live cells. Searching every allocated slot would confuse storage capacity with the current body.
Turn through repeated small squares and continue long enough for the indices to wrap. The body should look and behave like the preceding version. If it suddenly jumps near a wrap, inspect the slot index and its stored world coordinates separately.
Revisit: same body, different storage
Keep named copies of the complete unit 4 and unit 5 programs before experimenting. Our intended change is where we store the body, while preserving its movement and collision rules. Explain lines 390–400 in each copy before running either: which coordinates move, and which indices move?
In each temporary copy, add this diagnostic line:
415 IF steps=8 OR steps=9 OR steps=12 THEN STOPRUN without steering. The head travels right and stops after eight successful moves. Before inspecting anything, predict t, h, n and the four live coordinates. Has a storage index reached its limit, or has the head reached the arena wall?
At each stop, enter PRINT t;" ";h;" ";n. To inspect all allocated pairs in the circular copy, enter:
FOR j=1 TO 12: PRINT j;" ";a(j);" ";b(j): NEXT jUse TO 4 in the shifting copy. Read the circular body from t, wrapping after slot 12, for exactly n pairs. The inspection may disturb the picture; the stored coordinates are what we are comparing. Enter CONTINUE to reach the next stop, predicting again before inspecting. STOP and PRINT change timing, so this is not a speed test.
Hint: separate the slot number from the square
The starting head is at column 9. Each successful move right adds one to that column. In the circular copy each ordinary move also advances both indices by one, wrapping after 12. In the shifting copy the indices stay at 1 and 4.
Compare the straight-route results
All these coordinates are on row 8. Both copies should give the same columns in tail-to-head order.
| Completed moves | Shifting live slots | Circular live slots | Live columns |
|---|---|---|---|
| 8 | 1,2,3,4 | 9,10,11,12 | 14,15,16,17 |
| 9 | 1,2,3,4 | 10,11,12,1 | 15,16,17,18 |
| 12 | 1,2,3,4 | 1,2,3,4 | 18,19,20,21 |
At move 9, slot 9 still contains column 14 in the circular copy. That coordinate has left the body. Slot 1 now holds the new head at column 18. Reading slots 1–4 as though they were always the body would give the wrong answer.
Delete line 415 by entering its number alone, then RUN. Repeat the earlier down/left/up route in both copies: entering the departing tail remains legal. Finally travel into a wall and check that the rejected move does not add a body cell. Restore the saved originals if an experiment goes wrong.
The comparison companion checks the retained emulator traces for a longer turning route. All 64 recorded movement and rejection events agree between the two representations. Its reconstructed slot numbers explain the recorded body order; they are not additional memory measurements. The straight route above is a separate inspection exercise.
Finish by explaining the trade-off without saying only “faster”. We replace three pair copies per move with index updates, but allocate twelve pairs instead of four and take on wrap and live-range reasoning. We have not measured total game speed. Growth in the next lesson changes the rules as well as using this storage; unit 4 does not already support it.
Sources
Steven Vickers, edited by Robin Bradbeer, ZX Spectrum BASIC Programming, second edition (Sinclair Research, 1983), 12 (arrays); circular storage is this program’s design.
Run it here
This unit's program, built for you.
- Program
- step-01.tap
- Machine
- ZX Spectrum 48K (PAL)