Zombies is a real-time maze-avoidance game in which the player navigates an island grid using the numeric keys 1–8 (representing eight compass directions) while trying to lure blind, sound-detecting zombies into potholes. The playing field is a 10×32 grid of border characters, randomly populated with “Z” zombie characters and at least one “O” pothole during setup, and the display file base address is obtained via PEEKs of system variables 16396–16397 for direct screen manipulation. Zombie movement uses a simple Manhattan-distance heuristic: each zombie shifts one cell horizontally and/or vertically toward the player’s current position each turn. The player’s position and all zombie positions are tracked as linear offsets into the display file, with each screen row being 33 bytes wide (32 characters plus a newline). PAUSE 55555 followed by POKE 16437,255 and INKEY$ forms the standard interrupt-driven keypress detection idiom for this platform.
Program Analysis
Program Structure
The program is organized into a main initialization block, a game loop, and a subroutine for the title/instructions screen:
- Lines 0–9: Declarations, variable initialization, and array allocation (
DIM Z(40)for up to 40 zombie positions). - Lines 10–181: Screen construction — draws the border wall of
Ocharacters, randomly scatters zombies (Z) and potholes (O), and prints the movement key legend. - Lines 188–246: Player turn — places the player character (
61==) at a random empty cell, waits for a valid key (1–8), computes the target cell, and moves or triggers a collision. - Lines 300–435: Zombie AI turn — iterates over all live zombies, moves each one step toward the player using a coordinate-difference heuristic.
- Lines 500–610: Collision resolution — distinguishes player capture, zombie–zombie collision, and zombie-into-pothole events.
- Lines 700–750: End-of-game screen, replay prompt, and
CLEAR/restart loop. - Lines 1000–1150: Title screen and instructions subroutine, called once at startup via
GOSUB 1000.
Display File Manipulation
Rather than using PRINT AT for every game element, the program obtains the display file base address at line 189:
LET DF=PEEK 16396+256*PEEK 16397
System variables at addresses 16396–16397 hold the low and high bytes of the display file pointer. All subsequent player and zombie movement is done with PEEK and POKE directly into this address space, which is considerably faster than interpreted PRINT AT calls and essential for a real-time game.
Each screen row is treated as 33 bytes wide (32 character cells plus a newline byte), so a zombie’s or player’s screen position is stored as a single linear offset. Row and column are recovered by integer division and modulo with 33 (lines 300–340).
Character Encoding Used as Game State
The program exploits the numeric values of display-file character codes to distinguish game objects without a separate state array:
| PEEK value | Character | Game object |
|---|---|---|
| 0 | (space) | Empty cell |
| 52 | O (rendered) | Pothole / border wall |
| 61 | = | Player |
| 63 | Z | Zombie |
Note that the border walls and potholes share the same character code (52 for O), so the collision handler at line 250 checks specifically for code 63 (zombie) to distinguish zombie-mouth from pothole collisions; anything else is assumed to be a wall or pothole.
Zombie AI Heuristic
Lines 350–390 implement a step-by-step pursuit algorithm. For each zombie, the row offset B is set to +33, −33, or 0 depending on whether the zombie is above, below, or level with the player. Then the column delta (+1, −1, or 0) is added. This produces a diagonal step toward the player in one move, making zombies aggressive and fast. The algorithm is deterministic — every zombie always moves toward the player with no randomness or pathfinding around obstacles.
Eight-Directional Movement Encoding
Line 241 computes the target offset B from the current offset Q using a compact Boolean arithmetic expression:
LET B=Q+(A=1 OR A=2 OR A=3)-(A=7 OR A=6 OR A=5)+33*(A=5 OR A=4 OR A=3)-33*(A=7 OR A=8 OR A=1)
On this platform, Boolean expressions evaluate to 1 (true) or 0 (false), so this single line simultaneously computes the column delta (±1) and row delta (±33) for all eight directions from the numpad layout printed on screen. This avoids a lookup table or multi-line conditional block.
Keypress Detection Idiom
Lines 220–231 and 710–720 use the standard pattern: PAUSE 55555 (a very long pause, effectively waiting for a keypress interrupt), followed by POKE 16437,255 to re-enable the keyboard, then INKEY$ to read the key. The PAUSE 55555 returns early when any key is pressed, making the sequence an efficient blocking key-read without a busy loop.
Zombie Position Array
The array Z(40) stores each zombie’s current display-file offset. Dead zombies (eliminated by falling into a pothole) are flagged with Z(A)=-1 at line 510, and line 325 skips negative entries during the movement loop. The counter N tracks surviving zombies; when N=0, the player wins (lines 545–560).
Random Grid Population
Lines 80–110 use Q=INT(RND*2000) to randomly decide the content of each interior cell. Values in the range 1795–1849 place a zombie; values ≥1850 place a pothole (O). This gives approximately a 2.75% chance of a zombie and a 7.5% chance of a pothole per cell, with the remaining ~89.75% left empty. The zombie’s display-file offset is stored as A*33+B+1, consistent with the 33-bytes-per-row convention.
Notable Anomalies
- Line 11 defines
A$as the splash string “SPLASH GOES A ZOMBIE” (using inverse-video block characters for spaces), but this variable is immediately overwritten at line 1140 by the INKEY$ result from the title screen. The intendedA$splash message is only restored to the correct string if the game is restarted viaGOTO 3, which does not re-run theGOSUB 1000subroutine — so after the first playthrough,A$at line 535 correctly prints the splash message set at line 11 on restart. - Line 610 POKEs the zombie’s destination cell with code 63 (zombie) after the player has been eaten, which is cosmetically redundant since the game ends immediately after.
- The movement key “9” (center / no-move) is absent; the guard at line 231 rejects keys outside “1”–”8″, so the player must always move.
- Border walls share character code 52 with potholes, making it impossible to distinguish walking into a wall from falling into a pothole by PEEK alone — both trigger the “OOPS INTO A POTHOLE” message at line 266, which is technically incorrect for wall collisions.
Content
Image Gallery
Source Code
0 REM (C)1981 ARTIC COMPUTING
1 GOSUB 1000
3 CLS
4 LET P2=0
5 LET Z2=0
6 LET Z1=0
8 DIM Z(40)
9 LET Z=0
10 FAST
11 LET A$="[S][P][L][A][S][H]█[G][O][E][S]█[A]█[Z][O][M][B][I][E]"
16 LET P1=0
18 FOR A=1 TO 32
20 PRINT "O";
30 NEXT A
40 PRINT
50 FOR A=1 TO 10
60 PRINT "O";
70 FOR B=1 TO 30
80 LET Q=INT (RND*2000)
90 IF Q<1795 THEN GOTO 120
100 IF Q>=1850 THEN GOTO 110
102 PRINT AT A,B;"Z"
103 LET Z=Z+1
104 LET Z(Z)=A*33+B+1
110 IF Q>=1850 THEN PRINT AT A,B;"O"
120 NEXT B
130 PRINT AT A,31;"O"
140 NEXT A
150 FOR A=1 TO 32
160 PRINT "O";
170 NEXT A
171 PRINT
172 PRINT
173 PRINT
178 PRINT "MOVEMENT"
179 PRINT "7 8 1"
180 PRINT "6 X 2"
181 PRINT "5 4 3"
182 SLOW
188 LET N=Z
189 LET DF=PEEK 16396+256*PEEK 16397
190 LET Q=INT (RND*320+1)
200 IF PEEK (DF+Q)<>0 THEN GOTO 190
210 POKE DF+Q,61
220 PAUSE 55555
221 POKE 16437,255
230 LET B$=INKEY$
231 IF B$<"1" OR B$>"8" THEN GOTO 220
240 LET A=VAL B$
241 LET B=Q+(A=1 OR A=2 OR A=3)-(A=7 OR A=6 OR A=5)+33*(A=5 OR A=4 OR A=3)-33*(A=7 OR A=8 OR A=1)
242 IF PEEK (DF+B)<>0 THEN GOTO 250
243 POKE DF+Q,0
244 POKE DF+B,61
246 LET Q=B
247 GOTO 300
250 IF PEEK (DF+B)=63 THEN GOTO 280
260 POKE DF+B,52
265 POKE DF+Q,0
266 PRINT AT 18,5;"OOPS INTO A POTHOLE"
270 GOTO 700
280 PRINT AT 18,1;"STRAIGHT INTO A ZOMBIES MOUTH"
285 POKE DF+Q,0
290 GOTO 700
300 LET P1=INT (Q/33)
310 LET P2=Q-P1*33
312 PRINT AT 12,0;" "
320 FOR A=1 TO Z
325 IF Z(A)<0 THEN GOTO 435
330 LET Z1=INT (Z(A)/33)
340 LET Z2=Z(A)-Z1*33
350 IF Z1<P1 THEN LET B=33
360 IF Z1>P1 THEN LET B=-33
370 IF Z1=P1 THEN LET B=0
380 IF Z2<P2 THEN LET B=B+1
390 IF Z2>P2 THEN LET B=B-1
400 POKE DF+Z(A),0
410 IF PEEK (DF+Z(A)+B)<>0 THEN GOTO 500
420 POKE DF+Z(A)+B,63
430 LET Z(A)=Z(A)+B
435 NEXT A
440 GOTO 220
500 IF PEEK (DF+Z(A)+B)=61 THEN GOTO 600
510 LET Z(A)=-1
520 LET N=N-1
530 IF PEEK (DF+Z(A)+B)=63 THEN GOTO 435
535 PRINT AT 12,0;A$
545 IF N=0 THEN GOTO 560
550 GOTO 435
560 PRINT AT 18,5;"WELL DONE"
562 PRINT "ZOMBIES ARE KILLED"
570 GOTO 700
600 PRINT AT 18,1;"YUM YUM YOU HAVE BEEN EATEN"
610 POKE DF+Z(A)+B,63
700 PRINT
701 PRINT "PRESS N/L TO PLAY AGAIN"
710 PAUSE 55555
711 POKE 16437,255
720 LET B$=INKEY$
721 IF CODE B$<>118 THEN STOP
730 CLEAR
750 GOTO 3
1000 PRINT AT 0,9;"█[Z][O][M][B][I][E][S]█"
1010 PRINT
1015 PRINT
1020 PRINT "YOU HAVE CRASHED ON A ISLAND"
1025 PRINT
1030 PRINT "THE ISLAND IS INHABITED BY"
1035 PRINT
1045 PRINT "MAN EATING ZOMBIES."
1050 PRINT
1055 PRINT
1060 PRINT "THE ZOMBIES ARE BLIND BUT THEY"
1065 PRINT
1070 PRINT "DETECT YOU BY THE SOUND OF YOUR"
1075 PRINT
1080 PRINT "HEART BEAT. YOUR ONLY HOPE IS"
1085 PRINT
1090 PRINT "LURE THEM INTO THE POTHOLES."
1100 PRINT
1105 PRINT
1110 PRINT "PRESS ANY KEY WHEN READY"
1120 PAUSE 55555
1130 POKE 16437,255
1140 LET A$=INKEY$
1150 RETURN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.