This is a shoot-’em-up game in which the player pilots a crosshair-shaped targeting reticle around the screen to intercept and shoot flying saucers before they land and conquer Earth. The playing field is drawn with PLOT/DRAW commands to create a stylized runway/landscape, and movement is handled by polling INKEY$ on keys 5, 6, 7, 8, with key 0 to fire. A custom UDG character (\p) is defined via a DATA/POKE USR loop at line 5000 to render the saucer sprite as a filled circle shape using binary pixel patterns. The saucer drifts downward with each game loop iteration (variable m increments by 0.2), speeds up once the score exceeds 1000, and wanders horizontally using a bounded random walk on variable n; after 10 landings the game ends with a block-graphic city construction sequence.
Program Analysis
Program Structure
The program is organized into several clearly separated routines, entered via GO SUB from the main initialization sequence at lines 3–9:
- Lines 5000–5040: UDG definition — reads binary pixel data and POKEs it into the UDG memory for character
\p(the saucer sprite). - Lines 4000–4050: Title/instructions screen — sets colors, prints the story and key assignments, waits for a keypress, then draws a starfield before jumping to line 5 to start play.
- Lines 5–120: Game initialization — sets up player position (
l,k), saucer position (m,n), score, landing counter (LAN), and draws the landscape with PLOT/DRAW. - Lines 125–999: Main game loop — erases the crosshair, reads movement keys, moves the saucer, handles firing, draws everything, then loops back via
GO TO 125. - Lines 1020–1080: Hit subroutine — animates a hit, plays a descending beep, resets the saucer, increments score by 100, returns.
- Lines 2000–2020: Landing subroutine — increments the landing counter, ends the game after 10 landings, otherwise resets the saucer.
- Lines 7000–7150: Game-over sequence — plays losing tones, draws a block-graphic alien city, prints defeat messages, then STOPs.
- Line 9999: SAVE with auto-run.
UDG Sprite Definition
Lines 5000–5040 define the saucer as a custom user-defined graphic. Eight bytes of pixel data are stored with DATA BIN ... statements and read into a loop that POKEs each byte to USR "\p"+n. The binary values describe a classic flying saucer silhouette: dark top and bottom rows, a wide middle body (BIN 01111110 = 0x7E), and a dotted underside (BIN 11011011 = 0xDB). This technique is standard for adding custom sprites without machine code.
Game Loop and Movement
The main loop (lines 125–999) uses a sprite-erasure pattern common in BASIC games: the crosshair’s four cells are overwritten with spaces (lines 125–128) before reading new key input and redrawing at the updated position (lines 200–230). This avoids leaving ghost characters on screen without requiring attribute-layer tricks.
Player movement is guarded by boundary checks:
l(column) clamped to 2–29 via checks at lines 130 and 140.k(row) clamped to 2–13 via checks at lines 150 and 160, restricting the player to the upper portion of the screen.
The saucer’s vertical position m is a floating-point variable incremented by 0.2 each frame (line 250), so INT m is used wherever a row address is needed. This gives the saucer a smooth apparent drift without requiring integer arithmetic for the motion logic. Horizontal wandering uses a bounded random walk: n is nudged by INT(RND*3)-1 each frame and clamped to 3–29 (lines 245–247).
Difficulty Scaling
Line 252 adds a progressive difficulty mechanic: if score>1000, m is incremented an additional 0.2 per frame (effectively doubling descent speed), and if m>2000 (which cannot occur normally since the game resets m at 15) a third increment would apply — making the second condition dead code in practice, likely a bug or oversight in the scaling logic.
Firing Mechanic
Firing is detected at line 240 by polling INKEY$="0". On a keypress, a short four-note beep burst plays via a FOR loop, then a hit is detected by comparing k=INT m AND l=INT n — a direct row/column equality check. This means the player must align the crosshair center precisely with the saucer’s cell, with no tolerance radius. The crosshair itself is drawn as four surrounding + characters (lines 200–230) rather than occupying the saucer’s cell directly, making exact hits rely on the center character coinciding with INT m, INT n.
Sound Design
The game uses BEEP throughout for audio feedback:
- A continuous random-pitch ambient noise (line 248:
BEEP 0.005,(RND*35)+15) runs every game loop iteration, simulating engine hum. - Firing produces four rapid 40-semitone beeps in a loop (line 240).
- A hit triggers a descending chromatic glide from semitone 50 to 45 in 0.5 steps (line 1030).
- The game-over sequence at lines 7000–7075 plays descending single BEEP notes alongside each city segment drawn.
Landscape Drawing
The runway/battlefield graphic is constructed entirely with PLOT/DRAW commands (lines 10–120) before the game loop begins. This is drawn once at startup and is not re-drawn during gameplay, relying on the fact that the saucer and crosshair are printed only in the upper screen area (rows 0–15) and the landscape sits in the lower INK-7 border region. Line 255 includes a defensive re-draw of the screen border rectangle if n goes out of range, though the clamping at lines 246–247 should prevent this.
Game-Over City Animation
The defeat screen at lines 7030–7076 builds an alien city from block graphic characters row by row, with a descending BEEP note between each row, creating a theatrical stacking animation. The city uses \:: (solid block █) and combinations of half-block characters to draw a pyramid-like structure across five rows.
Anomalies and Notes
- Line 3005 defines a sound loop (
FOR n=0 TO 100: BEEP 0.1,40: NEXT n) that is never called via GO SUB or GO TO from the main program — it is unreachable dead code. - The variable
LANis initialized at line 9 asLAN=0but incremented aslan(lowercase) at line 2000; on the Spectrum, variable names are case-insensitive so this is not a bug. INK INT 2at line 7030 appliesINTto a literal integer, which is valid but redundant — likely a copy-paste habit from lines usingINT(RND*7).- The title screen subroutine at line 4050 ends with
GO TO 5rather than RETURN, bypassing the GOSUB stack — a deliberate flow-control shortcut to skip re-initializing after the instruction screen.
Content
Source Code
1 REM saucers
3 GO SUB 5000
4 GO SUB 4000
5 LET l=16: LET k=11: LET pos=10
6 LET m=3: LET n=INT (RND*26)+2
8 LET score=0
9 LET LAN=0
10 PLOT 0,0: DRAW 97,50
20 PLOT 255,0: DRAW -97,50
30 PLOT 137,50: DRAW 50,-50
40 PLOT 117,50: DRAW -50,-50
50 PLOT 70,50: DRAW -70,-15
60 PLOT 185,50: DRAW 70,-15
70 PLOT 0,50: DRAW 255,0
80 PLOT 127,50: DRAW 0,-50
90 PLOT 0,40: DRAW 255,0
100 PLOT 0,15: DRAW 255,0
110 PLOT 0,14: DRAW 255,0
120 INK 7: PLOT 0,0: DRAW 0,175: DRAW 255,0: DRAW 0,-175: DRAW -255,0
125 PRINT AT k,l-1;" "
126 PRINT AT k,l+1;" "
127 PRINT AT k-1,l;" "
128 PRINT AT k+1,l;" "
130 IF INKEY$="8" AND l<29 THEN LET l=l+1
140 IF INKEY$="5" AND l>2 THEN LET l=l-1
150 IF INKEY$="6" AND k<13 THEN LET k=k+1
160 IF INKEY$="7" AND k>2 THEN LET k=k-1
180 PRINT AT INT m,INT n;" "
200 PRINT AT k,l-1;"+"
210 PRINT AT k-1,l;"+"
220 PRINT AT k+1,l;"+"
230 PRINT AT k,l+1;"+"
240 IF INKEY$="0" THEN FOR t=0 TO 3: BEEP 0.02,40: NEXT t: IF k=INT m AND l=INT n THEN GO SUB 1000
245 LET n=n+INT (RND*3)-1
246 IF n<3 THEN LET n=n+1
247 IF n>29 THEN LET n=n-1
248 BEEP 0.005,(RND*35)+15
250 LET m=m+0.2
252 IF score>1000 THEN LET m=m+0.2: IF m>2000 THEN LET m=m+0.2
255 IF n<1 OR n>31 THEN PLOT 0,0: DRAW 0,175: DRAW 255,0: DRAW 0,-175: DRAW -255,0
260 IF m>15 THEN GO SUB 2000
300 INK INT (RND*7)+2: PRINT AT INT m,INT n;"\p": INK 7
310 PRINT AT 1,1;"SCORE:";score;" "
999 GO TO 125
1020 PRINT AT k,l;"\p": PRINT ; OVER 1;AT k,l;"\.'"
1030 FOR h=50 TO 45 STEP -0.5: BEEP 0.01,h: NEXT h
1040 PRINT AT k,l;" "
1050 LET m=3: LET n=INT (RND*26)+2
1060 LET score=score+100
1080 RETURN
2000 LET lan=lan+1
2010 IF lan>10 THEN GO TO 7000
2015 LET m=3: LET n=INT (RND*26)+2
2020 RETURN
3005 FOR n=0 TO 100: BEEP 0.1,40: NEXT n
4000 PAPER 1: INK 7: BORDER 1: CLS
4010 PRINT AT 0,10; FLASH 1;"SAUCER"; FLASH 0
4020 INK 6: PRINT : PRINT "Save the Earth,the aliens are invading and are trying to buildcities on the Earth.": PRINT : PRINT "Shoot them before they build their cities and conquer EARTH and use you for toothpicks."
4030 INK 7: PRINT : PRINT : PRINT " 5 FOR LEFT": PRINT : PRINT " 8 FOR RIGHT": PRINT : PRINT " 6 FOR DOWN": PRINT : PRINT " 7 FOR UP": PRINT : PRINT " 0 TO FIRE"
4040 PRINT AT 21,1; FLASH 1;"Good Luck.......": FLASH 0
4045 PAUSE 9999
4050 BORDER 0: PAPER 0: INK 6: CLS : FOR n=0 TO 50: BEEP 0.005,(RND*35)+15: INK INT (RND*7)+1: PLOT INT (RND*250)+5,INT (RND*120)+50: NEXT n: INK 4: GO TO 5
5000 DATA BIN 0,BIN 00011000,BIN 01111110,BIN 11011011,BIN 11111111,BIN 01100110,BIN 0,BIN 0
5010 FOR n=0 TO 7: READ j
5030 POKE USR "\p"+n,j: NEXT n
5040 RETURN
7000 BEEP 0.7,1: BEEP 0.5,1: BEEP 1,6
7010 PRINT AT 1,1;"THEY HAVE CONQUERED EARTH"
7020 PRINT AT 2,3;"AND ARE BUILDING THEIR CITY"
7025 PAUSE 100
7030 INK INT 2: PRINT AT 12,10;"\::"
7035 BEEP 0.5,5
7040 PRINT AT 13,9;"\.:\::\:."
7045 BEEP 0.5,4
7050 PRINT AT 14,9;"\: \::\ :"
7055 BEEP 0.5,3
7060 PRINT AT 15,9;"\::\::\::"
7065 BEEP 0.5,2
7070 PRINT AT 16,8;"\.:\::\::\::\:."
7075 BEEP 0.5,1
7076 INK 6
7080 PRINT AT 8,1;"YOU HAVE LOST EARTH, THE "
7090 PRINT AT 9,1;"ALIENS HAVE DOOMED MANKIND"
7100 PRINT AT 10,1;"TO BECOME TOOTH PICKS"
7110 PRINT AT 18,1;"ENTER 'RUN' TO GO BACK INTO "
7120 PRINT AT 19,1;"TIME TO TRY AND SAVE THE"
7130 PRINT AT 20,1;"EARTH AGAIN !!!!"
7140 FLASH 1: PRINT AT 5,1;"YOUR SCORE WAS ";score: FLASH 0
7150 STOP
9999 SAVE "saucers" LINE 1: BEEP 1,32
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

