Compute!'s Guide to Adventure Games Hat

How They Work

This chapter discusses the way adventure programs actually do their work. The discussion here will cover practically everything that could go into a full-blown professional adventure. But don't be intimidated by it. Writing something in BASIC that handles a reasonable number of commands and provides treasure-hunting fun isn't nearly as onerous as writing the equivalent of Zork.

But the same basic ideas are involved. If you go through this chapter to get an idea of what's involved in what the pros write, you'll get a feel for how to scale the concepts down to a programming project of a few weekends. Chapter 10 deals specifically with that sort of project, but getting a broader picture first is always helpful.

All adventure programs, simple or complex, have certain basic features. A program need not be formally broken down according to this list of features, nor do the names that I am using here represent an industry standard. This breakdown is simply intended to show what functions the program must accomplish. These features are as follows:

  • The command parser interprets the player's commands.
  • Output routines tell the player what is going on.
  • Action routines cause things to happen in the game world as a direct result of the player's commands.
  • Automatic routines cause things to happen because of the passage of time or because of some event independent of the player's actions

These pieces of the program depend on an assortment of data structures The major ones are listed below:

  • Vocabulary lists give the words in the program's vocabulary, grouped according to parts of speech.
  • The internal map itemizes locations that your character can reach and the paths that allow access from one location to another.
  • Object descriptors itemize various tools, treasures, and other objects available to you. The descriptors indicate where the objects are on the internal map, and they specify any distinctive characteristics the objects may have.
  • The character descriptor holds information about your character (such as location, inventory, and state of health).

The Parser

Any program that accepts free-format input has to have a command parser. To parse a command means to break it down into its word components and identify their syntactic relationship.

Before going into the details of the parser, it's helpful to review a little English grammar. A sentence has two parts: a subject and a predicate. In a simple sentence like "I go," I is the subject and go is the predicate. In an imperative sentence, which expresses a command, the subject is omitted. The sentence "Go!" is made up only of a predicate.

Not surprisingly, commands in an adventure program are imperative sentences. This allows us to greatly simplify grammatical structure and means that the program has to analyze only the predicate.

For the purposes of a game, the predicate can be considered to have three parts: the verb, the direct object, and the indirect object (or prepositional phrase). Take the sentence, "Give me your gun." Give is the verb; gun is the direct object (the thing being given), and me is the indirect object (the recipient of the action).

You can also express the indirect object as a prepositional phrase; the preceding example could have been expressed as "Give your gun to me." The preposition to relates me to give.

An object (direct or indirect) or a prepositional phrase consists of a noun, possibly modified by one or more adjectives. An adjective describes the thing named by the noun, and - what is important for adventures - it can specify which object you are talking about. If you say, "Give me the smoking gun," then you are referring to one gun in particular (and not the cold gun that might also be nearby).

There can be more than one object. Multiple objects can be listed explicitly, one at a time; for instance, "Take the bell, the book, and the candle." The word all or every can imply multiple objects, as in "Take all the gems" or "Take everything."

Just as a noun can be modified by adjectives, a verb can be modified by adverbs If you're afraid of being overheard, you might say (or whisper), "Give me the gun quietly." In that case, quietly is the adverb modifying give.

Beyond this basic structure, the only other grammatical feature that an adventurer might encounter is a noun of address. This is just a noun added at the front to indicate whom you're talking to; for instance, "Robot, fix the nuclear reactor."

This is enough grammar to get us where we want to go. You won't be needing strange tenses, and you can ignore such niceties as subordinate clauses. You don't have to care whether there's a way to cleanly split an infinitive, or whether it's bad to end a sentence with a preposition. If a parser can handle the grammar described so far, it can do a very impressive job.

A parser operating on the command BURN RED BOOK, for example, would do the work necessary to determine that BURN is a verb, BOOK is the object of the verb, and RED is an adjective modifying BOOK. It is also responsible for recognizing meaningless input so that the program can complain to the player.

The parser takes the words of the command and converts them to units of internal storage called tokens. The tokens are passed to the action dispatcher, which calls appropriate action routines, depending on what tokens it sees. The parser might produce fewer tokens than the number of words it sees, since it can throw words out or combine two words (for instance, PICK UP) into one token.

The command parser is one of the most important parts of an adventure program, since you can get frustrated very quickly if you can't tell the program what you want to do. The power of a command parser depends upon the size of its vocabulary, the number of letters it uses per word, and the complexity of the syntax it can handle.

