--- title: "Buster-Bill’s Obsession" id: 55439 type: "computer_media" slug: "block-buster-2" url: "http://localhost/computer_media/block-buster-2/" markdown_url: "http://localhost/computer_media/block-buster-2.md" published_at: "2024-06-23T22:54:06+00:00" modified_at: "2026-03-30T21:29:18+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/06/SCR-20240623-jkmz.png" excerpt: "A grid-based puzzle-action game where you steer a character across scrolling block rows, defusing time bombs before they detonate — with nine escalating difficulty levels." 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/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_contents: - id: 55430 title: "Timex Sinclair Public Domain Library Tape 2004" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-2004/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/06/SCR-20240623-jkmz.png" media_type_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 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 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”. ## 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 ```