MINOTAUR

This file is part of and CATS Library Tape 1. Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Game

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

VariableRole
aCurrent maze row (1–4)
pCurrent position index within maze row
kDirection of travel (±1)
qOrientation flag (±1, horizontal/vertical)
x, yPlayer grid coordinates
x1, y1, a1Minotaur position
t1, t2, t3Weapon collected flags
mVisible corridor depth
dDistance 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.

Content

Appears On

From Blackjack to Star Trek, Moon Lander to a first-person dungeon crawler — this tape packs 21 games and puzzles into one of CATS' earliest library volumes. Card sharks, arcade fans, and puzzle solvers alike will find something here.

Related Products

Related Articles

Related Content

Image Gallery

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.

People

No people associated with this content.

Scroll to Top