Skip to content

Move a lane around

Rotate two character strings to draw wrapping traffic.

  • Sinclair ZX Spectrum
  • BASIC

Give the lane something to carry. Three vehicles will wrap from one edge to the other. For now, SPACE advances them by one cell so you can inspect each position.

Replace lines 100, 110, 3100, 7000.

Add lines 140, 150, 160, 170, 240, 370, 380, 390, 400, 410, 420, 440, 1080, 1081, 1095, 1200, 1210, 1240, 7200, 7210, 7220, 7230, 7240, 7250, 7260, 7270.

changes.bas
 100 LET p=10: LET d=-1: LET v=2: LET t=v: DIM a$(30): DIM b$(30)
 110 LET x=7: LET y=2: LET steps=0
 140 FOR j=0 TO 2: FOR k=0 TO 3
 150 LET c=2 *p+10 *j+k: LET c=c-30 *INT (c/30)+1
 160 LET a$(c)=CHR$ (144 +k): LET b$(c)=CHR$ (148 +k)
 170 NEXT k: NEXT j
 240 IF k$=" " THEN GO TO 370
 370 LET p=p+d
 380 IF p=15 THEN LET p=0
 390 IF p=-1 THEN LET p=14
 400 IF d=1 THEN LET a$=a$(29 TO 30)+a$(1 TO 28): LET b$=b$(29 TO 30)+b$(1 TO 28)
 410 IF d=-1 THEN LET a$=a$(3 TO 30)+a$(1 TO 2): LET b$=b$(3 TO 30)+b$(1 TO 2)
 420 GO SUB 1200: IF y=1 THEN GO SUB 3000
 440 LET steps=steps+1
1080 GO SUB 1200: LET z$=">": IF d=-1 THEN LET z$="<"
1081 PRINT AT row,0; PAPER 0; INK colour;z$;AT row,31;z$
1095 PRINT AT 12,2; PAPER 0; INK 6;"SPACE moves the lane"
1200 LET row=4: LET colour=6: IF d=-1 THEN LET colour=5
1210 PRINT AT row,1; PAPER 0; INK colour;a$;AT row+1,1;b$
1240 RETURN
3100 IF y=1 THEN GO SUB 1200: RETURN
7000 RESTORE 7200: FOR j=0 TO 103: READ n: POKE USR "a"+j,n: NEXT j
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

RUN the changed program.

Complete source at this checkpoint
quickstep.bas
  10 GO SUB 7000
 100 LET p=10: LET d=-1: LET v=2: LET t=v: DIM a$(30): DIM b$(30)
 110 LET x=7: LET y=2: LET steps=0
 140 FOR j=0 TO 2: FOR k=0 TO 3
 150 LET c=2 *p+10 *j+k: LET c=c-30 *INT (c/30)+1
 160 LET a$(c)=CHR$ (144 +k): LET b$(c)=CHR$ (148 +k)
 170 NEXT k: NEXT j
 180 GO SUB 1000: GO SUB 3000
 200 LET k$=INKEY$
 210 IF k$="" THEN GO TO 200
 220 IF k$="q" OR k$="Q" THEN GO TO 9000
 230 IF k$="r" OR k$="R" THEN GO TO 100
 240 IF k$=" " THEN GO TO 370
 250 LET dx=0: LET dy=0
 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 nx=x+dx: LET ny=y+dy
 310 IF nx<0 OR nx>14 OR ny<0 OR ny>2 THEN LET nx=x: LET ny=y
 340 IF nx=x AND ny=y THEN GO TO 470
 341 GO SUB 3100: LET x=nx: LET y=ny: GO SUB 3000: GO TO 470
 370 LET p=p+d
 380 IF p=15 THEN LET p=0
 390 IF p=-1 THEN LET p=14
 400 IF d=1 THEN LET a$=a$(29 TO 30)+a$(1 TO 28): LET b$=b$(29 TO 30)+b$(1 TO 28)
 410 IF d=-1 THEN LET a$=a$(3 TO 30)+a$(1 TO 2): LET b$=b$(3 TO 30)+b$(1 TO 2)
 420 GO SUB 1200: IF y=1 THEN GO SUB 3000
 440 LET steps=steps+1
 470 IF INKEY$<>"" THEN GO TO 470
 480 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 2 STEP 2
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 GO SUB 1200: LET z$=">": IF d=-1 THEN LET z$="<"
1081 PRINT AT row,0; PAPER 0; INK colour;z$;AT row,31;z$
1090 PRINT AT 20,2; PAPER 0; INK 7;"I up  J left  K down  L right"
1095 PRINT AT 12,2; PAPER 0; INK 6;"SPACE moves the lane"
1110 PRINT AT 21,2; INK 7;"R retry              Q quit";
1120 RETURN
1200 LET row=4: LET colour=6: IF d=-1 THEN LET colour=5
1210 PRINT AT row,1; PAPER 0; INK colour;a$;AT row+1,1;b$
1240 RETURN
3000 LET shade=0: IF y<>1 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=1 THEN GO SUB 1200: RETURN
3110 IF y=0 AND x=7 THEN PRINT AT 2,15; PAPER 4;"  ";AT 3,15;"  ": RETURN
3120 PRINT AT 2+2*y,1+2*x; PAPER 1; INK 5;g$;AT 3+2*y,1+2*x;g$: RETURN
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
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

