SURVIVE is a single-player maze/avoidance game written in ZX81 BASIC, published in SYNC magazine (Volume 3, Issue 5). The player navigates a character “U” around a 21×30 grid using keys 5–8, earning points by moving over cells; a mine array M(21,30) tracks visited squares, subtracting 2 from each cell on visit so revisiting costs score. A timer variable T counts down each turn, and when it expires a danger marker flashes using alternating block-graphic pairs before checking whether it has caught the player. The right-edge boundary triggers the end-of-game score display, with a 100-point bonus awarded for reaching column 31.
Program Analysis
Program Structure
The program is divided into three logical regions:
- Initialisation (lines 1000–1040): Sets starting position, resets variables, dimensions the mine array, draws the play-field, then jumps to the main loop.
- Main game loop (lines 4–75): Reads keys, moves the player, updates the score from the mine array, and manages the countdown timer.
- End-game / save block (lines 80–115, 2000–2040): Handles the hazard-flash animation, detects a player collision, and displays the final score.
Line 3 immediately jumps to line 1000 so initialisation always runs first, even after a RUN at line 2040.
Movement and Boundary Handling
Lines 5 and 10 compute the row and column deltas L and C in a single expression using the Boolean arithmetic idiom: a true condition evaluates to 1, false to 0, so the net delta is −1, 0, or +1 depending on which key is pressed and whether the boundary has been reached. Lines 20 and 25 then apply those deltas unconditionally, keeping movement tight. The right boundary check at line 35 (Y>30) deliberately falls through to the score display rather than clamping, making column 31 the game-ending goal.
Mine Array and Scoring
The two-dimensional array M(21,30) initialises to zero. Each time the player steps on a cell, line 40 adds M(X,Y)+2 to the score; on first visit this yields +2, and line 42 sets that cell to −2, so any subsequent revisit would contribute 0 (−2+2). This neatly rewards exploration while neutralising already-visited squares without an explicit visited-flag array.
Timer and Hazard Logic
Variable T acts as a dual-purpose counter. It is decremented at line 45 on every pass through the loop:
- While positive, line 55 loops back to the movement code at line 5.
- When it reaches zero (line 50), execution falls to line 60, which sets a new random delay of 3–5 ticks and calculates a projected hazard position (
H,I) by extrapolating the player’s current direction byTsteps, clamped to the grid at lines 67–73.
When T is negative (implicitly, lines 50–55 both fail), the program reaches line 60 on every subsequent iteration until a new T is set — this appears to be an anomaly: after the flash at line 80 sets M(X,Y)=-3 and checks collision at line 105, if the player survives, control returns to line 5 with T still at its last random value rather than a fresh countdown, which may cause erratic timing.
Hazard Flash Animation
Line 80 prints a rapid sequence of alternating block-graphic pairs (\.' = ▖▝ and \.' combined with \'.=▚▖) four times at position (H,I), finishing with an inverse-video space (%X) to create a brief flicker effect. Because all five PRINT AT clauses are on a single line, ZX81 BASIC executes them in one statement with no opportunity for a keypress interrupt between frames, giving a fast but deterministic animation.
Score Display and Bonus
Line 110 prints the score banner in inverse video (%S%C%O%R%E% with spaces rendered inverse). The expression S+(100 AND Y>30) applies the Boolean-as-integer trick: if the player has exited via the right edge, Y>30 is 1, adding a 100-point bonus.
Key Variable Summary
| Variable | Role |
|---|---|
X | Player row (1–21) |
Y | Player column (1–30; >30 = exit) |
L | Row movement delta (reused as loop counter at init) |
C | Column movement delta |
T | Countdown timer; also used as random step size for hazard projection |
S | Player score |
M(21,30) | Mine/visit value array |
H, I | Projected hazard row and column |
Notable Anomaly
Line 1020 sets S=T rather than S=0. Because T was set to 0 at line 1010 this is functionally equivalent, but the dependency on declaration order is fragile. Additionally, variable L is used both as the loop control variable in the initialisation FOR loop (lines 1025–1035) and as the horizontal movement delta in the game loop (lines 5, 20), which works only because the FOR loop completes before L is re-assigned at line 5.
Content
Source Code
0 REM "SURVIVE" BY ROBERT MIDURA SYNC V3:5
2 CLS
3 GOTO 1000
4 SLOW
5 LET L=(INKEY$="6" AND X<21)-(INKEY$="7" AND X>1)
10 LET C=(INKEY$="8" AND Y<31)-(INKEY$="5" AND Y>1)
15 PRINT AT X,Y;" "
20 LET X=X+L
25 LET Y=Y+C
30 PRINT AT X,Y;"U"
35 IF Y>30 THEN GOTO 110
40 LET S=S+M(X,Y)+2
42 LET M(X,Y)=-2
45 LET T=T-1
50 IF NOT T THEN GOTO 80
55 IF T>0 THEN GOTO 5
60 LET T=INT (RND*3+3)
65 LET H=X+T*L
67 IF H>21 THEN LET H=21
68 IF H<1 THEN LET H=1
70 LET I=Y+T*C
72 IF I<1 THEN LET I=1
73 IF I>31 THEN LET I=31
75 GOTO 5
80 PRINT AT H,I;".'";AT H,I;"'.";AT H,I;".'";AT H,I;"'.";AT H,I;"%X"
100 LET M(X,Y)=-3
105 IF H<>X OR I<>Y THEN GOTO 5
110 PRINT AT 0,11;"% %S%C%O%R%E% ";S+(100 AND Y>30)
115 STOP
1000 LET X=10
1005 LET Y=1
1010 LET T=0
1015 DIM M(21,30)
1020 LET S=T
1025 FOR L=1 TO 21
1030 PRINT AT L,0;" ..............................."
1035 NEXT L
1040 GOTO 4
2000 STOP
2010 REM RAND USR 14336
2030 REM SAVE "SRVIVE.B1"
2035 SAVE "1021%9"
2040 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
