Skip to content

Read the motion

Display speed and direction without rounding away the stored motion.

  • Sinclair ZX Spectrum
  • BASIC

It is hard to judge a fraction of a pixel from a tiny moving triangle. Put the speed at the top and the horizontal and vertical motion underneath the arena. The display will expose the velocity the game already uses.

Replace lines 110, 1020, 1070.

Add lines 355, 6000, 6010, 6020, 6030, 6040, 6050, 6060, 6070, 6080, 6090.

changes.bas
 110 GO SUB 1000: GO SUB 3000: GO SUB 6000
 355 IF k$=" " THEN GO SUB 6000
1020 PRINT AT 1,2;"SPEED"
1070 PRINT AT 20,2; INK 7;"DRIFT"
6000 LET speed=SQR (vx*vx+vy*vy)
6010 PRINT AT 1,8; INK 7;INT (10 *speed+.5)/10;"  "
6020 IF vx*vx+vy*vy<=.16 THEN PRINT AT 1,17; INK 4;"SLOW    "
6030 IF vx*vx+vy*vy>.16 THEN PRINT AT 1,17; INK 6;"TOO FAST"
6040 LET u$="E": LET w$="N"
6050 IF vx<-.001 THEN LET u$="W"
6060 IF vy<-.001 THEN LET w$="S"
6070 IF ABS vx<.001 THEN LET u$="-"
6080 IF ABS vy<.001 THEN LET w$="-"
6090 PRINT AT 20,8; INK 7;u$;" ";INT (10 *ABS vx+.5)/10;"  ";AT 20,16;w$;" ";INT (10 *ABS vy+.5)/10;"  ": RETURN

RUN the changed program.

Complete source at this checkpoint
drift.bas
  10 GO SUB 7000
 100 LET x=48: LET y=56: LET vx=0: LET vy=0: LET h=2: LET steps=0
 110 GO SUB 1000: GO SUB 3000: GO SUB 6000
 200 LET k$=INKEY$
 210 IF k$="q" OR k$="Q" THEN GO TO 9000
 220 IF k$="r" OR k$="R" THEN GO TO 100
 230 GO SUB 3000
 240 IF k$="o" OR k$="O" THEN LET h=h+1
 250 IF k$="p" OR k$="P" THEN LET h=h-1
 260 IF h=9 THEN LET h=1
 270 IF h=0 THEN LET h=8
 280 IF k$<>" " THEN GO TO 320
 290 LET vx=vx+.2 *a(h): LET vy=vy+.2 *b(h)
 300 LET speed=SQR (vx*vx+vy*vy)
 310 IF speed>3 THEN LET vx=3 *vx/speed: LET vy=3 *vy/speed
 320 LET nx=x+vx: LET ny=y+vy
 330 IF nx<22 OR nx>233 OR ny<30 OR ny>145 THEN GO SUB 3000: LET e$="Hull lost. Try a gentler burn.": GO TO 4000
 340 LET x=nx: LET y=ny: LET steps=steps+1
 350 GO SUB 3000
 355 IF k$=" " THEN GO SUB 6000
 370 GO TO 200
