--- title: "Tunnel Run" id: 71473 type: "computer_media" slug: "tunnel-run" url: "http://localhost/computer_media/tunnel-run/" markdown_url: "http://localhost/computer_media/tunnel-run.md" published_at: "2026-09-09T09:02:41+00:00" modified_at: "2026-09-09T09:02:42+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/09/tunnel-run-1.png" excerpt: "Navigate your spacecraft through four increasingly dangerous sections — missile tunnel, asteroid field, winding passage, and landing pad — in this multi-stage survival game." 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 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" basic: - name: "User Defined Graphics (UDG)" slug: "user-defined-graphics-udg" taxonomy: "basic" url: "http://localhost/basic/user-defined-graphics-udg/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/Tunnel%20Run%20(198x)(-)(TS2068)(US)(Program).zip" tsrun_member: "Tunnel Run (198x)(-)(TS2068)(US)(Program).tap" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/09/tunnel-run-1.png" - url: "http://localhost/wp-content/uploads/2026/09/tunnel-run-2.png" media_type_tags: "Game" --- # Tunnel Run Tunnel Run is a four-stage space navigation game in which the player pilots a ship through a missile tunnel, an asteroid field, a winding tunnel, and finally a landing pad. Each stage uses a different subset of keys: 5 and 8 for lateral movement, 0 to fire in the asteroid section, and R for retro-boost during the landing approach. The program defines two custom UDG characters at startup by POKEing eight bytes each into the UDG area starting at USR “\\l”, creating a ship sprite and an asteroid graphic. Collision detection is performed using ATTR to read the color attributes of the cell occupied by the player’s ship, with each stage expecting a specific ink/paper color combination to confirm a safe position. Score is tracked throughout all four sections, with a persistent high score maintained across playthroughs within the same session. *** ### Program Structure The program is divided into clearly sequential sections separated by border/paper color changes used as visual cues for each stage: 1. **Initialization and title screen** (lines 10–110): UDG setup, instructions, and wait for keypress. 2. **Stage 1 — Missile Tunnel** (lines 120–450): Blue border, 200-frame loop; player moves laterally across row 0 while a scrolling floor is printed at row 21. ATTR-based collision checks for ink 0/paper 1 (value 8) or ink 4/paper 1 (value 12). 3. **Stage 2 — Asteroids** (lines 460–770): Red border; player can fire vertically with key 0, scoring −20 per shot and +100 per hit on target asteroid at column `c`. 4. **Stage 3 — Winding Tunnel** (lines 780–980): Yellow border; a scrolling gap `tu` drifts randomly; enemy craft appear at random within the gap. 5. **Stage 4 — Landing Pad** (lines 990–1230): Black background; player descends vertically (`x` increments each frame), must align with the yellow landing square at column `g`. 6. **End screens** (lines 1240–1290): Crash message or success display with high score and CIRCLE graphic; option to replay from line 90. ### UDG Initialization Lines 10–50 define two custom characters. The loop runs `f=0 TO 15`, POKEing 16 bytes total into `USR "\\l" + f`. The first eight bytes (from the DATA at line 30: `0,66,153,189,255,153,66,0`) define the UDG at `\\l` (the ship sprite). The second eight bytes (line 50: `126,60,24,126,126,126,90,24`) define the UDG at `\\m` (used as the player’s ship icon printed at row 0). Note that the DATA at line 30 is interleaved with the loop structure: the READ at line 20 inside the loop reads all 16 values sequentially across both DATA statements. ### Collision Detection via ATTR Each stage uses `ATTR (0,a)` to determine whether the player’s ship position is safe, relying on the background color rendered at row 0: | Stage | Expected ATTR value(s) | Meaning | | --- | --- | --- | | Missile Tunnel | 8 or 12 | INK 0 or INK 4 on PAPER 1 (blue) | | Asteroids | 16 | INK 0 on PAPER 2 (red) | | Winding Tunnel | 24 | INK 0 on PAPER 3 (yellow) | | Landing Pad | Column match with `g` | Positional, not ATTR-based | Any mismatch in stages 1–3 branches to line 1240 (crash). Stage 4 checks `a <> g` at row 15. ### Key BASIC Idioms - **Boolean movement**: `LET a=a+(INKEY$="8")-(INKEY$="5")` — a standard Sinclair BASIC idiom exploiting the fact that boolean expressions evaluate to 1 (true) or 0 (false). - **POKE 23692,255**: Resets the scroll counter to suppress the *scroll?* prompt, preventing the screen from pausing during fast-moving display loops. - **PAUSE 0 / INKEY$ loop**: Lines 100–110 use the standard idiom of draining the keyboard buffer before waiting for a fresh keypress. - **Tunnel drift clamping** (line 890): `LET tu=tu+(tu<1)-(tu>24)` keeps the tunnel gap within screen bounds using boolean arithmetic rather than an IF statement. ### Stage 2 — Asteroid Firing Mechanic When the player presses 0, lines 580–650 draw a vertical beam of `"I"` characters in INK 6 from row 1 to 21 at column `a`, then check whether `a = c` (the asteroid’s column). A hit awards +100 points and flashes the asteroid position. The asteroid position `c` drifts each frame via `LET c=c+(RND>.5)-(RND<.5)`, making it a moving target. A copyright symbol character (`\\*`) is used as a random obstacle sprite at line 700. ### Stage 4 — Landing Pad The landing section introduces vertical movement: `x` increments each frame simulating gravity. Pressing key 0 or `r` decrements `x` (retro-boost), allowing the player to control descent rate. The target column `g` is randomized at line 1010. A safe landing requires `a = g` when `x >= 15`; any other column at that row causes a crash. Horizontal wrapping is implemented at line 1090: `LET a=a+(a<0)-(a>31)`. ### Notable Techniques and Anomalies - The `CIRCLE 125,85,80` at line 1210 is used as a decorative success graphic on the win screen, one of the few uses of the graphics command in the program. - The transition loop at lines 280–380 acts as a brief “corridor” between stage 1’s main loop and stage 2, printing blank lines to scroll the tunnel display away. - In stage 3 (line 910), enemy craft appear stochastically: `IF RND>.7 THEN PRINT AT 21,(RND*5)+tu; INK 1;"\\l"` — the UDG ship character is reused as an enemy sprite. - The high score `high` is initialized at line 60 and persists across replays within the session since GO TO 90 does not re-execute line 60. - Stage 1’s floor at line 210 prints a yellow `"^"` character (caret, used as a missile sprite) at a random column between 10 and 16, creating an animated hazard on row 21 distinct from the wall blocks. ## Source Code ``` 10 CLS :FOR f=0 TO 15 20 READ a:POKE USR "\l"+f,a 30 DATA 0,66,153,189,255,153,66,0 40 NEXT f 50 DATA 126,60,24,126,126,126,90,24 60 LET high=0 70 PRINT " EXPEDITION"," __________"," You have been challenged to explore this exciting space mazeand return home ALIVE !"," There are 4 sections:","1> Missile tunnel. Keys 5,8. 2> Asteroids. Keys 5,8,0 to fire -20 points each shot, +100 per hit." 80 PRINT "3> Winding tunnel. Keys 5,8."," Avoid space craft.","4> Landing pad. Keys 5,8,r to retro-boost. Land on the yellow part." 90 PRINT '"Any key to start !!!" 100 IF INKEY$ <>"" THEN GO TO 100 110 PAUSE 0 120 LET a=15:LET sc=0 130 BORDER 1:PAPER 1:INK 0:CLS 140 PRINT AT 21,0;"\::\::\::\::\::\::\::\::\::\:: \::\::\::\::\::\::\::\::\::\::\::\::\::\::\::" 150 FOR f=1 TO 200 160 IF a <=9 OR a >=17 THEN GO TO 1240 170 PRINT AT 0,a;"\m" 180 POKE 23692,255 190 LET a=a+(INKEY$="8")-(INKEY$="5") 200 PRINT AT 21,0; 210 PRINT AT 21, INT (RND*7)+10; INK 6;"^" 220 PRINT INK 4;"\::\::\::\::\::\::\::\::\::\:: \::\::\::\::\::\::\::\::\::\::\::\::\::\::\::" 230 LET b= ATTR (0,a) 240 IF b <>8 AND b <>12 THEN GO TO 1240 250 LET sc=sc+1 260 BEEP .005,0:BEEP .005,-2 270 NEXT f 280 FOR f=1 TO 22 290 PRINT AT 0,a;"\m" 300 LET a=a+(INKEY$="8")-(INKEY$="5") 310 POKE 23692,255 320 PRINT AT 21,0,, 330 PRINT ,, 340 LET b= ATTR (0,a) 350 IF b <>8 AND b <>12 THEN GO TO 1240 360 LET sc=sc+1 370 BEEP .01,0:BEEP .01,-2 380 NEXT f 390 PRINT AT 0,a;"\m" 400 PRINT AT 10,10;"\{18}\{1}Score ";sc;"\{18}\{0}" 410 FOR f=1 TO 5 420 FOR n=10 TO 20 430 BEEP .1,n 440 NEXT n:NEXT f 450 PAUSE 250 460 BORDER 2:PAPER 2:CLS 470 PRINT AT 0,a;"\m" 480 FOR f=1 TO 3 490 BEEP .2,-2:BEEP .3,-2:BEEP .5,3.2 500 NEXT f 510 BEEP 1,3.2 520 LET c=20 530 FOR f=1 TO 200 540 PRINT AT 0,a;"\m" 550 LET a=a+(INKEY$="8")-(INKEY$="5") 560 IF INKEY$ <>"0" THEN GO TO 660 570 LET sc=sc-20 580 FOR n=1 TO 21 590 PRINT AT n,a; INK 6;"I" 600 NEXT n 610 IF a <>c THEN GO TO 650 620 LET sc=sc+100 630 FOR x=1 TO 30:PRINT AT 21,c; INK 6; PAPER 0; FLASH 1;"#":NEXT x 640 LET c=20 650 FOR n=1 TO 21:PRINT AT n,a;" ":NEXT n 660 POKE 23692,255 670 PRINT AT 21,c;" " 680 PRINT AT 21,0; INK 3; BRIGHT 1;"###"; AT 21,31;"#" 690 PRINT ,, 700 PRINT AT 21,(RND*30); INK 4;"\*" 710 LET b= ATTR (0,a) 720 IF b <>16 THEN GO TO 1240 730 LET c=c+(RND>.5)-(RND<.5) 740 BEEP .005,0:BEEP .005,-2 750 PRINT AT 21,c; INK 1;"\l" 760 NEXT f 770 FOR f=1 TO 22:PRINT AT 21,0,,:NEXT f 780 BORDER 3:PAPER 3:CLS 790 PRINT AT 0,a;"\m" 800 PRINT AT 10,10; FLASH 1;"Score ";sc 810 BEEP 1,9:BEEP 1,10.7:BEEP 1,9:PAUSE 130:BEEP 2,0 820 LET tu=13 830 PRINT AT 10,10;" " 840 FOR f=1 TO 172 850 PRINT AT 0,a;"\m" 860 LET a=a+(INKEY$="8")-(INKEY$="5") 870 POKE 23692,255 880 LET tu=tu+(RND>.5)-(RND<.5) 890 LET tu=tu+(tu<1)-(tu>24) 900 PRINT AT 21,tu; PAPER 3; INK 0;" " 910 IF RND>.7 THEN PRINT AT 21,(RND*5)+tu; INK 1;"\l" 920 PRINT INK 7; BRIGHT 1;"\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::" 930 IF f>149 THEN PRINT AT 21,0; PAPER 3; INK 0,, 940 LET b= ATTR (0,a) 950 IF b <>24 THEN GO TO 1240 960 LET sc=sc+1 970 BEEP .005,0:BEEP .005,-2 980 NEXT f 990 BORDER 0:PAPER 0:INK 7:CLS 1000 PRINT AT 0,a;"\m" 1010 LET g= INT (RND*30)+1 1020 PRINT AT 15,0; INK 1;"################################" 1030 FOR f=16 TO 21:PRINT AT f,0; INK 2;"\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::":NEXT f 1040 PRINT AT 15,g; INK 6;"#" 1050 LET x=0 1060 PAUSE 150 1070 PRINT AT x,a;"\m" 1080 LET a=a+(INKEY$="8")-(INKEY$="5") 1090 LET a=a+(a<0)-(a>31) 1100 LET x=x+1 1110 PAUSE 5 1120 IF INKEY$="0" OR INKEY$="r" THEN LET x=x-1 1130 IF x >=15 THEN GO TO 1150 1140 PRINT AT x,a;" ":GO TO 1070 1150 IF a <>g THEN GO TO 1240 1160 PAPER 0:INK 7:FLASH 1:CLS 1170 PRINT AT x,15;"\m" 1180 PRINT AT 17,10;"SCORE ";sc 1190 IF sc>high THEN LET high=sc 1200 PRINT AT 18,10;"HIGH ";high 1210 CIRCLE 125,85,80 1220 FOR f=1 TO 20:BEEP .01,f:NEXT f 1230 GO TO 1260 1240 CLS :PRINT AT 10,0;"You have crashed.Score:";sc;". " 1250 FOR f=0 TO -10 STEP -1:BEEP .01,f:NEXT f 1260 PRINT '"PRESS Y FOR ANOTHER GO,N TO STOP" 1270 IF INKEY$ <>"" THEN GO TO 1270 1280 PAUSE 0:FLASH 0:CLS 1290 IF INKEY$="y" OR INKEY$="Y" THEN GO TO 90 ```