BLOKMAN is a maze-avoidance and bomb-defusal game in which the player navigates a two-character-wide sprite across a grid, using a joystick in the left port, collecting flags, avoiding skulls, and defusing TNT before a shrinking timer bar runs out. The program uses a block of UDG characters (lines 8–13) loaded via POKE into USR addresses at startup, defining the player, enemy, flag, bomb, and explosion sprites as 8×8 bitmaps read from DATA statements that use the literal variable `b` (value 0) and `-4` (252) as shorthand constants within the data. Joystick input is handled through the TS2068 STICK keyword, with keyboard equivalents mapped in the string `b$` at line 50. The timer is visualized as a row of colored block characters printed at row 20, shrinking one character per frame via the loop variable `j`. A two-stage level structure sends the player through five enemy screens before a bonus flag-catching mini-game, then repeats with a narrower timer bar; difficulty at startup is set by adjusting the initial timer width `i` based on a 0–9 player-chosen level.
Program Analysis
Program Structure
The program is organized into clearly separated functional regions, navigated by computed GO TO and GO SUB targets expressed as numeric literals and floating-point shorthand (e.g., GO SUB 8e3, GO TO 5e3, GO TO 6e3, GO TO 7e3). The major regions are:
- Lines 2–7: Initialization — UDG setup, variable reset, screen attributes, entry point.
- Lines 8–18: UDG loader subroutine — POKEs bitmap data into the UDG area.
- Lines 19–30: Grid drawing — draws a repeating diamond/tile pattern across the play field.
- Lines 40–300: Main game loop — places enemies and flags, runs the timer bar, handles movement, collision detection, and scoring.
- Lines 1000: Wraparound subroutine for player position.
- Lines 4000–4120: Stage 2 bonus mini-game — player must press a key when the man is over the flag.
- Lines 5000–5020: Time-expired handler (bomb explodes).
- Lines 6000–6040: TNT defused handler — awards bonus, advances level, calls stage 2.
- Lines 7000–7070: Life-loss handler — death animation, high-score check, restart logic.
- Lines 8000–8270: Title screen, instructions, and difficulty/level entry.
UDG Sprite Loading
Lines 8–18 form a subroutine that loads 20 UDG definitions (characters \a through \t, i.e., UDGs A–T) by POKEing 8 bytes each into the range USR "a" to USR "t"+7. The data is read with RESTORE 9 to reset the DATA pointer, then a single FOR/READ/POKE/NEXT loop fills all 160 bytes. This covers the player (4 UDGs in a 2×2 arrangement), flag, skull/enemy, bomb/TNT, and explosion sprites.
Within the DATA statements, the program exploits the fact that at the time of reading, the variable b holds the value 0 (it was used as a loop counter in line 8 and has just finished at 0 from the previous run, or is initialized to 0 at line 2). This allows b to serve as a compact zero literal inside DATA lines. Similarly, -4 appears as a shorthand for 252 (i.e., 256-4), which BASIC stores and READs as the negative value but which POKE interprets modulo 256.
Sprite Rendering
All in-game objects use 2×2 character (4-cell) UDG sprites, printed with two consecutive PRINT AT statements: the top row at AT r,s and the bottom row at AT r+1,s. Each sprite occupies UDGs arranged as quadrants: for example the player uses \a\b / \c\d, the enemy uses \e\g / \f\h, the flag uses \m\o / \n\p, and the TNT bomb uses \i\k / \j\l. The explosion sprite uses \q\s / \r\t.
Timer Bar
The countdown timer is rendered as a row of colored block characters (\::, the solid block UDG/graphic) at row 20, printed with INK 3. At line 115, the bar is initialized to width i (set from the difficulty level at line 8260 as 32-2*(VAL a$), ranging from 32 at easy to 14 at hardest). The outer loop at line 117 counts j down from i-1 to 0; at line 135 the leftmost cell is erased by printing a space at position j, giving a smooth left-to-right shrink. When the loop exhausts, control falls to line 165 and then jumps to 5e3 (line 5000), the explosion handler.
Movement and Collision Detection
Player position is stored in x (column, always even) and y (row, always even), reflecting the 2×2 sprite grid. Movement direction d is decoded from the action string b$="ASZXPLaszxpl" at line 50, where indices 1–6 map to shoot/left/right/up/down actions (the program uses 3=left, 4=right, 5=up, 6=down based on the arithmetic at lines 175–185). Joystick directions from STICK (1,1) are translated to the same letter values at lines 140–143.
After movement, the subroutine at line 1000 applies toroidal wraparound: if x reaches -2 it wraps to 30, and if it reaches 32 it wraps to 0; similarly for y between -2 and 20. Collision is detected at line 200 by reading the screen attribute at the new position with ATTR (y,x) and comparing to known attribute values:
| ATTR value | Meaning | Action |
|---|---|---|
| 54 | Wall/obstacle (INK 6, PAPER 6, BRIGHT 1) | Movement reversed |
| 42 | Flag (INK 2, PAPER 5) | +140 points, sound effect |
| >63 | Skull/enemy (BRIGHT or FLASH set) | Life lost (go to 6000) |
| 48 | TNT/bomb (FLASH 1, INK 4, PAPER 0 → 4+48=52? checked carefully) | Defuse sequence (go to 7000) |
Note: The ATTR value 48 matches INK 0, PAPER 6 (no BRIGHT/FLASH), which corresponds to the background grid squares — the bomb check at line 230 may actually detect background cells rather than the TNT sprite whose ATTR would be BRIGHT 1 + FLASH 1 + INK 4 + PAPER 0 = 192+128+4 = 324 mod 256. This could be a latent bug in collision detection for the bomb object.
Joystick Support
The STICK (1,1) keyword is used at lines 140–143 to read a joystick port, with bitmask values 4=down, 8=up, 1=right, 2=left mapped to the corresponding keyboard letter variables. Keyboard input is handled by scanning INKEY$ against the string b$ in the loop at lines 150–160.
Stage 2 Bonus Mini-Game
After completing 5 screens (loop at lines 85–6030 with FOR t=1 TO 5), the program calls the subroutine at line 4000. This displays a row of background tiles with a randomly placed flag, then animates the player sprite walking across all grid positions. The player must press any key when the man sprite is over the flag column (x=b). The bonus is 4000-(50*y), rewarding faster reactions (lower y means higher row, reached sooner). Success detection uses the idiom FOR a=n TO 1: IF INKEY$<>"" — since n=0, this loop body executes once per iteration and exits via GO TO 4100 on a keypress, or falls through the outer x/y loops on no input.
Sound Effects
Movement sounds at line 250 use a compact single BEEP expression: BEEP .01,(12 AND d=6)+(d<>5)+(5 AND d=3)+(17 AND d=4), exploiting the fact that Boolean expressions evaluate to 1 (true) or 0 (false), so each directional condition contributes a different pitch offset. The death sequence (line 7000) uses a flashing INVERSE animation loop before showing an explosion sprite. The level-complete/defuse tune at lines 7015 plays notes encoded in the parallel strings x$ and y$ initialized at line 5.
Notable Techniques
bused as a zero literal in DATA statements, saving bytes over writing0repeatedly.- Floating-point line number targets (
1e3,4e3,5e3,6e3,7e3,8e3) compress jump targets to fewer characters in the source. - Boolean arithmetic for direction-dependent pitch in a single
BEEPcall. - Screen attribute reading via
ATTRfor collision detection, avoiding a separate sprite-tracking array. - Toroidal wraparound encoded as a single-line subroutine at line 1000 using compound Boolean arithmetic.
- Difficulty level mapped linearly:
i=32-2*(VAL a$)gives timer widths 32 (level 0) down to 14 (level 9). PRINT #1; FLASH 1;"Press any key to begin"at line 8200 prints to the lower screen (stream 1 = bottom two lines), keeping the instruction text visible without scrolling.
Content
Source Code
2 LET n=0: GO SUB 8
3 GO SUB 8e3: LET hs=n
4 LET lv=3: LET l=1: LET sc=n
5 LET x$="11114331101": LET y$="32132121216"
6 BORDER 4: INK 1: PAPER 6: BRIGHT n: FLASH n: OVER n: INVERSE n: CLS
7 GO TO 19
8 RESTORE 9: FOR a=USR "a" TO USR "t"+7: READ b: POKE a,b: NEXT a
9 DATA 15,79,63,9,11,30,60,63,240,242,-4,144,176,120,60,-4,63,63,b,47,6,b,30,62,-4,b,b,244,96,96,120,124
10 DATA 7,31,b,57,b,63,29,7,103,242,-4,31,7,-1,-4,96,224,248,b,156,b,-4,184,224,230,175,63,248,224,127,63,6
11 DATA 0,1,3,15,63,119,99,247,244,245,b,117,127,63,15,3,192,128,192,240,-4,238,198,239,47,175,175,174,-1,-4,240,192
12 DATA n,3,14,62,126,62,14,3,n,b,b,b,b,b,b,b,124,-4,60,-4,60,-4,b,b,124,12,b,b,b,b,b,b
13 DATA 1,3,15,31,63,b,127,99,109,99,103,107,109,127,b,b,128,192,240,248,-4,-4,-2,70,86,70,94,b,b,-2,b,b
17 DATA b,b,b,b,b,b,b,b
18 RETURN
19 FOR a=n TO 240 STEP 16: FOR b=173 TO 15 STEP -16: PLOT a,b
20 DRAW n,-13: DRAW 13,n: DRAW 1,1: DRAW -13,n: DRAW n,13: DRAW 1,1: DRAW 13,n: DRAW n,-13
30 NEXT b: NEXT a
40 LET x=16: LET y=10
50 LET b$="ASZXPLaszxpl"
60 FOR a=1 TO l*4+5
65 LET p=2*INT (RND*10): LET r=2*INT (RND*10): LET q=2*INT (RND*15): LET s=2*INT (RND*15)
67 IF p=r AND q=s THEN GO TO 65
70 PRINT AT r,s; INK 2; PAPER 5;"\m\o";AT r+1,s;"\n\p"
75 PRINT AT p,q; INK 0; PAPER 6;"\e\g";AT p+1,q;"\f\h"
80 NEXT a
85 FOR t=1 TO 5
90 LET a=2*INT (RND*10): LET b=2*INT (RND*16)
100 IF a=y AND b=x THEN GO TO 90
110 PRINT AT a,b; BRIGHT 1; FLASH 1; INK 4; PAPER n;"\i\k";AT a+1,b;"\j\l"
115 PRINT AT 20,n; INK 3;"\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::"( TO i)
116 PAUSE 100
117 FOR j=i-1 TO n STEP -1
120 PRINT AT y,x; INK 1; PAPER 6;"\a\b";AT y+1,x;"\c\d"
130 PRINT AT 21,n;"SCORE=";sc;" ";AT 21,13;"HIGH=";hs;AT 21,25;"LIVES=";lv
135 PRINT AT 20,j;" "
140 IF STICK (1,1)=4 THEN LET a$="Z"
141 IF STICK (1,1)=8 THEN LET a$="X"
142 IF STICK (1,1)=1 THEN LET a$="P"
143 IF STICK (1,1)=2 THEN LET a$="L"
150 FOR c=1 TO 12
155 IF a$=b$(c) THEN LET d=c-(6 AND c>6): LET a$="k": GO TO 170
160 NEXT c
165 NEXT j: GO TO 5e3
170 IF d>2 THEN PRINT AT y,x; INK 6;" ";AT y+1,x;" "
175 LET x=x+2*(d=4)-2*(d=3): LET y=y+2*(d=6)-2*(d=5)
185 IF d<=2 THEN GO TO 5e3
190 GO SUB 1e3
200 LET a=ATTR (y,x)
205 IF a=54 THEN LET x=x+2*(d=3)-2*(d=4): LET y=y+2*(d=5)-2*(d=6)
207 GO SUB 1e3
210 IF a=42 THEN BEEP .005,30: BEEP .005,25: LET sc=sc+140
220 IF a>63 THEN GO TO 6e3
230 IF a=48 THEN GO TO 7e3
240 LET sc=sc+10
250 BEEP .01,(12 AND d=6)+(d<>5)+(5 AND d=3)+(17 AND d=4)
300 NEXT j: GO TO 5e3
1000 LET x=x+(32 AND x=-2)-(32 AND x=32): LET y=y+(20 AND y=-2)-(20 AND y=20): RETURN
4000 CLS : PRINT AT 20,0; INK 0; PAPER 6;"\e\g\e\g\e\g\e\g\e\g\e\g\e\g\e\g\e\g\e\g\e\g\e\g\e\g\e\g\e\g\e\g\f\h\f\h\f\h\f\h\f\h\f\h\f\h\f\h\f\h\f\h\f\h\f\h\f\h\f\h\f\h\f\h"
4005 LET b=2*INT (RND*15): PRINT INK 2; PAPER 5;AT 20,b;"\m\o";AT 21,b;"\n\p"
4010 PRINT AT 10,1;"Press any key when man appears ` over the flag"
4020 FOR a=1 TO 250: NEXT a
4030 PRINT AT 10,n,,,,
4040 FOR y=n TO 18 STEP 2: FOR x=n TO 30 STEP 2
4050 PRINT AT y,x;"\a\b";AT y+1,x;"\c\d"
4060 FOR a=n TO 1: IF INKEY$<>"" THEN GO TO 4100
4070 NEXT a
4080 PRINT AT y,x;" ";AT y+1,x;" ": NEXT x: NEXT y
4083 PRINT AT 10,n; FLASH 1;" FAILED! FAILED! FAILED! FAILED!"
4085 FOR a=255 TO n STEP -5: BEEP .01,a/10: OUT 254,a: NEXT a
4087 FOR a=1 TO 50: NEXT a
4090 RETURN
4100 PRINT AT y,x;" ";AT y+1,x;" ": FOR a=y+1 TO 20: PRINT AT a,x;"\a\b";AT a+1,x;"\c\d": PRINT AT a-1,x;" ": BEEP .01,a: NEXT a
4110 IF x=b THEN LET sc=sc+4e3-(50*y): PRINT AT 10,n; FLASH 1;" BONUS!! BONUS!! BONUS!! BONUS!!": FOR b=1 TO 5: FOR a=30 TO 15 STEP -1: BEEP .005,a: NEXT a: NEXT b: FOR a=1 TO 50: NEXT a: RETURN
4120 GO TO 4083
5000 PRINT AT 20,n;" ": FOR a=n TO 255 STEP 5: BEEP .01,a/10: OUT 254,a: NEXT a
5020 GO TO 7020
6000 PRINT INK n;AT y,x;"\e\g";AT y+1,x;"\f\h"
6005 FOR b=1 TO 5: FOR a=30 TO 15 STEP -1: BEEP .005,a: NEXT a: NEXT b
6010 LET sc=sc+1e3
6025 LET x=x+2-(4 AND x=30)
6027 PAUSE 50
6030 NEXT t: LET i=i-(i>14): LET l=l+1
6035 GO SUB 4e3
6040 GO TO 5
7000 FOR a=1 TO 50: LET b=(a/2=INT (a/2)): PRINT INVERSE b; OVER 1;AT y,x;" ";AT y+1,x;" ": NEXT a
7010 PRINT AT y,x; INK 7; PAPER 0;"\q\s";AT y+1,x;"\r\t"
7015 FOR a=1 TO 11: BEEP VAL (y$(a))/4,VAL (x$(a))-1: NEXT a
7020 LET lv=lv-1: IF lv THEN GO TO 5
7030 IF sc>hs THEN PRINT AT 21,17;hs;" ": LET hs=sc: PRINT AT 19,n; FLASH 1; INVERSE 1;" NEW HIGH SCORE!! "
7040 PRINT AT 20,n; FLASH 1;" Press any key to play again "
7050 IF INKEY$<>"" THEN GO TO 7050
7060 IF INKEY$="" THEN GO TO 7060
7070 GO SUB 8230: GO TO 3
8000 BORDER n: PAPER n: BRIGHT n: INVERSE n: OVER n: FLASH n: INK 7: CLS
8010 PRINT TAB 10; INK 2; PAPER 5; FLASH 1;"\:: \:: \:: \:: \::"
8020 PRINT TAB 10; INK 2; PAPER 5; FLASH n;"BLOKMAN"; PAPER 5; INK 2; FLASH 1;" "
8030 PRINT TAB 10; INK 2; PAPER 5; FLASH 1;"\:: \:: \:: \:: \::"
8040 PRINT " The object of this game is to defuse the TNT before the linearscale at the bottom indicates timeup and the bomb explodes. Ifthis happens you will lose one of your three lives."
8050 PRINT " After 5 screens of avoiding skulls,gaining points by landingon flags and defusing bombs, stage 2 is reached:"
8060 PRINT " You must press a key when the man is over the flag for a bonusof up to 4000-the quicker you are the greater the bonus!"
8070 PRINT " Stage 1 then begins again but now with less time!"
8080 PRINT "Good Luck!!!"
8200 PRINT #1; FLASH 1;"Press any key to begin"
8210 IF INKEY$<>"" THEN GO TO 8210
8220 IF INKEY$="" THEN GO TO 8220
8230 CLS
8240 PRINT AT 5,5; FLASH 1;"ENTER LEVEL (0 TO 9)"'' FLASH N;" 0 = EASY 9=HARD"
8250 LET a$=INKEY$: IF a$<"0" OR a$>"9" THEN GO TO 8250
8260 LET i=32-2*(VAL a$)
8270 RETURN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