The simplest sort of command parser has a list of verbs and a list of objects in its vocabulary. It simply checks to see if the first word is a verb and the second word is an object, converts them to token form, and then calls the appropriate action routine. In fact, some programs dispense with syntax checking altogether; in Pyramid 2000, for example, the verb and the object can occur anywhere in the command, together with any number of noise words. Thus, you can say TAKE BOX or TAKE THE BOX or BOX THE TAKE with the same results.

Programs recognize a certain number of letters per word and usually just disregard the rest. Thus, if a particular program recognizes five-letter words, then INVENTORY, INVEN, and INVENTION will all be equivalent. Truncating the words used keeps the size of the program down, since a smaller number of bytes is necessary for every word in the vocabulary. But if words are too short - and those with as few as three letters are not uncommon - the parser can misinterpret them, often with amusing or frustrating results. For instance, you could drown if the program thought you were trying to SWing an object when you were really trying to SWim.

Beyond the simple verb-object parser, it's possible to add complexity a little at a time. A simple enhancement, found in recent versions of Adventure, is using multiple objects with certain verbs. This is particularly useful with the TAKE and DROP verbs, since you will often want to pick up or drop several items at a time. This is just a matter of the parser recognizing the conjunction and and expecting the word following it to be another object, then constructing a list of tokens to match the objects.

Other additions in recent versions of Adventure are the uses of prepositional phrases and indirect objects. These allow you to specify the object you want to use for an action (FIGHT DEMON WITH PITCHFORK) or what your action will affect (GIVE HAY TO HORSE).

The use of indirect objects and prepositional phrases begins to involve some sophisticated command handling, since prepositions are among the trickiest elements of the language. Consider the commands FEED SEED TO BIRD, FEED BIRD WITH SEED, and FEED BIRD SEED. In the first two cases, the parser has to recognize that the first noun is the direct object and that the second one (the one after the preposition) is the indirect object. It also has to record the preposition; otherwise the action routines won't be able to tell whether you want the bird to eat the seed or the seed to devour the bird.

For the third case, the parser has to use the rule that when a verb is followed by two nouns, the first one is the indirect object and the second one is the direct object. It may also create a token for WITH as the implied preposition.

