This is a text-based Star Trek strategy game in which the player commands a starship on a 10×10 grid, scanning for, moving toward, and firing upon alien vessels (Klingons, Romulans, or Kzintzis, chosen randomly at startup). The game uses two parallel 14×14 arrays — A() for logical state and B() for display state — to track grid contents, with cell values 0 (empty), 1 (alien), 2 (player), 3 (miss), and 4 (kill). Movement and firing use a numpad-style 8-direction compass layout, and sector names are derived by multiplying the player’s row and column coordinates together and mapping the product to named star sectors. The random crew commentary subroutine at line 7500 selects from five Star Trek characters to prefix messages, adding flavour to game events. Energy decreases with every action — scanning costs 10 or 20 energy, movement costs 50, and firing costs 100 — and exhaustion triggers the end-game scoring sequence.
Program Analysis
Program Structure
The program is organised as a main loop with a dispatch mechanism, surrounded by initialisation and utility subroutines. Execution flows as follows:
- Line 9000 — initialise arrays, position, alien type, and energy
- Line 8000 — print the grid display
- Line 6950 — clear the lower screen area
- Line 7000 — print status panel
- Line 7500 — print random crew prefix
- Lines 100–160 — main menu and input loop
- Line 160 — dispatch:
GOSUB 1000*D(D=1 → scan, D=2 → move, D=3 → fire) - Lines 170–190 — brief flashing animation loop
- Line 500 — loop back to status display
Dispatch Technique
Line 160 uses the idiom GOSUB 1000*D, where D is the menu choice (1, 2, or 3). This evaluates to GOSUB 1000, GOSUB 2000, or GOSUB 3000 respectively, avoiding a chain of IF … THEN GOSUB statements. This is a classic Sinclair BASIC memory-saving technique.
Array Usage
Two 14×14 arrays are declared at line 9000, intentionally larger than the 10×10 playfield. This provides a one-cell border on each side, so boundary-adjacent lookups (e.g., A(B-1,C)) do not cause an index-out-of-range error when the player is at row or column 1 or 10. Cell values encode object type:
| Value | Meaning | Display char |
|---|---|---|
| 0 | Empty | Inverse space (% ) |
| 2 | Player ship | Inverse $ (%$) |
| 3 | Missed shot | Inverse X (%X) |
| 4 | Destroyed alien | Inverse * (%*) |
| 1 | Alien vessel | (not explicitly printed — remains empty display) |
Note that alien vessels (value 1) have no distinct display character in the B() display array; they appear as empty space. This is presumably intentional — the player must use the scanner to detect them rather than seeing them on the map.
Dual Array Design
Array A() holds logical game state while B() holds the display state. Both are updated in parallel at key events (movement at lines 2040–2050/2270–2280, firing at lines 3260–3261). However, the alien initialisation loop (lines 9060–9140) only populates A(), not B(), so alien positions are invisible on the grid from the start — consistent with the design intent above.
Directional Input
Both movement (line 2000 block) and firing (line 3000 block) use a numpad-style compass where digits 1–8 represent directions. The layout is displayed as a 3×3 grid in PRINT strings:
- 8 = north-west, 1 = north, 2 = north-east
- 7 = west, (centre = player), 3 = east
- 6 = south-west, 5 = south, 4 = south-east
Each diagonal direction is implemented as two consecutive IF statements modifying both B (row) and C (column), rather than a single compound update. This is straightforward but verbose.
Sector Naming
The subroutine at line 8500 derives a sector name by computing Q = B * C (row × column) and mapping the product to one of ten named sectors (Antares, Rigel, Procyon, Vega, Canopus, Altair, Deneb, Pollux, Sirius, Capella). Since B and C each range 1–10, Q ranges 1–100, and sectors cover bands of 10. However, this means the naming is heavily non-uniform: the player starting at (5,5) is always in “Capella” (Q=25 → Procyon), and identical products from different coordinates map to the same sector.
Energy System
Initial energy is set at line 9340: E = 1000 + 2000*RND, giving a range of 1000–3000. Energy costs:
- Close scanner: −10
- Long-range scanner: −20
- Movement: −50
- Firing: −100
- Enemy return fire hit: −100*RND (random, up to 100)
When energy drops below 1, line 7030 redirects to line 3800 where the end-game scoring loop runs. The loop at 3805–3890 uses SCROLL to push results up the screen, with an internal counter (CNT) that stops after two passes by jumping to line 5900 (STOP).
Bugs and Anomalies
- Variable name collision: The loop variable at line 9060 uses
FOR A=1 TO 20, shadowing the arrayA(). In Sinclair BASIC, scalarAand arrayA()are distinct, so this does not cause an error, but it is potentially confusing. - Line 1126 unreachable branch: Line 1126 tests for nearby aliens and, if true,
GOTO 1127— which is the very next line regardless of the branch. The conditional jump is therefore a no-op, and the real proximity check and message print happens at line 1132 unconditionally. - Return fire never reaches line 3420: Line 3340 either goes to 3400 (not defined — falls through to 3420) or falls through to line 3360. If RND > 0.5,
GOTO 3400is taken; since line 3400 does not exist, execution continues at line 3420 (“THE [alien] MISSED”). If RND ≤ 0.5, lines 3360–3390 execute (hit). This is a valid use of the non-existent-line technique for a conditional skip. - Direction 5 (south) in movement: Line 2112 implements direction “5” as
B=B+1only, correctly moving south, but direction “4” (lines 2108–2110) moves both south and east — intended as south-east. Direction “3” (line 2106 only) moves east only. The diagonal for direction “5” as labelled in the display (654bottom row, centre = south) is correct. - Initial energy used for alien type selection: Lines 9260–9320 use the raw
RNDvalue inEto pick the alien race before overwriting E with the actual energy value at line 9340. This is intentional but subtle. - Animation loop at lines 170–190 prints and erases block graphic characters (
\ :\ :\ :\ :\ :— five “▌▐” pairs) at row 20, creating a brief flashing effect between turns.
FAST/SLOW Usage
The program switches to FAST mode during initialisation (line 15) and during grid redraw (line 8001), then returns to SLOW mode (line 90, line 8225) for interactive display. This minimises screen flicker during the computationally intensive grid print loop while keeping the display readable during normal play.
Crew Commentary Subroutine
Subroutine 7500 picks a random integer 0–4 and prints a crew member’s name as a message prefix. It is called before most player-facing messages throughout the game. The five crew members are Spock, Scott, Uhura, Chekov, and Sulu, matching the original series cast. Spock’s entry (R=0) emits a full sentence opener; the others print only the name followed by a colon and space.
Content
Source Code
1 REM "STAR TREK" BY TIM HARTNELL MODIFIED BY ANTHONY WILLING WITH IMPROVED HELM/DIRECTIONAL COMMANDS
7 REM
10 CLS
15 FAST
20 GOSUB 9000
25 GOSUB 8000
30 GOSUB 6950
40 GOSUB 7000
50 GOSUB 6950
80 GOSUB 7500
90 SLOW
100 PRINT "WHAT IS YOUR ORDER,"
120 PRINT TAB 5;"SIR?",,TAB 12;"1 - SCAN";TAB 12;"2 - MOVE";TAB 12;"3 - FIRE"
140 INPUT D
150 IF D<1 OR D>3 THEN GOTO 140
155 GOSUB 6950
160 GOSUB 1000*D
170 FOR W=1 TO 40
180 PRINT AT 20,10;"\ :\ :\ :\ :\ :";AT 20,10;" "
190 NEXT W
500 GOTO 30
\n1020 PRINT TAB 4;"% % SCANNER % % "
\n1030 GOSUB 7500
\n1040 PRINT "CLOSE (1) OR ","LONG-RANGE (2), SIR?"
\n1060 INPUT K
\n1080 LET E=E-10*K
\n1090 GOSUB 6950
\n1100 IF K=2 THEN GOTO 1500
\n1126 IF A(B+1,C)=1 OR A(B+1,C+1)=1 OR A(B,C+1)=1 OR A(B-1,C)=1 OR A(B-1,C-1)=1 OR A(B,C-1)=1 OR A(B+1,C-1)=1 OR A(B-1,C+1)=1 THEN GOTO 1127
\n1127 IF B<1 THEN LET B=1
\n1128 IF B>10 THEN LET B=10
\n1129 IF C<1 THEN LET C=1
\n1130 IF C>10 THEN LET C=10
\n1132 IF A(B+1,C)=1 OR A(B+1,C+1)=1 OR A(B,C+1)=1 OR A(B-1,C)=1 OR A(B-1,C-1)=1 OR A(B,C-1)=1 OR A(B+1,C-1)=1 OR A(B-1,C+1)=1 THEN PRINT Z$;"IN VICINITY,","SIR"
\n1140 RETURN
\n1500 GOSUB 7500
\n1520 PRINT AT 15,0;"DIRECTION?:( 1 ) (4 3) ( 2 )"
\n1525 PRINT AT 16,16;"(ENTER A NUMBER)"
\n1530 INPUT SCAN
\n1540 LET Z=0
\n1560 IF SCAN=1 AND A(B-2,C)=1 THEN GOTO 1621
\n1580 IF SCAN=2 AND A(B+2,C)=1 THEN GOTO 1621
\n1600 IF SCAN=3 AND A(B,C+2)=1 THEN GOTO 1621
\n1620 IF SCAN=4 AND A(B,C-2)=1 THEN GOTO 1621
\n1621 IF B<1 THEN LET B=1
\n1622 IF B>10 THEN LET B=10
\n1623 IF C<1 THEN LET C=1
\n1624 IF C>10 THEN LET C=10
\n1625 IF SCAN=1 AND A(B-2,C)=1 THEN LET Z=1
\n1626 IF SCAN=2 AND A(B+2,C)=1 THEN LET Z=1
\n1627 IF SCAN=3 AND A(B,C+2)=1 THEN LET Z=1
\n1628 IF SCAN=4 AND A(B,C-2)=1 THEN LET Z=1
\n1630 GOSUB 7500
\n1640 PRINT "LONG-RANGE SCANNER REPORT IS"
\n1660 IF Z=1 THEN PRINT "POSITIVE"
\n1680 IF Z=0 THEN PRINT "NEGATIVE"
\n1700 RETURN
\n2020 LET E=E-50
\n2040 LET A(B,C)=0
\n2050 LET B(B,C)=0
\n2060 PRINT "DIRECTION (812) (7 3) (654) ?"
\n2080 INPUT A$
\n2100 IF A$="1" THEN LET B=B-1
\n2102 IF A$="2" THEN LET B=B-1
\n2104 IF A$="2" THEN LET C=C+1
\n2106 IF A$="3" THEN LET C=C+1
\n2108 IF A$="4" THEN LET B=B+1
\n2110 IF A$="4" THEN LET C=C+1
\n2112 IF A$="5" THEN LET B=B+1
\n2114 IF A$="6" THEN LET B=B+1
\n2116 IF A$="6" THEN LET C=C-1
\n2118 IF A$="7" THEN LET C=C-1
\n2120 IF A$="8" THEN LET B=B-1
\n2122 IF A$="8" THEN LET C=C-1
\n2124 IF B<1 THEN LET B=1
\n2126 IF B>10 THEN LET B=10
\n2127 IF C<1 THEN LET C=1
\n2128 IF C>10 THEN LET C=10
\n2130 GOSUB 7500
\n2140 PRINT "NOW AT ";B;",";C
\n2260 IF A(B,C)=1 THEN GOTO 5500
\n2270 LET A(B,C)=2
\n2280 LET B(B,C)=2
\n2285 PAUSE 100
\n2290 GOSUB 8000
\n2300 RETURN
\n3000 REM FIRE
\n3010 GOSUB 7500
\n3020 PRINT "DIRECTION OF FIRE? (812) (7 3) (654) "
\n3040 INPUT A$
\n3042 IF A$="1" THEN LET G=B-1
\n3043 IF A$="1" THEN LET F=C
\n3044 IF A$="2" THEN LET G=B-1
\n3046 IF A$="2" THEN LET F=C+1
\n3048 IF A$="3" THEN LET F=C+1
\n3049 IF A$="3" THEN LET G=B
\n3050 IF A$="4" THEN LET G=B+1
\n3052 IF A$="4" THEN LET F=C+1
\n3054 IF A$="5" THEN LET G=B+1
\n3055 IF A$="5" THEN LET F=C
\n3056 IF A$="6" THEN LET G=B+1
\n3058 IF A$="6" THEN LET F=C-1
\n3060 IF A$="7" THEN LET F=C-1
\n3061 IF A$="7" THEN LET G=B
\n3062 IF A$="8" THEN LET G=B-1
\n3064 IF A$="8" THEN LET F=C-1
\n3066 IF F<1 THEN LET F=1
\n3068 IF F>10 THEN LET F=10
\n3070 IF G<1 THEN LET G=1
\n3072 IF G>10 THEN LET G=10
\n3180 LET E=E-100
\n3190 IF A(G,F)<>1 THEN GOTO 3300
\n3195 GOSUB 7500
\n3200 PRINT "YOU HIT THE ";Z$
\n3220 LET AL=AL+1
\n3260 LET B(G,F)=4
\n3261 LET A(G,F)=4
\n3290 RETURN
\n3300 GOSUB 7500
\n3305 LET B(G,F)=3
\n3310 PRINT "YOU MISSED, SIR"
\n3315 FOR G=1 TO 30
\n3317 NEXT G
\n3320 PRINT "THE ";Z$;"ARE","RETURNING FIRE"
\n3330 FOR G=1 TO 30
\n3335 NEXT G
\n3337 GOSUB 6950
\n3340 IF RND>.5 THEN GOTO 3400
\n3360 PRINT "THEY HIT US, SIR"
\n3365 PAUSE 100
\n3370 GOSUB 8000
\n3380 LET E=E-100*RND
\n3390 RETURN
\n3420 PRINT "THE ";Z$;"MISSED, SIR"
\n3460 RETURN
\n3800 GOSUB 6590
\n3804 LET CNT=0
\n3805 SCROLL
\n3806 LET CNT=CNT+1
\n3807 IF CNT=2 THEN GOTO 5900
\n3808 SCROLL
\n3810 PRINT "ENERGY BANKS EXHAUSTED"
\n3811 SCROLL
\n3820 PRINT "YOU KILLED ";AL;" ALIEN";
\n3830 IF AL<>1 THEN PRINT "S"
\n3850 SCROLL
\n3860 PRINT "ON THIS MISSION"
\n3870 SCROLL
\n3880 PRINT "YOUR COMMANDER RATING IS ";INT (AL/8*100)
\n3883 SCROLL
\n3885 PRINT
\n3890 GOTO 3805
\n3895 STOP
\n5000 REM END
\n5500 GOSUB 6950
\n5520 FOR V=1 TO 50
\n5525 PRINT AT 15,0;"THE ENTERPRISE HAS BEEN HIT BY A"
\n5530 PRINT AT 16,0;Q$;" VESSEL AND HAS BEEN DESTROYED"
\n5540 PRINT AT 15,0;"%T%H%E% %E%N%T%E%R%P%R%I%S%E% %H%A%S% %B%E%E%N% %H%I%T% %B%Y% %A"
\n5560 NEXT V
\n5900 STOP
\n6950 PRINT AT 13,0;" "
\n6955 PRINT AT 13,0;
\n6970 RETURN
\n7000 REM STATUS
\n7020 PRINT AT 2,14;"ENERGY BANK: ";INT E;" "
\n7030 IF E<1 THEN GOTO 3800
\n7040 IF AL>0 THEN PRINT AT 3,14;Q$;" KILL";AT 4,17;"TALLY: ";AL
\n7050 PRINT AT 7,14;"YOU ARE AT "
\n7060 PRINT AT 7,14;"YOU ARE AT ";B;",";C
\n7070 PRINT AT 8,14;" "
\n7075 PRINT AT 8,14;"IN ";
\n7080 GOSUB 8500
\n7100 PRINT " SECTOR"
\n7120 PRINT AT 12,0;
\n7490 RETURN
\n7500 LET R=INT (RND*5)
\n7520 IF R=0 THEN PRINT "SPOCK: AS YOU HUMANS SAY,"
\n7540 IF R=1 THEN PRINT "SCOTT: ";
\n7560 IF R=2 THEN PRINT "UHURA: ";
\n7580 IF R=3 THEN PRINT "CHEKOV: ";
\n7600 IF R=4 THEN PRINT "SULU: ";
\n7900 RETURN
\n7999 STOP
\n8000 REM PRINT OUT
\n8001 FAST
\n8005 PRINT AT 0,0;
\n8010 PRINT " 1234567890"
\n8020 FOR Q=1 TO 10
\n8025 PRINT AT Q,13;"\@@";AT Q,13;" "
\n8030 IF Q<10 THEN PRINT Q;" ";
\n8035 IF Q=10 THEN PRINT Q;
\n8040 FOR P=1 TO 10
\n8060 IF B(Q,P)=0 THEN PRINT "% ";
\n8080 IF B(Q,P)=2 THEN PRINT "%$";
\n8100 IF B(Q,P)=3 THEN PRINT "%X";
\n8120 IF B(Q,P)=4 THEN PRINT "%*";
\n8160 NEXT P
\n8200 NEXT Q
\n8210 PRINT
\n8220 PRINT " 1234567890"
\n8225 SLOW
\n8490 RETURN
\n8500 REM SECTOR
\n8520 LET Q=B*C
\n8540 IF Q<10 THEN PRINT "ANTARES";
\n8560 IF Q>9 AND Q<20 THEN PRINT "RIGEL";
\n8580 IF Q>19 AND Q<30 THEN PRINT "PROCYON";
\n8600 IF Q>29 AND Q<40 THEN PRINT "VEGA";
\n8620 IF Q>39 AND Q<50 THEN PRINT "CANOPUS";
\n8640 IF Q>49 AND Q<60 THEN PRINT "ALTAIR";
\n8660 IF Q>59 AND Q<70 THEN PRINT "DENEB";
\n8680 IF Q>69 AND Q<80 THEN PRINT "POLLUX";
\n8700 IF Q>79 AND Q<90 THEN PRINT "SIRIUS";
\n8720 IF Q>89 THEN PRINT "CAPELLA";
\n8740 RETURN
\n8999 STOP
\n9000 DIM A(14,14)
\n9020 DIM B(14,14)
\n9060 FOR A=1 TO 20
\n9080 LET X=INT (RND*10+1)
\n9100 LET Y=INT (RND*10+1)
\n9120 LET A(X,Y)=1
\n9140 NEXT A
\n9160 LET B=5
\n9180 LET C=5
\n9200 LET A(B,C)=2
\n9220 LET B(B,C)=2
\n9240 LET AL=0
\n9260 LET E=RND
\n9279 DIM Z$(10)
\n9280 IF E<.33 THEN LET Z$="KZINTZIS "
\n9300 IF E>.33 AND E<.66 THEN LET Z$="ROMULANS "
\n9320 IF E>.66 THEN LET Z$="KLINGONS "
\n9329 DIM Q$(7)
\n9330 LET Q$=Z$
\n9340 LET E=1000+2000*RND
\n9900 RETURN
\n9910 STOP
\n9920 REM RAND USR 14336
\n9930 REM SAVE "STREK.B1"
\n9935 SAVE "1021%6"
\n9940 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
