Goblins is a dungeon-exploration game in which the player collects eight treasures in sequence and delivers each to a green door while avoiding three pursuing goblins. The screen is constructed procedurally in the GO SUB 8000 routine, which draws a grid of ladder/platform characters and randomly removes some to create openings. All 15 user-defined graphics—including the player sprite, goblin, treasure icons, wall and floor tiles, and a dynamite symbol—are defined via POKE USR in GO SUB 9000 using DATA statements. The player has three lives, five sticks of dynamite (used to blast wall blocks adjacent to the player), and must collect treasures in strict order from the highest level downward. Attribute-color detection (ATTR) drives all collision logic, distinguishing walls (INK 5), the door (INK 32), treasure items (INK 7), and goblin sprites (INK 4) without any coordinate arrays for those objects.
Program Structure
The program is organized into clearly delineated GO SUB routines called from a main loop:
| Lines | Purpose |
|---|---|
| 9000–9060 | Define 15 UDGs via POKE USR and READ/DATA |
| 7000–7210 | Title screen, instructions, and controls display |
| 8000–8150 | Procedural screen construction (walls, floor, border, treasures) |
| 900–960 | Initialize goblin positions and player start |
| 100–310 | Main game loop: player input, goblin AI, collision detection |
| 1000–1060 | Treasure pickup logic |
| 2000–2040 | Deliver treasure to door |
| 3000–3040 | Lose a life |
| 3500–3540 | Descending BEEP sound effect |
| 4000–4070 | Dynamite use (four directional cases) |
| 4510–4570 | Explosion sound effect (ascending then descending) |
| 5000–5050 | End-of-game “another game?” prompt |
| 6000–6060 | Win sequence with flashing color cycling |
Screen Construction and Attribute-Based Collision
GO SUB 8000 builds the dungeon entirely with PRINT statements using UDG characters and specific INK colors. The routine draws a 2-column-stride grid of ladder pairs (UDG \a, INK 5) across rows 3–19, then randomly erases some with spaces to create gaps. Walls use a brick UDG and the border is drawn with a floor-tile UDG at full width.
All collision detection throughout the program is performed by reading the ATTR value at the relevant screen cell. Because each object type is given a unique INK or PAPER color combination, no separate coordinate arrays are needed for walls, doors, or treasure items:
ATTR = 5— wall/ladder (INK 5, blocks movement)ATTR = 7— treasure item (INK 7)ATTR = 4— goblin sprite (INK 4)ATTR = 6— player carrying treasure (INK 6)ATTR = 32— green door (INK 32? — actually used for door detection at line 170)ATTR = 134— player hits a specific combined attribute triggering trap/death sound
UDG Definitions
GO SUB 9000 reads 15 UDG definitions (labeled "A" through "O") from DATA at lines 9040–9050. Each is eight bytes POKEd via POKE USR a$+p,q. The UDGs include wall tiles, floor tiles, the player character, the goblin sprite, treasure icons (eight distinct symbols stored in j$), a dynamite symbol, and a door graphic. This is a standard and efficient technique for custom sprite work.
Player Movement and Input Handling
Lines 120–130 use compound Boolean arithmetic to update the player’s x and y coordinates in a single LET statement each. Each INKEY$ check is ANDed with boundary and ATTR conditions, so no IF statement is needed for movement itself:
- Line 120: horizontal movement, blocked by walls (ATTR 5) or treasures (ATTR 7) when not carrying
- Line 130: vertical movement, blocked only by walls (ATTR 5)
The previous position is saved in a, b (line 110), and only if the position changed is the old cell erased (line 150). This avoids unnecessary screen writes.
Goblin AI
Three goblins are tracked in arrays p(3) (column) and q(3) (row). Each frame (lines 190–300), each goblin:
- Erases its previous position
- Increments its column by +1 each tick (always moves right)
- Steps over wall cells by adding 1 again if ATTR = 7 (line 220 — note this checks INK 7, i.e., treasure, not wall; the wall check uses ATTR 5 for vertical movement only)
- Adjusts row toward player’s
yif the path isn’t blocked (lines 230–240) - Reverses horizontal direction if it hits an INK-4 cell (line 250, checking
ATTR = 4) - Bounces off screen edges (line 290)
Catching the player while the player carries treasure (ATTR = 6 at goblin position, line 260) triggers instant death via GO SUB 3500 and GO SUB 3000. Catching the player without treasure (ATTR at goblin matches player, line 160) triggers a life loss only.
Dynamite System
The player starts with five sticks of dynamite (d=5). GO SUB 4000 (line 4000) handles four cases based on player row and which adjacent cell is a wall (ATTR = 7): right-of-player-above, left-of-player-above, right-of-player-below, and left-of-player-below. Each successful use prints a FLASH INK 2 double-UDG explosion graphic, calls the sound routine (GO SUB 4500, lines 4510–4570), erases the graphic, decrements d, and erases a dynamite indicator from the status bar. When d=0, the word GONE is printed with FLASH.
Note that GO SUB 4500 is called but defined at line 4510 — there is no line 4500 — which works correctly because BASIC searches for the nearest line at or after the target.
Bugs and Anomalies
- Line 3010:
LET l=1-1always sets lives to zero rather than decrementing by one (LET l=l-1was intended). This means any goblin contact immediately depletes all lives. - Line 7030 is referenced by GO TO at line 7050 but does not exist in the listing; the loop presumably targets line 7040 or loops to the nearest available line.
- Line 7130:
PRINT 0;should almost certainly bePRINT #0;(print to lower screen); as written it prints a zero then continues with AT and attributes, producing a stray character. - Line 8140 contains two embedded
\{18}\{1}and\{16}\{0}sequences mid-string, which are AT and INK control codes embedded directly in the PRINT string — a valid but unusual technique to set attributes inline. - The
j$string (line 80) holds eight UDG characters for treasure display, but since UDGs are accessed by letter andj$is built as a string of UDG chars\gthrough\n, indexing withj$(t)in line 2010 correctly retrieves each treasure’s symbol in sequence. - Line 5040’s condition
z$ <>"y" OR z$ <>"Y" OR z$ <>"n" OR z$ <>"N"is always true (a string cannot equal all four values simultaneously), making the GO TO 5010 unconditional when neither y/Y nor n/N was entered — which is the intended behavior but written incorrectly using OR instead of AND.
Sound Effects
Three sound routines are used: a simple BEEP .005 scattered through screen construction for feedback during drawing; a descending chromatic glide in GO SUB 3500 (50 to 0 step −2) for death/transition effects; and an ascending-then-descending two-phase sweep in lines 4510–4570 for the dynamite explosion.
Content
Source Code
1 REM *********************** *Underlined characters* *are entered in * *GRAPHICS mode. * ***********************
10 PAPER 0:BORDER 0:INK 3:CLS
20 PRINT AT 11,5; PAPER 1; INK 7; FLASH 1;" Please wait a moment "
30 GO SUB 9000:GO SUB 7000
40 DIM p(3):DIM q(3)
60 GO SUB 8000:GO SUB 900
70 LET d=5:LET k=0:LET l=3:LET t=1
80 LET j$="\g\h\i\j\k\l\m\n"
100 REM Move man & goblins
110 LET a=x:LET b=y
120 LET x=x-(INKEY$="5" AND x>1 AND ATTR (y,x-1) <>5 AND ATTR (y,x-1) <>7)+(INKEY$="8" AND x<30 AND ATTR (y,x+1) <>5 AND ATTR (y,x+1) <>7)
130 LET y=y-(INKEY$="7" AND y>1 AND ATTR (y-1,x) <>5)+(INKEY$="6" AND y<19 AND ATTR (y+1,x) <>5)
140 IF INKEY$="0" THEN GO SUB 4000
150 IF a <>x OR b <>y THEN PRINT AT b,a;" "
160 IF ATTR (y,x+1)=7 AND NOT k OR ATTR (y,x-1)=7 AND NOT k THEN BEEP .1,30:GO SUB 1000
170 IF ATTR (y,x+1)=32 AND k THEN GO SUB 3500:GO SUB 2000
180 PRINT AT y,x; FLASH k; INK 6;"\c":BEEP .005,50
190 FOR z=1 TO 3
200 PRINT AT q(z),p(z);" "
210 LET p(z)=p(z)+1
220 IF ATTR (q(z),p(z))=7 THEN LET p(z)=p(z)+1
230 IF q(z)>y AND ATTR (q(z)-1,p(z)) <>5 THEN LET q(z)=q(z)-1
240 IF q(z)<y AND ATTR (q(z)+1,p(z)) <>5 THEN LET q(z)=q(z)+1
250 IF ATTR (q(z),p(z))=4 THEN LET p(z)=-p(z)
260 IF ATTR (q(z),p(z))=6 THEN GO SUB 3500:GO SUB 3000:GO TO 110
270 IF ATTR (q(z),p(z))=134 THEN GO SUB 3500: PRINT AT 21,28; INK 2;"\::\::": GO SUB 5000
280 PRINT AT q(z),p(z); INK 4;"\d"
290 IF p(z)=-1 OR p(z)=30 THEN LET p(z)=-p(z)
300 NEXT z
310 GO TO 110
900 REM Variables
910 FOR q=1 TO 3
920 LET p(q)=1
930 LET q(q)=12+q*2
940 NEXT q
950 LET x=30:LET y=2
960 RETURN
1000 REM Take treasure
1010 FOR c=1 TO 8
1020 IF t=c AND y=2+2*c THEN GO TO 1050
1030 NEXT c
1040 IF NOT k THEN GO SUB 4000: GO TO 1060
1050 LET k=1:PRINT AT y,x-1;" "; AT y,x+1;" "
1060 RETURN
2000 REM Treasure at door
2010 PRINT AT 0,8+(t*2); PAPER 2; INK 6;j$(t)
2020 LET k=0:LET t=t+1
2030 IF t=9 THEN GO TO 6000
2040 RETURN
3000 REM Loose life
3010 LET l=1-1:PRINT AT 21,28+1; INK 2;"\::"
3020 IF NOT k THEN PRINT AT q(z),p(z);" ":PRINT AT q(1),p(1);" ":PRINT AT q(2),p(2);" ":PRINT AT q(3),p(3);" ":GO SUB 900
3030 IF l=0 THEN PRINT AT 21,29 ; INK 2;"\::":GO SUB 3500:GO TO 5000
3040 RETURN
3500 REM Sound
3510 FOR s=50 TO 0 STEP -2
3520 BEEP .01,s
3530 NEXT s
3540 RETURN
4000 REM Use dynamite
4010 IF INKEY$ <>"0" OR d=0 THEN GO TO 4070
4020 IF INKEY$="0" AND y <>18 AND ATTR (y,x+1)=7 THEN PRINT AT y+1,x-2; FLASH 1; INK 2;"\a\a": GO SUB 4500:PRINT AT y+1,x-2; FLASH 0;" ": LET d=d-1:PRINT AT 21,10+d; INK 2;"\::"
4030 IF INKEY$="0" AND y <>18 AND ATTR (y,x-1)=7 THEN PRINT AT y+1,x+1; FLASH 1; INK 2;"\a\a": GO SUB 4500:PRINT AT y+1,x+1; FLASH 0;" ":LET d=d-1: PRINT AT 21,10+d; INK 2;"\::"
4040 IF INKEY$="0" AND y=18 AND ATTR (y,x+1)=7 THEN PRINT AT y-1 ,x-2; FLASH 1; INK 2;"\a\a": GO SUB 4500:PRINT AT y-1,x-2; FLASH 0;" ":LET d=d-1:PRINT AT 21,10+d; INK 2;"\::"
4050 IF INKEY$="0" AND y=18 AND ATTR (y,x-1)=7 THEN PRINT AT y- 1,x+1; FLASH 1; INK 2;"\a\a":GO SUB 4500:PRINT AT y-1,x+1; FLASH 0;" ":LET d=d-1:PRINT AT 21,10+d; INK 2;"\::"
4060 IF d=0 THEN PRINT AT 21,10 ; FLASH 1; PAPER 2; INK 6;"GONE'"
4070 RETURN
4510 FOR e=0 TO 50 STEP 6
4520 BEEP .005,e
4530 NEXT e
4540 FOR e=50 TO 20 STEP -1
4550 BEEP .005,e
4560 NEXT e
4570 RETURN
5000 REM Another Game?
5010 INPUT " Another Game? (y/n) "; LINE z$
5020 IF z$="y" OR z$="Y" THEN CLS :GO TO 40
5030 IF z$="n" OR z$="N" THEN CLS : GO TO 5050
5040 IF z$ <>"y" OR z$ <>"Y" OR z$ <>"n" OR z$ <>"N" THEN GO TO 5010
5050 PRINT '' INK RND*6; BRIGHT 1; TAB RND*15;"OK,BYE FOR NOW!": POKE 23692,255:GO TO 5050
6000 REM Win game
6010 CLS
6020 PRINT AT 10,10; BRIGHT 1; INK INT (RND*6)+1;"WELL DONE'' You have all the treasure"
6030 LET w= INT (RND*50)
6040 BEEP .01,w
6050 IF INKEY$="" THEN GO TO 6020
6060 STOP
7000 REM Game title
7010 PAPER 0:BORDER 0:CLS
7020 PRINT #0; AT 1,0; INK 7;" Press any key for instructions",
7040 PRINT AT 19,16; INK INT (RND*6)+1;" by Jerry Cohler":BEEP .01, INT (RND*50)
7050 IF INKEY$="" THEN GO TO 7030
7060 PAUSE 0:BEEP .1,30:CLS
7070 REM Instructions"
7080 PRINT AT 0,4; INK 4; BRIGHT 1;"I N S T R U C T I O N S"
7090 PRINT AT 2,0; INK 7;"Your quest is to retrieve all the treasure from the dungeon."
7100 PRINT AT 5,0; INK 7;"Treasure must be collected in order (highest level first) and taken to the green door."
7110 PRINT AT 9,0; INK 7;"Any item not due for collection will block your path (but not the goblins!!). However, if adjacent to such a treasure, you can use your dynamite to blast holesin the floor(or ceiling if on the bottom level)."
7120 PRINT AT 17,0; INK 7;"You have three lives - but if caught by a goblin while in possesion of treasure it's instant death'!"
7130 PRINT 0; AT 1,0; PAPER 6; INK 2; FLASH 1;" Press any keyfor controls..."
7140 PAUSE 0:BEEP .1,30:CLS
7150 REM Game controls
7160 PRINT AT 3,8; INK 4; BRIGHT 1;"C O N T R O L S"
7170 PRINT AT 7,1; INK 7;"5 6 7 8 0 left down up right dynamite"
7180 PRINT AT 13,6; FLASH 1; INK 6;"\c"; AT 13,8; FLASH 0; INK 7;"treasure collected"
7190 PRINT AT 21,3; PAPER 2; INK 6; FLASH 1;"Press any key to play..."
7200 PAUSE 0:CLS :BEEP .1,30
7210 RETURN
8000 REM Screen construction
8010 FOR a=1 TO 30 STEP 2:FOR b=3 TO 19 STEP 2
8020 PRINT AT b,a; INK 5;"\a\a":BEEP .005,(b+a)
8030 NEXT b:NEXT a
8040 FOR c=3 TO 17 STEP 2
8050 PRINT AT c,4+ INT (RND*5)*5;" "
8060 NEXT c
8070 PRINT AT 1,1; INK 5;"\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f\f"
8080 PRINT AT 20,0; PAPER 7; INK 3;"\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e\e"
8090 FOR d=1 TO 19
8100 PRINT AT d,0; PAPER 7; INK 3;"\e"; AT d,31; PAPER 7; INK 3;"\e":BEEP .005,(50-d)
8110 NEXT d
8120 PRINT AT 2,31; PAPER 4; INK 0;"\b"; AT 0,0; PAPER 2; INK 6;" TREASURE "
8130 PRINT AT 21,0; PAPER 2; INK 6;" DYNAMITE \o\o\o\o\o lives \c\c\c "
8140 PRINT AT 4,7; INK 7;"\g"; AT 6,22; INK 7;"\h"; AT 8,17; INK 7;"\i"; AT 10,12; INK 7;\{18}\{1}"\j"; AT 12,27; INK 7;"\k"; AT 14,7; INK 7;\{16}\{0}"\l"; AT 16,12; INK 7;"\m"; AT 18,22; INK 7;"\n"
8150 RETURN
9000 REM Graphics
9010 FOR n=1 TO 15:READ a$
9020 FOR p=0 TO 7:READ q:POKE USR a$+p,q
9030 NEXT p:NEXT n
9040 DATA "A",255,255,219,228,25,164,8,0,"B",255,129,189,189,129,161,129,129,"C",28,28,136,126,29,28,20,54,"D",57,57,17,255,185,57,170,238,"E",34,255,136,255,34,255,136,255,"F",255,255,255,60,24,126,90,24
9050 DATA "G",8,8,20,20,20,20,28,0,"H",8,20,42,69,42,20,8,0,"I",1,26,4,10,18,32,64,0,"J",16,84,56,254,56,84,16,0,"K",0,2,5,253,69,229,162,0,"L",16,68,40,130,56,186,56,0,"M",0,16,4,18,36,82,8,0,"N",24,189,153,90,126,90,126,0,"O",0,6,24,56,112,224,192,0
9060 RETURN
9990 CLS
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

