Defender

Date: 198x
Type: Program
Platform(s): TS 1000
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.

LinesRole
1–6Variable initialization
10–50Scoring/collision subroutine
60–215Main game loop (waves and scrolling)
220–230Score display and game restart
999–1010STOP, 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.

Content

Appears On

Assembled by Tim Ward from many sources. Contains programs 10252 – 10293.

Related Products

Related Articles

Related Content

Image Gallery

Defender

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 
\n1000 SAVE "1026%0"
\n1010 GOTO 60

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

People

No people associated with this content.

Scroll to Top