Tunnels & Traps: A Tiny BASIC Game

Back in 1980, I created a computer game for my TRS-80 Pocket Computer, inspired by Tunnels & Trolls. Given the passing of Rick Loomis, I thought I would dig it out.

As with T&T, I had attributes: ST (Strength), DX (Dexterity), and CN (Constitution, used for hit points). But I skipped LK (Luck) and CH (Charisma), and I used D&D’s WS (Wisdom) instead of T&T’s IQ (Intelligence). Like T&T, and unlike D&D, attributes could increase: in my case, quickly, after every monster, rolling three dice and if the total exceeds an attribute you can increase that attribute; if it doesn’t exceed any attribute, take that amount in gold instead. Powerful characters would gain wealth, while weaker characters would gain power. Unlike in T&T, attributes couldn’t increase past 18, though.

Combat was different. As with T&T, monsters had one number (this MR was based on how deep and how far you were in the dungeon), which was used for all their attributes (CN=3*MR, ST=3*MR, etc.). But missile attacks (F for FIRE, using DX plus a bonus for level deep) lost effectiveness after the first round, melee attacks (A for ATTACK, using ST) quickly lost effectiveness during an encounter, and spell attacks (C for CAST SPELL, using WS) gradually lost effectiveness. If you rolled under your attribute score (on 3 dice), you doubled your result, so better attributes minimized your downside. Your result was compared to the monster’s, and the difference was the damage and determined who it was dealt to (a difference of 0 harmed no one). After killing a monster, you could heal up to the monster’s level, providing the incentive for attacking tougher monsters.

Saving rolls were different too: they were done on three dice instead of two, with no cascading rolls in case of doubles. You had to roll under your attribute to succeed. A wisdom saving roll is needed when looking for traps, and a dexterity saving roll when jumping away from triggered traps or for parrying an attack when fleeing a monster.

A last shout-out to T&T: the troll was the toughest monster!

The Pocket Computer would only display 24 characters at a time. After a PRINT statement, you’d hit a key to see the next PRINT statement.

Screencap from a YouTube video

The Pocket Computer had a Tiny BASIC that lacked the ELSE statement (a common omission), but also RND, DATA, READ, and RESTORE. However, unlike TRS-80 Level I BASIC (my first programming language), which only had two string variables (up to 16 characters each), the PC-1 had 26 string variables of 7 characters each – sort of. They were actually an overlay over the 26 numeric variables: if you used A$, you couldn’t use A (numeric variable), and so forth. Also the array A() would alias these variables: A(1) was the same as A, A(2) was B, etc.

The PC-1 only had 1,425 bytes of RAM available for programming, and I must have hit that limit because my TRS-80 Level II BASIC listing had longer text descriptions and an additional command.

The goal is to collect the most gold. Commands:

  • Attack with a sword (uses ST)
  • Cast spell (uses WS)
  • Fire missile (uses DX)
  • Traps? (detects a trap in an empty room using WS)
  • Inventory (shows your attributes)
  • North
  • South
  • East
  • West
  • Up
  • Down

You can play Tunnels & Traps with Joshua Bell’s great Applesoft BASIC emulator. Copy and paste the following code, modified a bit to work there:

