Ambush

Developer(s): Michael Bews
Date: 1983
Type: Program
Platform(s): TS 2068
Tags: Arcade, Game

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:

LineMX rangeSniperKill flag
27529–351K$(6)
27861–672K$(7)
280101–1073K$(8)
282144–1554K$(9) — see bug note
285185–1955K$(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.

Content

Appears On

Related Products

Related Articles

Related Content

Image Gallery

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 

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

Scroll to Top