Skip to content

Cross two banks of traffic

Use arrays and lane records to build the full six-lane crossing.

  • Sinclair ZX Spectrum
  • BASIC

One lane gives you one gap to judge. Six lanes make you consider the step after that. Expand the board into two banks of traffic, with one resting strip halfway across.

Replace lines 100, 110, 140, 150, 160, 170, 310, 350, 360, 370, 380, 390, 400, 410, 420, 1030, 1080, 1200, 1210, 2000, 2015, 3000, 3100.

Add lines 120, 430, 1082, 2010, 7100, 7110.

changes.bas
 100 DIM p(6): DIM d(6): DIM t(6): DIM v(6): DIM r(6): DIM a$(6,30): DIM b$(6,30)
 110 LET x=7: LET y=8: LET steps=0: RESTORE 7100
 120 FOR i=1 TO 6: READ p(i),d(i),v(i),r(i): LET t(i)=v(i): NEXT i
 140 FOR i=1 TO 6: FOR j=0 TO 2: FOR k=0 TO 3
 150 LET c=2 *p(i)+10 *j+k: LET c=c-30 *INT (c/30)+1
 160 LET a$(i,c)=CHR$ (144 +k): LET b$(i,c)=CHR$ (148 +k)
 170 NEXT k: NEXT j: NEXT i
 310 IF nx<0 OR nx>14 OR ny<0 OR ny>8 THEN LET nx=x: LET ny=y
 350 FOR i=1 TO 6: LET t(i)=t(i)-1
 360 IF t(i)>0 THEN GO TO 430
 370 LET t(i)=v(i): LET p(i)=p(i)+d(i)
 380 IF p(i)=15 THEN LET p(i)=0
 390 IF p(i)=-1 THEN LET p(i)=14
 400 IF d(i)=1 THEN LET a$(i)=a$(i,29 TO 30)+a$(i,1 TO 28): LET b$(i)=b$(i,29 TO 30)+b$(i,1 TO 28)
 410 IF d(i)=-1 THEN LET a$(i)=a$(i,3 TO 30)+a$(i,1 TO 2): LET b$(i)=b$(i,3 TO 30)+b$(i,1 TO 2)
 420 GO SUB 1200: IF y=r(i) THEN GO SUB 3000
 430 NEXT i
1030 FOR j=0 TO 8 STEP 4
1080 FOR i=1 TO 6: GO SUB 1200: LET z$=">": IF d(i)=-1 THEN LET z$="<"
1082 NEXT i
1200 LET row=2 +2 *r(i): LET colour=6: IF d(i)=-1 THEN LET colour=5
1210 PRINT AT row,1; PAPER 0; INK colour;a$(i);AT row+1,1;b$(i)
2000 LET hit=0: IF ny=0 OR ny=4 OR ny=8 THEN RETURN
2010 LET lane=ny: IF ny>4 THEN LET lane=ny-1
2015 LET delta=nx-p(lane): LET delta=delta-5 *INT (delta/5)
3000 LET shade=0: IF y=0 OR y=4 OR y=8 THEN LET shade=1
3100 IF y<>0 AND y<>4 AND y<>8 THEN PRINT AT 2+2*y,1+2*x; PAPER 0;"  ";AT 3+2*y,1+2*x;"  ": RETURN
7100 DATA 0,1,3,1,9,-1,4,2,11,1,2,3
7110 DATA 7,-1,3,5,1,1,4,6,10,-1,2,7

RUN the changed program.