1 DIM A(26):GOTO 7
3 D=0:FOR I=1TO 3:R=23*R:R=R-32767*INT(R/32767):D=D+R-6*INT(R/6)+1:NEXT :RETURN 
5 PRINT "ST"; S; " DX"; F; " WS"; W; " CN"; C; " GD"; G:RETURN 
6 GOTO 30
7 PRINT "TUNNELS & TRAPS":INPUT "EXPLORE TUNNEL #?";N:R=N
8 GOSUB 3:S=D:GOSUB 3:F=D:GOSUB 3:W=D:GOSUB 3:C=D:G=0:GOSUB 5
9 PRINT:INPUT "COMMAND?";A$:GOSUB 3:GOSUB 10:GOTO 9
10 IF A$="A" THEN T=S-E:PRINT "SWORD";:GOTO 76
12 IF A$="C" THEN T=W-E/3:PRINT "SPELL";:GOTO 76
13 IF A$="D" THEN Z=Z-1:GOTO 6
14 IF A$="E" THEN X=X+1:GOTO 6
15 IF A$="F" THEN T=(F+L)*(E=0):PRINT "ARROW";:GOTO 76
18 IF A$="I" GOTO 5
20 IF A$="N" THEN Y=Y-1:GOTO 6
22 IF A$="S" THEN Y=Y+1:GOTO 6
23 IF A$="T" THEN T=W:GOTO 62
24 IF A$="U" THEN Z=Z+1:GOTO 6
26 IF A$="W" THEN X=X-1:GOTO 6
28 PRINT "NOR SOU EAST WEST UP DN":PRINT "ATTK CAST FIRE INV TRPS?":RETURN
30 IF M<0THEN GOSUB 62
31 IF M>0THEN A$="X":PRINT "PARRY";:T=W:GOSUB 80
32 L=INT((ABS(X)+ABS(Y)+3*ABS(Z))/3)+1:M=0:GOSUB 3:IF D>7 THEN M=L:REM
38 IF D<5 THEN PRINT "TRAP";:T=F:M=-L
40 IF M=0 THEN PRINT "NOTHING";
41 IF D<7 THEN M=-L
42 IF M=1 THEN PRINT "IMP";
44 IF M=2 THEN PRINT "KOBOLD";
46 IF M=3 THEN PRINT "GOBLIN";
48 IF M=4 THEN PRINT "HOBGOBLIN";
50 IF M=5 THEN PRINT "ORC";
52 IF M=6 THEN PRINT "HALFORC";
54 IF M=7 THEN PRINT "OGRE";
56 IF M>7 THEN PRINT "TROLL";
58 PRINT " HERE!"
60 IF D>4 THEN RETURN 
62 GOSUB 3:IF (D<T)AND(M<0) THEN PRINT "YOU JUST ESCAPE A TRAP!":M=0:RETURN
64 IF M>=0 THEN PRINT "NO TRAP DETECTED.":RETURN 
66 GOSUB 3:IF A$="T"THEN D=INT(D/2)
68 IF D>L THEN D=L
70 IF D>C THEN D=C-1
72 C=C-D:PRINT "A TRAP CAUSED ";D;" DAMAGE.":RETURN 
76 E=E+1:IF M<=0 THEN PRINT "S ARE USELESS HERE!":RETURN 
78 GOSUB 3:IF D<T THEN D=D+D
80 P=D:GOSUB 3:IF D<L*2 THEN D=D+D
82 IF P>=D AND A$<>"X" THEN M=M-(P-D)/3:PRINT " HIT FOR ";P-D;" DAMAGE."
83 IF P>=D AND A$="X" THEN PRINT " SUCCEEDED."
84 IF P<D THEN C=C+P-D:PRINT " MISSED.":PRINT "YOU TOOK ";D-P;" DAMAGE."
86 IF C<1 THEN PRINT "YOU DIED IN TUNNEL ";N;"!":GOSUB 5:PRINT:END 
88 IF M>0 THEN RETURN 
90 PRINT "YOU KILLED IT!":M=0:E=0
91 GOSUB 3:PRINT "ROLL OF ";D;": ";:IF C<L THEN C=L:IF C>18 THEN C=18
92 IF (D<=S)AND(D<=F)AND(D<=W)THEN G=G+D:PRINT "GOLD!":GOTO 5
93 T=23:A(6)=F:A(19)=S:A(23)=W
94 IF (S>=F)AND(S>=W)THEN T=19:U=23:V=6:IF (F>=W)THEN U=6: V=23
95 IF (F>S)AND(F>=W)THEN T=6:U=23:V=19:IF (S>=W)THEN U=19: V=23
96 IF (W>S)AND(W>F)THEN T=23:U=6:V=19:IF (S>=F)THEN U=19: V=6
97 IF D>A(T)THEN GOTO 195
98 IF D>A(U)THEN T=U:GOTO 195
99 T=V
195 IF (T=6)THEN F=F+1:PRINT "DEXTERITY!":GOTO 5
196 IF (T=19)THEN S=S+1:PRINT "STRENGTH!":GOTO 5
197 IF (T=23)THEN W=W+1:PRINT "WISDOM!":GOTO 5
198 GOTO 5