1000 BORDER 0: PAPER 0: INK 7: CLS
1010 PRINT AT 0,2; INK 5;"DRIFT";AT 0,19; INK 7;"FREE FLIGHT"
1020 PRINT AT 1,2;"SPEED"
1030 PLOT INK 1;15,24: DRAW INK 1;0,128: DRAW INK 1;225,0: DRAW INK 1;0,-128: DRAW INK 1;-225,0
1060 PRINT AT 19,2; INK 5;"O/P turn   SPACE thrust"
1070 PRINT AT 20,2; INK 7;"DRIFT"
1080 PRINT AT 21,2; INK 5;"R retry              Q quit";
1090 RETURN
3000 LET px=INT (x+.5): LET py=INT (y+.5)
3010 PLOT INK 7; OVER 1;px+c(h),py+d(h)
3020 DRAW INK 7; OVER 1;e(h)-c(h),f(h)-d(h)
3030 DRAW INK 7; OVER 1;g(h)-e(h),j(h)-f(h)
3040 DRAW INK 7; OVER 1;c(h)-g(h),d(h)-j(h): RETURN
4000 PRINT AT 1,0;"                                ";AT 1,2; INK 6;e$
4010 PRINT AT 20,2;"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
6000 LET speed=SQR (vx*vx+vy*vy)
6010 PRINT AT 1,8; INK 7;INT (10 *speed+.5)/10;"  "
6020 IF vx*vx+vy*vy<=.16 THEN PRINT AT 1,17; INK 4;"SLOW    "
6030 IF vx*vx+vy*vy>.16 THEN PRINT AT 1,17; INK 6;"TOO FAST"
6040 LET u$="E": LET w$="N"
6050 IF vx<-.001 THEN LET u$="W"
6060 IF vy<-.001 THEN LET w$="S"
6070 IF ABS vx<.001 THEN LET u$="-"
6080 IF ABS vy<.001 THEN LET w$="-"
6090 PRINT AT 20,8; INK 7;u$;" ";INT (10 *ABS vx+.5)/10;"  ";AT 20,16;w$;" ";INT (10 *ABS vy+.5)/10;"  ": RETURN
7000 DIM a(8): DIM b(8): DIM c(8): DIM d(8): DIM e(8): DIM f(8): DIM g(8): DIM j(8)
7010 FOR i=1 TO 8: LET angle=(i-1)*PI/4
7020 LET a(i)=COS angle: LET b(i)=SIN angle
7030 LET c(i)=INT (5 *a(i)+.5): LET d(i)=INT (5 *b(i)+.5)
7040 LET e(i)=INT (-3 *a(i)-3 *b(i)+.5): LET f(i)=INT (-3 *b(i)+3 *a(i)+.5)
7050 LET g(i)=INT (-3 *a(i)+3 *b(i)+.5): LET j(i)=INT (-3 *b(i)-3 *a(i)+.5)
7060 NEXT i: RETURN
9000 PAPER 0: INK 7: PRINT AT 21,0;"Finished. RUN to try again.     ";: STOP

After RUN, both components are zero. Burn, coast and turn. The readings change when thrust changes velocity; turning alone leaves them alone. SLOW means the ship is within the speed band we will use for docking. There is no dock yet.

One speed, two directions

Routine 6000 calculates speed from vx/vy. The top number is the total distance travelled per update. The lower DRIFT line gives each component separately, using E or W for horizontal motion and N or S for vertical motion.

ABS gives the magnitude without the sign. A vx of −.6 is therefore displayed as W .6. The direction letter keeps the information that ABS removes. Near zero, the display uses a dash instead of a compass letter, so a tiny arithmetic remainder does not imply a meaningful drift direction.

Lines 6050–6080 use .001 for that near-zero decision. They only choose letters: they do not set vx or vy to zero. A value can also round to 0 on the display while still being large enough to retain its direction letter.

Round the picture, keep the value

The expression INT (10*speed+.5)/10 rounds to tenths. For speed .56, multiplying by ten gives 5.6, adding .5 gives 6.1, INT gives 6, and division by ten gives .6.

The display does not replace the stored velocity with its rounded result. Its number is a convenient view of the state, not the state itself.

SLOW uses the full values: vxvx+vyvy must be at most .16. That is the square of .4, so it tests for speed at most .4 without another square root. Above it, the message is TOO FAST. These words refer to our future docking limit; they do not mean the ship has reached its maximum speed of 3.

Both components read .3. Is that necessarily slow enough for a .4 speed limit?

Show the explanation

No. With exact components .3 and .3, squared speed is .09+.09=.18, which exceeds .16. The total speed is about .4243. Each component can be below .4 while the combined motion exceeds it. Rounded display values add another reason to use the cue rather than judge from the digits alone.

Overwrite the whole field

PRINT AT puts each field in a known character position. The spaces after a number clear old digits when a new value is shorter. The two speed messages also occupy the same width. Without that padding, fragments of the previous reading could survive.

We call the display after drawing the starting ship and after each thrust update. Coasting and turning cannot change velocity, so neither needs another calculation and set of PRINTs. Drawing the ship first also avoids leaving it erased while the readout is updated.

This work still costs time in interpreted BASIC. A thrust update now does more than a coasting update. The units remain pixels per update, and steps remains an update count; neither has become a clock.

Try braking again. Watch one component fall towards zero, then change direction if you keep burning. If old digits remain, check the trailing spaces and AT positions. If the display changes on a turn without thrust, inspect the velocity assignments: h and vx/vy have different jobs.

Sources

Steven Vickers, edited by Robin Bradbeer, ZX Spectrum BASIC Programming, second edition (Sinclair Research, 1983), chapter 9, chapter 15. Chapter 9 describes ABS and INT; chapter 15 describes PRINT positioning. The display fields, rounding policy and low-speed band belong to this game.

Run it here

This unit's program, built for you.

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

Download drift.tap