Complete source at this checkpoint
quickstep.bas
  10 GO SUB 7000
 100 DIM p(6): DIM d(6): DIM t(6): DIM v(6): DIM r(6): DIM a$(6,30): DIM b$(6,30)
 110 LET x=7: LET y=8: LET steps=0: RESTORE 7100
 120 FOR i=1 TO 6: READ p(i),d(i),v(i),r(i): LET t(i)=v(i): NEXT i
 140 FOR i=1 TO 6: FOR j=0 TO 2: FOR k=0 TO 3
 150 LET c=2 *p(i)+10 *j+k: LET c=c-30 *INT (c/30)+1
 160 LET a$(i,c)=CHR$ (144 +k): LET b$(i,c)=CHR$ (148 +k)
 170 NEXT k: NEXT j: NEXT i
 180 GO SUB 1000: GO SUB 3000: LET dx=0: LET dy=0: LET tick=PEEK 23672
 200 LET k$=INKEY$
 220 IF k$="q" OR k$="Q" THEN GO TO 9000
 230 IF k$="r" OR k$="R" THEN GO TO 100
 280 LET elapsed=PEEK 23672 -tick: IF elapsed<0 THEN LET elapsed=elapsed+256
 290 IF elapsed<32 THEN GO TO 200
 291 LET k$=INKEY$
 292 LET dx=0: LET dy=0
 293 IF k$="q" OR k$="Q" THEN GO TO 9000
 294 IF k$="r" OR k$="R" THEN GO TO 100
 295 IF k$="i" OR k$="I" THEN LET dy=-1
 296 IF k$="k" OR k$="K" THEN LET dy=1
 297 IF k$="j" OR k$="J" THEN LET dx=-1
 298 IF k$="l" OR k$="L" THEN LET dx=1
 300 LET tick=PEEK 23672: LET nx=x+dx: LET ny=y+dy
 310 IF nx<0 OR nx>14 OR ny<0 OR ny>8 THEN LET nx=x: LET ny=y
 320 GO SUB 2000
 330 IF hit=1 THEN LET e$="That gap was not clear.": GO TO 4000
 340 IF nx=x AND ny=y THEN GO TO 350
 341 GO SUB 3100: LET x=nx: LET y=ny: GO SUB 3000
 350 FOR i=1 TO 6: LET t(i)=t(i)-1
 360 IF t(i)>0 THEN GO TO 430
 370 LET t(i)=v(i): LET p(i)=p(i)+d(i)
 380 IF p(i)=15 THEN LET p(i)=0
 390 IF p(i)=-1 THEN LET p(i)=14
 400 IF d(i)=1 THEN LET a$(i)=a$(i,29 TO 30)+a$(i,1 TO 28): LET b$(i)=b$(i,29 TO 30)+b$(i,1 TO 28)
 410 IF d(i)=-1 THEN LET a$(i)=a$(i,3 TO 30)+a$(i,1 TO 2): LET b$(i)=b$(i,3 TO 30)+b$(i,1 TO 2)
 420 GO SUB 1200: IF y=r(i) THEN GO SUB 3000
 430 NEXT i
 440 GO SUB 2000: LET steps=steps+1
 450 IF hit=1 THEN LET e$="The lane caught you.": GO TO 4000
 460 IF x=7 AND y=0 THEN LET e$="Across! A well-timed journey.": GO TO 4000
 470 LET dx=0: LET dy=0: GO TO 200
1000 BORDER 0: PAPER 0: INK 7: CLS
1010 PRINT AT 0,1; INK 5;"QUICKSTEP";AT 0,21; INK 4;"EXIT ABOVE"
1030 FOR j=0 TO 8 STEP 4
1040 PRINT AT 2+2*j,1; PAPER 1; INK 5;s$;AT 3+2*j,1;s$
1060 NEXT j
1070 PRINT AT 2,15; PAPER 4; INK 7;"  ";AT 3,15;"  "
1080 FOR i=1 TO 6: GO SUB 1200: LET z$=">": IF d(i)=-1 THEN LET z$="<"
1081 PRINT AT row,0; PAPER 0; INK colour;z$;AT row,31;z$
1082 NEXT i
1090 PRINT AT 20,2; PAPER 0; INK 7;"I up  J left  K down  L right"
1110 PRINT AT 21,2; INK 7;"R retry              Q quit";
1120 RETURN
1200 LET row=2 +2 *r(i): LET colour=6: IF d(i)=-1 THEN LET colour=5
1210 PRINT AT row,1; PAPER 0; INK colour;a$(i);AT row+1,1;b$(i)
1240 RETURN
2000 LET hit=0: IF ny=0 OR ny=4 OR ny=8 THEN RETURN
2010 LET lane=ny: IF ny>4 THEN LET lane=ny-1
2015 LET delta=nx-p(lane): LET delta=delta-5 *INT (delta/5)
2020 IF delta<2 THEN LET hit=1
2030 RETURN
3000 LET shade=0: IF y=0 OR y=4 OR y=8 THEN LET shade=1
3010 IF y=0 AND x=7 THEN LET shade=4
3020 PRINT AT 2+2*y,1+2*x; PAPER shade; INK 7;h$;AT 3+2*y,1+2*x;f$
3030 RETURN
3100 IF y<>0 AND y<>4 AND y<>8 THEN PRINT AT 2+2*y,1+2*x; PAPER 0;"  ";AT 3+2*y,1+2*x;"  ": RETURN
3110 PRINT AT 2+2*y,1+2*x; PAPER 1; INK 5;g$;AT 3+2*y,1+2*x;g$: RETURN
4000 PRINT AT 1,1; PAPER 0; INK 6;e$
4010 PRINT AT 20,2; PAPER 0; INK 7;"R plays again. Q quits.    "
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 103: READ n: POKE USR "a"+j,n: NEXT j
7010 LET h$=CHR$ 152 +CHR$ 153: LET f$=CHR$ 154 +CHR$ 155
7020 LET g$=CHR$ 156 +CHR$ 156: LET s$="": FOR j=1 TO 15: LET s$=s$+g$: NEXT j: RETURN
7100 DATA 0,1,3,1,9,-1,4,2,11,1,2,3
7110 DATA 7,-1,3,5,1,1,4,6,10,-1,2,7
7200 DATA 0,0,63,127,120,120,120,120
7210 DATA 0,0,255,255,24,24,24,24
7220 DATA 0,0,255,255,24,24,24,24
7230 DATA 0,0,252,254,30,30,30,30
7240 DATA 127,127,127,127,63,7,7,0
7250 DATA 255,255,255,255,255,192,192,0
7260 DATA 255,255,255,255,255,3,3,0
7270 DATA 254,254,254,254,252,224,224,0
7280 DATA 0,7,15,13,15,7,3,31
7290 DATA 0,224,240,176,240,224,192,248
7300 DATA 63,55,7,7,6,6,14,0
7310 DATA 252,236,224,224,96,96,112,0
7320 DATA 0,0,0,0,0,16,0,0
9000 PAPER 0: INK 7: PRINT AT 21,0;"Finished. RUN to try again.     ";: STOP

