Holtzman and Kershenblatt’s Castlequest was inspired by Crowther and Woods’ Adventure, and has a lot in common with it:
- cave-based treasure hunt with fantasy themes
- magic words of teleportation
- the “master game” and Last Lousy Point
- lamp, key(s), food, bottle
- wandering random-chance-of-death monsters;
- two liquids to fill the bottle with
- long and short room descriptions;
- navigation by compass points, plus
- mazes of twisty passages
But since Holtzman had never seen Adventure’s code, some of the behind-the-scenes mechanisms are pretty different. Let’s take a look at a few key differences between Adventure’s code and Castlequest’s.
Grammar in Adventure
Adventure’s grammar consists of four kinds of words:
- Motion words, like
- Object words, like
- Action words, like
- Message words, like
I said motion words were like
SOUTH, but actually those boring compass directions arrived very
late in Adventure’s gestation. Adventure’s first motion words were
FOREST. So you could stand outside the building and say
ENTER to go inside, or
follow the road instead.
Each object word identifies a specific object in the game. For example, the word
LAMP identifies the lamp
(object number 2 in the game; number 1 is the keys).
Action words may be transitive (
FEED) or intransitive (
Message words, like
INFO, are handled essentially outside of the game proper; when the game sees
one of these words in the command, it simply prints that word’s associated hard-coded message and immediately
returns to prompting for input. Besides
INFO, other message words in Adventure include
DIG (“Digging without a shovel is quite impractical”),
SESAME (“Good try, but that is an old worn-out
Commands consist of one or two words, of any kinds. Motion words take precedence
over action or object words, so that when you say
JUMP FISSURE the object word
FISSURE is dropped, yielding
and when you say
WALK WEST the action word
WALK is dropped, yielding
WEST. (There are also a few special
cases, such as that when the first word is the motion word
ENTER, we’ll just look at the second word;
ENTER TUNNEL a synonym for
Word order isn’t important: Adventure treats
AXE GET and
GET AXE as equally grammatical.
This is convenient when the user types just
AXE: the game will ask “What do you want to do with the axe?”
and then if you answer
GET, the game will concatenate the two commands (
AXE GET) and ta-da, it’s a valid command!
But if you answer with some other command, such as
NORTH, the game will see
AXE NORTH and the motion word
NORTH will take precedence.
Grammar in Castlequest
Castlequest’s grammar consists of two kinds of words:
Verbs can be transitive (
FEED) or intransitive (
UP). Verbs 1 through 10 are the compass directions:
Compare to Adventure’s first action words:
WALK, and then
Commands consist of a verb, optionally followed by a noun.
The parser considers
AXE TAKE to be ungrammatical nonsense. In fact, it considers
AXE alone to be
ungrammatical nonsense, because the string of characters
AXE is not recognized as a verb. (The parser
never tries to parse your first word as a noun.)
So when you see Castlequest say “Do WHAT with the axe??”,
it’s because you said
FROTZ AXE or something — it recognizes
AXE because it’s in noun position,
but doesn’t recognize your first word as any known verb. And Castlequest is not
equipped to handle a clipped reply like
TAKE — it’ll parse that as a brand-new command, and come
back with “I don’t see that here” (where that refers to the non-existent noun in your one-word command —
indeed object zero isn’t here).
Castlequest has no distinction between action and message words. This makes it more expensive for Castlequest
to add words like
FUCK, but also makes them as customizable as “action” verbs like
UNTIE. For example,
SWIM prints “I hate to tell you this, but I can’t swim” in only four locations;
everywhere else gives a bland error message. And
FUCK gives two different messages, depending
on whether you used it transitively or intransitively.
By the way, in both Adventure and Castlequest, words can have multiple accepted spellings; for example,
W both mean motion word 44 in Adventure and both mean verb 7 in Castlequest.
These spellings can look arbitrary different to human eyes. For example, in Castlequest both
CHOP translate to verb 37; in Adventure both
HILL translate to motion word 2.
KILL synonymous; Castlequest makes them two separate verbs.
The travel table
Travel in Adventure
Adventure’s vocabulary has more motion words than any other kind. Getting around the game world is the main point of the parser engine, because it was (originally) the main point of Crowther’s game. The game world is a collection of rooms; you navigate between rooms using motion words. The fact that you can also get lamps, water plants, and so on is almost an afterthought grafted onto this navigation engine.
So Adventure has this thing called the “travel table.” It’s a massive data file encoded like this:
1 2 2 44 29 1 3 3 12 19 43 1 4 5 13 14 46 30 1 5 6 45 43 1 8 63 2 1 2 12 7 43 45 30 2 5 6 45 46
This says that to get from room 1 (end of road) to room 2 (hill in road), you can enter motion
word 2 (
HILL), 44 (
W), or 29 (
To get from room 1 to room 3 (inside building), you can use 3 (
ENTER), 12 (
HOUSE), 19 (
IN), or 43 (
Motion words 5, 13, 14, 46, 30 (
D) take you to room 4 (valley).
Motion words 6, 45, 43 (
E) take you to room 5 (forest).
Finally, motion word 63 (
DEPRESSION) takes you from room 1 to room 8 (outside grate).
The travel table continues with six ways to get from room 2 to room 1… and so on.
As a historical note, observe again that the compass directions were added late — they have pretty high numbers,
and they appear at the tails of these lines, which were probably accreted in chronological order. (Crowther decided
that the road led
WEST from the building before Woods decided that it should also lead
Notice the richness of this format. It’s easy to add a new motion word; by default it will do nothing in
every room, except those where you add it to the travel table. This allowed Crowther to add simple navigational
BUILDING to move back to the starting room from pretty much anywhere above ground, and
of course also “magic words” like
XYZZY. These shortcuts are useful in speed-running; why type
W W W W W
to get from below the grate to the top of small pit, when you could get there in one turn with
The travel table can also encode more complex behaviors:
19 35074 49 19 211032 49 19 74 66
The numbers over 1000 use an ad-hoc but powerful scheme to encode both probabilities and conditions
on certain motions. This snippet says: If you’re in room 19 (hall of mt king) and you use motion
word 49 (
SW), you have a 35% chance of reaching room 74 (secret E/W canyon). Otherwise,
if object 1011 (the snake) is also present here then you’ll reach room 32.
Otherwise, you’ll reach room 74 (secret E/W canyon), which by the way you can
invariably reach by using motion word 66 (
SECRET). (Notice that travel table entries “fall through”
in the same way as C switch cases.)
Meanwhile, room 32 is a special room with only one exit in the travel table:
32 19 1
Motion word 1 is a magic number that means “forced motion”; such exits are automatically taken after describing the room you’re in.
You are in the hall of the mountain king, with passages off in all directions. A huge green fierce snake bars the way! > WEST You can't get by the snake. You're in hall of mt king. A huge green fierce snake bars the way!
To the player, it looks like the game has rejected your attempt to move; but what’s actually happened at the mechanical level is that you successfully moved to a (lighted) room with the description “You can’t get by the snake,” and then immediately moved back to the Hall of the Mountain King following a forced motion.
Here’s another interesting use of the travel table: The vocabulary word
BACK is grammatically
a motion word, but it’s actually intercepted in the parser. When you say
BACK, the game
tries to move you back into the room you were in last turn, by substituting some other motion
BACK. It scans the travel table to find all the exits from your current room, and
sees if any of them lead into your previous room. To see this in action, go
S twice from
end of road to reach slit in streambed; then say
HOUSE to move back to end of road;
BACK. “You can’t get there from here,” because the direct connection between these
two rooms is one-way. Of course this mechanic can also be used to create “in-universe”
one-way connections as well, such as the steep incline north of the Giant Room.
Movement in Castlequest
Castlequest’s movement system is much simpler. The first ten verbs are the compass directions;
these are the primary way of navigating in Castlequest. All ten of those verbs are handled by
MOVE subroutine, which consults a hard-coded 10×100 array. (There are exactly 100 rooms
C N NE E SE S SW W NW UP DOWN DATA W1 /0, 0, 0, 0, 0, 0, 2, 0, 0,-29, 2 3, 0, 1, 0, 0, 0, -4, 0, 0, 3, 3 33, 0, 0, 0, 8, 0, 5, 0, -2, 0,
This says that from room 1 (the bedroom), you can exit to the west or down — no other directions!
The exit for “down” is strange; the negative number indicates that some
special handling is needed. So when you try to go
DOWN from the bedroom, the game sets
LROOM to 1 and
ROOM to 29, just as usual, but then checks a ton of special cases.
One of those cases is:
IF ((ROOM .NE. 29 .OR. LROOM .NE. 1) .AND. 2 (ROOM .NE. 1 .OR. LROOM .NE. 29)) GOTO 621 IF (ROPE .EQ. 2) GOTO 103 WRITE(6,1006) GOTO 106 106 ROOM = LROOM LROOM = II GOTO 25 1006 FORMAT('0 There is no way to go in that direction.')
So if you try to go
DOWN when global variable
ROPE isn’t set to
2 (which in-game means
the rope is dangling out the window), you’ll get a bland error message and your
will get reset to 1.
The main 10×100 array is supplemented by two more 100-element arrays,
which indicate whether the verbs
OUT work in the current room. These lookup
tables don’t map to destinations, but rather to verb numbers — comparable, but not equivalent,
to Adventure’s handling of the word
BACK. When you’re in the bedroom and you say
that gets remapped to verb 7 (
WEST). When you’re in the library,
IN gets remapped to verb 10
DOWN), which then triggers its own special-case handling.
An odd effect of this mechanism is that the “level designer” can remap
OUT to verbs
that aren’t directions at all. Holtzman did this exactly once — I guess just for the heck of it.
IN from room 61 maps to verb 19 (
You are in the blue room. The entire room is a deep shade of royal blue. Exits go north, south and east. > N You're in a maze of short and winding passages. > NE You're in a maze of winding, long passages. > IN You had better watch your mouth.
ENTER mapping for the current room doesn’t exist, typing
IN gives a bland error message.
But if the
LEAVE mapping for the current room doesn’t exist,
OUT is remapped to
Thus, if you go
OUT to leave a room, and then type
OUT again, you’ll generally find yourself
IN; this confused the heck out of me, before I looked at the code.
Finally, when you type
BACK (verb 40), the game tries to move you back into the room
you were in last turn. Unlike in Adventure, this does not consult any travel table:
always succeeds, unless some unusual event has changed the value in variable
For example, when you exit the mirror maze into a random room, you can always say
return to the maze, even when the maze isn’t ordinarily adjacent to your random room.
Verbs such as
CROSS are handled in the same way as any other non-motion verb:
with special-purpose codepaths. And
CLIMB is merely a synonym for
The wandering monsters
Adventure’s dwarves and pirate
Adventure has five dwarves that wander around the map and throw knives at you. Surprisingly,
a large subset of Adventure’s codebase is dedicated to simulating these little guys. Each
dwarf’s state is represented by a set of entries in three arrays (
Just like the player, each dwarf has a current location
DLOC and a previous location
Dwarves avoid doubling back on their own paths, but otherwise wander randomly through the cave,
respecting the travel table. Each time the player moves, the dwarves also move. Whenever a
dwarf winds up in the same room as the player, we set
DSEEN and the dwarf starts preferentially
following the player and throwing knives. Since there are five dwarves wandering randomly,
it’s quite possible for the player to see messages like
There are 4 threatening little dwarves in the room with you! 3 of them throw knives at you! 2 of them get you!
The travel table can mark certain paths, such as the troll bridge, as “forbidden to dwarves.”
There is a sixth dwarf — the pirate — who gets special handling once he enters your room, but otherwise wanders according to the travel table just like any other dwarf. When the pirate moves into your room but you have no treasure to steal, there’s a 20% chance that “There are faint rustling noises from the darkness behind you.”
When you spot the first dwarf (the one who throws the axe and runs away), the game randomly kills between 0 and 2 of the dwarves, leaving only 3 to 5 of them alive (plus the pirate). Once you kill a dwarf, it never comes back; so you should need to kill only at most 5 dwarves in any single game of Adventure.
By the way, Luckett and Pike’s Adventure II (taxonomized as LUPI0440) grants three of the dwarves the ability to pick up and carry a single item as they wander, and also sometimes randomly revives dead dwarves.
The dwarf-movement parts of Adventure are one of the coolest parts of the implementation — that’s probably why Crowther bothered to write all that code — but they’re also the part that tends not to survive porting to other game engines. PLAT0550 keeps track of between 4 and 8 dwarves, but they don’t “wander” so much as “teleport to the player” at random times, until they’re all dead. MALM0350 doesn’t even bother to track a population size; there’s just a flag to tell whether the (one) dwarf is in your room, and when you kill the dwarf, it merely turns off that flag for a while.
Castlequest’s werewolf and gnome
Castlequest uses the MALM0350 model for both of its wandering monsters. Every turn you’re
in the castle, there’s about a 4% chance that the werewolf will show up. When it does, it sets
WOLF flag, meaning that it’ll stick to you forever, or until you kill it. There’s no way
to shake it from your trail — not boating, not
POOF-ing, not climbing ropes.
Likewise, every turn you’re in the cave, there’s about a 3% chance that the gnome will show up
GNOME flag). The gnome’s description is an homage to Adventure —
There is an ugly little gnome in the room with you! He shoots a poisoned dart at you! IT GETS YOU!!
— which is also a hint to the seasoned adventurer that the proper way to deal with gnomes is
THROW AXE. This gives you a reason to tote the axe while in the cave, and thus to have
a good chance of discovering the secret of the master game. Neither the werewolf nor the
gnome ever perma-die.
Castlequest will sometimes print “I think I hear footsteps behind you” — with probability 0.8% every time you move — unrelated to anything else going on in the game. (Remember, the werewolf and gnome don’t “move” the way Adventure’s pirate does; they just teleport in randomly.)
Doors and windows
In Adventure, the
PROP array holds one integer for each object in the game, both movable and immovable.
This integer represents the “state” of the object. The default state is 0; some objects (such as the iron keys)
never leave that state. The lamp has two possible states:
PROP(LAMP)=0 when it’s off and
it’s on. The bear has four states: hungry, tame but chained, unchained, and dead. Each one has a different
These states are generally written via special handling in the code, but the travel table can encode reads against them. For example,
8 303009 3 19 30 8 593 3
indicates that typing any of
DOWN in room 8 (depression) will lead to room 9 (below grate)
PROP(3)=1 — that is, if object 3 (the grate) is in state 1 (open).
Likewise, the path north of the Giant Room is open only if
In Castlequest, items don’t have “props”; there is no general-purpose way for an item to have state,
and no general-purpose way to change the description of an item. Each stateful puzzle in the game is
represented in the code by a named global variable; for example, the integer
BAT may hold 0 (gone)
or 1 (present); the integer
ROPE may hold 0 (loose), 1 (tied to bed), 2 (out window), or 3 (tied to hook).
ROPE can also be -2 if it’s fallen out the window, but I’m not sure why that needed to be different
The general-purpose array in
DOOR(100) — one integer per room. The game uses this
array to track the state of at most one door per room.
-2 means “the door will
neither open nor close”;
-1 means “I see no door here”;
0 means locked,
1 means closed, and
DOOR(80) are all initially zero,
because these rooms (the dim corridor, the kitchen, the attic, and the wine cellar) all
contain locked doors. The code for locking, unlocking, opening, and closing these doors is
mostly generic. Movement through these doors, on the other hand, must be handled via special
cases (negative numbers) in the 10×100 travel array.
The library contains an initially open door, which can be locked, unlocked, opened, and closed
The same is true of the locked room (next to the dim corridor) and the brick wall (next to the kitchen).
Of course, the game doesn’t “know” that these rooms’
DOOR entries are supposed to represent
the two sides of the same actual door, so you might find that the door appears locked from one direction
and open from the other.
But since these rooms also aren’t hooked up to any special cases for movement, such a locked door
won’t impede your travel.
You are in the upstairs hallway, a long corridor with passages to the north, east, and west. Stairs lead up and down. > OPEN DOOR The door will neither open nor close. > EAST You are in the library. A copy of Shakespeare's "HAMLET" lies on the desk. > OPEN DOOR The door is already open. > CLOSE DOOR OK > LOCK DOOR The door is locked. > WEST You're in the upstairs hallway. > EAST You are in the library. > OPEN DOOR The door is locked.
The windows in the bedroom and smoking room are handled by separate global variables
respectively, and don’t interact with the
DOOR array at all. You might wonder why Castlequest
bothers to make a whole 100-element array just to deal with four special doors. I don’t know. But
the game does start off inside a house, where you might expect every room to have a door; maybe
it initially seemed like simulating doors was going to be a big part of the game.
In Adventure, the
PROP of an object tends to change its long description (when present in the current room),
but never changes its name-when-inventoried. Rather than having separate items for “empty bottle”
and “bottle of water,” Adventure simply has an item with name
Small bottle and another slightly special
item with name
Water in the bottle; when you say
FILL BOTTLE it places the latter in your
inventory right next to the former. For the bird, it cheats even more: the short name of the bird
Little bird in cage, because anytime it’s in your inventory it is in the cage by definition.
In Castlequest, objects tend not to change their long descriptions; the lamp keeps its same description
no matter whether it’s on or off. But items in your inventory can combine in interesting ad-hoc ways; for example,
if you are carrying both the gun and the bullet, and the global
GUN flag is true (meaning that
you’ve loaded the gun), then
INVENTORY will print
Bullet in gun
Silver bullet Old gun
The way it accomplishes this is… local hacks in the
The gun and bullet are items 20 and 2 respectively:
IF (.NOT. GUN) GOTO 120 ITEMS(20) = 0 ITEMS( 2) = 0 WRITE(6,2000) 'Bullet in gun'
The short description of the bottle in Castlequest is
Empty bottle; so, when you are also carrying
object 5 (
Blood in bottle), the inventory code will temporarily suppress the printing of object 18:
IF (BOTTLE) ITEMS(18) = 0 [...] DO 10 II=1,NITEMS IF (ITEMS(II) .EQ. -1) WRITE(6,2000) OBJ(II) IF (ITEMS(II) .EQ. -1) NUMB = NUMB + 1 10 CONTINUE IF (BOTTLE) ITEMS(18) = -1
Incidentally, both Adventure and Castlequest make the same arbitrary decision to track
“number of items carried by the player” separately from “location of each item.” In theory,
the number of items carried by the player should always be exactly the same as the count of items
-1. In practice, though, tracking these quantities individually means there’s no
single source of truth, and the two quantities can get out of sync with each other.
See “A bug in Adventure’s endgame” (2020-02-06).
I haven’t explicitly found such a bug in Castlequest yet, but I’m sure it’s only a matter
Castlequest’s combining-item hacks are pretty much only in the inventory code (except some paths related to the rope and grappling hook in the room-description code). If you’re carrying the loaded gun and you drop it, it magically unloads itself and you see the bullet and gun separately in the room description. The rope and grappling hook also tend to spontaneously disassemble; even the bottle of blood!
> INVENTORY You are carrying the following object: Blood in bottle > DROP BOTTLE OK > LOOK You are at the bottom of a towering spiral stairway. A low passage exits south. There is a small pool of blood here. An empty bottle is discarded nearby.
Oh, you expected a conclusion?
Well, it’s pretty neat how Castlequest invented its own solutions to certain problems without
seeing Adventure’s. In some cases the games demonstrate convergent evolution (for example,
how items in room number zero are “destroyed” and items in room number
-1 are “in inventory”);
in other cases they’re creatively divergent (such as Castlequest’s handling of
or Adventure’s simulationist handling of
Anyway, this probably concludes my series of Castlequest posts, at least for a little while. Revisit the first post here:
- “Castlequest exhumed!” (2021-03-09)