Even trickier are the words that go with the verb to change its meaning. In these cases, it makes sense to treat the verb and the extra word as a single verb: for instance, KNOCK ON, WAKE UP, PUT AWAY. The parser can remove the preposition and give the action dispatcher a single verb to work with. WAKE UP becomes the same as WAKE; PUT AWAY can be translated to the equivalent verb STORE. The parser has to be careful of word order, since PUT FUR COAT ON and PUT ON FUR COAT both mean WEAR FUR COAT, but PUT FUR ON COAT means something else entirely (although I'm not sure what).

Adjectives are a little more straightforward, since they normally precede the noun they modify. They can be strung together (MOVE BIG UGLY RED BOX). But adjectives are useful only if nouns by themselves are ambiguous. If there is only one box in the game, adjectives are unnecessary. But if there are two boxes with different attributes, then the program must determine whether BOX alone is sufficient. There might not be any need for adjectives, since only one of the boxes might be at your current location; but if both boxes are present, then an adjective is required. The command parser can't determine by itself whether the adjective is required; it's the job of the action routines to determine if the information was specific enough.

Adverbs can be positioned at various places in a sentence. If the adverb can be identified, it isn't too much of a problem since it can be assumed to modify the verb. Adverbs modifying adjectives (MODERATELY HEAVY) would add a level of subtlety that isn't likely to be needed. But some common adverbs look just like prepositions. This leads to such problems as distinguishing between PUT SUIT ON and PUT SUIT ON TABLE.

In general, the more parts of speech that the parser recognizes, the more vulnerable it is to ambiguities. Zork III, for instance, includes a place mat among its objects. If you say PLACE MAT ON TABLE, the program will complain that the command doesn't contain a verb, since it regards PLACE as an adjective.

An example might help to bring all these points together. Consider a relatively complicated command, such as GIVE THE LITTLE PIG TO THE BIG BAD WOLF QUICKLY. Let's see how a parser might handle this command.

Assume that the parser doesn't allow anything to precede the verb. This means that the first thing in the line has to be the verb, or the command is invalid. The first step is easy, then: GIVE is the verb of the command.

Next, THE is just a noise word that the parser lets you throw in to make your command look more like real English. It just skips over it.

LITTLE is an adjective, so it has to modify a subsequent noun. When PIG is found, the parser takes it as an object of GIVE, with LITTLE modifying PIG. At this point, the parser can't tell whether PIG is the direct or indirect object of GIVE; if the sentence were GIVE THE LITTLE PIG SOME FOOD, PIG would be the indirect object.

TO is a preposition, so what follows has to be the indirect object or a prepositional phrase. Therefore, PIG can now be identified as the direct object.

Throwing out another THE, the parser then finds the adjectives BIG and BAD, followed by the noun WOLF. So WOLF is the indirect object of GIVE, with BIG and BAD being its modifying adjectives, and TO being the preposition that relates the indirect object to the verb.

Finally, QUICKLY is an adverb, and as such all it can do is modify the verb. So the parser will create tokens for the following elements:

  • The verb (GIVE), modified by an adverb (QUICKLY).
  • The object (PIG), modified by one adjective (LITTLE).
  • The indirect object (WOLF), related to the verb by a preposition (TO) and modified by two adjectives (BIG and BAD).

A common way to express the grammar of programming languages is using the Backus Normal Form (BNF). Figure 9-1 shows how the grammar of an adventure could be expressed in BNF. For those not familiar with the notation, a simple example should help to give the idea. Take the definition:

basic-command ::= predicate object [adverb]

This line defines what a basic-command is. It tells us that a command consists of a predicate followed by an object, optionally followed by an adverb. The brackets indicate that an item is optional. An item in braces, such as {adjective}, can be repeated any number of times or not used at all. A vertical bar (|) is used to separate multiple alternative definitions of a term.

A strict BNF definition goes all the way down to defining names in terms of their individual letters. The one used here doesn't involve that much painstaking detail; completely defining the language would be contrary to the spirit of adventuring anyway. Besides, most of the undefined terms should be self-evident; for instance, the term short-adverb indicates such words as UP, DOWN, AWAY, and so on, which are used to change the meaning of a verb.


[Panel]

Figure 9-1. Expressing Adventure Game Grammar in BNF Form

command ::= basic-command [adverb]
basic-command ::= predicate object |
predicate indirect-object object |
predicate object preposition indirect-object
predicate :: = verb | verb short-adverb
indirect-object ::= object
object ::= {adjective} noun | object AND {adjective} noun


By simplifying or omitting definitions, you can create a system of grammar for simpler adventure programs. The simplest grammar, and the most common, needs just one definition:

command ::= verb noun

This grammatical framework also makes further enhancements easy to manage. For instance, allowing multiple commands separated by the word THEN can be expressed by adding the definition:

compound-command::= command {THEN command}

Putting a message on a terminal or screen is basically a simple matter, but it is complicated by a couple of things. First, it would be nice to keep players from cheating by dumping the program to a printer and looking for any obvious text strings. Second, computer storage has to be used economically, so it would be helpful to find a storage method that does better than using one byte per character.

In order to prevent cheating, the program may encrypt the output messages. There are many ways known to do this that require little overhead. It may be possible to break the encryption scheme, but if you're going to go to that much work, you might as well solve the adventure legitimately.

Reducing storage requirements is a more complicated matter. One way to do this is to pack three characters instead of two into two 8-bit bytes. The drawback to this method is that it allows only 40 different character codes to be stored (com- pared with 256, if 8 bits per character are used). This doesn't give enough characters to distinguish upper- and lowercase.

Another way to compact text is to take advantage of the fact that only 128 of the 256 possible character codes in a byte are defined in the ASCII character set. The remaining 128 codes can be used to represent common words or groups of letters. These groups must be stored somewhere in the program so that it can translate the codes, but they need to be stored only once rather than every time a message needs them. This method can, of course, be combined with encryption.

Timing can also be a factor to consider in output routines. Some adventures - for instance, Adventure International's Strange Odyssey - make good use of programmed delays to add suspense to an event or to call your attention to the fact that something has slowed down.

Describing a World

Before seeing how an adventure program makes its simulated world respond to your commands, it's worth taking a look at the data structures that describe that world. The main parts of the description are the internal map, object descriptors, and character descriptor

The main purposes of the internal map are to put an identifying tag on each location and to keep track of the connections among them. These locations are generally called rooms even though they may not be constructed or even enclosed. An obvious way to store the connections is to have an X by Y array of room numbers, with X being the number of rooms and Y being the number of different motion commands (e.g., NORTH, UP, etc.). Then, CONN(20,3) would contain the number of the room reached by going in direction 3 from room 20.

For every room there is a text description and possibly a graphic image to identify the room for you. Often there are two descriptions: a long one that is given when you first enter a room or when you type LOOK, and a short one when you return to a room you've already visited.

Each room may have some internal descriptive information associated with it. Many adventures characterize rooms by whether or not they are illuminated; the ones that aren't won't be described to you (and may even be fatal!) unless you bring your own light source. The room may also be under water (in which case you'd better not stay too long) or accessible to certain creatures. And if the program is going to decide whether to give a long or a short description, it has to remember if each room has been visited yet or not.

Specific exits from a room may also have their own characteristics. A passage may be too narrow to let you carry a bulky object through. You may not be able to leave unless a door has been unlocked and opened. Using the exit may trigger a trap unless you've taken precautions.

The object descriptors indicate where an object is and what its characteristics are. The most important characteristic of an object is whether it is fixed or movable. If the object is fixed, no power on earth can change its location; this fact can be convenient for letting the program do certain things with it. Saying that an object is movable, though, doesn't necessarily mean that you can just pick it up and carry it off. The way to move something may not be at all obvious.

Doors, gates, and the like are an interesting class of fixed objects, since they are located between two locations rather than in one of them. If you open, close, or lock a door, the change in its state must be reported in the descriptions of both rooms.

Objects may act as containers that hold other objects or as surfaces that support them. This relationship can be carried out on multiple levels; a table might support a box, which contains a bag, which contains a book and a bookmark. Realistically, a container has a limited capacity, so the program must either consider the sizes of objects or else have a list of specific objects that can go in a container. The number of objects that a container can hold is also limited. Finally, two objects can't contain or support each other.

Some objects present special problems because they aren't in just one place at a time. Water is the most common example. A cave complex might contain a pool, a lake, and a stream, all of which contain water. But if you type TAKE WATER in one of these places, it won't work for the program to check to see if you're in a room that contains the object WATER. Instead, it has to determine if the room contains a water source and, if it does, create the specific object WATER (presumably in a container that you have handy). If you later type POUR WATER, you usually can't get it back into the bottle - so the specific object WATER has to be destroyed until the next time you get it from a water source.

Other characteristics of objects depend on the particular adventure. For instance, color, size, and so on can be used to distinguish otherwise similar objects. These characteristics usually have no other effect on the game, unless you're wearing a red cape and enter a room housing a bull. The characteristics of an object may also specify what you can do with the object (whether you can eat it, drink it, open it, and so on). The action routines make use of these characteristics.

The remaining data structure to be considered is the character descriptor. Two items are basic enough to be included in virtually every adventure: where your character is and what he or she is carrying. Another common factor is the state of the character's health, which determines how much more hardship he can take before dying, and which may also affect his current ability to take various actions.

There may be distinctions that specify how the character is carrying an object. He may be wearing it (for instance, a helmet), in which case it might not count against the number of objects he can carry. In Empire of the Over-Mind, the character can hold only one object out of several that he is carrying. The only one he can actively use is the object he is holding, and he might not have time to reach for another one in a critical situation.

If there is more than one character under your control (as in Suspended), or if there are multiple players, each controlling a character, there must be a character descriptor for each one. Interaction between characters can occur when they are in the same room.

Action Routines

Now we come to the heart of the matter: what happens in the imaginary world created by the program. The factors involved are what your character does and what independent agents and other forces in the game do. These correspond to the action routines and automatic routines in the program.

As you may remember, the parser takes the words that you type for your command and translates them into tokens. The action routines take these tokens and convert them into action in the simulated world of the adventure; they, in turn invoke an output routine to tell you what has happened. For instance, if you type TAKE COIN - and nothing unusual is involved in this action - then the action routines will remove the object COIN from the list of objects in the room and add it to your inventory. Other things may happen instead. The coin might not be in the room, in which case the action is impossible. You might be so heavily loaded that you can't carry another nickel, in which case it's again impossible (but with a different output message required to explain the reason). Or the coin may be linked by a magic spell to another object in your inventory, causing something entirely unexpected to happen.

Action routines draw no clear-cut distinction between valid and erroneous commands. Every command causes something to happen, even if it's just putting out a message. Some messages may be considered error messages (for example, "Don't be ridiculous!"), but even an error message might give useful information. If the action is impossible, the message might explain why; or in the case of an EXAMINE or READ command, getting a message might be the only reason you entered the command. The strongest distinction that can be made is that some commands actually make something happen, with permanent effect, while others just produce output.

The more complicated the commands are, the more complicated the action routines have to get. In the case of a simple verb-object adventure, command handling is fairly straightforward.

The action routines are entered through a top level routine that we can call the action dispatcher, which determines what particular action routine to invoke. Usually, this depends on the verb. But special circumstances may make the dispatcher override the normal action routine for a verb (in Zork for instance, if the character is in a vehicle). This is, in effect, a second dispatcher that determines what commands are allowed in the special situation. Some commands that are normally allowed, such as JUMP, might be impossible or fatal in the boat. Eventually, though, the program will call some action routine for the verb, whether it's the usual one or something unique to the situation.

The action routine for a verb first has to check whether the parser gave it an object in the command. If there isn't one but one is required by the verb, then the action routine has to issue an error report and takes no action. If the verb doesn't need an object (for instance, WEST, SCREAM), it can go directly to determining what the effect of the action will be.

If there is an object, the action routine must determine if you can get at the object. An object that has a specific location must either be in the room with your character or in your inventory for you to be able to do anything with it. (For some verbs, this might not be strictly true. It can make perfect sense for you to EXAMINE or ASK ABOUT an object that you can see at a distance.) Other objects, such as air, are everywhere and always available. So are directions, which are treated as objects in giving commands (GO SOUTH).

The next step, once the object is determined to be legitimate, is to see how the verb applies to the object. This is often just a matter of checking an appropriate entry in the object descriptors. If the verb is EAT, the action routine must check to determine if the object is designated as edible. For TAKE, it must check to see if the object is movable. If the action is inapplicable, the result will be just a message.

The possibility of applying the verb to the object may depend on other circumstances, such as inventory and location. Taking water requires a bottle; attacking an enemy is possible only if you're carrying a weapon. The room itself may affect the outcome of the action; waving a rod might have no effect in one place but could release a magic spell somewhere else.

If the parser allows commands more complicated than just a verb and an object, the action routines have to be able to handle those complications. If adjectives are allowed, the routine must determine if the command uniquely describes an object. If a room contains a short rope and a long rope, and the player types TAKE ROPE, the routine has to ask for more information.

Some programs take one of two short cuts with adjectives. For instance, they may ignore local information in deciding whether an adjective is needed. In Bedlam, there are a green key and a red key. Even if only one of the keys is in sight, you have to tell it which key you mean. On the other hand, there are programs like Empire of the Over-Mind that will make an arbitrary choice if you don't give enough adjectives to indicate which object you mean. Asking for too much information is the safer route, even if it's irritating; at least the program won't do something you didn't want it to do.

Infocom's adventures let you enter additional information in ambiguous situations, rather than making you enter the whole command over again. The dialogue might look like this:

>TAKE ROPE
Which rope do you mean, the long rope or the short rope?
>LONG
Taken.

When multiple objects occur, the action must be repeated once for each object specified. If the multiple object takes the form of ALL (something), then the program has to check each available item to see if it matches that "something."

If indirect objects and prepositional phrases are allowed, the action routines have to check for the validity of both objects. After that, the routine for the particular verb has to determine if an indirect object or prepositional phrase is allowed, required, or forbidden.

A prepositional phrase is like an adjective in that it narrows down a possible range of choices. The difference is that it affects the action itself rather than the object. For example, if the command is FIGHT VAMPIRE, a prepositional phrase can specify what weapon you want to use. If you have only one weapon, no qualification is needed, so the routine should consider the two-word command sufficient. But if you're carrying both a sword and a stake, for instance, it needs a prepositional phrase to tell just what you want to do.

The preposition that introduces the object has to be appropriate to the action. FIGHT VAMPIRE TO SWORD makes no sense, and the routine for the verb FIGHT must respond accordingly. Usually, only a few prepositions make sense with any given verb.

If the parser follows the model described in this chapter, prepositions will always introduce an indirect object or phrase, never a direct object. The reason for this is that the parser takes any preposition before the direct object and makes it part of the verb. In the command SWING OVER PIT WITH ROPE, SWING OVER is the verb, and the parser gives it to the action routine as a single unit.

Adverbs are similar to prepositional phrases in that they change the meaning of verbs. If you type OPEN DOOR in a murder mystery, the result might be, "Mr. Jenkins is in the room." Typing OPEN DOOR QUIETLY might get you the more useful response, "Mr. Jenkins, who has not noticed your quiet approach, is writing in a pocket notebook."

Automatic Routines

Further variations in the adventure are provided bv events that are essentially out of your control. A lamp may run out of power after a certain amount of use. A bomb that you're trying to defuse may explode at a certain time, taking you with it, if you haven't accomplished your task by then. Another character may be running around the dungeon trying to frustrate you. These events are handled by automatic routines, also known as daemons (a term that originated with the Multics operating system to describe programs that run independently of any user).

Daemons may be set in motion at the beginning of the adventure, or they may be triggered by some action the character performs (such as entering a certain room). They run each time the player makes a move, although thev may not have any visible effect for some time. A simple daemon might just count moves that the player makes and cause something to happen - perhaps making the sun set - after a certain number of moves. A complicated one might change something after each move.

One of the most complicated types of daemons is one that manipulates an adversary character (maybe even a demon). This almost requires keeping a complete second character descriptor. The daemon has to keep track of where the adversary is and determine how he moves each turn. If the adversary is involved in combat, it must keep track of his state of health. If he picks up objects from rooms or steals them from you, it has to keep track of his inventory. If he enters your presence, it has to tell you what he's doing. It may even let you overhear him if he's in a nearby room.

Combat, Wounds, and Recovery

In the borderland between the automatic routines and the action routines, your character will come into conflict with his adversaries. When this happens, the program must determine the outcome of the combat. Who will win - you or the monster? (Following the terminology established by Dungeons and Dragons, I'll use the word monster here to mean any creature, human or otherwise, that opposes your character.)

Combat in adventures can be utterly simple or extremely complicated. The simplest kind of combat is the kind in which you only need to give the right command to rout your opponent. Figuring out what the command is may be devilishly difficult, but once you give the command, the program simply eliminates the monster and congratulates you on your cleverness. For instance, if you meet the Wicked Witch of the West, it may be necessary only to THROW WATER (provided you have some water) to wipe her out.

Unfortunately, the monster may be able to dispose of you just as easily. You have to have some way to defeat him, of course, or there's no point to the game. But it may happen that if you don't defeat him immediately, or if you meet him in a certain place, then he will determinedly rob, kill, or otherwise inconvenience you.

One step toward greater complexity is to add a random factor. The correct command may be simply to FIGHT the monster, but it may work only 50 percent of the time (and the monster may fight back with a 30 percent chance of success). This can allow a battle to go on for several rounds before only one survivor remains.

Only a simple-minded view of combat would leave the loser defeated and the winner unscratched. In any real fight, even the winner should come out battered and bruised. One way to simulate this is to assign each character a certain number of "hit points" and to let each successful attack take one or more hit points away. Then, the loser is the one whose hit points fall to zero first.

Another way to make combat more realistic is to provide an assortment of weapons, some of which are more effective than others. An ax might have a low probability of hitting a target but do a lot of damage when it does; a sword might be more likely to hit but do less damage. The effectiveness of the weapon might even vary with the monster it's being used against.

When you use a weapon in combat, you may run a small risk of breaking or dropping it. In that case you must pick it up, switch to another weapon (if you have one), or get out in a hurry.

If you survive the battle, you may find yourself tired and bleeding-in other words, down to a very few hit points. A command such as DIAGNOSE or HEALTH will tell you just how badly off you are. Then you have to recover from your wounds. Recovery may simply be a matter of time, in which case you just have to avoid fighting long enough to heal. On the other hand, you may need to use a healing potion, invoke a healing spell, or even journey to some place of healing - but taking the necessary actions may require further travel and involve the risk of getting beaten up again.

But what happens to your foe while you're off licking your wounds? The most realistic answer would be that your adversary should heal in a manner similar to your own. Some adventures provide that whenever you encounter a monster, he is presumed in perfect health. You can wear him down as long as you continue to fight - but if you leave and come back, he will be fully recovered.

Language Considerations

Obviously, writing an adventure program can be a complicated task if the program is going to do much. This leads to an important question: What is a reasonable language for writing adventures?

The most popular language today, without question, is BASIC. But BASIC has restrictions. The line numbers make it difficult to expand a program in the middle or to treat one piece separately from the rest. The organization of BASIC programs into separate statements keeps programs from having any readable structure; you can't, for example, have an IF-THEN construction followed by more than a line's worth of statements to be executed if the condition is true. And BASIC has no way of describing groups of data other than with arrays.

What are some of the alternatives? Broadly, there are two classes: assembly languagehigher-level languages.

In assembly language, the statements correspond to individual machine instructions. Each instruction has a mnemonic that identifies its function. For example, the instruction that adds two numbers might have the mnemonic ADD. Assembly language lets the programmer use the full power of the computer. Programs written in assembly language will generally take up less memory and run faster than any other programs.

But writing programs one machine instruction at a time is tedious, and it leaves lots of opportunities for programmers to introduce bugs into their code accidentally. Also, an assembly program can't be moved to a computer that uses a different processor, but must be completely rewritten in the assembly language of the new machine. For these reasons, an assortment of higher-level languages have been devised. Some, like BASIC, use an interpreter to run programs. An interpreter is a program that takes each line of the user's language and executes it. Others use a compiler to translate the user's program into machine language. Interpreted programs save the programmer a step, since they don't have to be translated; on the other hand, they run more slowly. Compiled programs can be nearly as compact and as fast as assembly language programs if the compiler is a good one. Any language can be either interpreted or compiled, but most are designed for one use or the other.

Popular compiler languages include Pascal, FORTRAN, and C; newcomers that may soon gain equal popularity are Ada and Modula-2. FORTRAN is the oldest living higher-level language and is used mostly for scientific and mathematical programming. It has little to recommend itself for text-intensive programs like adventures. Still, the original Adventure was written in FORTRAN, and so was the translation of Zork called Dungeon.

The other languages mentioned above all have certain features in common. They allow programs to be organized into subroutines and blocks, which lets the flow of a program correspond to its readable structure. They provide facilities for organizing data in logically related groups (called records in Pascal and structures in C), even if the data are of different types. These capabilities make the job of writing a complex program much easier, since they allow breaking the program and data into manageable pieces.

A more esoteric language, but one worth considering, is LISP. LISP programs don't look at all like programs in most other languages; to the uninitiated (and often to the initiated as well) they look like masses of parentheses. But LISP has some very distinctive features. Its basic data structure is the list, which is simply a series of data items of any kind. A list can consist of lists, and programs themselves are lists. Items can be added to a list at any time; this is convenient for describing variable sets of objects, such as the items in a room. Another useful feature of LISP is the property list, which lets such properties as the adventurer's inventory and state of health be associated with an item. LISP is usually an interpreted language, but compilers have been written for it.

The original Zork was written in MDL, a greatly extended version of LISP. The resulting product is striking evidence of how much the right language can help the programmer.

Trends

With the range of options being as wide as it is, you can easily see why some adventures are much more sophisticated than others in their ability to handle your commands and make things happen around your character. An adventure that runs in a small amount of memory just won't have room for that much programming. But even if a lot of memory is available, there's a trade-off between using that memory for the program and using it for data structures.

For several reasons, we can expect to see future adventures grow in sophistication. The amount of memory in the typical home computer is steadily growing. The 16K "standard" main memory is giving way to 64K or even 128K - and by the time you read this, even more memory may be the norm. Increasingly powerful processors are also moving into home computers, and compilers for programming languages such as C and Modula-2 are becoming available as well. Better processors and languages will ease the programmer's job in creating fancier parsers and action routines. Floppy disk drives are getting cheaper, making it more feasible to get away from tape-loaded programs that have to fit everything into memory at once.

Finally, buyers' expectations will rise. When players see what Cyborg or Zork can do, they aren't likely to remain satisfied with adventures that limit them to two-word sentences. The more complex an adventure program gets in terms of what it can handle, the simpler the player's job will be. The ideal would be a program that could handle any grammatical command or question and come up with a reasonable response. If this sort of program existed, the player could deal with it as easily and flexibly as with a human gamesmaster in a Dungeons and Dragons game.

Getting to that point within the next century might be too much to hope for. But you can be sure that the state of the art will keep advancing.


Richard A. Bartle (richard@mud.co.uk)
21st January 1999: cgtaghtw.htm