--- title: "Defender" id: 57505 type: "computer_media" slug: "defender" url: "http://localhost/computer_media/defender/" markdown_url: "http://localhost/computer_media/defender.md" published_at: "2024-10-05T21:12:01+00:00" modified_at: "2026-04-03T07:58:26+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/260_Defn.png" excerpt: "A wave-dodging arcade game using block-graphic scrolling, a GOSUB scoring trick, and reverse-loop wave progression — all in under 30 lines of BASIC." 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: 56737 title: "Timex Sinclair Public Domain Library Tape 1006" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1006/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/Timex%20Sinclair%20Public%20Domain%20Library%20Tape%201006%20%28198x%29%28Timex%20Sinclair%20Public%20Domain%20Library%29%28TS1000%29%28US%29%28Cassette%29.zip/Timex%20Sinclair%20Public%20Domain%20Library%20Tape%201006%20%28198x%29%28Timex%20Sinclair%20Public%20Domain%20Library%29%28TS1000%29%28US%29%28Cassette%29%2FSide%20A%2F260_Defn.tzx" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/260_Defn.png" media_type_tags: "Game" --- This program implements a side-scrolling wave-dodging game where the player controls a cursor (“>”) that must avoid or intercept incoming wave patterns. The wave data is stored in the string A$, which encodes block-graphic wave segments using ZX81/TS1000 escape sequences, and scrolls across the screen one character at a time. Scoring is handled via a GOSUB to line 10, which checks whether the player’s vertical position matches the wave position and awards points based on remaining energy (10−E). The game loops through waves in reverse (FOR E=9 TO 20 STEP -C, where C=1 makes this count upward from 9) and resets between waves via GOTO 80. The SAVE command at line 1000 uses inverse video in the filename as an auto-run flag. *** ## Program Analysis ### Program Structure The program is organized into an initialization phase, a main game loop, a scoring subroutine, and a save/restart block. Variables are initialized at lines 1–5, and the wave graphic string is defined at line 6. The main game loop begins at line 60 and iterates through wave levels via a `FOR E` loop (lines 70–210). A subroutine at lines 10–50 handles collision detection and scoring. Lines 220–230 display the final score and restart the game. | Lines | Role | | --- | --- | | 1–6 | Variable initialization | | 10–50 | Scoring/collision subroutine | | 60–215 | Main game loop (waves and scrolling) | | 220–230 | Score display and game restart | | 999–1010 | STOP, SAVE with auto-run, and restart | ### Wave Data and Block Graphics The string `A$` at line 6 contains a repeating block-graphic wave pattern using ZX81/TS1000 block character escapes (`\.:`, `\:.`, `\ :`, `\..`, etc.). During gameplay, a 10-character window (`A$(A TO A+9)`) is printed at row `F` (row 7), creating the illusion of a scrolling wave. Each iteration of the inner `FOR A` loop advances this window by one character, producing smooth horizontal scrolling. ### Player Movement and Controls The player’s vertical position is stored in `X`, initialized to 3. Line 150 uses a compact arithmetic idiom to update `X` without an `IF` chain: `LET X=X+(INKEY$="" AND X<5)-(INKEY$="Z" AND X>C)` This evaluates the boolean expressions (which return 1 or 0 in Sinclair BASIC) to move the cursor down when no key is pressed and `X<5`, or up when “Z” is pressed and `X>1`. The “M” key triggers the scoring subroutine via `GOSUB 10` at line 170. ### Scoring Subroutine (Lines 10–50) The subroutine is structured unconventionally: lines 10–50 are physically before the initialization block but are only called via `GOSUB 10` from line 170. The logic checks if the player (`X`) is at the wave row (`Y`) at line 20, returning early if not. If aligned, points equal to `10-E` are awarded (provided that position hasn’t already been scored), the slot in `B$` is cleared, and the subroutine returns. ### Wave Level Loop and Anomalies The outer loop `FOR E=9 TO 20 STEP -C` at line 70, with `C=1`, actually steps from 9 to 20 in increments of +1 (since `STEP -1` would require `C=-1`). This means the loop runs through values 9, 10, 11 … 20, giving 12 wave levels. The inner loop `FOR B=C TO 20` at line 90 initializes `B$(E)` with `CHR$(E+14)` but `B` itself is not used inside the loop body — the actual scroll variable is `A` in the nested `FOR A` loop at lines 130–180. ### Notable Techniques and Idioms - Boolean arithmetic for branchless movement update (line 150). - Substring slicing of a long graphic string for scrolling (line 140, `A$(A TO A+9)`). - Dual `AT` clauses in a single `PRINT` statement (lines 80, 140) to update multiple screen regions efficiently. - Score HUD printed with inverse-video characters at line 80 using `%`-prefixed letters. - The `DIM B$(18)` at line 100 inside the outer loop resets the board array each wave level. - `GOTO 215` bypasses the `NEXT E` at line 210 when a collision is detected, jumping directly to the inter-wave goto at 215, which loops back to the score display header via `GOTO 80`. ### Bugs and Anomalies - Line 20 references `Y` before `Y` is assigned (it is set at line 120 inside the inner loop). On first entry to the subroutine before the inner loop runs, `Y` would be 0 (default), which may cause spurious scoring. - The inner `FOR B` loop (lines 90–not explicitly NEXT’d before 100) and the `DIM` at line 100 inside the outer loop will reset `B$` on every outer iteration, which is the intended wave-reset behavior but makes `FOR B` redundant as its loop variable is unused. - Line 190 uses `GOTO 220` (score display) only when a collision occurs; otherwise `NEXT E` at line 210 advances the wave, and line 215’s `GOTO 80` re-enters the wave setup — this skips the score display between non-collision waves. ## Source Code ``` 1 LET C=1 2 LET D=0 3 LET F=6 4 LET X=3 5 LET C$=" " 6 LET A$=".::. :.. ... :. .:...::. :.. ... :. .:" 10 PRINT AT X,C;" " 20 IF X<>Y THEN RETURN 30 LET S=S+(10-E)*(B$(E)<>C$) 40 LET B$(E)=C$ 50 RETURN 60 LET S=D 70 FOR E=9 TO 20 STEP -C 80 PRINT AT D,D;"% %S%C%O%R%E% % % % ";AT D,F+C;S;AT F+C,D;"% % %W%A%V%E% ";CHR$ (166-E);"% % " 90 FOR B=C TO 20 100 DIM B$(18) 110 LET B$(E)=CHR$ (E+14) 120 LET Y=INT (RND*5)+C 130 FOR A=C TO C+E 140 PRINT AT Y,C;B$(A TO A+8);AT X,D;C$;AT F,D;A$(A TO A+9) 150 LET X=X+(INKEY$="" AND X<5)-(INKEY$="Z" AND X>C) 160 PRINT AT X,D;">" 170 IF INKEY$="M" THEN GOSUB 10 180 NEXT A 190 IF X=Y AND B$(E)<>C$ THEN GOTO 220 210 NEXT E 215 GOTO 80 220 PRINT AT D,F;S 225 PAUSE 500 230 GOTO 60 999 STOP 1000 SAVE "1026%0" 1010 GOTO 60 ```