3-D Escape

This file is part of and Timex Sinclair Public Domain Library Tape 1005. Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Game

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:

  1. 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.
  2. 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.
  3. Win screen (lines 600–1080): Draws an ASCII-art “escaped” trophy picture with inverse text, waits for a keypress, then restarts with RUN.
  4. 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 valueFacing
+1East
-1West
+12South
-12North

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 of GO TO 10 to 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

Appears On

Assembled by Tim Ward from many sources. Contains programs 10211 – 10251.

Related Products

Related Articles

Related Content

Image Gallery

3-D Escape

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.

People

No people associated with this content.

Scroll to Top