I/J/K/L still move the person. Press and release SPACE to move traffic left. Vehicles and player may overlap at this stage: we have not written a contact rule.

One phase describes three vehicles

p is the starting cell of one vehicle. The other two begin five and ten cells further around a fifteen-cell ring. Each vehicle occupies two cells, leaving three-cell gaps.

Initially p=10. The starts are 10, 0 and 5 after wrapping. Direction d=−1 moves the phase left; a phase of −1 becomes 14. The rightward branch is also present: when d=1, phase 15 becomes 0. Neither edge is a wall for traffic.

The vehicle artwork occupies four character columns and two rows. Its eight DATA lines fill the eight UDG slots reserved in the first lesson. The two-row person still uses the following four slots, so changing the loader to start at 7200 keeps all the character codes consistent.

Build two rows of picture

DIM a$(30) and DIM b$(30) create fixed-length strings filled with spaces. a$ will draw the vehicle tops; b$ draws their bottoms. Their thirty character positions cover fifteen logical cells.

The nested loops visit three vehicles, j=0..2, and four character columns per vehicle, k=0..3. The starting character offset is 2*p+10*j+k. The factor two converts cells to character columns; the ten separates successive vehicle starts by five cells.

Line 150 wraps that offset into 0..29, then adds one because string positions begin at 1. With p=14, the first vehicle’s four columns land at positions 29, 30, 1 and 2. Its two halves appear at opposite edges, but it is still one wrapping vehicle.

Rotate the picture with the phase

A string slice such as a$(3 TO 30) includes both end positions. Moving left joins positions 3–30 to positions 1–2. Moving right joins positions 29–30 to positions 1–28. In either case, the row stays thirty characters long and moves by one two-character cell.

The phase changes first, then both strings rotate in the same direction. Numeric position and drawing must agree. Printing a row does not tell the program where its vehicles are; p still owns that fact.

Why does rotating by one character produce the wrong movement for this board?

Show the explanation

It moves only half a logical cell. The picture would shift eight pixels while the phase says it moved sixteen. The later collision rule would then disagree with what the player sees.

Keep the player visible

Printing a lane can cover the person. Line 420 restores the player afterwards when y=1. Moving off the lane calls its drawing routine to restore whatever traffic was underneath; restoring plain spaces would leave holes in vehicles.

Advance fifteen times. The phase and picture return to their starting positions. Move onto a vehicle, advance once, then move away. Its artwork should be restored. If a vehicle gains a blank half, check both row strings and the restoration at 3100. If SPACE keeps advancing while held, check that the release gate is still present.

Sources

Steven Vickers, edited by Robin Bradbeer, ZX Spectrum BASIC Programming, second edition (Sinclair Research, 1983), chapter 8, chapter 12, chapter 14. String slicing, fixed-length string arrays and UDG storage support the drawing. The phase and five-cell spacing belong to this lane design.

Run it here

This unit's program, built for you.

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

Download quickstep.tap