And below is the PC-1 source code. You’ll have to forgive the lack of comments. There wasn’t sufficient memory to have any! And it is spaghetti code, inspired by assembly language: the common subroutines had one-digit line numbers that jumped down because every byte counted, and RETURN took less space than GOTO 9. The whole thing later got much further developed in the comparative luxury of 16KB RAM on the TRS-80 Model I Level 2. But that’s a post for another day.

  1:GOTO 7
  3:D=0:FOR I=1TO 3:R=23*R:R=R-32767*INT(R/32767):D=D+INT(R/6)+1:NEXT :RETURN 
  5:PRINT "ST";S;" DX";F;" WS";W;" CN";C;" GD";G:RETURN 
  6:GOTO 30
  7:INPUT "TUNNEL #?";N:R=N
  8:GOSUB 3:S=D:GOSUB 3:F=D:GOSUB 3:W=D:GOSUB 3:C=D:GOSUB 5
  9:INPUT A$:GOSUB 3:GOSUB 10:GOTO 9
 10:IF A$="A"THEN T=S-E:PRINT "SWORD";:GOTO 76
 12:IF A$="C"THEN T=W-E/2:PRINT "SPELL";:GOTO 76
 13:IF A$="D"THEN Z=Z-1:GOTO 6
 14:IF A$="E"THEN X=X+1:GOTO 6
 15:IF A$="F"THEN T=F*(E=0):PRINT "ARROW";:GOTO 76
 20:IF A$="N"THEN Y=Y-1:GOTO 6
 22:IF A$="S"THEN Y=Y+1:GOTO 6
 23:IF A$="T"THEN T=W:GOTO 62
 24:IF A$="U"THEN Z=Z+1:GOTO 6
 26:IF A$="W"THEN X=X-1:GOTO 6
 28:RETURN 
 30:IF M<0GOSUB 62
 31:IF M>0THEN A$="X":PRINT "PARRY";:T=F:GOSUB 80
 32:L=INT((ABS X+ABS Y+ABS Z)/3):M=0:GOSUB 3:IF D>8THEN M=L
 38:IF D<5THEN PRINT "TRAP";:T=F:M=-L
 40:IF M=0THEN PRINT "NOTHING";
 41:IF D<7THEN M=-L
 42:IF M=1PRINT "IMP";
 44:IF M=2PRINT "KOBOLD";
 46:IF M=3PRINT "GOBLIN";
 48:IF M=4PRINT "HOBGOBLIN";
 50:IF M=5PRINT "ORC";
 52:IF M=6PRINT "HALFORC";
 54:IF M=7PRINT "OGRE";
 56:IF M>7PRINT "TROLL";
 58:PRINT " HERE!"
 60:IF D>4 RETURN 
 62:GOSUB 3:IF (D<T)*(M<0)PRINT "YOU ESCAPE TRAP!":M=0:RETURN 
 64:IF M>=0PRINT "NOT FOUND":RETURN 
 66:GOSUB 3:IF A$="T"THEN D=INT(D/2)
 68:IF D>LTHEN D=L
 70:IF D>CTHEN D=C-1
 72:C=C-D:PRINT "TRAP HIT FOR ";D;".":RETURN 
 75:E=E+1:IF M<=0PRINT "S USELESS HERE!":RETURN 
 77:GOSUB 3:IF D<TTHEN D=D+D
 79:P=D:GOSUB 3:IF D<L*2THEN D=D+D
 81:IF (P>=D)*(A$<>"X")THEN M=M-(P-D)/3:PRINT " HIT FOR ";P-D;"."
 83:IF (P>=D)*(A$="X")PRINT "!"
 84:IF P<DTHEN C=C+P-D:PRINT " MISSED":PRINT "YOU TOOK ";D-P;" HITS."
 86:IF C<1PRINT "YOU DIED!":GOSUB 5:END 
 88:IF M>0RETURN 
 90:PRINT "YOU SLEW IT!":M=0:E=0
 91:GOSUB 3:PRINT "ROLL OF ";D;":";:IF C<LTHEN C=L:IF C>18THEN C=18
 92:IF (D<=S)*(D<=F)*(D<=W)THEN G=G+D:PRINT "GD!":GOTO 5
 93:IF (S>=F)*(S>=W)THEN T=19:U=23:V=6:IF (F>=W)THEN U=6:V=23
 94:IF (F>S)*(F>=W)THEN T=6:U=23:V=19:IF (S>=W)THEN U=19:V=23
 95:IF (W>S)*(W>F)THEN T=23:U=6:V=19:IF (S>=F)THEN U=19:V=6
 96:IF D>A(T)GOTO 99
 97:IF D>A(U)THEN T=U:GOTO 99
 98:T=V
 99:A(T)=A(T)+1:IF (T=6)PRINT "DX!"
100:IF (T=19)PRINT "ST!"
101:IF (T=23)PRINT "WS!"
102:GOTO 5