--- title: "Torpedo" id: 57573 type: "computer_media" slug: "torpedo" url: "http://localhost/computer_media/torpedo/" markdown_url: "http://localhost/computer_media/torpedo.md" published_at: "2024-10-05T22:14:30+00:00" modified_at: "2026-04-03T07:58:21+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/torpedo.png" excerpt: "A block-graphics submarine dodges across the screen while you fire angled torpedoes — but you only get 20 moves to sink as many as you can." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_contents: - id: 56721 title: "Synchro-Sette April 1983" type: "computer_media" url: "http://localhost/computer_media/synchro-sette-april-1983/" media_type: "Program" mediadate: "April 1983" images: - url: "http://localhost/wp-content/uploads/2024/10/torpedo.png" media_type_tags: "Game" --- # Torpedo This program implements a torpedo-firing game where a submarine sprite moves across the screen and the player fires torpedoes by pressing keys numbered 1–5, each corresponding to a different firing angle calculated via the TAN function. The submarine is represented using a string of ZX81 block graphics stored in A$, and collision detection compares the torpedo’s Y position (derived from the tangent trajectory) against the submarine’s screen column. A high-score table tracking the best player name and score persists across rounds within the same session, resetting only on a full restart. The game limits each round to 20 combined movement/fire events (variable T) before displaying the score as a percentage of hits to total shots. *** ## Program Analysis ### Program Structure The program is organised into a main loop, two subroutines, and an end-of-round section: 1. Lines 1–15: Initialisation — clear screen, reset variables, ask for player name, prime turn counter `T=-1`. 2. Lines 20–90: Main game loop — reset score display, call submarine movement subroutine (`GOSUB 100`), then call key/fire subroutine (`GOSUB 200`), repeat. 3. Lines 100–160: Submarine movement — decrements column `C` by a random fraction, wraps back to line 30 when the sub reaches the left edge, draws the submarine sprite and a sea-floor dotted line. 4. Lines 200–290: Input and torpedo trajectory — reads a keypress, validates it, computes a TAN-based trajectory, plots/unplots the torpedo path, calls collision detection. 5. Lines 300–399: Collision detection — checks whether the torpedo Y-coordinate at a given X matches the submarine’s position. 6. Lines 400–470: Hit animation — flashes the submarine position with alternating block-graphic patterns in a loop, then animates the sub sinking downward before returning to line 30. 7. Lines 500–560: End-of-round scoring — displays hits and percentage score, updates high score if beaten, pauses, clears, and loops back for the next player. 8. Lines 9998–9999: `SAVE` the program (with inverse-O in the filename as a cosmetic touch) and `GOTO 0` to restart the system after saving. ### Submarine Sprite The submarine graphic is stored in `A$` at line 5 as a string of ZX81 block graphics and spaces. The string contains hull, conning tower, and wake characters, printed at row 5 using `PRINT AT 5,C;A$`. The dotted line at row 7 represents the sea floor or water surface and is redrawn each movement step. ### Torpedo Trajectory Calculation Lines 200–280 implement the firing mechanic. The player presses a key whose `CODE` falls between 31 and 36 (ZX81 internal codes for keys `1`–`5` shifted). The angle is computed as: `M = TAN((10 * VAL B$) * 2 * PI / 360)` This converts `VAL B$` (1–5) multiplied by 10 into degrees (10°, 20°, 30°, 40°, 50°) and takes the tangent, giving the slope of the torpedo’s path. The torpedo is then plotted and immediately unplotted (`PLOT`/`UNPLOT` at lines 250–260) as it steps across X from 1 to 63, creating a moving dot effect. Firing is also gated by `C$=B$` (line 210) to prevent the same key being used twice consecutively. ### Collision Detection The subroutine at lines 300–320 computes the torpedo’s integer Y position (`A = INT(INT(M*X)+0.5)`) and the submarine’s integer column position (`B = INT(INT(C*2)+0.5)`). A hit is registered if `A` is 32 or 33 (the vertical screen rows corresponding to the submarine) and `B` is within ±2 columns of `X`. The use of double `INT(...+0.5)` is an unconventional rounding idiom — the inner `INT` is redundant since the argument is already the result of arithmetic, making the outer `INT(...+0.5)` a standard rounding-to-nearest. ### Turn Limit and Scoring The variable `T` counts turns and is incremented at line 35 (each movement step) and line 215 (each fire event). When `T >= 20`, the game ends and jumps to line 500. The final score is displayed as a percentage: `INT(100*S/T)`, rewarding accuracy over volume of fire. The high score (`HS`) and high-score holder’s name (`N$`) persist in memory across rounds within the same session. ### Hit Animation Lines 400–420 loop 10 times, printing alternating pairs of block-graphic patterns at the submarine’s position to simulate an explosion flash. Eight distinct `AT` clauses appear within a single `PRINT` statement at line 410. Lines 440–460 then animate the sub sinking by printing a fragment at successive rows while clearing the previous row, stepping from row 5 down to row 21. ### Key Validation Line 210 checks three conditions with `OR`: the key code must be ≥ 31, ≤ 36, and not equal to the previously pressed key (`C$ = B$`). Because all three are combined with `OR`, the logic is: if the code is out of range *or* it is a repeat press, do nothing. This effectively blocks both invalid keys and repeated use of the same angle in consecutive moves, adding a tactical element. ### Notable Techniques and Anomalies - Starting `T` at `-1` (line 15) rather than 0 compensates for the `T=T+1` at line 35 executing before the first meaningful turn. - The `SAVE "TORPED%O"` at line 9998 uses the ZX81 inverse-video escape `%O` to render the final letter of “TORPEDO” in inverse video in the filename. - `GOTO 9999` / `GOTO 0` at the end triggers a system restart after saving, a common ZX81 idiom. - The `PLOT`/`UNPLOT` pair (lines 250–260) in the torpedo loop creates a single moving pixel without leaving a trail, using the ZX81’s pixel commands rather than character-cell printing. - The check `CODE B$ < 31 OR CODE B$ > 36` uses ZX81 internal character codes, where digit keys 1–5 have codes 29–33 in some ROM versions; the exact range used here may be platform-specific and worth verifying against the target hardware’s key code table. ## Source Code ``` 1 CLS 2 LET C$="" 3 LET HS=0 5 LET A$="): ..:!!. -" 10 PRINT ,,"CURRENT PLAYER 'S NAME?" 12 INPUT F$ 13 PRINT AT 1,0;F$;" IS NOW PLAYING. " 15 LET T=-1 20 LET S=0 30 PRINT AT 5,2;" " 35 LET T=T+1 36 IF T>=20 THEN GOTO 500 40 LET C=28 50 GOSUB 100 60 GOSUB 200 90 GOTO 50 100 LET C=C-RND 110 IF C<2 THEN GOTO 30 120 PRINT AT 5,C;A$ 130 PRINT AT 7,0;". . . . . . . . . . . . . . . ." 160 RETURN 200 LET B$=INKEY$ 210 IF CODE B$<31 OR CODE B$>36 OR C$=B$ THEN RETURN 212 LET C$=B$ 215 LET T=T+1 216 IF T>=20 THEN GOTO 500 220 LET M=(TAN ((10*VAL B$)*2*PI/360)) 230 FOR X=1 TO 63 STEP 2 240 IF M*X>43 THEN RETURN 250 PLOT X,M*X 260 UNPLOT X,M*X 270 GOSUB 300 280 NEXT X 290 RETURN 300 LET A=INT (INT (M*X)+.5) 310 LET B=INT (INT (C*2)+.5) 320 IF (A=32 OR A=33) AND (B>=X-2) AND (B<=X+1) THEN GOTO 400 399 RETURN 400 FOR N=1 TO 10 410 PRINT AT 5,C-1;"@@##@@##";AT 6,C-1;"##@@##@@";AT 5,C-1;"!!!!!!!!";AT 6,C-1;";;;;;;;;";AT 5,C-1;";;;;;;;;";AT 6,C-1;"!!!!!!!!";AT 5,C-1;"~~~~~~~~";AT 6,C-1;",,,,,,,,";AT 5,C-1;" ";AT 6,C-1;" " 420 NEXT N 430 LET S=S+1 440 FOR N=5 TO 21 450 PRINT AT N,C-1;",,;;,,,,";AT N-1,C-1;" " 460 NEXT N 470 GOTO 30 500 PRINT AT 21,0;S;" SUBS TORPEDOED - SCORE = ";INT (100*S/T) 510 IF S<=HS THEN GOTO 540 520 LET N$=F$ 530 LET HS=S 540 PRINT AT 19,0;N$;" - HIGH SCORE - ";HS 550 PAUSE 200 560 CLS 600 GOTO 10 9998 SAVE "TORPED%O" 9999 GOTO 0 ```