Buster-Bill’s Obsession

This file is part of and Timex Sinclair Public Domain Library Tape 2004. Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Game

“Buster-Bill’s Obsession” (also titled “Block Buster” in the instructions) is a puzzle-action game in which the player steers a character around a grid of blocks, defusing time bombs before they detonate. The 20×30 character grid is stored entirely as a string array m$(20,30), allowing row-shift mechanics using BASIC’s string slicing — pressing keys 1 or 2 rotates the player’s current row left or right by concatenating substrings. Sixteen user-defined graphics (UDGs “a” through “p”) are loaded from DATA statements to draw the player sprite, safe blocks, flags, and bombs. The game supports up to nine progressive difficulty levels, with the number of bombs per level determined by the formula l*3+3, and includes a persistent high-score tracked across playthroughs within the same session.


Program Analysis

Program Structure

The program is organized into clearly delineated sections, each introduced by a REM statement:

  • Lines 5–60: Initialization, UDG setup (via GO SUB 5000), title screen, and key-wait.
  • Lines 70–900: Core game loop — screen/level setup, main movement and collision logic, bomb handling, and row-shift mechanics.
  • Lines 1000–1060: Life-loss handler with death animation.
  • Lines 2000–2060: Game-over screen with replay prompt and high-score update.
  • Lines 3000–3040: Level-completion bonus and level advancement.
  • Lines 4000–4540: Screen setup, map initialization, display refresh, and random-position subroutine.
  • Lines 5000–5090: UDG data loader.
  • Lines 6000–6070: Instructions screen.

Grid and Map Representation

The entire playfield is stored as a two-dimensional string array m$(20,30), where each cell holds one character of the display. This means collision detection, block content checks, and rendering all operate directly on this string array rather than a separate numeric map. Block tiles occupy two rows and two columns of UDG characters, so the logical grid is 10×15 tiles within the 20-row, 30-column character space (column 0 is reserved for the score/status line).

The player position (a, b) and all sprite positions are stored in even/odd row and column coordinates matching this two-character-wide tile layout.

Row-Shift Mechanic

The most technically notable feature is the row-scrolling mechanic on keys 1 and 2. Lines 610–640 and 700–720 implement a circular left or right shift of both rows of the player’s current tile row using BASIC string concatenation and slicing:

  • Left shift (key 1): m$(a+x) = m$(a+x, 3 TO ) + m$(a+x) — appends the full string to its own tail, then implicitly truncates to 30 chars.
  • Right shift (key 2): m$(a+x) = m$(a+x, 29 TO ) + m$(a+x) — similarly wraps from the rightmost two characters.

After each shift, the flag position (f2) and bomb position (b2) are adjusted by ±2 to track with their tile, and wrap-around at columns −1 and 31 is corrected at lines 810–820.

Movement and Wrapping

Player movement uses keys 5–8 (standard cursor keys). Lines 510–520 add or subtract 2 from a (row) or b (column) and then wrap with arithmetic: a = a + (20 AND a<1) - (20 AND a>19). This idiom exploits BASIC’s boolean values (1/0) as integers — a well-known Spectrum BASIC optimization that avoids IF statements inside the tight game loop.

Bomb Timer and Display

The bomb timer t counts down from 50 (adjusted by level: 50-1*4 — note the 1 here should arguably be l, suggesting a possible bug where bomb timing does not scale with level). When active, line 190 displays a flashing countdown at the bomb location and emits a beep pitched to the timer value. The bomb is placed in the map array with UDG characters, and its map coordinates are stored in b1, b2. A value of t=-1 signals no active bomb.

UDG Character Set

Sixteen UDGs (“a” through “p”) are defined via 128 bytes of DATA loaded at line 5010. Their roles as used in the program are:

UDG pairRole
\:: / \a (top), \b / \c (bottom)Safe block tile
\d / \e (top), \f / \g (bottom)Flag
\h / \i (top), \j / \k (bottom)Unactivated bomb
\l / \m (top), \n / \o (bottom)Player sprite

