--- title: "MINOTAUR" id: 54512 type: "computer_media" slug: "minotaur" url: "http://localhost/computer_media/minotaur/" markdown_url: "http://localhost/computer_media/minotaur.md" published_at: "2024-06-02T16:09:36+00:00" modified_at: "2026-03-30T21:44:05+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2019/02/20230809-041332.jpg" excerpt: "Navigate a first-person 3D maze, collect three hidden weapons, and survive a wandering enemy in this text-and-graphics dungeon crawler." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_contents: - id: 51212 title: "CATS Library Tape 1" type: "computer_media" url: "http://localhost/computer_media/cats-library-tape-1/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/06/minotaur-2.png" - url: "http://localhost/wp-content/uploads/2024/06/minotaur-1.png" media_type_tags: "Game" --- # MINOTAUR 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: 1. **Lines 10–150:** Title screen, introduction text, and variable initialization. 2. **Lines 160–840:** Main game loop — 3D corridor rendering, player movement, Minotaur proximity display, and weapon pickup logic. 3. **Lines 850–870:** Corridor-transition subroutine (switching maze sections). 4. **Lines 1001–1170:** Minotaur AI pathfinding subroutine. 5. **Lines 2015–2110:** Distance-scaled Minotaur sprite rendering (six LOD levels). 6. **Lines 2401:** Map display subroutine using pre-built strings. 7. **Lines 3010–3030:** Win/lose detection and endgame sequence. 8. **Lines 3210–3350:** Weapon placement and player start-position initialization. 9. **Lines 3410–3440:** Weapon pickup detection. 10. **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 TO` from 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 4000` at line 20 jumps to line 4010, skipping line 4000 itself (which doesn’t exist); `RETURN` is at line 4510 after the DATA block. ## 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-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 xx1 THEN LET l=1 1040 IF yy1 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 ```