Minotaur is a first-person maze exploration game in which the player navigates a labyrinth to collect three hidden weapons (sword, shield, and helmet) before confronting the Minotaur. The maze is represented using four string arrays (`a$`) that encode corridor connectivity with numeric characters, and the 3D perspective corridor view is rendered entirely with PLOT and DRAW commands using INVERSE mode to create wall outlines. A wandering Minotaur enemy is implemented via a pathfinding subroutine at line 1000 that tracks the player’s position through the same string-based maze structure. Weapons are placed randomly by finding screen positions containing the character “-” (an open floor tile), and collection is checked by comparing the player’s coordinates against stored weapon locations. Custom UDG characters defined via POKEs to USR addresses are used to render a decorative title screen creature built from strings `m$` through `r$`.
Program Analysis
Program Structure
The program is organized into several functional regions:
- Lines 10–150: Title screen, introduction text, and variable initialization.
- Lines 160–840: Main game loop — 3D corridor rendering, player movement, Minotaur proximity display, and weapon pickup logic.
- Lines 850–870: Corridor-transition subroutine (switching maze sections).
- Lines 1001–1170: Minotaur AI pathfinding subroutine.
- Lines 2015–2110: Distance-scaled Minotaur sprite rendering (six LOD levels).
- Lines 2401: Map display subroutine using pre-built strings.
- Lines 3010–3030: Win/lose detection and endgame sequence.
- Lines 3210–3350: Weapon placement and player start-position initialization.
- Lines 3410–3440: Weapon pickup detection.
- Lines 4010–4550: UDG definition via DATA/POKE and sprite string construction.
Maze Data Representation
The maze is stored in a 4-element string array a$(4,34). Each element is a string of digit characters where "0" represents a wall/blocked cell, "5" represents a straight corridor, and "1"–"4" represent junctions or cross-references to other maze rows. The current maze row is tracked by variable a, the position within the row by p, and direction by k (±1). Transitions between rows use subroutine at line 850, which copies the relevant position index (x or y) into p and switches the row pointer a.
3D Corridor Rendering
The first-person corridor view is drawn entirely using PLOT and DRAW with INVERSE 1 to XOR walls onto the screen. The outer loop (lines 210–280) draws the receding side walls of the visible corridor segments using angled lines. The inner loop (lines 420–520) revisits each segment to decide whether to draw left or right openings based on maze data. The depth variable m limits drawing to the actual visible corridor length before a dead end.
Line 530 draws the end-cap wall at depth m, and line 410 draws the floor/ceiling intersection lines at screen bottom. The use of INVERSE 1 throughout means drawing the same line twice erases it, which is exploited intentionally for corridor opening cutouts.
Minotaur AI (Lines 1001–1170)
The Minotaur’s position is tracked with x1, y1, a1, and q1 (maze row and orientation), mirroring the player variables. The pathfinding logic at lines 1020–1170 adjusts the Minotaur’s direction toward the player using simple comparison (l and j for horizontal and vertical bias) and then attempts movement. If blocked, it reverses bias and tries again. This produces a basic but functional chase behavior through the string-encoded maze.
Distance-Scaled Sprite System
When the Minotaur is in the same corridor as the player, subroutine GO SUB 2010 is called with distance d. Line 2020 uses a computed GO TO (GO TO 2030+10*d) to jump to one of six rendering routines (lines 2040–2101), each printing a different-sized slice of the pre-built sprite strings m$ through q$ using INVERSE 1. The sprite strings reference UDG characters (\a through \u notation) defined in the GO SUB 4000 block.
UDG Definitions
Lines 4010–4550 define custom UDG characters for the Minotaur sprite. 55 bytes are POKEd into UDG memory via POKE USR CHR$ 144+n, a inside a FOR loop reading from DATA statements. The UDGs include solid vertical stripe patterns, half-block patterns, and mixed patterns used to build the creature’s body in the distance-sprite strings.
Weapon Placement and Pickup
Subroutine at line 3210 randomly selects screen positions and checks SCREEN$ for the character "-" to find valid floor locations. Up to four positions are stored in w(2,4). The fourth position determines the player’s starting location (line 3301). Pickup detection at lines 3410–3430 compares the player’s current x, y coordinates against the stored weapon positions and sets flags t1, t2, t3.
Map Display
The map subroutine (line 2401) prints pre-constructed strings e$ through i$ (defined at lines 101–140) directly to the screen with INK 4 on PAPER 0. These strings use \:: block graphic escapes (full block █) to represent walls and - for corridors, forming a top-down ASCII-art map of the entire maze layout.
Key Variables
| Variable | Role |
|---|---|
a | Current maze row (1–4) |
p | Current position index within maze row |
k | Direction of travel (±1) |
q | Orientation flag (±1, horizontal/vertical) |
x, y | Player grid coordinates |
x1, y1, a1 | Minotaur position |
t1, t2, t3 | Weapon collected flags |
m | Visible corridor depth |
d | Distance to Minotaur (for sprite scaling) |
Notable Anomalies and Bugs
- Line 75:
a$(3)is only 24 characters long vs. 34 for rows 1–2, meaning accesses beyond index 24 will read uninitialized (space) characters. Row 4 (line 80) is similarly short at 24 characters. - Line 300 is referenced by a
GO TOfrom line 230 but does not exist; execution falls through to line 310, which is the intended behavior for ending the forward wall-drawing loop early. - Line 401 is referenced from line 740 but does not exist; execution falls through to line 410, cleanly re-entering the rendering section without re-clearing the screen — this is intentional for the move-forward action.
- Line 801 is referenced from line 450 but does not exist; execution falls through to line 810, which draws the end-wall cutout. This is intentional dead-end rendering.
- Line 1000 is called via
GO SUB 1000(line 601) but the first actual line is 1001; the interpreter finds 1001 as the next line, so this works correctly. - Line 2400 is referenced but the actual subroutine code is at line 2401; similarly line 3200 maps to 3210, and line 3300 maps to 3301 — all are safe fall-through non-existent line targets.
- Line 3010 uses
SCREEN$ (8,7)="\m"(UDG “m”, char 157) to detect the Minotaur sprite on screen as the win condition, coupling the display state to game logic. - The
GO SUB 4000at line 20 jumps to line 4010, skipping line 4000 itself (which doesn’t exist);RETURNis at line 4510 after the DATA block.
Content
Source Code
10 INK 0: PAPER 4: BORDER 2: CLS
15 PRINT AT 6,7;" M I N O T A U R ";AT 8,0;" For the amusement of the king you have been dropped into the Minotaurs maze. Your only hope is to find 3 weapons hidden in the maze using a map provided."
20 GO SUB 4000
30 DIM a$(4,34): LET a1=3: LET l=-1: LET j=-1: LET x1=20: LET y1=16: LET q1=1: LET s=1: LET t1=0: LET t2=0: LET t3=0
40 LET k=1: IF RND<.5 THEN LET k=-1
60 LET a$(1)="0354553554003554555355554530455530"
70 LET a$(2)="0354553554553554555300004535455530"
75 LET a$(3)="015525551002555102551520"
80 LET a$(4)="015520001552555152001520"
101 LET e$="---------\::\::---------------\::-----"
110 LET f$="-------------------\::\::\::\::---------"
120 LET g$="-\::-\::\::-\::\::-\::\::-\::\::-\::\::\::-\::\::\::\::-\::-\::-\::\::\::-"
130 LET h$="-\::\::\::\::-\::\::\::\::\::-\::\::\::\::\::\::-\::\::\::\::\::\::-\::\::\::\::\::-"
140 LET i$="\::\::-\::\::\::\::\::-\::\::\::\::\::-\::\::\::\::\::\::\::\::-\::\::\::-\::\::\::\::"
145 PRINT AT 18,4;"Use keys 5,6,7,&8 to move";AT 20,8;"Press any key"
150 IF INKEY$="" THEN GO TO 150
160 GO SUB 2400: GO SUB 3200
170 PRINT AT 8,16; INVERSE 1;"Press";AT 9,16;"any key": INK 0: PAPER 6: FOR n=1 TO 3: PRINT AT w(1,n),w(2,n);"X": NEXT n
180 BEEP .8,-16: BEEP .8,-22: IF INKEY$="" THEN GO TO 180
190 INK 6: PAPER 2: CLS : PAPER 0
195 PRINT AT 3,17;"Press M for map";AT 5,17;"Press A to pick";AT 6,17;" up armour "
201 FOR n=0 TO 21: PRINT AT n,0;" ": NEXT n
205 LET m=8
210 FOR n=0 TO m-1
230 IF a$(a,n*k+p)="0" THEN LET m=n+1: GO TO 300
260 PLOT 8*n,16*n: DRAW 8,16: DRAW 0,159-16*n
270 PLOT 127-8*n,16*n: DRAW -8,16: DRAW 0,159-16*n
280 NEXT n
310 FOR n=0 TO 6
320 PRINT AT 14+n,6; INVERSE 1;r$(1+4*n TO 4+4*n): NEXT n
330 IF t1=1 THEN PRINT AT 15,5; INK 6;" ";AT 16,5;" ";AT 17,5;" ";AT 9,17; INVERSE 1;" Sword "
340 IF t2=1 THEN FOR n=0 TO 3: PAPER 4: PRINT AT 15+n,9; INK 0;"/": NEXT n: PRINT AT 11,17;" Shield ": PAPER 0: PLOT 72,48: DRAW 7,0: PLOT 72,32: DRAW 7,0
350 IF t3=1 THEN PRINT AT 14,7; PAPER 2;"WW";AT 10,17;" Helmet "
410 PLOT 0,0: DRAW 8,16: DRAW INVERSE 1;-7,0: PLOT 127,0: DRAW -8,16: DRAW INVERSE 1;7,0
420 FOR n=m-1 TO 0 STEP -1
430 LET b=VAL a$(a,p+n*k)
440 IF b=5 THEN GO TO 520
450 IF b=0 THEN LET m=m-1: GO TO 801
460 IF b=1 OR b=2 THEN IF a$(b,x+k)="0" THEN GO TO 490
470 IF b=4 OR b=3 THEN IF a$(b,y-k)="0" THEN GO TO 501
480 PLOT 8*n,16*n: DRAW INVERSE 1;8,16: DRAW -7,0: DRAW 7,0: DRAW 8,16: DRAW INVERSE 1;-7,0
490 IF b=1 OR b=2 THEN IF a$(b,x-k)="0" THEN GO TO 520
501 IF b=3 OR b=4 THEN IF a$(b,y+k)="0" THEN GO TO 520
510 PLOT 127-8*n,16*n: DRAW INVERSE 1;-8,16: DRAW INVERSE 1;-8,16: DRAW 7,0: DRAW -7,0: DRAW -8,16: DRAW INVERSE 1;7,0
520 NEXT n
530 PLOT m*8,m*16: DRAW INVERSE 1;8,16: PLOT 127-m*8,m*16: DRAW INVERSE 1;-8,16
601 PAUSE 30: IF RND<.7 THEN GO SUB 1000
610 IF x=x1 AND y=y1 THEN GO TO 3000
620 IF q=-1 THEN LET y=p: IF x=x1 AND (y1-y)*k<=m THEN LET d=(y1-y)*k: IF d>-1 THEN GO SUB 2010
630 IF q=1 THEN LET x=p: IF y=y1 AND (x1-x)*k<m THEN LET d=(x1-x)*k: IF d>-1 THEN GO SUB 2010
640 INK 6
710 IF INKEY$="a" THEN BEEP .1,24: GO TO 3400
720 IF INKEY$="m" THEN GO SUB 2400: GO TO 170
730 IF INKEY$="6" THEN LET k=k*-1: GO TO 201
740 IF INKEY$="7" THEN IF a$(a,p+k)<>"0" THEN LET p=p+k: BEEP .2,0: GO TO 401
750 IF INKEY$="5" AND b<>5 THEN LET k=-q*k: GO SUB 850: GO TO 201
760 IF INKEY$="8" AND b<>5 THEN LET k=q*k: GO SUB 850: GO TO 201
770 GO TO 601
810 PLOT 8*(n+1),175: DRAW INVERSE 1,0,-159+16*n: DRAW INVERSE 1,-8,-16: DRAW 16*(7-n)+15,0: DRAW INVERSE 1,-8,16: DRAW INVERSE 1;0,159-16*n
820 PLOT 8*(n+1),16*(n+1): DRAW INVERSE 1;16*(6-n)+15,0
830 PLOT 48,48: DRAW 31,0: PLOT 48,32: DRAW 31,0
840 GO TO 520
850 IF b=1 OR b=2 THEN LET p=x
860 IF b=3 OR b=4 THEN LET p=y
870 LET a=b: LET q=-q: RETURN
1001 IF q1=1 THEN IF a$(a1,y1)="5" THEN LET y1=y1+j: RETURN
1010 IF q1=-1 THEN IF a$(a1,x1)="5" THEN LET x1=x1+1: RETURN
1020 IF x<x1 THEN LET l=-1
1030 IF x>x1 THEN LET l=1
1040 IF y<y1 THEN LET j=-1
1050 IF y>y1 THEN LET j=1
1060 IF q1=-1 THEN GO TO 1120
1070 LET b1=VAL a$(a1,y1)
1080 IF x=x1 THEN IF a$(a1,y1+j)<>"0" THEN LET y1=y1+j: RETURN
1090 IF a$(b1,x1+1)<>"0" THEN LET a1=b1: LET q1=-1: LET x1=x1+1: RETURN
1101 IF a$(a1,y1+j)<>"0" THEN LET y1=y1+j: RETURN
1110 LET j=-j: LET l=-l: GO TO 1090
1120 LET b1=VAL a$(a1,x1)
1130 IF y=y1 THEN IF a$(a1,x1+1)<>"0" THEN LET x1=x1+1: RETURN
1140 IF a$(b1,y1+j)<>"0" THEN LET a1=b1: LET q1=1: LET y1=y1+j: RETURN
1160 IF a$(a1,x1+l)<>"0" THEN LET x1=x1+l: RETURN
1170 LET j=-j: LET l=-l: GO TO 1140
2015 INK 3
2020 GO TO 2030+10*d
2040 FOR e=0 TO 8: PRINT AT 5+e,5; INVERSE 1;m$(7*e+1 TO 7*e+7): NEXT e: RETURN
2050 FOR e=0 TO 8: PRINT AT 5+e,5; INVERSE 1;n$(1+e*6 TO 6+e*6): NEXT e: RETURN
2060 FOR e=0 TO 8: PRINT AT 5+e,5; INVERSE 1;o$(1+e*5 TO 5+e*5): NEXT e: RETURN
2070 FOR e=0 TO 7: PRINT AT 5+e,6; INVERSE 1;p$(1+e*4 TO 4+e*4): NEXT e: RETURN
2080 FOR e=0 TO 5: PRINT AT 5+e,7; INVERSE 1;q$(1+e*3 TO 3+e*3): NEXT e: RETURN
2090 FOR e=0 TO 3: PRINT AT 5+e,7; INVERSE 1;q$(19+e*2 TO 20+e*2): NEXT e: RETURN
2101 PRINT AT 5,7;"\a";AT 6,7;"\a"
2110 RETURN
2401 PAPER 0: INK 4: PRINT AT 0,0;e$: PRINT g$;g$;f$;h$;h$;h$;e$;i$;i$;f$;g$;g$;g$;e$;i$;f$;h$;h$;e$;g$;f$: RETURN
3010 IF t1=1 AND t2=1 AND t3=1 THEN IF SCREEN$ (8,7)="\m" THEN PRINT AT 21,0; FLASH 1;" You have killed the minotaur ": GO TO 3030
3020 INK 5: PAPER 1: PRINT AT 20,0; FLASH 1;" You have failed and payed the price of failure "
3030 FOR n=1 TO 20: BEEP .2,INT (RND*30): NEXT n: INK 3: GO SUB 2040: INK 6: PAPER 0: STOP
3210 DIM w(2,4)
3220 FOR n=1 TO 4
3230 LET w1=INT (RND*22): LET w2=INT (RND*32)
3240 IF SCREEN$ (w1,w2)="-" THEN LET w(1,n)=w1: LET w(2,n)=w2: NEXT n: GO TO 3300
3250 LET w1=w1+1: LET w2=w2+1: IF w1>21 THEN LET w1=0
3260 IF w2>32 THEN LET w2=0
3270 GO TO 3240
3301 LET y=w(1,4)+2: LET x=w(2,4)+2
3330 IF a$(3,w1+2)="2" OR a$(3,w1+2)="1" THEN LET b=VAL a$(3,w1+2): LET q=-1: GO SUB 850: GO TO 170
3350 LET b=VAL a$(1,w2+2): LET q=1: GO SUB 850: GO TO 170
3410 IF y-2=w(1,1) AND x-2=w(2,1) THEN LET t1=1
3420 IF y-2=w(1,2) AND x-2=w(2,2) THEN LET t2=1
3430 IF y-2=w(1,3) AND x-2=w(2,3) THEN LET t3=1
3440 GO TO 330
4010 LET m$="\::\b\d \c\a\::\::\b \a\::\::\b"+CHR$ 129+" "+CHR$ 130+"\a\::"+CHR$ 131+"\f \m \g"+CHR$ 131+" \d \c \d \c \d \c "
4020 LET n$="\::\: \d\c"+CHR$ 133+"\::\::\: "+CHR$ 136+CHR$ 132+CHR$ 133+"\::\::\: "+CHR$ 132+CHR$ 136+" \:: \d \c \d \c \d \c \::\d\c\d\c\::"
4030 LET o$="\::\: "+CHR$ 133+"\::\::\: "+CHR$ 133+"\::"+CHR$ 131+CHR$ 130+"-"+CHR$ 129+CHR$ 131+" \c \d\c \d\..\d \c\..\::\d\e\c\::\::\d\e\c\::"
4040 LET p$="\::\d\c\::\::\d\c\:: \c \d\c \d\b \a\b\c\d\a\b\c\d\a"
4050 LET q$="\b \a\f \g \e \e\: \e"+CHR$ 133+"\: \e"+CHR$ 133+"\: "+CHR$ 133+" "+CHR$ 136+CHR$ 132+"\: "+CHR$ 133+"\: "+CHR$ 133
4060 LET r$="\:: \::"+CHR$ 131+" "+CHR$ 131+"=\/== ="+CHR$ 136+" "+CHR$ 136+"\: "+CHR$ 133+"\: "+CHR$ 133+"\: "+CHR$ 133+"\: "+CHR$ 133
4510 FOR n=1 TO 55: READ a: POKE USR CHR$ 144+n,a: NEXT n: RETURN
4520 DATA 127,127,127,127,127,127,127,127,254,254,254,254,254,254,254,254
4530 DATA 1,1,1,1,1,1,1,1,128,128,128,128,128,128,128,128
4540 DATA 24,24,24,24,24,24,24,24
4550 DATA 254,254,254,254,0,0,0,0,127,127,127,127,0,0,0,0
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