Note that \:: (the solid block graphic, char 143) is used as part of the block tile pattern alongside UDG “a”, giving a distinctive checkerboard appearance to the playing field.

Level Progression and Scoring

The number of bombs per level is l*3+3, stored in the numeric array m() which records each bomb’s row for later retrieval. Level advances when bo (bombs defused) exceeds l*3+3 (line 170). The level cap is 9 (l=l+(l<9)). Scoring events include: +2 per block cleared, +20 for reaching a flag, +15 per bomb defused, and a level-completion super bonus of 55*l.

Notable Bugs and Anomalies

  • Non-scaling bomb timer: Line 200 sets t=50-1*4. The literal 1 is almost certainly meant to be the level variable l, meaning bombs always have a timer of 46 regardless of level. This appears to be a typographical error.
  • Duplicate + operator: Line 4520 reads LET z=INT (RND*15)*2++1 — the double + is syntactically valid (unary plus) and evaluates correctly to *2+1, but is likely a typo.
  • Score multiplier: Line 150 computes sc=sc+1*15. The 1* is redundant (again, likely meant to be l*15 for level-scaled scoring), so bomb defusal always awards exactly 15 points.
  • String truncation in row-shift: The shifted row string is assigned back into the fixed-length m$() array entry, which automatically truncates to 30 characters — a convenient side effect of Sinclair BASIC’s fixed-length string arrays.
  • Death animation: Line 1010 iterates x from 7 to 0 step −0.2, using a fractional step to slow the flash-to-dark color transition of the player sprite — an effective visual effect using the INK attribute cycle.

Key Input Handling

The main loop polls INKEY$ without a PAUSE, meaning the game runs as fast as BASIC allows. The guard IF x$<"5" THEN GO TO 540 at line 500 efficiently filters out non-movement keys before the movement arithmetic, falling through to the row-shift handler at line 540 only for keys “1” and “2”.

Content

Appears On

One of a series of library tapes compiled from multiple user groups.

Related Products

Related Articles

Related Content

Image Gallery

Buster-Bill’s Obsession

