Muncher II is a Pac-Man–style maze game written in BASIC by James S. Broaddus (1983) in which the player navigates a character through a 21×21 grid collecting dots, power pellets, and bonus items while evading two ghosts. The entire maze is stored as a two-dimensional string array B$(21,21), with each cell representing a wall (“% “), dot (“.”), power pellet (“*”), tunnel (“-“), or space, allowing collision detection by simple string indexing. Two ghosts are tracked by separate coordinate pairs (GX/GY and GX1/GY1) and move by comparing their position to the player’s coordinates, with a skill-level parameter A controlling how often random direction changes override pursuit. Eating a power pellet (line 2000) sets a counter C to −1 and a COUNT timer so that ghosts temporarily display in a vulnerable appearance and are worth bonus points if caught. The tunnel mechanic at the left and right walls is implemented by checking for the “-” character and wrapping the player’s X coordinate between columns 2 and 20.
Program Analysis
Program Structure
The program is organised into several distinct functional blocks reached by GOTO and GOSUB:
- Lines 1–7: Variable initialisation and skill-level selection subroutine (
GOSUB 7000). - Lines 8–210: Maze construction — each row of the 21×21 board is assigned to
B$(N)as a literal string. - Lines 220–240: Initial maze render loop.
- Lines 250–600: Main game loop covering player input, movement, scoring, ghost AI, and win/loss detection.
- Lines 1000–1040: Bonus item (“£”) spawning subroutine.
- Lines 1500–1650: Horizontal movement handlers with tunnel-wrap logic.
- Lines 1700–1830: Vertical movement handlers.
- Lines 2000–2060: Power-pellet consumption — activates vulnerable ghost mode.
- Lines 3000–3110: Ghost movement execution — each branch moves a ghost coordinate then jumps into the continuation of the ghost-update block.
- Lines 4000–4020: Stop-movement handler for unrecognised keys.
- Lines 5000–5080: Level-clear animation (sweeping line across row 10) and restart.
- Lines 7000–7110: Title screen and skill-level input.
- Lines 8000–8110: Ghost-catch scoring — awards points and resets ghost positions.
- Lines 9000–9600: Death/game-over sequence, high-score update, and
SAVE.
Maze Representation
The maze is held in a DIM B$(21,21) two-dimensional string array where each element is a two-character cell. Wall cells contain "% " (percent-space), dots are "." plus a space, power pellets are "*", tunnel exits are "-", and the bonus item is represented by "£". Because BASIC’s DIM pads shorter strings, assigning a 42-character string to a 21-element row fills every two-character cell simultaneously. Collision detection is then performed by reading individual cells such as B$(Y,X+1) directly.
A practical consequence is that the program never builds a separate collision map — the display state is the game state. Eating a dot or pellet is effected by LET B$(Y,X)=" " at line 400, which simultaneously clears the map and prevents re-collection.
Player Movement
Direction is encoded in two variables: R (horizontal delta, −1/0/1) and E (vertical delta, −1/0/1). Movement keys 5/6/7/8 route to subroutines at lines 1500–1830 that update R, E, and the avatar character A$ (">", "<", "A", "V"). Any other non-directional key jumps to line 4000 which zeroes both deltas, halting the player. The actual position update at lines 370–371 tests the destination cell before committing the move.
Tunnel Mechanic
Row 12 of the maze contains "-" characters at columns 1 and 21, representing tunnel exits. The movement routines detect a "-" in the direction of travel and set CHECK=0, which causes lines 1530/1630 to teleport X to column 2 or 20 respectively before continuing normal movement processing.
Ghost AI
Two ghosts share coordinate pairs (GX,GY) and (GX1,GY1). Each game-loop iteration compares the ghost coordinates to the player’s Y,X and attempts to step toward them if the destination cell is not a wall. The skill-level parameter A (range 0.2–0.6, lower = harder) is used as a probability threshold: IF RND<A THEN GOTO [skip] at lines 425, 436, 446, 456, 465, 474, 478, 486 randomly bypasses each pursuit decision. Lower A values skip fewer steps, making ghosts pursue more reliably.
Power-Pellet and Vulnerable Mode
Eating a "*" cell (line 375 → subroutine 2000) sets movement-step variable C to -1 and resets COUNT to 0. C normally equals 1; when −1, ghost movement subroutines step coordinates in the opposite direction (effectively reversing pursuit). Ghost appearance changes: lines 520–535 print "%"" (inverse ghost) when C=1 and plain """" when C=-1. Lines 500–510 increment COUNT and restore C=1 when it reaches 30, ending vulnerable mode.
Scoring and Win Condition
| Item | Points | T adjustment |
|---|---|---|
Dot (".") | +1 | — |
Power pellet ("*") | +10 | +10 (adds to target) |
Bonus item ("£") | +10 | +10 |
| Catch ghost (vulnerable) | +10 | +10 |
The win condition is S=T (line 415). T is initialised to 180 and incremented when power pellets and bonus items are added to the board, keeping it synchronised with total collectable points. On level clear, line 5070 adds 180 to T before looping to line 8 to reset the maze without resetting the score.
Bonus Item Spawning
Line 330 calls GOSUB 1000 with probability 1/50 per loop iteration. The subroutine places a "£" at the fixed position (row 14, column 11) only if that cell is empty. If a dot was previously there, line 1020 decrements T by 1 (since the dot was already counted in T) and the "£" replaces it at higher value, preserving score balance.
Death and Game-Over Sequence
A ghost catching the player (lines 540/550) routes to line 9000. If COUNT<=30 the player is in a power-up window and the ghost is caught instead (subroutine 8000 awards points and respawns the ghost). Otherwise, a life is lost. The death animation at lines 9010–9019 alternates printing A$ and CHR$(CODE A$+128) (the inverse-video version of the avatar character) in a short loop. At zero lives, the high score is updated and a restart prompt is shown.
Notable Techniques and Anomalies
- The
SAVE "1025%2"at line 9590 uses an inverse-video “2” in the filename — on Sinclair BASIC this is the auto-run flag embedded in the filename, causing the program toRUNautomatically when loaded. - Line 1810 contains a copy-paste bug: the down-movement guard checks
B$(Y-1,X)<>"-"instead ofB$(Y+1,X)<>"-", making the downward-movement tunnel guard test the wrong row. - Lines 3060 and 3070 incorrectly write
LET GY=GY1+C(overwriting ghost 1’s Y with ghost 2’s Y plus C) instead ofLET GY1=GY1+C. This is a variable-name bug causing ghost 1 to be displaced when ghost 2 moves right. - The level-clear animation at lines 5010–5060 prints a sweeping pattern across row 10, using embedded
">"and"<"characters to simulate a wiper effect before restarting at line 8. A$doubles as both the display character and a state indicator for direction — direction-sensitive scoring or behaviour could easily be added by testingA$.- Line 8060 contains a logic error:
IF GY>=X OR GX1<>Yshould likely beGY1>=Xto compare ghost 2’s column against the player, mirroring the ghost-1 check at line 8000.
Content
Source Code
1 LET S=0
2 LET HS=0
3 LET T=180
4 DIM B$(21,21)
5 LET COUNT=30
6 LET LIVES=3
7 GOSUB 7000
8 LET CHECK=1
9 LET E=0
10 LET R=1
11 LET B$(1)="% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % "
20 LET B$(2)="% .........% .........% "
30 LET B$(3)="% .% % % .% % % .% .% % % .% % % .% "
40 LET B$(4)="% *% % % .% % % .% .% % % .% % % *% "
50 LET B$(5)="% .% % % .% % % .% .% % % .% % % .% "
60 LET B$(6)="% ...................% "
70 LET B$(7)="% .% % % .% .% % % % % .% .% % % .% "
80 LET B$(8)="% .....% ...% ...% .....% "
90 LET B$(9)="% % % % % .% .......% .% % % % % "
100 LET B$(10)="% % % % % .% .......% .% % % % % "
110 LET B$(11)="% % % % % .% .% ---% .% .% % % % % "
120 LET B$(12)="-.......% % .......-"
130 LET B$(13)="% % % % % .% .% ---% .% .% % % % % "
140 LET B$(14)="% % % % % .% .... ..% .% % % % % "
150 LET B$(15)="% % % % % .% .% % % % % .% .% % % % % "
160 LET B$(16)="% .........% .........% "
170 LET B$(17)="% *% % % .% % % .% .% % % .% % % *% "
180 LET B$(18)="% ...% ...........% ...% "
190 LET B$(19)="% % % .% .% % % % % % % % % .% .% % % "
200 LET B$(20)="% % % ...............% % % "
210 LET B$(21)="% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % "
220 FOR N=1 TO 21
230 PRINT AT N,1;B$(N)
240 NEXT N
250 LET Y=14
255 LET E$=" "
260 LET X=12
265 LET D$=" "
270 LET A$=">"
275 LET C=1
280 LET GX=12
285 LET GY=12
290 LET GX1=12
295 LET GY1=11
300 PRINT AT GX,GY;"%""
310 PRINT AT GX1,GY1;"%""
320 PRINT AT Y,X;A$
325 PRINT AT 0,0;"LIVES:";LIVES;AT 0,19;"HI-SCORE:";HS
330 IF INT (RND*50)=0 THEN GOSUB 1000
335 PRINT AT Y,X;" "
340 IF INKEY$="8" OR (B$(Y,X+1)="-" AND R=1) THEN GOTO 1500
350 IF INKEY$="5" OR (B$(Y,X-1)="-" AND R=-1) THEN GOTO 1600
355 IF INKEY$<>"5" AND INKEY$<>"6" AND INKEY$<>"7" AND INKEY$<>"8" AND INKEY$<>"" THEN GOTO 4000
360 IF INKEY$="7" THEN GOSUB 1700
365 IF INKEY$="6" THEN GOSUB 1800
370 IF B$(Y,X+R)<>"-" AND B$(Y,X+R)<>"% " THEN LET X=X+R
371 IF B$(Y+E,X)<>"% " THEN LET Y=Y+E
375 IF B$(Y,X)="*" THEN GOSUB 2000
380 IF B$(Y,X)="." THEN LET S=S+1
390 IF B$(Y,X)="£" THEN LET S=S+10
395 IF B$(Y,X)="£" THEN LET T=T+10
400 LET B$(Y,X)=" "
405 PRINT AT 0,8;"SCORE:";S
410 PRINT AT Y,X;A$
415 IF S=T THEN GOTO 5000
420 PRINT AT GX,GY;D$;AT GX1,GY1;E$
425 IF RND<A THEN GOTO 436
430 IF GY<X AND B$(GX,GY+C)<>"% " AND B$(GX,GY+C)<>"% " THEN GOTO 3000
436 IF RND<A THEN GOTO 450
440 IF GY>X AND B$(GX,GY-C)<>"% " AND B$(GX,GY-C)<>"-" THEN GOTO 3020
446 IF RND<A THEN GOTO 456
450 IF GX<Y AND B$(GX+C,GY)<>"% " THEN GOTO 3040
456 IF RND<A THEN GOTO 470
460 IF GX>Y AND B$(GX-C,GY)<>"% " THEN LET GX=GX-C
465 IF RND<A THEN GOTO 474
470 IF GY1<X AND B$(GX1,GY1+C)<>"% " AND B$(GX1,GY1+C)<>"-" THEN GOTO 3060
474 IF RND<A THEN GOTO 478
475 IF GY1>X AND B$(GX1,GY1-C)<>"% " AND B$(GX1,GY1-C)<>"-" THEN GOTO 3080
478 IF RND<A THEN GOTO 486
480 IF GX1<Y AND B$(GX1+C,GY1)<>"% " THEN GOTO 3100
486 IF RND<A THEN GOTO 500
490 IF GX1>Y AND B$(GX1-C,GY1)<>"% " THEN LET GX1=GX1-C
500 LET COUNT=COUNT+1
510 IF COUNT>=30 THEN LET C=1
520 IF C=-1 THEN PRINT AT GX,GY;""""
525 IF C=1 THEN PRINT AT GX,GY;"%""
530 IF C=-1 THEN PRINT AT GX1,GY1;""""
535 IF C=1 THEN PRINT AT GX1,GY1;"%""
540 IF GX=Y AND GY=X THEN GOTO 9000
550 IF GX1=Y AND GY1=X THEN GOTO 9000
560 LET D$=B$(GX,GY)
570 LET E$=B$(GX1,GY1)
580 LET CHECK=1
600 GOTO 330
\n1000 IF B$(14,11)="£" THEN RETURN
\n1010 PRINT AT 14,11;"£"
\n1020 IF B$(14,11)="." THEN LET T=T-1
\n1030 LET B$(14,11)="£"
\n1040 RETURN
\n1500 PRINT AT Y,X;" "
\n1510 LET A$=">"
\n1520 IF B$(Y,X+1)="-" THEN LET CHECK=0
\n1530 IF CHECK=0 THEN LET X=2
\n1535 IF CHECK=0 THEN GOTO 375
\n1540 IF B$(Y,X+1)<>"% " THEN LET R=1
\n1545 IF B$(Y,X+1)<>"% " THEN LET E=0
\n1550 GOTO 370
\n1600 PRINT AT Y,X;" "
\n1610 LET A$="<"
\n1620 IF B$(Y,X-1)="-" THEN LET CHECK=0
\n1630 IF CHECK=0 THEN LET X=20
\n1635 IF CHECK=0 THEN GOTO 375
\n1640 IF B$(Y,X-1)<>"% " THEN LET R=-1
\n1645 IF B$(Y,X-1)<>"% " THEN LET E=0
\n1650 GOTO 370
\n1700 PRINT AT Y,X;" "
\n1710 IF B$(Y-1,X)<>"% " AND B$(Y-1,X)<>"-" THEN LET E=-1
\n1715 IF B$(Y-1,X)<>"% " AND B$(Y-1,X)<>"-" THEN LET R=0
\n1720 LET A$="A"
\n1730 RETURN
\n1800 PRINT AT Y,X;" "
\n1810 IF B$(Y+1,X)<>"% " AND B$(Y-1,X)<>"-" THEN LET E=1
\n1815 IF B$(Y+1,X)<>"% " AND B$(Y-1,X)<>"-" THEN LET R=0
\n1820 LET A$="V"
\n1830 RETURN
\n2000 LET S=S+10
\n2010 LET C=-1
\n2020 LET B$(Y,X)=" "
\n2030 PRINT AT Y,X;A$
\n2040 LET T=T+10
\n2050 LET COUNT=0
\n2060 RETURN
\n3000 LET GY=GY+C
\n3010 GOTO 470
\n3020 LET GY=GY-C
\n3030 GOTO 470
\n3040 LET GX=GX+C
\n3050 GOTO 470
\n3060 LET GY=GY1+C
\n3070 GOTO 500
\n3080 LET GY1=GY1-C
\n3090 GOTO 500
\n3100 LET GX1=GX1+C
\n3110 GOTO 500
\n4000 LET R=0
\n4010 LET E=0
\n4020 GOTO 360
\n5000 CLS
\n5005 PRINT AT 10,31;"*"
\n5010 FOR N=1 TO 27
\n5020 PRINT AT 10,N;" %" >"
\n5025 REM SHEET CLEARED
\n5030 NEXT N
\n5040 FOR N=27 TO 1 STEP -1
\n5050 PRINT AT 10,N;""" < "
\n5060 NEXT N
\n5070 LET T=T+180
\n5080 GOTO 8
\n7000 PRINT AT 0,12;"MUNCHER II";TAB 11;"\''\''\''\''\''\''\''\''\''\''"
\n7010 PRINT AT 9,0;"--------------------------------"
\n7020 PRINT AT 11,3;"SELECT SKILL LEVEL (1-5)"
\n7030 PRINT TAB 7;"(5 IS THE EASIEST)"
\n7040 PRINT AT 14,0;"--------------------------------"
\n7050 PRINT TAB 5;" JAMES S. BROADDUS 1983"
\n7060 LET A=CODE INKEY$-28
\n7070 IF A>5 OR A<1 THEN GOTO 7060
\n7080 LET A=A/10
\n7090 LET A=A+.1
\n7100 CLS
\n7110 RETURN
\n8000 IF GY<>X OR GX<>Y THEN GOTO 8060
\n8010 LET S=S+10
\n8020 LET T=T+10
\n8030 LET GY=12
\n8040 LET GX=12
\n8045 PRINT AT Y,X;A$
\n8050 GOTO 500
\n8060 IF GY>=X OR GX1<>Y THEN GOTO 500
\n8070 LET S=S+10
\n8080 LET T=T+10
\n8090 LET GY1=11
\n8100 LET GX1=12
\n8105 PRINT AT Y,X;A$
\n8110 GOTO 500
\n9000 IF COUNT<=30 THEN GOTO 8000
\n9005 LET LIVES=LIVES-1
\n9010 FOR N=1 TO 10
\n9015 PRINT AT Y,X;A$
\n9016 FOR Q=1 TO 2
\n9017 NEXT Q
\n9018 PRINT AT Y,X;CHR$ (CODE A$+128)
\n9019 NEXT N
\n9020 IF LIVES<=0 THEN GOTO 9500
\n9025 CLS
\n9030 GOTO 210
\n9500 IF HS<S THEN LET HS=S
\n9510 PRINT AT 10,0;"HIT ANY KEY TO RESTART"
\n9520 IF INKEY$="" THEN GOTO 9520
\n9530 LET S=0
\n9540 CLS
\n9550 LET T=180
\n9560 LET LIVES=3
\n9570 GOTO 8
\n9580 CLEAR
\n9590 SAVE "1025%2"
\n9600 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
