This program is a two-player artillery game in which players take turns firing a cannon at targets — a moon (UDG A–C), ducks (UDG D–J), and a barrel — by entering a launch angle between 45 and 85 degrees. The first 80 lines define ten custom UDG characters (A through J) using POKE USR to create sprite graphics for the moon, barrel/cannon components, and a duck shape. Projectile trajectory is computed using parabolic formulas: horizontal position as `x = INT(0.056 * i * (90 – a))` and vertical height as `y = INT(i * (100 – i) * 0.07)`, with collision detection performed via the ATTR function to test whether the plotted pixel lands on a colored cell. The game tracks hits on moons and total shots across five rounds, displaying running scores in the border area.
Program Analysis
Program Structure
The program divides into three logical phases:
- UDG Setup (lines 10–80): Reads bitmap data for ten custom characters (A–J) from DATA statements and POKEs them into UDG memory using
USR a$as the base address. - Game Loop (lines 90–320): Iterates five rounds (
FOR g=1 TO 5), placing targets at random screen positions, accepting angle input from up to two players, simulating a projectile, and checking for collision. - Subroutine / End (lines 340–510): Displays a final summary and provides a “Press ENTER to continue” pause routine at line 500.
UDG Character Definitions
Lines 10–80 define UDGs A through J, each encoded as 8 bytes of bitmap data stored in the DATA statements on lines 70–80. The inner loop (FOR d=0 TO 7) POKEs each byte to USR a$+d, where a$ holds the character letter as a one-character string. The ten UDGs collectively form the graphical sprites used in the game:
| UDG | Role |
|---|---|
| A, B, C | Moon — top, middle, bottom rows |
| D, E, F, G, H | Barrel/cannon top assembly (five cells wide) |
| I, J | Barrel/cannon base flanks |
Trajectory Calculation
The projectile path is computed inside FOR i=0 TO 100 at lines 220–242. Two formulas drive position:
x = INT(0.056 * i * (90 - a))— horizontal screen column, scaled by the complement of the angle.y = INT(i * (100 - i) * 0.07)— vertical pixel height using a symmetrical parabola centered ati=50.
Each step plots a pixel with PLOT x, y and emits a tone with BEEP 0.1, f/100 where f = i*(100-i), giving a pitch that rises and falls to match the arc shape.
Collision Detection via ATTR
Line 240 converts pixel coordinates to character-cell coordinates: lin = 21 - INT(y/8) and col = INT(x/8) (note: col is derived from y in the listing — this is likely a bug where the variable name col is misleadingly computed from the vertical coordinate rather than the horizontal one). The ATTR value of that cell is then tested; if it exceeds 64 (meaning a colored, non-black attribute is present), a hit is registered at line 250.
A secondary check at line 280 tests col>d AND col<d+4 to distinguish a hit on the barrel/duck from hitting the moon (line number <= 3). This layered ATTR-plus-position check avoids storing any explicit sprite-position arrays beyond the already-known variables d, m.
Input Validation
Line 200 enforces that the entered angle a falls between 45 and 85 degrees inclusive. If the condition fails, an inline error message is printed as part of the INPUT prompt and the program loops back to line 200 with GO TO 200. The use of an expression inside INPUT (...) to show the bad value to the user is a compact Spectrum BASIC idiom.
Scoring and Display
Three counters are maintained: hd (duck hits), hm (moon hits), and s (total shots). These are displayed in the right-hand border column using AT positioning. The barrel number each round is rendered by printing CHR$(g+48) — converting the loop counter g (1–5) to its ASCII digit character — between INVERSE-on and INVERSE-off attributes, giving it a highlighted appearance on the barrel graphic.
Notable Techniques
- Using
PAPER 1: INK 7: BORDER 7with a column of spaces printed atTAB 31in a loop creates a right-side score panel with a contrasting background color. - The moon “falling” animation at lines 270–275 scrolls the UDG moon down 18 rows by reprinting it one row lower each iteration, with a descending BEEP pitch (
1-z). - The two-player loop at line 180 (
FOR b=1 TO 2) withNEXT bat line 297 gives each player one shot per round;GO TO 310orGO TO 320at lines 275 and 298 break out of this inner loop on a successful hit or after both players have fired. GO TO VAL "200"is not used here; instead a plainGO TO 200is used for the angle re-entry loop — no memory optimization applied to that jump.
Potential Bugs and Anomalies
- col computed from y: Line 240 sets
col = INT(y/8)rather thanINT(x/8). This means the column variable used in the duck-hit test at line 280 is based on the vertical coordinate of the projectile, not the horizontal one, making the barrel hit detection potentially incorrect. - lin computed from y: Similarly,
lin = 21 - INT(y/8)is the screen row, which is correct for vertical position. The naming ofcolfor the secondINT(y/8)expression (same computation) means bothlinandcolcarry the same base value, which appears unintentional. - Two players, one shot each, but shared score: Both
b=1andb=2shots increment the sharedscounter, so shots are counted globally rather than per player.
Content
Source Code
10 FOR c=1 TO 10
20 READ a$
30 FOR d=0 TO 7
40 READ x: POKE USR a$+d,x
50 NEXT d
60 NEXT c
70 DATA "A",1,6,14,28,28,60,60,120,"B",120,120,248,248,248,248,120,120,"C",120,60,60,28,28,14,6,1
80 DATA "D",0,0,0,15,15,0,0,0,"E",60,18,124,252,254,255,255,255,"F",0,0,0,0,0,240,255,255,"G",0,0,0,0,0,0,240,255,"H",0,0,0,8,16,32,64,128,"I",1,1,3,7,7,3,3,1,"J",255,255,255,254,252,248,224,128
90 LET hd=0: LET hm=0: LET s=0
100 FOR g=1 TO 5
110 CLS : PAPER 1: INK 7: BORDER 7: FOR i=0 TO 20: PRINT TAB 31;" ": NEXT i
120 LET d=INT (RND*16+12): LET d2=2*d
130 LET m=INT (RND*7+12): LET m2=2*m
140 PRINT AT 0,0;"Barrel "' FLASH 1;"Angle=? "; FLASH 0;AT 0,25;hm;" moons";AT 2,25;s;" shots"
150 PRINT BRIGHT 1; INK 4;AT 1,m;"\a";AT 2,m;"\b";AT 3,m;"\c"
160 PRINT INK 3;AT 21,0;"\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\.."
170 PRINT BRIGHT 1; INK 6;AT 20,d;"\d\e\f\g\h";AT 21,d;"\i\::"; INVERSE 1;CHR$ (g+48); INVERSE 0;"\j"
180 FOR b=1 TO 2
190 PRINT AT 0,6;b; FLASH 1;AT 1,0;"Angle=? ": INPUT "Enter angle of fire: ";a
200 LET a=INT a: IF a<45 OR a>85 THEN INPUT (a;" is not between 45 and 85."'"Reenter angle: ");a: GO TO 200
210 PRINT AT 1,0;"Angle= ";a
220 FOR i=0 TO 100
230 LET x=INT (0.056*i*(90-a)): LET f=i*(100-i): LET y=INT (f*0.07): PLOT x,y
235 BEEP 0.1,f/100
240 LET lin=21-INT (y/8): LET col=INT (y/8): IF ATTR (lin,col)>64 THEN GO TO 250
242 NEXT i
245 PRINT AT 20,d-4;"Miss": GO TO 295
250 IF lin>3 THEN GO TO 280
260 LET hm=hm+1: PRINT AT 1,25;hm
270 FOR z=1 TO 18: PRINT INK 4;AT z,m;" ";AT z+1,m;"\a";AT z+2,m;"\b";AT z+3,m;"\c": BEEP 0.1,1-z: NEXT z
275 GO TO 310
280 IF col>d AND col<d+4 THEN GO TO 300
290 PRINT AT 20,d-4;"Ouch!";: BEEP .5,5: BEEP .5,3
295 LET s=s+1: PRINT AT 2,25;s
297 NEXT b
298 GO TO 320
300 LET hd=hd+1: PRINT AT 0,25;hd: PRINT AT 20,d-4;"DEAD": BEEP 1,-10: BEEP .5,-10: BEEP 1,-10
310 LET s=s+1: PRINT AT 2,25;s: GO SUB 500
320 NEXT g
340 PRINT FLASH 1;AT 10,11;"The end";AT 12,3;"You killed ";hd;" ducks"
350 STOP
500 INPUT "Press ENTER to continue ";z$
510 RETURN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
