This is a lunar lander simulation game in which the player must guide a spacecraft to a safe touchdown by managing thrust, fuel, and vertical velocity. Each game round randomly initialises fuel (100–400 units), velocity (1–150), and altitude (500 units). The player enters a thrust value each turn; velocity changes by 5 minus thrust, altitude decreases by the new velocity, and fuel is consumed by the absolute thrust value. A safe landing requires vertical velocity of 10 or less on touchdown; excess fuel remaining is added to the score. The subroutine at line 275 implements a crude delay loop with a flashing “INPUT THRUST” prompt rendered in three alternating states using inverse-video characters.
Program Analysis
Program Structure
The program is organised into a main game loop and several subroutines:
- Initialisation (lines 5–40): Calls the screen-fill subroutine at 211, then sets up game variables: column
K=5, fuelF(100–400), velocityV(1–150), and altitudeH=500. - Display loop (lines 50–105): Draws the ground line, status bar, border frame, terrain, and lander sprite, then calls the blinking prompt subroutine.
- Game logic (lines 110–150): Reads thrust
T, updates velocity and altitude, deducts fuel, checks termination conditions. - End states (lines 160–200): Prints either a “LANDED” score or “CRASHED” message, pauses, and restarts.
- Screen fill subroutine (lines 211–235): Fills all 512 character cells with inverse-video space pairs to create a starfield background.
- Star-twinkle subroutine (lines 240–270): Scatters two random inverse dots on the upper half of the screen each frame.
- Blinking prompt subroutine (lines 275–325): Cycles “INPUT THRUST” through inverse, normal, and blank forms using a delay loop, then falls through to
INPUT Tat line 110. - Delay subroutine (lines 335–350): A tight loop that counts to 2 before returning, providing a minimal timing pause.
- Save block (lines 360–380):
CLEARfollowed bySAVEandRUN, used to store the program.
Game Mechanics
Each turn the player supplies a thrust value T. The physics update is:
V = V + 5 - T— gravity adds 5 per turn; thrust subtracts.H = H - V— altitude decreases by current velocity.F = F - ABS(T)— fuel consumed equals absolute thrust.
The landing row is computed as (600-H)/50, mapping altitude 0–500 to screen rows 2–12. The terrain row uses the same formula scaled from Z (the previous altitude), producing a brief trace of where the lander was. The score on safe landing is F - V, rewarding both fuel conservation and a gentle touchdown.
Display Techniques
The starfield background is created at startup by printing 512 inverse-space characters (the % sequence) in a nested loop (lines 215–235). This runs in FAST mode for speed. The SLOW command at line 68 re-enables the display driver for the animated portion. The lander sprite at line 90 is %<\..%>, a three-character combination of inverse angle-brackets and a block graphic, giving a simple capsule silhouette. The ground line at line 50 uses a row of \..\.. lower-half block graphics to simulate lunar terrain.
The blinking prompt at lines 280–310 prints “INPUT THRUST” in three alternating styles — inverse video, normal text, and a blank — cycling four times before accepting input, creating a flashing effect without any timer hardware.
Notable Techniques
- Altitude-to-row mapping: The expression
(600-H)/50converts altitude linearly to a screen row. AtH=500the lander appears near the top (row 2); atH≈0it sits near row 12, just above the status bar. - Lateral drift:
K = K + RNDat line 80 slowly drifts the lander’s column position rightward each frame, adding a cosmetic horizontal motion element. - Dual-call twinkle: Subroutine 240 is called once via
GOSUB 240at line 115 and is shared with the star-fill loop exit (line 270RETURN), placing two random star dots per frame. - Minimal delay loop: Lines 335–350 count
Tto 2 before returning; at ZX81 speed this provides a short, roughly consistent pause between prompt flash states. - Prompt-flow re-entry: After four flash cycles, line 320 jumps to line 110 (
INPUT T) rather than falling through, so the blinking subroutine at 275 never issuesINPUTitself — the main loop owns that statement.
Bugs and Anomalies
- Variable collision in delay loop: The delay subroutine at lines 335–350 reuses
Tas its counter variable. SinceTholds the last thrust input, calling this subroutine after line 110 overwrites it before it is used in the physics at line 120. On the first call from line 115 this is harmless (thrust has not yet been entered), but the structure is fragile. - Score can go negative: The landing score
F - Vat line 170 is not clamped; a very slow, fuel-heavy approach can yield a negative displayed score. - No input validation:
INPUT Tdoes not constrain thrust to a sensible range, so a large positive or negative value can teleport the lander or make velocity deeply negative (rising indefinitely). - Lander can leave the screen: If
Kdrifts past column 29 thePRINT ATat line 90 will wrap or cause an error, since the sprite is three characters wide. - Fuel check uses
F<1notF<=0: Because thrust is integer but fuel is not validated to remain non-negative, the conditionF<1(line 145) triggers the crash branch even when exactly 0 fuel remains on the final burn, which is the intended behaviour but slightly unintuitive.
Variable Summary
| Variable | Role |
|---|---|
K | Lander column (drifts right each turn) |
F | Fuel remaining |
V | Vertical velocity (descent rate) |
H | Altitude (counts down to 0) |
Z | Previous altitude (for terrain ghost) |
T | Player thrust input / delay counter |
A, B | Loop counters for starfield fill |
P | Flash-cycle counter in prompt subroutine |
Content
Source Code
1 REM % %S%T%A%R% %L%A%N%D%E%R%
5 GOSUB 211
10 LET K=5
20 LET F=100+INT (RND*300)+1
25 LET V=INT (RND*150)+1
30 LET H=500
40 LET Z=H
50 PRINT AT 13,9;"\..\..\..\..\..\..\..\..\..\..\..\..\..\.."
60 PRINT AT 16,13;"F ";F;" ";TAB 13;"H ";H;" ";TAB 13;"V ";V;" "
61 PRINT AT 19,12;"% % % % % % % % "
63 PRINT AT 16,12;"% ";TAB 19;"% "
64 PRINT AT 17,12;"% ";TAB 19;"% "
65 PRINT AT 18,12;"% ";TAB 19;"% "
68 SLOW
70 PRINT AT (600-Z)/50,K;"% % % "
80 LET K=K+RND
90 PRINT AT (600-H)/50,K;"%<\..%>"
100 LET Z=H
105 GOSUB 275
110 INPUT T
115 GOSUB 240
120 LET V=V+5-T
130 LET H=H-V
140 LET F=F-ABS (T)
145 IF F<1 THEN GOTO 190
150 IF H>0 THEN GOTO 60
160 IF V>10 THEN GOTO 190
170 PRINT AT 2,8;"%L%A%N%D%E%D% ";F-V;"% %P%O%I%N%T%S% ";
180 GOTO 200
190 PRINT AT 2,6;"% %C%R%A%S%H%E%D% %A%T% ";V;"%F%E%E%T"
200 PAUSE 500
205 CLS
210 RUN
211 FAST
215 FOR A=1 TO 16
220 FOR B=1 TO 32
225 PRINT "% ";
230 NEXT B
235 NEXT A
240 PRINT AT RND*9,RND*31;"%."
246 PRINT AT RND*9,RND*31;"%."
270 RETURN
275 PRINT AT 14,10;" "
280 PRINT AT 14,10;"%I%N%P%U%T% %T%H%R%U%S%T"
285 LET P=0
290 GOSUB 335
295 PRINT AT 14,10;"INPUT THRUST"
300 GOSUB 335
305 PRINT AT 14,10;"% % % % % % % % % % % % "
310 GOSUB 335
315 LET P=P+1
320 IF P=4 THEN GOTO 110
325 GOTO 295
335 LET T=0
340 LET T=T+1
345 IF T=2 THEN RETURN
350 GOTO 340
360 CLEAR
370 SAVE "1027%5"
380 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
