This is a naval warfare game in which the player controls a surface ship and must locate and destroy a hidden enemy submarine using depth charges. The player uses keys 5 and 8 to move the ship horizontally, keys 6 and 7 to adjust the depth of the charge, and key 2 to release the charge; key 1 provides sonar feedback via a BEEP whose pitch varies with the distance to the submarine, calculated using a Pythagorean distance formula. Fourteen custom UDG characters (codes 144–157) are defined at startup from DATA statements to draw the ship, submarine, explosion, and torpedo graphics. The submarine can counter-attack with a torpedo, and a scoring system rewards lower scores, incentivizing players to find the submarine with minimal keypresses and sonar use.
Program Analysis
Program Structure
The program is organized into clearly delineated sections separated by line-number ranges:
- Lines 10–30: Title screen and initialization of UDG graphics and instructions subroutines.
- Lines 40: Sets the best score variable
bsto 9999. - Lines 100–120: Game setup — screen colors, ocean surface rendering, and initialization of game variables.
- Lines 130–350: Main game loop handling ship movement (keys 5/8), score display, and a 1000-point threshold for revealing the submarine.
- Lines 400–550: Sonar (key 1), depth-charge depth control (keys 6/7), and submarine counter-attack timer logic.
- Lines 600–620: Submarine reveal (key 0).
- Lines 700–930: Depth-charge drop sequence, hit detection, explosion animation, and miss handling.
- Lines 1000–1130: Instructions subroutine.
- Lines 2000–2086: UDG graphics definition subroutine, reading 14 × 8 = 112 bytes from DATA.
- Lines 3000–3090: Submarine counter-attack (torpedo) logic.
- Lines 9998–9999: SAVE and VERIFY.
UDG Graphics
Fourteen UDG characters (ASCII codes 144–157, i.e., \a through \n) are defined by the subroutine at line 2000. The FOR q=144 TO 157 loop reads 8 bytes per character from DATA using POKE USR CHR$ q+n,a, populating the UDG table. These UDGs render the three-character-wide ship (\a\b\c), the three-character submarine (\d\e\f), the depth charge (\g), explosion frames (\h\j), and torpedo/debris tiles (\i\k\l\m\n).
Note a subtle bug in line 2030: POKE USR CHR$ q+n,a evaluates q+n as an integer and converts it to a character, but since n runs 0–7 inside the outer loop, this means for q=144, pokes go to USR CHR$ 144 through USR CHR$ 151 — effectively stepping through 8 successive UDG memory locations. This happens to work correctly because USR CHR$ x for consecutive UDG codes returns consecutive 8-byte addresses, so all 14 UDGs are correctly defined.
Game Mechanics and Variables
| Variable | Purpose |
|---|---|
c | Ship column position (0–29) |
sc | Current score (lower is better — incremented on every action) |
bs | Best (lowest) score across games |
h | Score multiplier: 1 normally, 0 after submarine is revealed (key 0 or score >1000) |
dc | Depth of charge (internal; displayed as dc-3, range 2–18) |
ls | Submarine’s row (random, 7–19) |
cs | Submarine’s column (random, 1–28) |
k | Counter driving the submarine’s counter-attack timer |
k1 | Warning beep count before torpedo fires |
q | Timer threshold multiplier (0.5–1.0, randomized after attack) |
d | Distance to submarine for sonar tone calculation |
Sonar and Distance Calculation
Line 400 computes the Euclidean distance from the ship to the submarine: LET d=INT (SQR ((ls-3)^2+(ABS (cs-c))^2)). The row offset uses ls-3 because the ship sits at row 3. When the player presses key 1 (sonar), line 430 fires two BEEP calls: a fixed-pitch ping and then BEEP .1,70-d*3, so a closer submarine produces a higher-pitched return echo — a neat analog of real sonar.
Scoring System
The game uses an inverted scoring system where lower scores are better, as stated in the instructions. The score sc is incremented (and multiplied by h) on virtually every player action — moving the ship, pressing sonar, adjusting depth, and releasing charges. Setting h=0 when the submarine is revealed (line 620) freezes the score at its current value for the remainder of that game, since all subsequent increments are multiplied by 0. This elegantly penalizes cheating without requiring a separate flag.
Counter-Attack Logic
Lines 450 and 3000–3090 implement the submarine’s torpedo attack. Variable k increments each main-loop iteration (scaled by q); when it exceeds 40*q, the counter-attack sequence triggers. Four warning BEEPs are issued (line 3000, using k1 as a counter) before the torpedo fires. The torpedo’s target column ct is randomized near the ship’s position (line 3020), and if it falls within columns c, c+1, or c+2 (the ship’s three-cell width), a hit animation plays and the ship teleports to a random side (line 3090: c=29*a where a is 0 or 1).
Hit Detection for Depth Charges
Line 740 checks for a miss: IF n>ls+1 OR nn to be within one row of the submarine's row ls, and the ship's column c to overlap the submarine's column cs within a ±2 tolerance. On a miss (line 900), a brief flash and sound play, the depth counter resets to 3, and the loop continues.
Ocean Surface Rendering
Lines 100–110 paint the sea: PAPER 1 sets blue paper, then a loop prints 128 spaces, filling rows 0–21 with blue. After this, PAPER 7 restores white paper for the header area. The ship and submarine UDGs are then printed with PAPER 1; INK 7 inline attributes, keeping them white-on-blue against the ocean background.
Notable BASIC Idioms
- Bounded movement:
LET c=c-1*(c>0) (line 300) and LET c=c+1*(c<29) (line 310) use boolean expressions as 0/1 multipliers to clamp column position without IF statements.
- OVER 1 animation: Several animation loops (lines 720–820) use
OVER 1 (XOR mode) with double-printing to erase and redraw graphics without CLS, producing flicker-free frame animation.
- PAUSE 0 / INKEY$ wait: Used at lines 1060, 1130, and 890 to wait for any keypress before proceeding.
- Score freeze via multiplication: Multiplying every score increment by
h (which becomes 0 on reveal) freezes the score without additional conditional logic.
Anomalies and Minor Issues
- Line 2030's
POKE USR CHR$ q+n,a is unusual — a more idiomatic approach would pre-compute the base address — but it works correctly due to consecutive UDG memory layout.
- Line 1120 prints
"to play." without a preceding phrase; the intended text (likely "Press any key to play.") was split across lines, and the "Press any key" portion from line 1100's context is consumed by the PAUSE 0 at line 1130, leaving line 1120 stranded in some execution paths.
- The
RESTORE at line 2045 is called after all 14 UDGs are read; this resets the DATA pointer but serves no functional purpose since DATA is not read again during gameplay.
- Line 850's message
"You had help no score." appears grammatically incomplete; it likely intended "You had help — no score recorded" or similar.
Content
Source Code
10 CLS : PRINT AT 0,11;"SUBMARINE"
15 PLOT 88,167: DRAW 72,0
20 PRINT AT 10,6;"Please wait a moment."
25 GO SUB 2000: REM graphics
30 GO SUB 1000: REM instructions
40 LET bs=9999
100 PAPER 1: BORDER 5: CLS
105 PAPER 7
110 FOR n=0 TO 127: PRINT " ";: NEXT n
120 LET c=15: LET sc=0: LET h=1: LET dc=3: LET k=0: LET k1=0: LET q=1
130 PRINT AT 3,c;"\a\b\c"
140 LET ls=7+INT (RND*13)
150 LET cs=1+INT (RND*28)
160 PRINT AT 0,0;"DEPTH OF CHARGE SCORE"
170 PRINT AT 0,17;dc-3;AT 0,28;sc
300 IF INKEY$="5" THEN LET sc=(sc+1)*h: PRINT AT 3,c;" ": LET c=c-1*(c>0): PRINT AT 3,c;"\a\b\c"
310 IF INKEY$="8" THEN LET sc=(sc+1)*h: PRINT AT 3,c;" ": LET c=c+1*(c<29): PRINT AT 3,c;"\a\b\c"
320 PRINT AT 0,28;sc
350 IF sc>1000 AND h=1 THEN GO TO 610
400 LET d=INT (SQR ((ls-3)^2+(ABS (cs-c))^2))
410 IF INKEY$<>"1" THEN GO TO 450
420 LET sc=(sc+1)*h: PRINT AT 0,28;sc
430 BEEP .1,40: FOR n=0 TO d*5: NEXT n: BEEP .1,70-d*3
450 LET k=(k+1)*h: IF k>40*q THEN GO TO 3000
500 IF INKEY$="6" THEN LET sc=(sc+1)*h: LET dc=dc+1*(dc<21)
510 IF INKEY$="7" THEN LET sc=(sc+1)*h: LET dc=dc-1*(dc>5)
520 PRINT AT 0,17;dc-3;" ";AT 0,28;sc;" "
530 IF dc=3 THEN GO TO 600
540 IF INKEY$="2" THEN GO TO 700
550 FOR n=0 TO 20: NEXT n: GO TO 500
600 IF INKEY$<>"0" THEN GO TO 300
610 PRINT PAPER 1; INK 7;AT ls,cs;"\d\e\f"
620 LET h=0: GO TO 300
700 LET sc=(sc+2)*h: PRINT AT 0,28;sc
710 FOR n=4 TO dc-1: FOR m=0 TO 1
720 PRINT PAPER 1; INK 7; OVER 1;AT n,c+1;"\g": BEEP .3,1
730 NEXT m: NEXT n
740 IF n>ls+1 OR n<ls-1 OR c>cs+2 OR c+2<cs THEN GO TO 900
745 FOR n=0 TO 5: PRINT PAPER 1; INK 6; OVER 1;AT ls,cs;"\d\e\f": BEEP .05,20: NEXT n
750 PRINT PAPER 1;AT ls,cs;" "
760 FOR n=0 TO 9: PRINT PAPER 1; INK 6; OVER 1;AT ls,cs;"\h\j\h"
770 BEEP .05,20: NEXT n: PRINT PAPER 1; INK 6; OVER 1;AT ls,cs;" "
780 FOR x=ls TO 21: FOR n=0 TO 3
790 PRINT PAPER 1; INK 7; OVER 1;AT x,cs-1;"\i\k\h\k\l"
800 IF x+1<=21 THEN PRINT PAPER 1; INK 7; OVER 1;AT x+1,cs-1;"\k\h\j\k\i"
810 IF x+2<=21 THEN PRINT PAPER 1; INK 7; OVER 1;AT x+2,cs-1;"\m\k\i\k\n"
820 BEEP .05,40-x*4: NEXT n: NEXT x
830 PAUSE 100: CLS : IF sc<bs*h THEN LET bs=sc
835 PLOT 56,151: DRAW 152,0
840 PRINT AT 2,7;"SUBMARINE DESTROYED";AT 6,4;"Score:";sc
845 PRINT AT 10,4;"Best Score:",bs
850 IF sc=0 THEN LET a$="You had help no score.": GO TO 870
860 IF sc<30 THEN LET a$="Join the Navy.": GO TO 870
861 IF sc<60 THEN LET a$=" Excellent.": GO TO 870
862 IF sc<100 THEN LET a$=" Well Done.": GO TO 870
863 LET a$=" Try Harder."
870 PRINT AT 14,4;a$
880 PRINT AT 18,1;"Press any key for another game."
890 PAUSE 0: FOR n=0 TO 10: BEEP RND,30-RND*40
895 NEXT n: GO TO 100
900 LET dc=3: FOR m=0 TO 7: BEEP .05,-10
910 PRINT PAPER 1; INK 6; OVER 1;AT n,c+1;"\h"
920 NEXT m: PRINT PAPER 1;AT n,c+1;" "
930 GO TO 300
1000 REM instructions
1010 PRINT AT 10,6;" "
1020 PRINT AT 2,0;" You are on a ship that is to"'"hunt and destroy an enemy"'"submarine that you cannot see."
1030 PRINT AT 6,11;"CONTROLS";AT 8,10;"1","Sonar.";AT 10,10;"2","Releases Charge.";AT 12,8;"5 & 8","Moves Ship.";AT 14,8;"6 & 7","Depth of Charge."
1040 PRINT AT 16,0;" To start with and if you get"'"stuck press 0 and the submarine"'"will appear. Lowest score wins."
1050 PRINT AT 20,9;"Press any key."
1060 PAUSE 0: PRINT AT 2,0;" ";: FOR n=1 TO 136
1070 PRINT " ";: NEXT n
1080 PRINT AT 3,0;" Beware the submarine can "'"strike back. You will get a"'"warning sound when a torpedo"'"is coming towards you."'''
1090 PRINT " It is your decision whether to"'"attempt to move or not. It is"'"a matter of luck."''''
1100 PAUSE 0: PRINT " This is the warning sound."
1110 FOR n=0 TO 50: BEEP .05,50: NEXT n
1120 PRINT AT 20,22;" ";AT 21,12;"to play."
1130 PAUSE 0: RETURN
2000 REM graphics
2010 FOR q=144 TO 157
2020 FOR n=0 TO 7
2030 READ a: POKE USR CHR$ q+n,a
2040 NEXT n: NEXT q
2045 RESTORE : RETURN
2050 DATA 0,0,0,1,1,255,255,127
2051 DATA 0,24,24,217,255,255,255,255
2052 DATA 128,128,128,240,240,255,254,252
2060 DATA 0,0,0,0,127,255,255,127
2061 DATA 128,240,240,240,255,255,255,255
2062 DATA 0,0,0,0,249,255,255,249
2070 DATA 0,0,126,126,126,126,0,0
2080 DATA 20,82,72,32,148,1,82,84
2081 DATA 5,0,40,0,21,64,37,136
2082 DATA 9,64,36,64,32,4,32,132
2083 DATA 0,40,2,72,18,64,10,64
2084 DATA 64,16,64,8,130,40,68,34
2085 DATA 5,64,16,4,0,18,0,2
2086 DATA 85,0,84,0,136,32,20,160
3000 IF k1<4 THEN BEEP .05,50: LET k1=k1+1: GO TO 600
3010 LET q=.5+(RND/2): LET k=0: LET k1=0
3020 LET ct=c-3+INT (RND*8+.5)
3030 IF c=ct OR c+1=ct OR c+2=ct THEN GO TO 3050
3040 GO TO 600
3050 PRINT AT 3,c;" ": FOR n=4 TO 21: FOR m=0 TO 1
3060 PRINT PAPER 1; INK 5; OVER 1;AT n,c;"\a\b\c"
3070 BEEP .1,20-n*2: NEXT m: NEXT n
3080 LET sc=sc+20: LET a=INT (RND+.5)
3090 LET c=29*a: PRINT AT 3,c;"\a\b\c": GO TO 300
9998 SAVE "Submarine" LINE 0
9999 VERIFY ""
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