Source Code

    5 REM buster-BILL'S OBSESSION
   10 LET hi=0: GO SUB 5000
   20 PAPER 7: BORDER 7: INK 0: BRIGHT 0: CLS : PRINT "PRESS A KEY:'I' FOR INSTRUCTIONS"
   30 IF INKEY$<>"" THEN GO TO 30
   40 IF INKEY$="" THEN GO TO 40
   50 IF INKEY$="i" OR INKEY$="I" THEN GO SUB 6000
   60 RANDOMIZE : LET sc=0: LET l=1: LET l$="\p\p\p "
   70 LET a=19: LET b=15: LET aa=a: LET bb=b
   80 LET d=-1+(INT (RND*10)*2+2 AND l>2): GO SUB 4000
   90 LET f1=y: LET f2=z: LET t=-1: LET bo=1: LET b1=t: LET b2=t
  100 REM ** main game **
  110 PRINT AT f1,f2; INK 6;"\d\e";AT f1+1,f2;"\f\g";AT a,b; INK 7;"\l\m";AT a+1,b;"\n\o"
  120 IF aa<>a OR bb<>b THEN LET sc=sc+2: PRINT AT aa,bb;"  ";AT aa+1,bb;"  ": LET m$(aa,bb TO bb+1)="  ": LET m$(aa+1,bb TO bb+1)="  ": IF m$(a,b)=" " AND (f1<>a OR f2<>b) THEN GO TO 1000
  130 LET aa=a: LET bb=b
  140 IF a=f1 AND b=f2 THEN BEEP .02,20: BEEP .02,25: BEEP .02,30: GO SUB 4500: LET f1=y: LET f2=z: LET sc=sc+20
  150 IF a=b1 AND b=b2 THEN LET bo=bo+1: BEEP .03,30: BEEP .03,25: BEEP .03,20: LET sc=sc+1*15: LET t=-1: LET b1=t: LET b2=t
  160 IF m$(a,b)="\h" OR t=0 THEN GO TO 1000
  170 IF bo>1*3+3 THEN GO TO 3000
  180 PRINT AT 0,6; INK 7;sc
  190 IF t>0 THEN LET t=t-1: PRINT AT b1,b2; FLASH 1; INK 6; PAPER 2;"**";AT b1+1,b2;"**";AT b1+(INT (t/2)=t/2),b2;t: BEEP .01,t+10: GO TO 500
  200 LET y=m(bo): LET z=INT (RND*15)*2+1: IF m$(y,z)="\h" THEN LET t=50-1*4: LET b1=y: LET b2=z: LET m$(y,z TO z+1)="\::\a": LET m$(y+1,z TO z+1)="\b\c"
  500 LET x$=INKEY$: IF x$<"5" THEN GO TO 540
  510 LET a=a+(2 AND x$="6")-(2 AND x$="7"): LET b=b+(2 AND x$="8")-(2 AND x$="5")
  520 LET a=a+(20 AND a<1)-(20 AND a>19): LET b=b+(30 AND b<1)-(30 AND b>29)
  530 GO TO 100
  540 IF x$<>"1" AND x$<>"2" OR t=a THEN FOR x=1 TO 15: NEXT x: GO TO 100
  600 IF x$="2" THEN GO TO 700
  610 FOR x=0 TO 1: LET m$(a+x)=m$(a+x,3 TO )+m$(a+x): NEXT x
  620 IF a=f1 THEN LET f2=f2-2
  630 IF a=b1 THEN LET b2=b2-2
  640 GO TO 800
  700 FOR x=0 TO 1: LET m$(a+x)=m$(a+x,29 TO )+m$(a+x): NEXT x
  710 IF a=f1 THEN LET f2=f2+2
  720 IF a=b1 THEN LET b2=b2+2
  800 FOR x=0 TO 1: PRINT AT a+x,1;m$(a+x): NEXT x
  810 LET f2=f2+(30 AND f2=-1)-(30 AND f2=31)
  820 LET b2=b2+(30 AND b2=-1)-(30 AND b2=31)
  900 GO TO 100
 1000 REM ** LOSE A LIFE **
 1010 FOR x=7 TO 0 STEP -.2: PRINT AT a,b; INK x;"\l\m";AT a+1,b;"\n\o": NEXT x
 1020 FOR x=50 TO 10 STEP -1: BEEP .01,x: BEEP .01,x+3: NEXT x
 1030 LET l$=l$(2 TO ): IF l$=" " THEN GO TO 2000
 1040 IF m$(a,b)="\h" OR m$(a,b)=" " THEN GO SUB 4500: LET a=y: LET b=z: LET aa=a: LET bb=b: IF m$(a,b)=" " THEN GO TO 1040
 1050 IF t=0 THEN LET bo=bo+1: LET t=-1: LET b1=t: LET b2=t
 1060 GO SUB 4060: GO TO 170
 2000 REM * GAME OVER *
 2010 PRINT AT 9,0; PAPER 1; FLASH 1;"<<<<<< G A M E    O V E R >>>>>><<<<<<<<< AGAIN (Y/N)?>>>>>>>>>>"
 2020 IF sc>hi THEN LET hi=sc: GO SUB 4070
 2030 FOR x=1 TO 50: BEEP .004,x
 2040 IF INKEY$="y" OR INKEY$="Y" THEN GO TO 20
 2050 IF INKEY$="n" OR INKEY$="N" THEN STOP 
 2060 NEXT x: GO TO 2030
 3000 REM * NEXT LEVEL *
 3010 LET l=l+(l<9): LET x$="SUPER BONUS =-= SUPER BONUS =-= "
 3020 FOR x=1 TO 50: PRINT AT 21,0; INK 6; FLASH 1;x$: BEEP .01,x: LET x$=x$(2 TO )+x$
 3030 LET x$=x$( TO 32): NEXT x
 3040 LET sc=sc+55*l: GO TO 70
 4000 REM * SET UP SCREEN *
 4010 PAPER 0: INK 5: BORDER 0: BRIGHT 1: CLS 
 4020 DIM m$(20,30): DIM m(l*3+3)
 4030 FOR x=1 TO 20 STEP 2: LET m$(x)="\::\a\::\a\::\a\::\a\::\a\::\a\::\a\::\a\::\a\::\a\::\a\::\a\::\a\::\a\::\a": LET m$(x+1)="\b\c\b\c\b\c\b\c\b\c\b\c\b\c\b\c\b\c\b\c\b\c\b\c\b\c\b\c\b\c": NEXT x
 4040 FOR x=1 TO l*3+3: GO SUB 4500: LET m$(y,z TO z+1)="\h\i": LET m$(y+1,z TO z+1)="\j\k"
 4050 LET m(x)=y: NEXT x
 4060 FOR x=1 TO 20: PRINT AT x,1; INK 5-(2 AND (x=d OR x=d+1));m$(x): NEXT x
 4070 PRINT AT 0,0; BRIGHT 0; INK 7;"SCORE:";sc;TAB 11;"HIGH:";hi;TAB 21;l$;TAB 25;"LEVEL:";l
 4500 REM * RANDOM POSITION *
 4510 LET y=INT (RND*10)*2+1
 4520 LET z=INT (RND*15)*2++1
 4530 IF m$(y,z)="\h" OR y=19 AND z=15 THEN GO TO 4510
 4540 RETURN 
 5000 REM * U.D.G. DATA *
 5010 FOR x=0 TO 127: READ y: POKE USR "a"+x,y: NEXT x: RETURN 
 5020 DATA 248,244,242,242,242,242,242,242,255,255,255,255,64,32
 5030 DATA 31,0,242,242,242,242,10,6,254,0,0,0,15,63,127,127,127
 5040 DATA 127,60,60,24,248,216,216,216,216,127,112,64,0,0,0,0,0
 5050 DATA 216,248,24,24,24,24,24,0,96,231,255,63,57,57,63,30
 5060 DATA 12,206,254,248,56,56,248,240,30,15,28,60,247,227,96,0
 5070 DATA 240,224,112,120,222,142,12,0,7,7,63,15,31,61,57,63,192
 5080 DATA 192,248,224,240,120,56,248,123,92,79,39,12,88,112,0
 5090 DATA 188,116,228,200,96,52,28,0,60,255,90,126,165,24,36,66
 6000 CLS : PRINT "BLOCK BUSTER"'"------------"''"Use the cursor keys to steer theman around the screen. If you gooff one edge of the screen you  will appear on the opposite side"
 6010 PRINT '"You must defuse the time bombs  before they detonate, but you   may only do this once they are  activated."
 6020 PRINT '"You may not move onto an empty  space, or onto an unactivated   bomb."
 6030 PRINT '"The ROW of blocks you are on canbe shifted left or right using  keys '1' and '2'. This may help you to reach a bomb, etc."
 6040 PRINT '"Bonus points are given for      reaching a flag."
 6050 PRINT ''"\::\a : A safe block"'"\b\c"''"\d\e : A flag"'"\f\g"''"\h\i :An unactivated bomb"'"\j\k"''"\l\m :Your man"'"\n\o"'''"Good luck..."''''"PRESS ANY KEY"
 6060 IF INKEY$="" THEN GO TO 6060
 6070 RETURN 
 9998 STOP 
 9999 SAVE "BlokBustr" LINE 1

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

People

No people associated with this content.

Scroll to Top