--- title: "Star Trek" id: 57415 type: "computer_media" slug: "star-trek-3" url: "http://localhost/computer_media/star-trek-3/" markdown_url: "http://localhost/computer_media/star-trek-3.md" published_at: "2024-10-05T12:50:54+00:00" modified_at: "2026-04-03T07:58:31+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/216_StTk.png" excerpt: "Command a starship across a 10×10 grid, hunting alien fleets with limited energy — but one wrong move sends the Enterprise to its doom." 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/" indiv: - name: "Tim Hartnell" slug: "tim-hartnell" taxonomy: "indiv" url: "http://localhost/indiv/tim-hartnell/" - name: "Tony Willing" slug: "tony-willing" taxonomy: "indiv" url: "http://localhost/indiv/tony-willing/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_contents: - id: 56736 title: "Timex Sinclair Public Domain Library Tape 1005" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1005/" media_type: "Program" programmers: - name: "Tim Hartnell" slug: "tim-hartnell" taxonomy: "indiv" url: "http://localhost/indiv/tim-hartnell/" - name: "Tony Willing" slug: "tony-willing" taxonomy: "indiv" url: "http://localhost/indiv/tony-willing/" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/216_StTk.png" media_type_tags: "Game" --- # Star Trek 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: 1. Line 9000 — initialise arrays, position, alien type, and energy 2. Line 8000 — print the grid display 3. Line 6950 — clear the lower screen area 4. Line 7000 — print status panel 5. Line 7500 — print random crew prefix 6. Lines 100–160 — main menu and input loop 7. Line 160 — dispatch: `GOSUB 1000*D` (D=1 → scan, D=2 → move, D=3 → fire) 8. Lines 170–190 — brief flashing animation loop 9. 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 array `A()`. In Sinclair BASIC, scalar `A` and array `A()` 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 3400` is 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+1` only, 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 (`654` bottom row, centre = south) is correct. - **Initial energy used for alien type selection:** Lines 9260–9320 use the raw `RND` value in `E` to 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. ## 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 1020 PRINT TAB 4;"% % SCANNER % % " 1030 GOSUB 7500 1040 PRINT "CLOSE (1) OR ","LONG-RANGE (2), SIR?" 1060 INPUT K 1080 LET E=E-10*K 1090 GOSUB 6950 1100 IF K=2 THEN GOTO 1500 1126 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 1127 IF B<1 THEN LET B=1 1128 IF B>10 THEN LET B=10 1129 IF C<1 THEN LET C=1 1130 IF C>10 THEN LET C=10 1132 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" 1140 RETURN 1500 GOSUB 7500 1520 PRINT AT 15,0;"DIRECTION?:( 1 ) (4 3) ( 2 )" 1525 PRINT AT 16,16;"(ENTER A NUMBER)" 1530 INPUT SCAN 1540 LET Z=0 1560 IF SCAN=1 AND A(B-2,C)=1 THEN GOTO 1621 1580 IF SCAN=2 AND A(B+2,C)=1 THEN GOTO 1621 1600 IF SCAN=3 AND A(B,C+2)=1 THEN GOTO 1621 1620 IF SCAN=4 AND A(B,C-2)=1 THEN GOTO 1621 1621 IF B<1 THEN LET B=1 1622 IF B>10 THEN LET B=10 1623 IF C<1 THEN LET C=1 1624 IF C>10 THEN LET C=10 1625 IF SCAN=1 AND A(B-2,C)=1 THEN LET Z=1 1626 IF SCAN=2 AND A(B+2,C)=1 THEN LET Z=1 1627 IF SCAN=3 AND A(B,C+2)=1 THEN LET Z=1 1628 IF SCAN=4 AND A(B,C-2)=1 THEN LET Z=1 1630 GOSUB 7500 1640 PRINT "LONG-RANGE SCANNER REPORT IS" 1660 IF Z=1 THEN PRINT "POSITIVE" 1680 IF Z=0 THEN PRINT "NEGATIVE" 1700 RETURN 2020 LET E=E-50 2040 LET A(B,C)=0 2050 LET B(B,C)=0 2060 PRINT "DIRECTION (812) (7 3) (654) ?" 2080 INPUT A$ 2100 IF A$="1" THEN LET B=B-1 2102 IF A$="2" THEN LET B=B-1 2104 IF A$="2" THEN LET C=C+1 2106 IF A$="3" THEN LET C=C+1 2108 IF A$="4" THEN LET B=B+1 2110 IF A$="4" THEN LET C=C+1 2112 IF A$="5" THEN LET B=B+1 2114 IF A$="6" THEN LET B=B+1 2116 IF A$="6" THEN LET C=C-1 2118 IF A$="7" THEN LET C=C-1 2120 IF A$="8" THEN LET B=B-1 2122 IF A$="8" THEN LET C=C-1 2124 IF B<1 THEN LET B=1 2126 IF B>10 THEN LET B=10 2127 IF C<1 THEN LET C=1 2128 IF C>10 THEN LET C=10 2130 GOSUB 7500 2140 PRINT "NOW AT ";B;",";C 2260 IF A(B,C)=1 THEN GOTO 5500 2270 LET A(B,C)=2 2280 LET B(B,C)=2 2285 PAUSE 100 2290 GOSUB 8000 2300 RETURN 3000 REM FIRE 3010 GOSUB 7500 3020 PRINT "DIRECTION OF FIRE? (812) (7 3) (654) " 3040 INPUT A$ 3042 IF A$="1" THEN LET G=B-1 3043 IF A$="1" THEN LET F=C 3044 IF A$="2" THEN LET G=B-1 3046 IF A$="2" THEN LET F=C+1 3048 IF A$="3" THEN LET F=C+1 3049 IF A$="3" THEN LET G=B 3050 IF A$="4" THEN LET G=B+1 3052 IF A$="4" THEN LET F=C+1 3054 IF A$="5" THEN LET G=B+1 3055 IF A$="5" THEN LET F=C 3056 IF A$="6" THEN LET G=B+1 3058 IF A$="6" THEN LET F=C-1 3060 IF A$="7" THEN LET F=C-1 3061 IF A$="7" THEN LET G=B 3062 IF A$="8" THEN LET G=B-1 3064 IF A$="8" THEN LET F=C-1 3066 IF F<1 THEN LET F=1 3068 IF F>10 THEN LET F=10 3070 IF G<1 THEN LET G=1 3072 IF G>10 THEN LET G=10 3180 LET E=E-100 3190 IF A(G,F)<>1 THEN GOTO 3300 3195 GOSUB 7500 3200 PRINT "YOU HIT THE ";Z$ 3220 LET AL=AL+1 3260 LET B(G,F)=4 3261 LET A(G,F)=4 3290 RETURN 3300 GOSUB 7500 3305 LET B(G,F)=3 3310 PRINT "YOU MISSED, SIR" 3315 FOR G=1 TO 30 3317 NEXT G 3320 PRINT "THE ";Z$;"ARE","RETURNING FIRE" 3330 FOR G=1 TO 30 3335 NEXT G 3337 GOSUB 6950 3340 IF RND>.5 THEN GOTO 3400 3360 PRINT "THEY HIT US, SIR" 3365 PAUSE 100 3370 GOSUB 8000 3380 LET E=E-100*RND 3390 RETURN 3420 PRINT "THE ";Z$;"MISSED, SIR" 3460 RETURN 3800 GOSUB 6590 3804 LET CNT=0 3805 SCROLL 3806 LET CNT=CNT+1 3807 IF CNT=2 THEN GOTO 5900 3808 SCROLL 3810 PRINT "ENERGY BANKS EXHAUSTED" 3811 SCROLL 3820 PRINT "YOU KILLED ";AL;" ALIEN"; 3830 IF AL<>1 THEN PRINT "S" 3850 SCROLL 3860 PRINT "ON THIS MISSION" 3870 SCROLL 3880 PRINT "YOUR COMMANDER RATING IS ";INT (AL/8*100) 3883 SCROLL 3885 PRINT 3890 GOTO 3805 3895 STOP 5000 REM END 5500 GOSUB 6950 5520 FOR V=1 TO 50 5525 PRINT AT 15,0;"THE ENTERPRISE HAS BEEN HIT BY A" 5530 PRINT AT 16,0;Q$;" VESSEL AND HAS BEEN DESTROYED" 5540 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" 5560 NEXT V 5900 STOP 6950 PRINT AT 13,0;" " 6955 PRINT AT 13,0; 6970 RETURN 7000 REM STATUS 7020 PRINT AT 2,14;"ENERGY BANK: ";INT E;" " 7030 IF E<1 THEN GOTO 3800 7040 IF AL>0 THEN PRINT AT 3,14;Q$;" KILL";AT 4,17;"TALLY: ";AL 7050 PRINT AT 7,14;"YOU ARE AT " 7060 PRINT AT 7,14;"YOU ARE AT ";B;",";C 7070 PRINT AT 8,14;" " 7075 PRINT AT 8,14;"IN "; 7080 GOSUB 8500 7100 PRINT " SECTOR" 7120 PRINT AT 12,0; 7490 RETURN 7500 LET R=INT (RND*5) 7520 IF R=0 THEN PRINT "SPOCK: AS YOU HUMANS SAY," 7540 IF R=1 THEN PRINT "SCOTT: "; 7560 IF R=2 THEN PRINT "UHURA: "; 7580 IF R=3 THEN PRINT "CHEKOV: "; 7600 IF R=4 THEN PRINT "SULU: "; 7900 RETURN 7999 STOP 8000 REM PRINT OUT 8001 FAST 8005 PRINT AT 0,0; 8010 PRINT " 1234567890" 8020 FOR Q=1 TO 10 8025 PRINT AT Q,13;"@@";AT Q,13;" " 8030 IF Q<10 THEN PRINT Q;" "; 8035 IF Q=10 THEN PRINT Q; 8040 FOR P=1 TO 10 8060 IF B(Q,P)=0 THEN PRINT "% "; 8080 IF B(Q,P)=2 THEN PRINT "%$"; 8100 IF B(Q,P)=3 THEN PRINT "%X"; 8120 IF B(Q,P)=4 THEN PRINT "%*"; 8160 NEXT P 8200 NEXT Q 8210 PRINT 8220 PRINT " 1234567890" 8225 SLOW 8490 RETURN 8500 REM SECTOR 8520 LET Q=B*C 8540 IF Q<10 THEN PRINT "ANTARES"; 8560 IF Q>9 AND Q<20 THEN PRINT "RIGEL"; 8580 IF Q>19 AND Q<30 THEN PRINT "PROCYON"; 8600 IF Q>29 AND Q<40 THEN PRINT "VEGA"; 8620 IF Q>39 AND Q<50 THEN PRINT "CANOPUS"; 8640 IF Q>49 AND Q<60 THEN PRINT "ALTAIR"; 8660 IF Q>59 AND Q<70 THEN PRINT "DENEB"; 8680 IF Q>69 AND Q<80 THEN PRINT "POLLUX"; 8700 IF Q>79 AND Q<90 THEN PRINT "SIRIUS"; 8720 IF Q>89 THEN PRINT "CAPELLA"; 8740 RETURN 8999 STOP 9000 DIM A(14,14) 9020 DIM B(14,14) 9060 FOR A=1 TO 20 9080 LET X=INT (RND*10+1) 9100 LET Y=INT (RND*10+1) 9120 LET A(X,Y)=1 9140 NEXT A 9160 LET B=5 9180 LET C=5 9200 LET A(B,C)=2 9220 LET B(B,C)=2 9240 LET AL=0 9260 LET E=RND 9279 DIM Z$(10) 9280 IF E<.33 THEN LET Z$="KZINTZIS " 9300 IF E>.33 AND E<.66 THEN LET Z$="ROMULANS " 9320 IF E>.66 THEN LET Z$="KLINGONS " 9329 DIM Q$(7) 9330 LET Q$=Z$ 9340 LET E=1000+2000*RND 9900 RETURN 9910 STOP 9920 REM RAND USR 14336 9930 REM SAVE "STREK.B1" 9935 SAVE "1021%6" 9940 RUN ```