Caverns is a text-based maze adventure game in which the player navigates a 416-cell grid (stored as a single string) searching for the Power-Sword, treasure, and the exit while fighting randomly selected monsters. The map is represented as a flat string `L$` built by concatenating sub-strings `Z$` through `K$`, with cell contents encoded as single characters (e.g., `CHR$ 128` for walls, `CHR$ 0` for open passages, and letters for special locations). Combat uses a percentage-based random system where the player’s survival chance (`PER`) starts at 70 and increases by 5 with each victory, and holding the Power-Sword raises the chance to 95. The game supports saving progress to tape mid-session, and the final boss is a dragon that can only be fought if the player has obtained the Power-Sword.
Program Analysis
Program Structure
The program is organized into a main game loop beginning around line 570, with subroutines handling movement, combat, and display. Lines 20–55 initialize game state and build the map; lines 240–500 display the introduction and instructions; lines 570–1270 form the core movement/input loop; lines 1270–3490 handle cell-event dispatch; lines 5000–5210 handle generic monster combat; lines 5250–7000 handle the dragon endgame; and lines 8000–8180 provide an alternative title-screen entry point. Line 9000 is an isolated SAVE line that serves as a tape-save entry.
Map Representation
The entire cavern is stored as a single string L$ of 416 characters, constructed at line 41 by concatenating twelve sub-strings (Z$ through K$, bookended by two Z$ copies). This string acts as a flat 2D grid with a row width of 32 characters, so moving north/south adjusts the position pointer P by ±32, and moving east/west adjusts it by ±1. Cell contents are encoded as single characters:
CHR$ 128— wall (originally"X"in the source data, converted at lines 53–54)CHR$ 0— open passage (originally".")" "— empty traversable space where items can be placed- Various letters (
A–O,S, etc.) — special locations and items
Lines 52–55 scan the whole map string and convert "X" to CHR$ 128 and "." to CHR$ 0, likely because these two characters need a representation that avoids clashing with keyboard input characters.
Cell Event Dispatch
After every move, lines 1270–1440 check L$(P) against a table of known cell types and GOTO the corresponding handler. The mapping is:
| Cell Value | Event | Handler Line |
|---|---|---|
"A" | Pit trap | 1450 |
"S" | Power-Sword found | 2370 |
"B" | Gas room | 1690 |
"C" | $50,000 treasure | 1820 |
"D" | Secret passage | 1910 |
"E" | Unseen force (moves player) | 1990 |
"F" | Thief (halves treasure) | 2100 |
"H" | Cave-in | 2420 |
"I" | $150,000 treasure | 2510 |
"J" | Monster encounter | 2550 |
"K" | Teleport to entrance | 2600 |
"L" | $500,000 treasure | 2730 |
"M" | Dragon / exit | 5250 |
"O" | Victory (reached exit) | 2830 |
CHR$ 128 | Wall — can’t go this way | 2870 |
CHR$ 0 | Open passage | 3390 |
Combat System
Generic monster combat is handled by the subroutine at line 5000. A random monster name is selected using the string-AND idiom (line 5010), and a random number F (1–100) is compared to PER (starting at 70). If F < PER the player wins; otherwise they die. Each victory raises PER by 5 (capped at 99), creating a leveling system. Holding the Power-Sword (S=1) overrides PER to 95 for all non-dragon fights.
The dragon fight (lines 6000–7000) is separate: without the Power-Sword the player dies immediately. With the sword, the combat probability is recalculated as 50 + INT(PER/2), giving a harder fight than ordinary monsters.
Random Item Placement
At lines 60–120 the player start position and 10 item positions are chosen randomly from empty cells (" ") in L$. Items 1–4 are monsters ("J"), items 5–6 are cave-ins ("C" — noting this is a naming collision; the dispatch table at line 1290 sends "C" to the $50,000 treasure handler, not cave-in), items 7–8 are teleporters ("K"), and items 9–10 are thieves ("F"). The sword start position is hard-coded at L$(192) (line 46) and the exit at L$(257) (line 45).
Key BASIC Idioms
- String-AND selection: Lines 110 and 5010 use
("STRING" AND condition)to select one of several string values based on a boolean condition, a common ZX81/TS1000 technique for avoiding IF chains. - SCROLL-based display: Rather than CLS, the game uses repeated
SCROLLcalls (subroutine at 3500 issues three SCROLLs) to push old text off screen, keeping the display tidy within the limited screen area. - Flat string as 2D array: Using a single string as a 2D map with arithmetic indexing is a well-known memory-saving idiom, avoiding the need for a two-dimensional array.
- PAUSE 40000 at line 370 serves as a long wait for keypress (effectively indefinite on this hardware).
Bugs and Anomalies
- Line 1450 is missing: The dispatch at line 1270 sends cell
"A"to line 1450, but the pit handler code starts at line 1460. The GOTO 1450 will find line 1460 as the next executable line, so this works correctly in practice. - Line 1690 is missing: Similarly, cell
"B"(gas room) dispatches to line 1690, but the handler begins at line 1700. Same next-line behavior applies. - Lines 2470–2480 (cave-in handler):
LET L=L$(126)=CHR$ 128uses chained assignment syntax that setsL$(126)toCHR$ 128and assigns the boolean result (0 or 1) to a numeric variableL. This is intentional for the side effect of modifying the map, but the variableLis unused. - Line 1530:
LET T=T-Tis a roundabout way of writingLET T=0; it zeroes the treasure counter after falling in a pit. - Line 5080:
LET PER=(PER+5)+(PER<99)— the boolean(PER<99)adds 1 if PER is below 99, so the total increment is 6 when PER < 99 and 5 otherwise. This means PER can reach 100, bypassing the intended 99 cap. - Lines 2551 and 2560: The monster encounter handler at line 2550 dispatches to line 2551 (GOSUB 5000) rather than the label line 2550 itself, and line 2565 checks
IF D THENbutDis never initialized before first use, relying on the default value of 0. - Line 2410 returns to line 570 after finding the sword, which resets the facing direction display but does not re-enter the map at a new position — this is correct behavior.
- Lines 8000–8180 form an alternative entry point that is never called from within the main game flow; they appear to be an earlier version of the intro screen, reachable only by direct RUN or line-number entry.
Content
Image Gallery
Source Code
0# COPYRIGHT 1983 ORIGINAL PROGRAMS
1 REM ********CAVERNS********
2 REM ****BY [W][E][S][L][E][Y]█[W][E][E][K][S]****
3 REM AND STEVE
4 REM COPYRIGHT 1983
5 REM ORIGINAL PROGRAMS
20 LET PER=70
30 LET S=0
35 LET T=0
40 FAST
41 LET L$=Z$+A$+B$+C$+D$+E$+F$+G$+H$+I$+J$+K$+Z$
45 LET L$(257)="O"
46 LET L$(192)="I"
52 FOR N=1 TO 416
53 IF L$(N)="X" THEN LET L$(N)=CHR$ 128
54 IF L$(N)="." THEN LET L$(N)=CHR$ 0
55 NEXT N
60 LET I=INT (RND*416+1)
65 IF L$(I)<>" " THEN GOTO 60
70 LET L$(I)="S"
80 FOR I=1 TO 10
90 LET J=INT (RND*416+1)
100 IF L$(J)<>" " THEN GOTO 90
110 LET L$(J)=("J" AND I<=4)+("C" AND (I=5 OR I=6))+("K" AND (I=7 OR I=8))+("F" AND (I=9 OR I=10))
120 NEXT I
240 CLS
250 PRINT TAB 6;"WELCOME TO CAVERNS"
260 PRINT TAB 4;"THE ULTIMATE ADVENTURE"
270 PRINT ,," YOU MUST TRY TO FIND THE EXIT"
280 PRINT "OUT OF THIS DREADFUL PLACE."
290 PRINT "BUT, BE CAREFUL, MONSTERS LURK"
300 PRINT "EVERYWHERE, AND THE EXIT IS PRO-"
310 PRINT "TECTED BY A BEAST TOO POWERFUL"
320 PRINT "FOR A MAN TO KILL WITHOUT THE"
330 PRINT "POWER-SWORD, SO IT MUST BE FOUND"
340 PRINT "BEFORE YOU LEAVE. THE SWORD WILL"
345 PRINT "ALSO INCREASE YOUR CHANCES OF"
347 PRINT "KILLING THE OTHER MONSTERS."
350 PRINT " THERE IS TREASURE EVERYWHERE"
352 PRINT "SO YOU COULD END UP A VERY RICH"
354 PRINT "PERSON."
360 PRINT ,,TAB 10;"GOOD LUCK"
365 PRINT ,," TOUCH ANY KEY TO START"
370 PAUSE 40000
380 CLS
410 PRINT ,,"USE THESE KEYS TO MOVE:"
420 PRINT ,,"(F)=FORWARD"
430 PRINT "(B)=BACKWARD"
440 PRINT "(L)=LEFT"
450 PRINT "(R)=RIGHT"
460 PRINT "(S)=SAVE GAME"
470 PRINT ,,"WHEN YOU SAVE THE GAME, YOU"
480 PRINT "START IN THE SAME POSITION YOU"
490 PRINT "LEFT THE NEXT TIME YOU PLAY."
500 PAUSE 1000
530 PRINT ,,"YOU HAVE ENTERED THE CAVERNS..."
540 LET P$="W"
550 LET M=0
555 LET S=0
560 LET P=192
570 SCROLL
580 PRINT "YOU ARE FACING:";P$
590 SCROLL
600 SLOW
610 LET M=M+1
620 SCROLL
630 PRINT "WHICH DIRECTION?"
635 SCROLL
640 PRINT "(F)-(B)-(L)-(R)-(S)"
645 GOSUB 3500
650 LET M$=INKEY$
655 IF M$="" THEN GOTO 650
680 IF M$<>"F" THEN GOTO 730
690 IF P$="N" THEN GOSUB 1190
700 IF P$="E" THEN GOSUB 1230
710 IF P$="S" THEN GOSUB 1210
720 IF P$="W" THEN GOSUB 1250
725 GOTO 1270
730 IF M$<>"B" THEN GOTO 780
740 IF P$="N" THEN GOSUB 1210
750 IF P$="E" THEN GOSUB 1250
760 IF P$="S" THEN GOSUB 1190
770 IF P$="W" THEN GOSUB 1230
775 GOTO 1270
780 IF M$<>"L" THEN GOTO 950
790 IF P$<>"N" THEN GOTO 830
800 LET P$="W"
810 GOSUB 1250
815 GOTO 1270
830 IF P$<>"E" THEN GOTO 870
840 LET P$="N"
850 GOSUB 1190
855 GOTO 1270
870 IF P$<>"S" THEN GOTO 910
880 LET P$="E"
890 GOSUB 1230
895 GOTO 1270
910 IF P$<>"W" THEN GOTO 950
920 LET P$="S"
930 GOSUB 1210
940 GOTO 1270
950 IF M$<>"R" THEN GOTO 1120
960 IF P$<>"N" THEN GOTO 1000
970 LET P$="E"
980 GOSUB 1230
990 GOTO 1270
1000 IF P$<>"E" THEN GOTO 1040
1010 LET P$="S"
1020 GOSUB 1210
1030 GOTO 1270
1040 IF P$<>"S" THEN GOTO 1080
1050 LET P$="W"
1060 GOSUB 1250
1070 GOTO 1270
1080 IF P$<>"W" THEN GOTO 1180
1090 LET P$="N"
1100 GOSUB 1190
1110 GOTO 1270
1120 SCROLL
1130 PRINT "PUT TAPE IN RECORDER, PRESS RE-"
1131 SCROLL
1132 PRINT "CORD AND THEN PRESS ENTER (N/L)."
1140 GOSUB 3500
1160 IF INKEY$ <>CHR$ 118 THEN GOTO 1160
1170 SAVE "CAVERNS"
1180 GOTO 570
1190 LET P=P-32
1200 RETURN
1210 LET P=P+32
1220 RETURN
1230 LET P=P+1
1240 RETURN
1250 LET P=P-1
1260 RETURN
1270 IF L$(P)="A" THEN GOTO 1450
1275 IF L$(P)="S" THEN GOTO 2370
1280 IF L$(P)="B" THEN GOTO 1690
1290 IF L$(P)="C" THEN GOTO 1820
1300 IF L$(P)="D" THEN GOTO 1910
1310 IF L$(P)="E" THEN GOTO 1990
1320 IF L$(P)="F" THEN GOTO 2100
1340 IF L$(P)="H" THEN GOTO 2420
1350 IF L$(P)="I" THEN GOTO 2510
1360 IF L$(P)="J" THEN GOTO 2550
1370 IF L$(P)="K" THEN GOTO 2600
1380 IF L$(P)="O" THEN GOTO 2830
1390 IF L$(P)="L" THEN GOTO 2730
1395 IF L$(P)="M" THEN GOTO 5250
1420 IF L$(P)=CHR$ 128 THEN GOTO 2870
1430 IF L$(P)=CHR$ 0 THEN GOTO 3390
1440 STOP
1460 SCROLL
1470 PRINT "YOU HAVE FALLEN INTO A PIT."
1480 SCROLL
1490 PRINT "IN GETTING OUT YOU LOST YOUR"
1500 SCROLL
1510 PRINT "TREASURE."
1520 SCROLL
1530 LET T=T-T
1540 GOSUB 3500
1680 GOTO 570
1700 SCROLL
1710 PRINT "YOU HAVE ENTERED A ROOM OF GAS."
1715 SCROLL
1720 PRINT "WHEN THE GAS CLEARS YOUR POSI-"
1730 SCROLL
1740 PRINT "TION IN THE CAVERN IS DIFFERENT."
1770 GOSUB 3500
1800 LET P=P+32
1810 GOTO 1270
1830 SCROLL
1840 PRINT "YOU FOUND A TREASURE OF "
1850 SCROLL
1860 PRINT "** ** $50,000 ** **"
1870 LET T=T+50000
1875 LET P=P+32
1890 GOSUB 3500
1900 GOTO 1270
1920 SCROLL
1930 PRINT "YOU HAVE FOUND A SECRET PASSAGE"
1940 SCROLL
1950 PRINT "THAT GETS YOU CLOSER TO THE"
1951 SCROLL
1952 PRINT "EXIT."
1960 SCROLL
1970 LET P=P-7
1980 GOTO 1270
2000 SCROLL
2010 PRINT "AN UNSEEN FORCE HAS MOVED YOU"
2015 SCROLL
2020 PRINT "CLOSER TO THE ENTRANCE."
2030 GOSUB 3500
2080 LET P=P+5
2090 GOTO 1270
2110 SCROLL
2120 PRINT "A THIEF STOLE HALF OF YOUR"
2130 SCROLL
2140 PRINT "TREASURE. YOU NOW HAVE :"
2150 SCROLL
2160 LET T=T-(INT (T/2))
2170 PRINT "$";T
2180 GOSUB 3500
2210 GOTO 3390
2370 SCROLL
2380 PRINT " YOU HAVE FOUND THE POWER-SWORD."
2385 LET S=1
2390 GOSUB 3500
2410 GOTO 570
2430 SCROLL
2440 PRINT "THIS PART HAS CAVED IN."
2450 SCROLL
2470 LET L=L$(126)=CHR$ 128
2480 LET L=L$(217)=CHR$ 128
2490 SCROLL
2500 GOTO 2870
2520 GOSUB 3500
2530 PRINT "YOU FOUND A TREASURE WORTH :"
2535 SCROLL
2540 LET L$(P)=" "
2541 PRINT "** ** $150,000 ** **"
2542 LET T=T+150000
2543 LET P=P-1
2544 GOSUB 3500
2545 GOTO 1270
2551 GOSUB 5000
2560 SCROLL
2565 IF D THEN PRINT "TOO BAD. YOU LOSE. HA HA HA HA."
2566 IF NOT D THEN GOTO 570
2570 GOSUB 3500
2571 PRINT "PLAY AGAIN?"
2572 IF INKEY$ ="" THEN GOTO 2572
2573 IF INKEY$ ="Y" THEN GOTO 1
2580 STOP
2610 SCROLL
2620 PRINT "YOU WERE TELEPORTED TO THE"
2630 SCROLL
2640 PRINT "ENTRANCE, TOO BAD."
2650 SCROLL
2660 LET P=160
2670 LET P$="W"
2720 GOTO 570
2740 SCROLL
2750 PRINT "YOU HAVE FOUND A TREASURE WORTH:"
2760 SCROLL
2770 PRINT "** ** $500,000 ** **"
2780 GOSUB 3500
2800 LET T=T+500000
2805 LET L$(P)=" "
2810 LET P=P+1
2820 GOTO 1270
2830 CLS
2840 PRINT AT 10,5;"YOU MADE IT, AND YOU HAVE : "
2850 PRINT TAB 10;"$";T
2865 GOTO 2571
2880 SCROLL
2890 PRINT "YOU CAN""T GO THIS WAY."
2900 IF M$<>"F" THEN GOTO 2950
2910 IF P$="N" THEN GOSUB 1210
2920 IF P$="E" THEN GOSUB 1250
2930 IF P$="S" THEN GOSUB 1190
2940 IF P$="W" THEN GOSUB 1230
2950 IF M$<>"B" THEN GOTO 3000
2960 IF P$="N" THEN GOSUB 1190
2970 IF P$="E" THEN GOSUB 1230
2980 IF P$="S" THEN GOSUB 1210
2990 IF P$="W" THEN GOSUB 1250
3000 IF M$<>"L" THEN GOTO 3170
3010 IF P$<>"N" THEN GOTO 3050
3020 GOSUB 1210
3030 LET P$="E"
3040 GOTO 3340
3050 IF P$<>"E" THEN GOTO 3090
3060 GOSUB 1250
3070 LET P$="S"
3080 GOTO 3340
3090 IF P$<>"S" THEN GOTO 3130
3100 LET P$="W"
3110 GOSUB 1190
3120 GOTO 3340
3130 IF P$<>"W" THEN GOTO 3170
3140 LET P$="N"
3150 GOSUB 1230
3160 GOTO 3340
3170 IF M$<>"R" THEN GOTO 3340
3180 IF P$<>"N" THEN GOTO 3220
3190 LET P$="W"
3200 GOSUB 1210
3210 GOTO 3340
3220 IF P$<>"E" THEN GOTO 3260
3230 LET P$="N"
3240 GOSUB 1250
3250 GOTO 3340
3260 IF P$<>"S" THEN GOTO 3300
3270 LET P$="E"
3280 GOSUB 1190
3290 GOTO 3340
3300 IF P$<>"W" THEN GOTO 3340
3310 LET P$="S"
3320 GOSUB 1230
3340 GOSUB 3500
3380 GOTO 570
3400 SCROLL
3410 PRINT "ALL IS CLEAR."
3420 GOSUB 3500
3440 PRINT "LAST MOVE : ";M$
3450 GOSUB 3500
3490 GOTO 570
3500 SCROLL
3505 SCROLL
3510 SCROLL
3515 RETURN
5000 LET M=INT (RND*5+1)
5010 LET M$=("WEREWOLF" AND M=1)+("VAMPIRE" AND M=2)+("MUMMY" AND M=3)+("ZOMBIE" AND M=4)+("GHOUL" AND M=5)
5020 SCROLL
5030 PRINT "YOU HAVE TO FIGHT A ";M$;"."
5040 SCROLL
5060 LET F=INT (RND*100+1)
5061 FOR I=1 TO 30
5062 NEXT I
5063 SCROLL
5065 SCROLL
5066 IF S THEN LET PER=95
5070 IF F<PER THEN PRINT "YOU KILLED IT."
5075 GOSUB 3500
5080 IF F<PER THEN LET PER=(PER+5)+(PER<99)
5085 IF S THEN LET PER=95
5090 IF F<PER THEN LET D=0
5100 IF F<PER THEN RETURN
5200 LET D=1
5210 RETURN
5250 SCROLL
5300 PRINT "THE EXIT AT LAST. HOWEVER, IT IS"
5310 SCROLL
5320 PRINT "GUARDED BY A DRAGON."
6000 IF NOT S THEN LET D=1
6005 IF NOT S THEN GOTO 6035
6010 LET PER=50+(INT (PER/2))
6020 LET F=INT (RND*100+1)
6030 IF PER>F THEN GOTO 7000
6035 SCROLL
6040 PRINT "THE DRAGON ATE YOU, TOO BAD."
6042 SCROLL
6045 PRINT "[H][A] HA [H][A] HA [H][A] HA [H][A] HA [H][A] HA [H][A]"
6050 GOTO 2571
7000 CLS
7010 PRINT TAB 8;"CONGRATULATIONS..."
7020 PRINT "YOU HAVE KILLED THE DRAGON."
7030 GOTO 2840
8000 PRINT TAB 10;"CAVERNS"
8010 PRINT ,," IN THIS FAST MOVING ADVENTURE"
8020 PRINT "GAME, YOU ARE IN A MAZE THAT IS"
8030 PRINT "FULL OF MONSTERS, PITS, STRANGE"
8040 PRINT "GAS, AND THE POWER-SWORD. ALL"
8050 PRINT "YOU KNOW OF THIS MAZE IS THAT IT"
8060 PRINT "IS DANGEROUS. AT FIRST IT IS"
8070 PRINT "DIFFICULT, BUT AS YOU FIGHT YOUR"
8080 PRINT "EXPERIENCE INCREASES MAKING IT"
8090 PRINT "EASIER. THE COMPUTER WILL TELL"
8100 PRINT "YOU THE DIRECTION YOU ARE FACING"
8110 PRINT "AND THE LAST MOVE YOU MADE. THE"
8120 PRINT "REST IS UP TO YOU. GOOD LUCK."
8130 PRINT ,," TOUCH ANY KEY TO START."
8150 IF INKEY$ ="" THEN GOTO 8150
8160 PRINT ,,TAB 8;"SETTING UP"
8170 PAUSE 150
8180 GOTO 1
9000 SAVE "CAVERN[S]"
9010 SLOW
9020 GOTO 8000
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.