Home / Commodore 64 / Phase 0 / Lesson 16
Lesson 16 of 64

Mini-Game: Maze Craze

What you'll learn:

  • Combine Week 2 systems into a playable maze chase with scoring and lives.
  • Use state values with `ON...GOTO` to juggle title, play, and game-over screens.
  • Generate mazes from DATA, animate enemies, and trigger sound cues on events.
25% Complete

Lesson 16 – Mini-Game: Maze Craze

Eight lessons later, you have maps, logic, data, functions, and sound. Now stitch them into a frantic keyboard maze. You’ll build three states (title, play, game over), generate the maze from DATA, animate enemies, and shout about high scores.

[🎥 suggested: clip showing the player dodging enemies in the maze]


The One-Minute Tour

  • Title, play, and game-over screens hang off a STATE variable.
  • ON STATE GOTO ... routes to the right section.
  • Maze data loads via DATA/READ; enemies loop in an array.
  • Collision logic from Lesson 13 guards the player; functions from Lesson 15 handle scoring.

The Game Listing

NEW
5 REM === Maze Craze ===
10 PRINT CHR$(147)
20 SCREEN=1024:COLOR=55296
30 DIM MAP(11,17),EX(2),EY(2),EDX(2)
35 DEF FNBONUS(L)=L*80
36 DEF FNPEN(S)=S-25
40 STATE=1:SCORE=0:LIVES=3:LEVEL=1
50 ON STATE GOTO 1000,2000,3000

1000 REM --- TITLE SCREEN ---
1010 PRINT CHR$(147)
1020 PRINT "MAZE CRAZE"
1030 PRINT "-----------"
1040 PRINT
1050 PRINT "W/A/S/D OR ARROW KEYS TO MOVE"
1060 PRINT "AVOID ENEMIES, REACH EXIT (*)"
1070 PRINT
1080 PRINT "PRESS SPACE TO START"
1090 GET K$:IF K$<>" " THEN 1090
1100 STATE=2:GOTO 50

2000 REM --- GAME STATE ---
2010 PRINT CHR$(147)
2020 GOSUB 6000        : REM Load map
2030 GOSUB 6100        : REM Place enemies
2040 GOSUB 6200        : REM Draw HUD & player
2050 GOSUB 6300        : REM Run gameplay loop
2060 IF LIVES<=0 THEN STATE=3:GOTO 2070
2065 STATE=2
2070 GOTO 50

3000 REM --- GAME OVER ---
3010 PRINT CHR$(147)
3020 PRINT "GAME OVER"
3030 PRINT
3040 PRINT "FINAL SCORE:";SCORE
3050 PRINT "LEVEL:";LEVEL
3060 PRINT
3070 PRINT "PLAY AGAIN? (Y/N)"
3080 GET K$:IF K$="" THEN 3080
3090 IF K$="Y" THEN SCORE=0:LIVES=3:LEVEL=1:STATE=1:GOTO 50
3100 PRINT : PRINT "THANKS FOR PLAYING!"
3110 END

6000 REM --- LOAD MAP INTO ARRAY ---
6010 RESTORE 9000
6020 FOR R=0 TO 11
6030 FOR C=0 TO 17
6040 READ MAP(R,C)
6050 NEXT C
6060 NEXT R
6070 RETURN

6100 REM --- SET ENEMY POSITIONS ---
6110 RESTORE 9200
6120 FOR I=0 TO 2
6130 READ EX(I),EY(I)
6140 EDX(I)=1-(I AND 1)*2
6150 NEXT I
6160 RETURN

6200 REM --- DRAW MAP, HUD, PLAYER ---
6210 PX=9:PY=10
6220 FOR R=0 TO 11
6230 FOR C=0 TO 17
6240 LOC=SCREEN+(R+4)*40+(C+11)
6250 VAL=MAP(R,C)
6260 CHAR=32:COL=1
6270 IF VAL=1 THEN CHAR=160:COL=0
6280 IF VAL=2 THEN CHAR=4:COL=2
6290 IF VAL=3 THEN CHAR=120:COL=2
6300 POKE LOC,CHAR
6310 POKE COLOR+(R+4)*40+(C+11),COL
6320 NEXT C
6330 NEXT R
6340 POKE SCREEN+PY*40+PX,64
6350 PRINT CHR$(19)
6360 PRINT "SCORE:";SCORE;"  LIVES:";LIVES;"  LEVEL:";LEVEL;"      ";
6370 RETURN

6300 REM --- GAME LOOP ---
6310 GOSUB 6400        : REM read input
6320 GOSUB 6500        : REM move enemies
6330 IF LIVES<=0 THEN RETURN
6340 MY=PY-4:MX=PX-11
6350 IF MAP(MY,MX)=2 THEN SCORE=SCORE+FNBONUS(LEVEL):LEVEL=LEVEL+1:PRINT CHR$(19):PRINT "SCORE:";SCORE;"  LIVES:";LIVES;"  LEVEL:";LEVEL;"      ";:STATE=2:RETURN
6360 GOTO 6310

6400 REM --- PLAYER MOVE ---
6410 GET K$:IF K$="" THEN RETURN
6420 NX=PX:NY=PY
6430 IF K$="W" OR K$=CHR$(145) THEN NY=PY-1
6440 IF K$="S" OR K$=CHR$(17) THEN NY=PY+1
6450 IF K$="A" OR K$=CHR$(157) THEN NX=PX-1
6460 IF K$="D" OR K$=CHR$(29) THEN NX=PX+1
6470 MX=NX-11:MY=NY-4
6480 IF MX<0 OR MX>17 OR MY<0 OR MY>11 THEN RETURN
6490 IF MAP(MY,MX)=1 THEN RETURN
6500 POKE SCREEN+PY*40+PX,32
6510 PX=NX:PY=NY
6520 POKE SCREEN+PY*40+PX,64
6530 RETURN

