This program is a first-person 3-D maze game where the player must navigate through a randomly selected labyrinth to find an escape point before their oxygen supply runs out. The maze is stored as a 132-character string (M$) representing a 12×11 grid, with “1” for walls and “0” for open passages, and three preset maze layouts are chosen randomly at startup. Navigation uses four keys: F (forward), T (turn around), L (turn left), and R (turn right), with the current facing direction encoded as an integer offset (±1 for east/west, ±12 for north/south). Perspective walls are drawn using block graphics characters to simulate depth, rendering up to three cells ahead and one cell to each side. An oxygen counter (O) decrements by 0.5 on each move, displayed as a bar gauge on the game-over screen if the player runs out.
Program Analysis
Program Structure
The program is organized into four logical sections:
- Initialisation / title screen (lines 3000–3750): Called as a GOSUB from line 10, this subroutine draws the title, shows instructions, selects a random maze, places the player and exit, and builds supporting strings before returning.
- Main game loop (lines 290–540): Reads a keypress, updates the player’s position and facing direction, redraws the view, decrements oxygen, and branches to win or lose endings.
- Win screen (lines 600–1080): Draws an ASCII-art “escaped” trophy picture with inverse text, waits for a keypress, then restarts with
RUN. - Lose screen (lines 1500–1780): Draws an oxygen gauge animation, waits for a keypress, then restarts with
RUN.
Maze Representation
The maze is a flat 132-character string M$ held in one of three hard-coded variants selected by R=INT(RND*3)+1. Each character is either "1" (wall) or "0" (open cell). The grid is 12 columns wide, so moving north/south changes the index by ±12 and moving east/west changes it by ±1. The player’s absolute cell index is X, and the facing offset is X1.
Direction Encoding
Rather than using a separate direction variable, the program encodes facing as a signed integer offset stored in X1:
| X1 value | Facing |
|---|---|
| +1 | East |
| -1 | West |
| +12 | South |
| -12 | North |
Turning left and right is implemented entirely with arithmetic on these four values using Boolean expressions evaluated as 0/1. For example, line 295 computes the new X1 for a left turn as:
(X1=12)+(-1*(X1=-12))+(-12*(X1=1))+(12*(X1=-1))
Only one of the four terms is non-zero at a time, so the expression cleanly maps each current heading to the appropriate new heading.
3-D View Rendering
The corridor view is drawn in five depth layers. At each depth step, a “left-side wall” expression and a “right-side wall” expression are derived by rotating X1 90 degrees left or right using the same Boolean arithmetic. Walls at each depth are tested with M$(index)="1" or ="0"; if a wall is present a corridor slice is printed using block graphics characters (\: \:. etc.) and if a passage is open a solid filled rectangle of \## inverse blocks is printed instead. The variable D (set to 2*X1 at line 317) is used for the deepest look-ahead cell directly ahead.
A notable anomaly exists at line 390: one PRINT argument reads 4,7;"% \:'" (missing the AT keyword), which will either cause a syntax error at run-time or silently print a literal number depending on the interpreter. This appears to be a transcription error for AT 14,7.
Similarly, line 460 prints AT 11,16 instead of the expected AT 11,13 for the right mid-depth wall block, creating a slight visual mis-alignment on the right side.
Oxygen System
O is initialised to 20 at line 3460. It is decremented by 0.5 on every move that actually updates the display (line 480). The display string O$ is a 40-character string of \ : block pairs (line 3450), and the status bar at line 340 slices it to length O with O$(TO O), giving a shrinking bar. When INT O < 0 (line 490), the lose screen is triggered. The lose-screen routine (lines 1500–1780) redraws the gauge graphically with a FOR loop at lines 1600–1620 that sweeps inverse blocks leftward to simulate a draining gauge.
Player and Exit Placement
At lines 3470–3510 the starting position X and exit position EX are chosen randomly, rejecting any cell that is a wall (M$(X)<>"0") or where the exit is the same cell as, or immediately adjacent (distance 1 or 12) to, the start. The exit marker %E (inverse “E”) is printed at the appropriate screen position (line 510) when the exit cell is directly ahead.
Wall-string Construction
The wall-closed string W$ (used at lines 360–370 to show a solid wall directly ahead) is built programmatically in a loop at lines 3520–3570, concatenating rows of spaced percent-sign block characters. This avoids embedding a very long literal string and keeps the GOSUB subroutine self-contained.
Scrolling Ticker
The “PRESS ANY KEY TO START THE GAME” message on the title screen scrolls horizontally using a classic one-character rotation idiom at line 3241: LET I$=I$(2 TO)+I$(1). The loop continues until a key is detected (line 3250), at which point the game begins.
Notable Idioms
POKE 16418,0(line 20) clears the ZX81 system variable that controls the display file pointer, a common trick to reset the screen state programmatically.RUN(lines 1080, 1780) is used instead ofGO TO 10to fully reinitialise all variables between games.- The SAVE line (3766) stores the program with an auto-run flag in the filename.
- Boolean expressions used as 0/1 integers for direction arithmetic avoid needing IF/THEN chains or lookup tables, saving both code space and execution time.
Content
Source Code
1 REM "3-D ESCAP%E"
10 GOSUB 3000
20 POKE 16418,0
30 LET X1=1
40 PRINT AT 10,0;"% % %P%R%E%S%S% %A% %M%O%V%E%M%E%N%T% %K%E%Y% %T%O% %S%T%A%R%T"
290 LET A$=INKEY$
293 IF A$="F" AND M$(X+X1)="0" THEN LET X=X+X1
294 IF A$="T" THEN LET X1=-X1
295 IF A$="L" THEN LET X1=(X1=12)+(-1*(X1=-12))+(-12*(X1=1))+(12*(X1=-1))
297 IF A$="R" THEN LET X1=(X1=-12)+(-1*(X1=12))+(-12*(X1=-1))+(12*(X1=1))
299 IF A$="F" OR A$="T" OR A$="L" OR A$="R" THEN GOTO 315
310 GOTO 290
317 LET D=2*X1
320 CLS
330 IF X=EX THEN GOTO 600
340 PRINT AT 23,0;"%O%X%Y%G%E%N% % ";O$( TO O);" "
360 IF M$(X+X1)="1" THEN PRINT AT 1,0;W$
370 IF M$(X+X1)="1" THEN GOTO 290
380 IF M$(X+(X1=12)+(-1*(X1=-12))+(-12*(X1=1))+(12*(X1=-1)))="1" THEN PRINT AT 1,0;"\ :\:.";AT 2,0;"\ :% \:.";AT 3,0;"\ :% % \:.";AT 4,0;"\ :% % % \:.";AT 5,0;"\ :% % % % \:.";AT 6,0;"\ :% % % % % \:.";AT 7,0;"\ :% % % % % % ";AT 8,0;"\ :% % % % % % ";AT 9,0;"\ :% % % % % % ";AT 10,0;"\ :% % % % % % % ";AT 11,0;"\ :% % % % % % ";AT 12,0;"\ :% % % % % % ";AT 13,0;"\ :% % % % % % ";AT 14,0;"\ :% % % % % % ";AT 15,0;"\ :% % % % % % ";AT 16,0;"\ :% % % % % \:'";AT 17,0;"\ :% % % % \:'";AT 18,0;"\ :% % % \:'";AT 19,0;"\ :% % % \:'";AT 20,0;"\ :% \:'";AT 21,0;"\ :\:'"
390 IF M$(X+(X1+(X1=12)+(-1*(X1=-12))+(-12*(X1=1))+(12*(X1=-1))))="1" THEN PRINT AT 7,7;"\:.";AT 8,7;"% \:.";AT 9,7;"% % \:.";AT 10,7;"% % % ";AT 11,7;"% % % ";AT 12,7;"% % % ";AT 13,7;"% % \:'";4,7;"% \:'";AT 15,7;"\:'"
400 IF M$(X+D)="1" THEN PRINT AT 10,10;"% % % ";AT 11,10;"% % % ";AT 12,10;"% % % "
410 IF M$(X+(X1+(X1=-12)+(-1*(X1=12))+(-12*(X1=-1))+(12*(X1=1))))="1" THEN PRINT AT 7,15;"\.:";AT 8,14;"\.:% ";AT 9,13;"\.:% % ";AT 10,13;"% % % ";AT 11,13;"% % % ";AT 12,13;"% % % ";AT 13,13;"\':% % ";AT 14,14;"\':% ";AT 15,15;"\':"
420 IF M$(X+(X1=-12)+(-1*(X1=12))+(12*(X1=1))+(-12*(X1=-1)))="1" THEN PRINT AT 1,21;"\.:\: ";AT 2,20;"\.:% \: ";AT 3,19;"\.:% % \: ";AT 4,18;"\.:% % % \: ";AT 5,17;"\.:% % % % \: ";AT 6,16;"\.:% % % % \: ";AT 7,16;"% % % % % % \: ";AT 8,16;"% % % % % % \: ";AT 9,16;"% % % % % % \: ";AT 10,16;"% % % % % % \: ";AT 11,16;"% % % % % % \: ";AT 12,16;"% % % % % % \: ";AT 13,16;"% % % % % % \: ";AT 14,16;"% % % % % % \: ";AT 15,16;"% % % % % % \: ";AT 16,16;"\':% % % % % \: ";AT 17,17;"\':% % % % \: ";AT 18,18;"\':% % % \: ";AT 19,19;"\':% % \: ";AT 20,20;"\':% \: ";AT 21,21;"\':\: "
430 IF M$(X+(X1=12)+(-1*(X1=-12))+(-12*(X1=1))+(12*(X1=-1)))="0" THEN PRINT AT 7,0;"\##\##\##\##\##\##\##";AT 8,0;"\##\##\##\##\##\##\##";AT 9,0;"\##\##\##\##\##\##";AT 10,0;"\##\##\##\##\##\##\##";AT 11,0;"\##\##\##\##\##\##\##";AT 12,0;"\##\##\##\##\##\##\##";AT 13,0;"\##\##\##\##\##\##\##";AT 14,0;"\##\##\##\##\##\##\##";AT 15,0;"\##\##\##\##\##\##\##"
440 IF M$(X+(X1+(X1=12)+(-1*(X1=-12))+(-12*(X1=1))+(12*(X1=-1))))="0" THEN PRINT AT 9,7;"\,,\,,\,,";AT 13,7;"\''\''\''";AT 10,7;"\##\##\##";AT 11,7;"\##\##\##";AT 12,7;"\##\##\##"
450 IF M$(X+D)="0" THEN PRINT AT 10,10;"\:. \.:";AT 11,10;"% % % ";AT 12,10;"\:' \':"
460 IF M$(X+(X1+(X1=-12)+(-1*(X1=12))+(-12*(X1=-1))+(12*(X1=1))))="0" THEN PRINT AT 9,13;"\,,\,,\,,";AT 13,13;"\''\''\''";AT 10,13;"\##\##\##";AT 11,16;"\##\##\##";AT 12,13;"\##\##\##"
470 IF M$(X+(X1=-12)+(-1*(X1=12))+(-12*(X1=-1))+(12*(X1=1)))="0" THEN PRINT AT 7,16;"\##\##\##\##\##\##\##";AT 8,16;"\##\##\##\##\##\##\##";AT 9,16;"\##\##\##\##\##\##\##";AT 10,16;"\##\##\##\##\##\##\##";AT 11,16;"\##\##\##\##\##\##\##";AT 12,16;"\##\##\##\##\##\##\##";AT 13,16;"\##\##\##\##\##\##\##";AT 14,16;"\##\##\##\##\##\##\##";AT 15,16;"\##\##\##\##\##\##\##"
480 LET O=O-.5
490 IF INT O<0 THEN GOTO 1500
510 IF X+D=EX OR X+X1=EX AND M$(X+X1)="0" THEN PRINT AT 11,11;"%E"
540 GOTO 290
600 CLS
610 PRINT "%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*"
620 FOR A=1 TO 19
630 PRINT "%*% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %*"
640 NEXT A
650 PRINT "%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*"
660 FOR A=2 TO 7
670 PRINT AT A,2;" "
680 NEXT A
690 PRINT AT 8,3;" % ";AT 7,4;" "
700 FOR A=2 TO 7
710 PRINT AT A,8;" "
720 NEXT A
730 FOR A=2 TO 8
740 PRINT AT A,8;" "
750 NEXT A
760 PRINT AT 2,9;" ";AT 5,9;" ";AT 8,9;" "
770 FOR A=2 TO 8
780 PRINT AT A,12;" "
790 NEXT A
800 PRINT AT 8,13;" "
810 FOR A=2 TO 8
820 PRINT AT A,16;" "
830 NEXT A
840 PRINT AT 8,17;" "
850 FOR A=11 TO 17
860 PRINT AT A,2;" "
870 NEXT A
880 PRINT AT 11,3;" ";AT 12,5;" ";AT 13,6;" ";AT 14,6;" ";AT 15,6;" ";AT 16,5;" ";AT 17,3;" "
890 FOR A=12 TO 16
900 PRINT AT A,8;"\ :% % \: "
910 NEXT A
920 PRINT AT 11,9;" ";AT 17,9;" "
930 FOR A=11 TO 17
940 PRINT AT A,14;" % % % "
950 NEXT A
960 PRINT AT 12,15;" ";AT 13,16;" ";AT 14,17;" "
970 FOR A=11 TO 17
980 PRINT AT A,20;" "
990 NEXT A
\n1000 PRINT AT 11,21;" ";AT 14,21;" ";AT 17,21;" "
\n1010 FOR A=11 TO 16
\n1020 PRINT AT 2,25;" "
\n1030 NEXT A
\n1040 PRINT AT 17,25;"%O";AT 16,25;"\.."
\n1050 PRINT AT 3,20;"%Y%O%U% %E%S%C%A%P%E%D";AT 5,20;"%Y%O%U% %H%A%D%:";INT O;AT 7,20;"%O%X%Y%G%E%N";AT 9,20;"%U%N%I%T%S% %L%E%F%T%."
\n1055 PRINT AT 19,4;"%P%R%E%S%S% %A%N%Y% %K%E%Y% %F%O%R% %R%E%P%L%A%Y"
\n1060 IF INKEY$="" THEN GOTO 1060
\n1070 CLS
\n1080 RUN
\n1500 CLS
\n1510 FOR A=1 TO 22
\n1520 PRINT "% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % "
\n1530 NEXT A
\n1535 PRINT AT 3,10;"%O%X%Y%G%E%N% %G%A%U%G%E"
\n1540 PRINT AT 5,1;"\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\.."
\n1550 PRINT AT 12,1;"\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''"
\n1560 FOR A=6 TO 11
\n1570 PRINT AT A,1;"\ : % % % % % % % % % % % % % % % % \: "
\n1580 NEXT A
\n1590 PRINT AT 13,1;"%0";AT 13,14;"%1%0";AT 13,28;"%2%0"
\n1600 FOR A=12 TO 2 STEP -1
\n1610 PRINT AT 6,A;"% ";AT 7,A;"% ";AT 8,A;"% ";AT 9,A;"% ";AT 10,A;"% ";AT 11,A;"% "
\n1620 NEXT A
\n1630 FOR A=6 TO 11
\n1640 PRINT AT A,3;" % % % % % % % % % % % % % "
\n1650 NEXT A
\n1670 PRINT AT 6,4;" ";AT 8,4;" ";AT 11,4;" "
\n1680 PRINT AT 7,8;" % ";AT 8,9;" "
\n1690 PRINT AT 6,13;" ";AT 7,16;" ";AT 8,16;" ";AT 9,14;" "
\n1700 PRINT AT 6,18;" % % % "
\n1710 PRINT AT 17,25;" "
\n1720 FOR A=8 TO 11
\n1730 PRINT AT A,25;"\: \ :"
\n1740 NEXT A
\n1750 PRINT AT 18,3;"%P%R%E%S%S% %A%N%Y% %K%E%Y% %F%O%R% %R%E%P%L%A%Y"
\n1760 IF INKEY$="" THEN GOTO 1760
\n1770 CLS
\n1780 RUN
\n3000 PRINT "%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*"
\n3010 FOR A=1 TO 19
\n3020 PRINT "%*% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %*"
\n3030 NEXT A
\n3035 PRINT "%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*"
\n3040 FOR A=1 TO 8
\n3050 PRINT AT A,2;" "
\n3060 NEXT A
\n3070 PRINT AT 1,2;" ";AT 4,2;" ";AT 8,2;" "
\n3080 PRINT AT 2,7;" ";AT 4,7;" ";AT 7,7;" ";AT 3,6;" ";AT 5,10;" ";AT 6,10;" "
\n3090 FOR A=3 TO 6
\n3100 PRINT AT A,12;" "
\n3110 NEXT A
\n3120 PRINT AT 3,13;" ";AT 6,13;" "
\n3130 PRINT AT 4,17;"\: % % \ :";AT 5,17;"\: \..\..\ :";AT 6,17;"\:.% % \.:";AT 3,18;"\''\''"
\n3140 PRINT AT 4,22;"\ .\. ";AT 5,22;"\..\..";AT 5,22;"\ ."
\n3150 PRINT AT 4,25;"%E"
\n3160 PRINT AT 11,10;"%I%N%S%T%R%U%C%T%I%O%N%S"
\n3170 PRINT AT 11,10;"\..\..\..\..\..\..\..\..\..\..\..\.."
\n3180 PRINT AT 14,2;"%Y%O%U% %A%R%E% %T%R%A%P%P%E%D% %I%N% %A% %C%O%M%P%L%E%X"
\n3190 PRINT AT 15,1;"%M%A%Z%E%.%A%L%L% %Y%O%U% %H%A%V%E% %T%O% %D%O% %I%S"
\n3200 PRINT AT 16,1;"%E%S%C%A%P%E%.%U%S%E% %-%:% %F%-%F%O%R%W%A%R%D"
\n3210 PRINT AT 17,15;"%T%-%T%U%R%N% %A%R%O%U%N%D"
\n3220 PRINT AT 18,15;"%L%-%T%U%R%N% %L%E%F%T"
\n3230 PRINT AT 19,15;"%R%-%T%U%R%N% %R%I%G%H%T"
\n3240 LET I$="PRESS ANY KEY TO START THE GAME "
\n3241 LET I$=I$(2 TO )+I$(1)
\n3242 PRINT AT 21,0;I$
\n3250 IF INKEY$="" THEN GOTO 3241
\n3260 CLS
\n3300 LET R=INT (RND*3)+1
\n3310 IF R=1 THEN LET M$="111111111111110001000001110111011101100010000001111000111101100010010001111111010111100000000011101010101001100010101101101000000001111111111111"
\n3320 IF R=2 THEN LET M$="111111111111101000001001101011011101100000000001111101010101100001011001101100000011101110111011100000000001101111010111101000000001111111111111"
\n3330 IF R=3 THEN LET M$="111111111111100001000001111101010101100000000101101010111101100010000001101000101111111011100011100001001001111101101101100000000001111111111111"
\n3450 LET O$="\ :\ :\ :\ :\ :\ :\ :\ :\ :\ :\ :\ :\ :\ :\ :\ :\ :\ :\ :\ :"
\n3460 LET O=20
\n3470 LET X=INT (RND*132)+1
\n3480 IF M$(X)<>"0" THEN GOTO 3470
\n3490 LET EX=INT (RND*132)+1
\n3500 IF M$(EX)<>"0" THEN GOTO 3470
\n3510 IF X=EX OR X+3=EX OR X-3=EX OR X+12=EX OR X-12=EX THEN GOTO 3470
\n3520 LET W$="\'. \.' "
\n3540 FOR P=1 TO 19
\n3550 LET W$=W$+" % % % % % % % % % % % % % % % % % % % % % % "
\n3560 NEXT P
\n3570 LET W$=W$+"\.' \'."
\n3750 RETURN
\n3760 CLEAR
\n3766 SAVE "1025%1"
\n3770 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

