“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 pair | Role |
|---|---|
\:: / \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 literal1is almost certainly meant to be the level variablel, meaning bombs always have a timer of 46 regardless of level. This appears to be a typographical error. - Duplicate
+operator: Line 4520 readsLET 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. The1*is redundant (again, likely meant to bel*15for 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
xfrom 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
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.