The player now starts at (7,8). Safe rows are 0, 4 and 8; traffic occupies rows 1, 2, 3, 5, 6 and 7. The exit stays at (7,0). Each cell remains 16×16 pixels, so the field fills 240×144 pixels without shrinking the artwork.

Six lanes of yellow and cyan vehicles between three dotted strips, with a player at the bottom.
Three consecutive lanes separate each pair of safe strips. A gap in one lane is only part of the crossing.

Give each lane its own state

A scalar such as p stores one value. DIM p(6) creates six indexed entries. p(1) is the first lane’s phase; p(6) is the last lane’s phase. We do the same for direction d, countdown t, period v and logical row r.

The string arrays have a different final dimension: DIM a$(6,30) provides six strings of thirty characters. a$(i) is lane i’s whole upper row, while a$(i,c) addresses one character. b$ holds the lower rows. The Foundations collection lesson explores indexed storage further; here each shared index identifies one lane across all seven arrays.

RESTORE 7100 positions the DATA reader at the lane records. For each i, line 120 reads phase, direction, period and row, then copies the period into the countdown. The records are:

Lane Row Phase Direction Period
1 1 0 Right 3
2 2 9 Left 4
3 3 11 Right 2
4 5 7 Left 3
5 6 1 Right 4
6 7 10 Left 2

This table is the level design. The bottom lane retains the practice lane’s phase, direction and period, but its row changes from 1 to 7. R re-creates the arrays and reads every record again. Without RESTORE, a later retry would continue reading from the wrong place.

Update only what is due

The new outer loop visits i=1..6. It reduces t(i), skips movement when that value is positive, and reloads only that lane’s countdown when it reaches zero. Phase changes and string rotations use the same i.

At the opening update no lane moves: all countdowns exceed one. On update two, lanes 3 and 6 move. On update three, lanes 1 and 4 move. The shared clock does not force every lane to advance together.

Drawing uses row=2+2*r(i). The arrow and colour follow direction, while the dotted strips mark places to wait. After printing a due lane, line 420 restores the person immediately if that is the player’s row. A player waiting on safe ground need not be erased while other lanes redraw.

Find the lane beneath a cell

The contact query rejects rows 0, 4 and 8 before touching an array. In rows 1–3, lane=ny. In rows 5–7, lane=ny−1. The missing index is not a missing lane: logical row 4 belongs to the resting strip.

A candidate move reaches logical row 6. Which phase entry must the contact query read, and why not p(6)?

Show the explanation

It reads p(5), because rows 1,2,3,5,6,7 belong to lanes 1,2,3,4,5,6. The central safe row shifts the mapping for the lower bank. p(6) describes row 7 instead.

Try waiting at the start while several traffic patterns pass. Move into the lower bank, retreat if needed, and use the central strip to plan the upper bank. Retry should restore all six phases, not just the player. If contact seems to belong to another row, check the row-to-lane mapping before changing the vehicle artwork. This stage still samples held keys directly; short taps can be missed during drawing.

Sources

Steven Vickers, edited by Robin Bradbeer, ZX Spectrum BASIC Programming, second edition (Sinclair Research, 1983), chapter 6, chapter 12. READ/RESTORE supplies the lane records, and numeric and fixed-length string arrays store the separate lanes. The row mapping and movement periods come from this board.

Run it here

This unit's program, built for you.

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

Download quickstep.tap