--- title: "Ambush" id: 70758 type: "computer_media" slug: "ambush" url: "http://localhost/computer_media/ambush/" markdown_url: "http://localhost/computer_media/ambush.md" published_at: "2026-08-23T15:08:16+00:00" modified_at: "2026-08-23T16:28:37+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/08/ambush-1.png" alt: "Ambush screen" excerpt: "Eliminate five hidden snipers before they shoot you down — a tense pixel-graphics ambush game with real tracer-bullet animation and a running best-time record." 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/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" indiv: - name: "Michael Bews" slug: "michael-bews" taxonomy: "indiv" url: "http://localhost/indiv/michael-bews/" genre: - name: "Arcade" slug: "arcade" taxonomy: "genre" url: "http://localhost/type/arcade/" - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_type: "Program" programmers: - name: "Michael Bews" slug: "michael-bews" taxonomy: "indiv" url: "http://localhost/indiv/michael-bews/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Ambush%20%281983%29%28Bews%2C%20Michael%29%28TS2068%29%28UK%29%28Program%29.zip" mediadate: "1983" images: - url: "http://localhost/wp-content/uploads/2026/08/ambush-1.png" alt: "Ambush screen" - url: "http://localhost/wp-content/uploads/2026/08/ambush-2.png" alt: "Ambush screen" media_type_tags: "Arcade, Game" --- # Ambush Ambush is a shooting game in which the player must eliminate five hidden snipers before being shot. The player controls a rifle sight horizontally using N and M keys, raises or lowers aim with A and Z, and fires with B; snipers randomly pop up and return fire using pixel-precise PLOT/DRAW routines with OVER 1 for tracer-bullet animation. User-defined graphics (UDGs A through U) represent trees, a wall, crouching and standing soldier poses, and sniper figures, all initialized from DATA statements via POKE USR loops. Hit detection is performed by comparing the MX aiming variable against hardcoded pixel-range thresholds for each sniper position. A best-time record (TB) persists across multiple rounds within the same session. *** ### Program Structure The program is divided into clearly labeled sections using REM statements. Execution begins at line 50 with a `GO TO 600` to the initialization block, which sets up UDGs, loads DATA, prints the intro screen, then falls through to the scene-drawing routine at line 700. The main loop runs through three cooperating sections: 1. **Lines 100–180**: Keyboard input and timing counter 2. **Lines 200–298**: Action dispatcher — bounds-clamping MX, handling firing 3. **Lines 300–360**: Sniper AI — random sniper selection, state machine advancement Subroutine at lines 400–460 handles sniper firing with tracer animation and player-hit detection. End-game branches lead to lines 830 (player shot) or 850 (all snipers killed), both returning to line 700 to restart the scene. ### UDG Initialization Lines 615–660 perform extensive UDG setup using `POKE USR` loops reading from DATA blocks. Nine UDGs (chars 144–152, referenced as `CHR$ (143+y)`) are loaded in a nested loop at line 615. Subsequent loops load UDGs J/K (sniper figures), L (wall tile), R/S (man-down pose), T/U (man-standing pose), and char 156 (a crouching man). The wall string `W$` at line 630 is a 23-character run of the wall UDG, printed as a solid barrier at row 14. ### Sniper State Machine The string `K$` (10 characters, initialized to `"0000000000"`) encodes all game state. Characters 1–5 represent each sniper’s activity counter; characters 6–10 are kill flags (`"1"` = dead). Each game loop iteration picks a random sniper `K` (1–5), skips it if already dead, and increments its counter character using `CHR$ (CODE K$(K)+1)`. Specific counter values trigger actions: - Value `"4"`: sniper becomes visible (pops up) - Value `"5"` or `"7"` while player is not aiming (`MN=0`): sniper fires (calls line 400 subroutine) - Value `>"7"`: sniper retreats, counter resets to `"0"` ### Tracer Bullet Animation Both player and sniper fire are drawn using `PLOT OVER 1` / `DRAW OVER 1` pairs. Drawing the same line twice with OVER 1 (XOR mode) erases it cleanly without disturbing the background — a standard Spectrum animation idiom. The player’s shot origin is fixed at pixel (82,72), and the draw vector is `(MX-60, Y)` where `Y` is calculated from the aiming zone of `MX` (lines 235–248). A short BEEP loop (lines 262 and 430) adds a firing sound effect. ### Hit Detection Player-to-sniper hits are checked at lines 275–285 using hardcoded MX threshold ranges. Each range corresponds to one sniper’s screen column position: | Line | MX range | Sniper | Kill flag | | --- | --- | --- | --- | | 275 | 29–35 | 1 | K$(6) | | 278 | 61–67 | 2 | K$(7) | | 280 | 101–107 | 3 | K$(8) | | 282 | 144–155 | 4 | K$(9) — see bug note | | 285 | 185–195 | 5 | K$(10) | Sniper-to-player hit detection (line 450) checks whether the sniper’s shot X coordinate lands in the range 78–86, representing the player’s position behind the wall. ### Aiming System The horizontal aim variable `MX` (initialized to 80) is adjusted by 8 units per keypress of N (left) or M (right). The vertical component `Y` is derived from the current `MX` value via a series of threshold comparisons (lines 235–248), effectively mapping horizontal aim zones to the correct vertical angles to reach each sniper’s screen position. `MN` flags whether the player is aiming up (A key sets `MN=1`, Z clears it), and the `FR` flag is set by B to trigger firing on the next action pass. ### Bugs and Anomalies - **Line 282 kill flag**: `LET K$="1"` replaces the entire 10-character string with a single `"1"` instead of setting `K$(9)="1"`. This effectively corrupts the kill-flag array when sniper 4 is hit, likely causing immediate win detection or unpredictable behavior. - **Line 680 duplicate loop variable**: `FOR y=1 TO 5:FOR y=1 TO 5` — the inner loop reuses `y`, shadowing the outer `y`. Only 5 values are read per sniper row instead of the intended nested 5×5 = 25; the outer loop variable is overwritten immediately. - **Line 440 draw vector**: `DRAW OVER 1;NX,-P(K,2)` uses `P(K,2)` (the sniper’s Y pixel coordinate) as the vertical draw distance on the erase pass, rather than `P(K,5)` used on the draw pass (line 425). This means the erase stroke does not exactly retrace the fired line. - **Line 440 DRAW syntax**: Uses semicolon separator rather than comma, consistent with the rest of the program’s DRAW usage — not a bug per se but worth noting for portability. - **Line 282 array bounds**: If `K$` were not corrupted as above, `K$(9)` would be valid within the 10-character string; the intent is clear despite the bug. ### Scoring and Persistence `TM` is a frame-count timer incremented each keyboard-poll cycle (line 120). The best-time record `TB` (initialized to 5000) is updated at line 850 only if the current time is lower, persisting across replays within the session. On restart (line 870–890), `MX`, `K$`, `TM`, `MN`, and `FR` are reset, but `TB` is preserved. ## Source Code ``` 10 REM **SNIPER AMBUSH ©1983 MICHAEL BEWS 20 DIM P(5,5):DIM L(11,2):DIM S(5,2):DIM S$(2,2) 30 LET TB=5000:LET TM=0:LET K$="0000000000":LET MN=0:LET AI=0:LET FR=0:LET MX=80 40 PAPER 4:INK 7:BORDER 1 50 GO TO 600 100 REM **KEYBOARD INPUT 120 LET TM=TM+1:PRINT INK 0; PAPER 6; AT 15,28;TM 125 LET A= PEEK 23556:IF A=255 THEN GO TO 200 130 IF A=65 THEN PRINT 13,10;R$:LET MN=1:GO TO 200 140 IF A=90 THEN PRINT AT 13,10;Q$:LET MN=0:GO TO 200 150 IF (A=66 AND MN=1) THEN LET FR=1:GO TO 200 160 IF A=78 THEN LET MX=MX-8:GO TO 200 170 IF A=77 THEN LET MX=MX+8:GO TO 200 180 GO TO 120 199 REM *ACTION ROUTINE 210 IF MX<0 THEN LET MX=0 215 IF MX>220 THEN LET MX=220 220 IF FR=0 THEN GO TO 300 225 LET FR=0 230 LET Y=23 235 IF MX<42 THEN LET Y=Y+32:GO TO 250 240 IF MX<70 THEN LET y=y+7:GO TO 250 242 IF MX<124 THEN LET Y=Y+8:GO TO 250 244 IF MX<164 THEN GO TO 250 248 LET Y=Y-8 250 REM *TRACERS AND SOUND 255 PLOT OVER 1,82,72 260 DRAW OVER 1;MX-60,Y 262 FOR X=57 TO 53 STEP -1:BEEP .005,X:NEXT X 265 PLOT OVER 1,82,72 270 DRAW OVER 1;MX-60,Y 272 REM *CHECK FOR HIT 275 IF MX>29 AND MX<35 AND K$(1)>"0" THEN PRINT AT 5,4;"[UDG-R][UDG-S]"; INK 0; PAPER 4;"[UDG-G][UDG-H]":LET K$(6)="1":GO TO 290 278 IF MX>61 AND MX<67 AND K$(2)>"0" THEN PRINT AT 8,8;"[UDG-R][UDG-S]"; INK 0; PAPER 4;"[UDG-G][UDG-H]":LET K$(7)="1":GO TO 290 280 IF MX>101 AND MX<107 AND K$(3)>"0" THEN PRINT AT 8,13;"[UDG-R][UDG-S]"; INK 0; PAPER 4;"[UDG-G][UDG-H]":LET K$(8)="1":GO TO 290 282 IF MX>144 AND MX<155 AND K$(4)>"0" THEN PRINT AT 9,19;"[UDG-R][UDG-S]"; INK 0; PAPER 4;"[UDG-G][UDG-H]":LET K$="1":GO TO 290 285 IF MX>185 AND MX<195 AND K$(5)>"0" THEN PRINT AT 10,24;"[UDG-R][UDG-S]"; INK 0; PAPER 4;"[UDG-G][UDG-H]":LET K$(10)="1" 289 REM *CHECK FOR WIN 290 IF K$(6 TO 10)="11111" THEN GO TO 850 299 REM *SNIPER ROUTINE 310 LET K=1+ INT (RND*5) 315 IF K$(K+5)="1" THEN GO TO 120 320 IF K$(K)="4" THEN PRINT INK 0; PAPER 4; AT S(K,1),S(K,2);S$(2); 330 IF K$(K)="5" OR (K$(K)="7" AND MN=0) THEN GO SUB 400 340 LET K$(K)= CHR$ (CODE K$(K)+1) 350 IF K$(K)>"7" THEN PRINT INK 0; PAPER 4; AT S(K,1),S(K,2);S$(1);:LET K$(K)="0" 360 GO TO 120 399 REM *SNIPER FIRING 405 LET N=1+ INT (RND*P(K,4)) 410 LET NX=N-P(K,3) 420 PLOT OVER 1;P(K,1),P(K,2) 425 DRAW OVER 1;NX,-P(K,5) 430 FOR X=50 TO 47 STEP -1:BEEP .005,X:NEXT X 435 PLOT OVER 1;P(K,1),P(K,2) 440 DRAW OVER 1;NX,-P(K,2) 450 IF P(K,1)+NX>78 AND P(K,1)+NX<86 THEN PRINT AT 13,10;Q$; AT 15,9;P$;:GO TO 830 460 RETURN 598 STOP 599 REM **INITIALISATION 602 CLS :PRINT INK 1; PAPER 7;"A M B U S H ©1983 MICHAEL BEWS"; AT 4,0;"YOU HAVE BEEN AMBUSHED BY GUNMENIN A FOREST. THE ONLY SAFE PLACE IS LOW WALL BEHIND WHICH YOU TAKE COVER." 603 PRINT AT 9,0; INK 1; PAPER 7;"YOU MUST TRY TO KILL ALL GUNMEN BY AIMING OVER THE TOP OF THE WALL. BUT THE SNIPERS ARE QUITE GOOD SHOTS!"; AT 16,0;"HOW LONG CAN YOU SURVIVE?......." 608 LET f$= CHR$ 21+ CHR$ 1+" "+ CHR$ 21+ CHR$ 0 610 LET G$= CHR$ 21+ CHR$ 1+" "+ CHR$ 21+ CHR$ 0 615 FOR x=0 TO 7:FOR y=1 TO 9:READ a:POKE USR CHR$ (143+y)+x,a:NEXT y:NEXT x 620 FOR x=0 TO 7:READ a,b:POKE USR "[UDG-J]"+x,a:POKE USR "[UDG-K]"+x,b:NEXT x 625 FOR X=1 TO 11:READ L(X,1):READ L(X,2):NEXT X 630 FOR X=0 TO 7:READ A:POKE USR "[UDG-L]"+X,A:NEXT X:LET W$="[UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L][UDG-L]" 640 LET t$="[UDG-A][UDG-B][UDG-C]"+f$+"[UDG-D][UDG-E][UDG-F]"+f$+"[UDG-G][UDG-H][UDG-I]" 645 LET S$(1)="[UDG-G][UDG-H]":LET S$(2)="[UDG-J][UDG-K]" 650 FOR X=0 TO 7:READ A:POKE USR CHR$ (156)+X,A:NEXT X 655 FOR X=0 TO 7:READ A,B:POKE USR "[UDG-R]"+X,A:POKE USR "[UDG-S]"+X,B:NEXT X 660 FOR X=0 TO 7:READ A,B:POKE USR "[UDG-T]"+X,A:POKE USR "[UDG-U]"+X,B:NEXT X 665 LET Q$= CHR$ 16+ CHR$ 0+ CHR$ 17+ CHR$ 4+" "+G$+"[UDG-L]"+G$+"[UDG-M]":LET P$="[UDG-R][UDG-S]" 670 LET R$= CHR$ 16+ CHR$ 0+ CHR$ 17+ CHR$ 4+"[UDG-T]"+G$+"[UDG-U]"+G$+" " 675 FOR X=1 TO 5:READ S(X,1):READ S(X,2):NEXT X 680 FOR x=1 TO 5:FOR y=1 TO 5:FOR y=1 TO 5:READ P(X,Y):NEXT Y:NEXT X 690 INPUT INK 7; PAPER 2;"PRESS {ENTER} TO FIND OUT!!!";X$ 700 REM **PRINT SCENE** 705 CLS :PRINT PAPER 6; INK 1;"A M B U S H ©1983 MICHAEL BEWS" 707 FOR x=10 TO 26 STEP 4:PRINT INK 0; PAPER 4; AT 1,x;t$:NEXT x 710 FOR x=1 TO 11:PRINT INK 0; AT L(X,1),L(X,2);t$;:NEXT X 715 FOR X=3 TO 11:PRINT AT L(X,1)+2,L(X,2)+2; INK 6;"[UDG-I]";:NEXT X 720 PRINT INK 0; AT 15,0;T$; AT 17,3;T$ 730 PRINT INK 0; AT 14,0;W$ 800 PRINT AT 13,10;Q$ 805 PRINT AT 15,23; INK 0; PAPER 6;"TIME:" 810 PRINT AT 20,0; PAPER 2; INK 7;"SHOOT SNIPERS AS THEY FIRE.KEYS'A&Z'(UP/DN)'N&M'(AIM) 'B'(FIRE)" 820 GO TO 100 829 REM ** PLAYER SHOT! 830 PRINT AT 20,0; PAPER 7; INK 2;" "; AT 20,0; FLASH 1;"YOU HAVE BEEN SHOT! IN TIME:";TM; AT 21,0;"THIS IS THE SNIPER WHO SHOT YOU↑" 832 PRINT INK 0; PAPER 4; FLASH 1; AT S(K,1),S(K,2);S$(2); 835 BEEP .8,1:PAUSE 5:BEEP .5,1:PAUSE 2:BEEP .3,1:BEEP .8,1:BEEP .5,4:PAUSE 2:BEEP .5,1:PAUSE 1:BEEP .8,1:PAUSE 4:BEEP .5,0:BEEP 1,1 840 GO TO 860 849 REM *ALL SNIPERS DEAD 850 IF TB>TM THEN LET TB=TM 855 PRINT AT 20,0; PAPER 7; INK 2;" "; AT 20,0; FLASH 1;"YOU SHOT ALL SNIPERS IN TIME:";TM; AT 21,0;"BEST TIME SO FAR:";TB 857 FOR Y=1 TO 5:FOR X=1 TO 10:BEEP .05,X*2:NEXT X:NEXT Y 860 INPUT INK 0; PAPER 7; FLASH 1;"PRESS {ENTER} FOR NEXT GAME";X$ 870 LET MX=80:LET K$="0000000000" 880 LET TM=0:LET MN=0:LET FR=0 890 GO TO 700 998 STOP 999 REM **tree 1000 DATA 3,15,0,127,255,252,0,60,8,3,255,192,63,255,188,0,60,0,15,255,240,55,247,248,0,60,138,15,255,248,31,63,248,0,60,40,126,255,126,31,60,240,0,60,138,127,255,254,24,60,48,0,60,162,255,255,252,0,60,0,0,60,160,127,223,252,0,60,0,0,60,32 1009 REM * sniper 1010 DATA 0,252,63,252,2,124,3,252,0,124,1,252,1,124,3,60 1019 REM **tree PRINT AT FILE 1020 DATA 2,4,1,18,3,6,2,20,3,24,4,11,4,29,6,10,6,15,7,21,8,26 1029 REM **WALL 1030 DATA 51,255,219,255,253,255,183,255 1039 REM **MAN CROUCHING 1040 DATA 249,113,113,33,255,113,123,219 1049 REM *MAN DOWN 1050 DATA 0,0,0,0,0,0,0,0,160,0,247,224,255,241,247,255 1059 REM *MAN STANDING 1060 DATA 248,115,113,255,115,251,118,255,38,253,250,251,254,247,112,255 1069 REM *sniper print at file 1070 DATA 5,6,8,10,8,15,9,21,10,26 1079 REM *sniper firing coords 1080 DATA 47,127,10,50,54,79,110,39,70,37,119,110,60,60,37,167,102,127,80,29,207,94,200,120,21 4000 BORDER 7:PAPER 7:INK 0:CLS ```