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
1000 IF B$(14,11)="£" THEN RETURN
1010 PRINT AT 14,11;"£"
1020 IF B$(14,11)="." THEN LET T=T-1
1030 LET B$(14,11)="£"
1040 RETURN
1500 PRINT AT Y,X;" "
1510 LET A$=">"
1520 IF B$(Y,X+1)="-" THEN LET CHECK=0
1530 IF CHECK=0 THEN LET X=2
1535 IF CHECK=0 THEN GOTO 375
1540 IF B$(Y,X+1)<>"% " THEN LET R=1
1545 IF B$(Y,X+1)<>"% " THEN LET E=0
1550 GOTO 370
1600 PRINT AT Y,X;" "
1610 LET A$="<"
1620 IF B$(Y,X-1)="-" THEN LET CHECK=0
1630 IF CHECK=0 THEN LET X=20
1635 IF CHECK=0 THEN GOTO 375
1640 IF B$(Y,X-1)<>"% " THEN LET R=-1
1645 IF B$(Y,X-1)<>"% " THEN LET E=0
1650 GOTO 370
1700 PRINT AT Y,X;" "
1710 IF B$(Y-1,X)<>"% " AND B$(Y-1,X)<>"-" THEN LET E=-1
1715 IF B$(Y-1,X)<>"% " AND B$(Y-1,X)<>"-" THEN LET R=0
1720 LET A$="A"
1730 RETURN
1800 PRINT AT Y,X;" "
1810 IF B$(Y+1,X)<>"% " AND B$(Y-1,X)<>"-" THEN LET E=1
1815 IF B$(Y+1,X)<>"% " AND B$(Y-1,X)<>"-" THEN LET R=0
1820 LET A$="V"
1830 RETURN
2000 LET S=S+10
2010 LET C=-1
2020 LET B$(Y,X)=" "
2030 PRINT AT Y,X;A$
2040 LET T=T+10
2050 LET COUNT=0
2060 RETURN
3000 LET GY=GY+C
3010 GOTO 470
3020 LET GY=GY-C
3030 GOTO 470
3040 LET GX=GX+C
3050 GOTO 470
3060 LET GY=GY1+C
3070 GOTO 500
3080 LET GY1=GY1-C
3090 GOTO 500
3100 LET GX1=GX1+C
3110 GOTO 500
4000 LET R=0
4010 LET E=0
4020 GOTO 360
5000 CLS
5005 PRINT AT 10,31;"*"
5010 FOR N=1 TO 27
5020 PRINT AT 10,N;" %" >"
5025 REM SHEET CLEARED
5030 NEXT N
5040 FOR N=27 TO 1 STEP -1
5050 PRINT AT 10,N;""" < "
5060 NEXT N
5070 LET T=T+180
5080 GOTO 8
7000 PRINT AT 0,12;"MUNCHER II";TAB 11;"''''''''''''''''''''"
7010 PRINT AT 9,0;"--------------------------------"
7020 PRINT AT 11,3;"SELECT SKILL LEVEL (1-5)"
7030 PRINT TAB 7;"(5 IS THE EASIEST)"
7040 PRINT AT 14,0;"--------------------------------"
7050 PRINT TAB 5;" JAMES S. BROADDUS 1983"
7060 LET A=CODE INKEY$-28
7070 IF A>5 OR A<1 THEN GOTO 7060
7080 LET A=A/10
7090 LET A=A+.1
7100 CLS
7110 RETURN
8000 IF GY<>X OR GX<>Y THEN GOTO 8060
8010 LET S=S+10
8020 LET T=T+10
8030 LET GY=12
8040 LET GX=12
8045 PRINT AT Y,X;A$
8050 GOTO 500
8060 IF GY>=X OR GX1<>Y THEN GOTO 500
8070 LET S=S+10
8080 LET T=T+10
8090 LET GY1=11
8100 LET GX1=12
8105 PRINT AT Y,X;A$
8110 GOTO 500
9000 IF COUNT<=30 THEN GOTO 8000
9005 LET LIVES=LIVES-1
9010 FOR N=1 TO 10
9015 PRINT AT Y,X;A$
9016 FOR Q=1 TO 2
9017 NEXT Q
9018 PRINT AT Y,X;CHR$ (CODE A$+128)
9019 NEXT N
9020 IF LIVES<=0 THEN GOTO 9500
9025 CLS
9030 GOTO 210
9500 IF HS<S THEN LET HS=S
9510 PRINT AT 10,0;"HIT ANY KEY TO RESTART"
9520 IF INKEY$="" THEN GOTO 9520
9530 LET S=0
9540 CLS
9550 LET T=180
9560 LET LIVES=3
9570 GOTO 8
9580 CLEAR
9590 SAVE "1025%2"
9600 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
