--- title: "Submarine" id: 53813 type: "computer_media" slug: "submarine" url: "http://localhost/computer_media/submarine/" markdown_url: "http://localhost/computer_media/submarine.md" published_at: "2024-05-13T18:56:53+00:00" modified_at: "2026-03-30T21:45:51+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2019/02/20230809-041332.jpg" excerpt: "Hunt an invisible enemy submarine with depth charges, sonar pings, and evasive maneuvers — all while the sub fires back at you." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" - name: "Games" slug: "games" taxonomy: "category" url: "http://localhost/category/software/games/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_contents: - id: 53877 title: "SINCUS Exchange Tape 101 – Entertainment" type: "computer_media" url: "http://localhost/computer_media/sincus-exchange-tape-101-entertainment/" media_type: "Program" images: - url: "http://localhost/wp-content/uploads/2024/05/submarine-2.png" - url: "http://localhost/wp-content/uploads/2024/05/submarine-1.png" media_type_tags: "Game" --- # Submarine 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 `bs` to 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 ncs+2 OR c+2. A hit requires the charge's final row n 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.` ## 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 ncs+2 OR c+2