This program implements a whack-a-mole style arcade game for one player. A mole (represented by a space in an inverse-video row) appears at a random horizontal position and the player moves a crosshair cursor left and right using the N and M keys. The game reads the keyboard via INKEY$ and detects a hit by PEEKing the ZX81’s display file pointer at system variables 16398–16399, checking whether the character under the cursor is 128 (inverse space), indicating the mole’s position. Score is accumulated in variable S and displayed when a hit is detected, after which the program halts.
Program Analysis
Program Structure
The program is organised into a short main loop with two subroutines:
- Initialisation (lines 1–6): Clears the screen by printing ten inverse-space rows, then sets the paddle column
P=3and scoreS=0. - Main game loop (lines 7–19): Picks a random mole column, calls the display subroutines, checks for a hit or a random dwell timeout, then briefly animates the mole sinking back before repeating.
- Mole display subroutine (lines 20–23): SCROLLs the play area and prints a row of inverse-space characters with a plain space gap at the mole’s position.
- Input/detection subroutine (lines 24–29): Reads the display file at the cursor position to detect a hit, prints a crosshair
+, increments the stroke counter, and moves the cursor via N/M keys. - End of game (lines 30–40): Prints the score at the cursor position and STOPs.
Inverse-Video Graphics Technique
The playing field is rendered entirely using inverse-space characters (character code 128 on the ZX81). The mole appears as a gap — a plain space — in an otherwise solid inverse row, making it visually distinct without any custom graphics. Line 3 prints the initial border rows using "% % " where % represents inverse characters.
Display-File PEEK Hit Detection
The most technically interesting aspect is the hit-detection method in lines 24–25. Rather than tracking the mole’s column numerically and comparing it to the cursor column P, the program directly reads the ZX81 display file:
- System variables at addresses 16398 and 16399 hold the low and high bytes of the display file start address (D_FILE).
PEEK 16398 + 256*PEEK 16399calculates the base address of the display file.PEEKof that address retrieves the character currently under the crosshair position.- A result of
128(inverse space) means the cursor is over part of the mole row, i.e., a hit; line 10 and 17 branch to the end sequence if this is true.
This is a compact and elegant approach that avoids storing or comparing the mole’s column explicitly.
Key BASIC Idioms
- Movement via boolean arithmetic: Line 28 uses
P = P + (INKEY$="M") - (INKEY$="N"). On the ZX81, a true condition evaluates to 1 and false to 0, so this single expression moves the cursor right on M and left on N without any IF statements. - Random dwell time: Line 11 uses
IF RND<.7 THEN GOTO 8to keep the mole visible for a geometrically distributed random duration before it dips back down, giving the game unpredictable timing. - Partial disappearance animation: Lines 12–18 loop four times, SCROLLing and occasionally printing an inverse-space pair at the cursor row (line 15) to simulate the mole partially hiding.
Variable Summary
| Variable | Role |
|---|---|
A | Mole column position (lines 7, 22); also loop counter (line 12) |
P | Player crosshair column |
S | Stroke (swing) counter, displayed as score |
R | Character read from display file at cursor position |
Bugs and Anomalies
- Dual use of
A: VariableAis used both as the mole’s column (set at line 7, used at line 22) and as the loop counter in the animation loop (lines 12–18). Because the FOR loop overwritesA, the mole’s original column is lost during the sink animation. This is intentional — the mole position is no longer needed at that stage — but could cause confusion. - Score semantics:
Scounts every pass through the input subroutine (line 27), not just successful hits. It therefore measures the total number of swings rather than hits, making it more of a “how many attempts” counter. A hit ends the game immediately, so the final value ofSis always the number of misses plus one. - No boundary check on
P: The cursor columnPis not clamped, so the player can move it off the left or right edge of the screen, potentially causing a display error. - PRINT AT row discrepancy: The mole is printed at row 9 (lines 14–15, 21), but the crosshair is printed at row 3 (line 24). The crosshair and mole are on different rows, so the PEEK detection does not compare the crosshair character against the mole row directly — it reads from the display file base address, which on a real ZX81 would be the very first character of the display file (the top-left newline marker), not the character at row 3, column P. This may represent a bug in the intended hit-detection logic.
Content
Source Code
1 REM "MOLE"
2 FOR A=1 TO 10
3 PRINT "% % "
4 NEXT A
5 LET P=3
6 LET S=0
7 LET A=INT (RND*5+1)
8 GOSUB 20
9 GOSUB 24
10 IF R=128 THEN GOTO 30
11 IF RND<.7 THEN GOTO 8
12 FOR A=1 TO 4
13 SCROLL
14 PRINT AT 9,0;"% % "
15 IF RND<.3 AND A>1 AND A<4 THEN PRINT AT 9,P;"% "
16 GOSUB 24
17 IF R=128 THEN GOTO 30
18 NEXT A
19 GOTO 7
20 SCROLL
21 PRINT AT 9,0;"% % % % % % % "
22 PRINT AT 9,A;" "
23 RETURN
24 PRINT AT 3,P;
25 LET R=PEEK (PEEK 16398+256*PEEK 16399)
26 PRINT "+"
27 LET S=S+1
28 LET P=P+(INKEY$="M")-(INKEY$="N")
29 RETURN
30 PRINT AT 4,P;S
40 STOP
50 SAVE "1016%4"
60 LIST
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