6500 REM --- ENEMY AI ---
6510 FOR I=0 TO 2
6520 POKE SCREEN+(EY(I)+4)*40+(EX(I)+11),32
6530 NX=EX(I)+EDX(I)
6540 IF NX<0 OR NX>17 THEN EDX(I)=-EDX(I):NX=EX(I)+EDX(I)
6550 IF MAP(EY(I),NX)=1 THEN EDX(I)=-EDX(I):NX=EX(I)+EDX(I)
6560 EX(I)=NX
6570 POKE SCREEN+(EY(I)+4)*40+(EX(I)+11),81
6580 IF PX=EX(I)+11 AND PY=EY(I)+4 THEN GOSUB 6600
6590 NEXT I
6595 RETURN

6600 REM --- PLAYER HIT ---
6610 LIVES=LIVES-1
6620 SCORE=FNPEN(SCORE):IF SCORE<0 THEN SCORE=0
6630 PRINT CHR$(19)
6640 PRINT "SCORE:";SCORE;"  LIVES:";LIVES;"  LEVEL:";LEVEL;"      ";
6650 FOR D=1 TO 150:NEXT D
6660 IF LIVES<=0 THEN RETURN
6670 PX=9:PY=10
6680 POKE SCREEN+PY*40+PX,64
6690 RETURN

9000 REM === MAZE DATA (0 floor, 1 wall, 2 exit, 3 hazard) ===
9010 DATA 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
9020 DATA 1,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,1
9030 DATA 1,0,1,1,0,1,0,1,1,1,1,0,1,1,0,1,0,1
9040 DATA 1,0,1,0,0,1,0,0,0,0,1,0,0,1,0,1,0,1
9050 DATA 1,0,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1
9060 DATA 1,0,0,0,0,0,0,1,0,1,1,0,0,0,0,1,0,1
9070 DATA 1,0,1,1,1,1,0,1,0,0,0,1,1,1,0,1,0,1
9080 DATA 1,0,0,0,0,1,0,1,1,1,0,0,0,1,0,0,0,1
9090 DATA 1,0,1,1,0,1,0,0,0,1,1,1,0,1,1,1,0,1
9100 DATA 1,0,0,1,0,0,0,1,0,0,0,0,0,0,3,0,0,1
9110 DATA 1,3,0,0,0,1,0,1,1,1,1,1,1,0,0,0,2,1
9120 DATA 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1

9200 REM === ENEMY DATA (three patrollers) ===
9210 DATA 2,9,15,4,8,6

Code Tour

  • Lines 20–36 define shared constants, arrays, and helper functions (FNBONUS, FNPEN).
  • Lines 50–70 implement the state machine: 1 = title, 2 = gameplay, 3 = game over.
  • Lines 1000–1100 render the attract screen and wait for space.
  • Lines 2000–2070 load the map, seed enemies, draw the HUD, then step into the loop.
  • Lines 6000–6690 house the engine pieces: loaders, renderer, input handler, enemy AI, and damage.
  • Lines 9000–9210 store the maze and patrol tables — swap the DATA to remix the experience.

Experiment Section

  • Add more patrols by extending EX()/EY() and the DATA block (remember to adjust the loop bound).
  • Randomise exits or hazards with RND(1) when you load the map.
  • Treat tile value 5 as a shield; once picked up it lets you survive the next hit.
  • Trigger Lesson 7 sound routines inside the bonus and penalty handlers.
  • Increase difficulty each level: speed up EDX, reduce LIVES, or shrink the maze offsets.
  • Log high scores: store the best SCORE and print a celebratory message when the player beats it.

Concept Expansion

State machines are how commercial games juggle menus, attract screens, gameplay, and endings. Each state owns only the logic it needs; transitions are just assignments to STATE. The same structure scales when you move to assembly or modern engines.


Game Integration

  • Reuse this state skeleton for future projects — swap the maze generator, keep the menu shell.
  • Support multiple floors by cycling through additional DATA sets as LEVEL rises.
  • Embed the Lesson 11 parser for in-maze terminals or story moments.
  • Export the map arrays to disk from the dev container for easier iteration later.

From the Vault

  • Paradroid — classic inspiration for patrolling bots and deck layouts.
  • Demo Scene 101 — cue ideas for title screen polish and transitions.

Quick Reference

STATE = 1 : ON STATE GOTO 1000,2000,3000   : REM title/play/game over
LOC = SCREEN + ROW*40 + COL                : REM screen address
READ MAP(R,C) : RESTORE 9000               : REM DATA-driven map load
DEF FNBONUS(L)=L*80                        : REM level-based bonuses
DEF FNPEN(S)=S-25                          : REM damage penalty helper

What You’ve Learnt

  • Orchestrated title, gameplay, and game-over states with ON...GOTO.
  • Populated a maze from DATA while aligning screen and colour RAM.
  • Animated enemy patrols and prevented wall clipping with compound checks.
  • Centralised scoring maths inside helper functions for readability.
  • Delivered the Week 2 capstone: a replayable maze game begging for expansion.

Next stop: Week 3 takes these systems into bigger maps, sprite tricks, and performance tuning.