Caverns

Developer(s): Wesley Weeks
Date: 1983
Type: Program
Platform(s): TS 1000
Tags: Game

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 (AO, 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 ValueEventHandler Line
"A"Pit trap1450
"S"Power-Sword found2370
"B"Gas room1690
"C"$50,000 treasure1820
"D"Secret passage1910
"E"Unseen force (moves player)1990
"F"Thief (halves treasure)2100
"H"Cave-in2420
"I"$150,000 treasure2510
"J"Monster encounter2550
"K"Teleport to entrance2600
"L"$500,000 treasure2730
"M"Dragon / exit5250
"O"Victory (reached exit)2830
CHR$ 128Wall — can’t go this way2870
CHR$ 0Open passage3390

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 SCROLL calls (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$ 128 uses chained assignment syntax that sets L$(126) to CHR$ 128 and assigns the boolean result (0 or 1) to a numeric variable L. This is intentional for the side effect of modifying the map, but the variable L is unused.
  • Line 1530: LET T=T-T is a roundabout way of writing LET 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 THEN but D is 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

Appears On

Related Products

Related Articles

Related 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.

Scroll to Top