In the 1980s and early 1990s, I spent a lot of time playing and writing text adventure games on my Atari 8-bit computer. Text adventures, or interactive fiction, are games where the player reads a story on the screen. The player can change the outcomes of the story by issuing text instructions to the computer. These lines are an extract from Colossal Cave, by Will Crowther and Don Woods, the very first text adventure written in the mid-1970s:
You are standing at the end of a road before a small brick building. Around you is a forest. A small stream flows out of the building and down a gully. > go south You are in a valley in the forest beside a stream tumbling along a rocky bed. > enter stream Your feet are now wet.
The basic premise of a text adventure is that the player finds herself in a simulated world. Unlike contemporary simulations, this virtual world is created only with text. This might seem a strange idea to modern games, but simulated worlds through text have existed for thousands of years. A text adventure enhances the traditional book by giving the player the freedom to act within the narrative. The objective of these games is to solve problems to reach a specified goal.
This article describes how to write a text adventure in the R language. This article replicates a game and the techniques described by Hal Renko and Sam Edwards in the Dutch book Adventures! (1986).
The sections below describe the detailed workings and the narrative of the game. If you don't like spoilers, then perhaps you should download the code and play The Secret of Landusia before reading further.
The Secret of Landusia Text Adventure
This section explains the detailed workings of the Landusia game. The text adventure consists of four files:
adventure.R: The code.
actions.md: Markdown file with descriptions of rooms and actions.
map.csv: Matrix of the relationship between rooms.
objects.csv: Matrix of the status of objects and actors.
The Game Loop
The computer displays the current state of the game with a short piece of prose. The player then enters a command in natural language, which changes the state of the game. When the state of the player or the game reaches a certain point, the player has either won or lost the game. The game loop processes the commands of the player and determines the new state of the game. The state of the game consists of:
Player: The player can have many attributes, such as health, objects
they are carrying and their location.
Map: Logic of the game localities and a description of each area.
Objects: Are either placed in a room or are with the player.
Actors: Can act independently.
The initialisation defines the vocabulary through a vector of verbs and a vector of directions. The code reads the map, objects and prose into memory and sets the starting variables for the player. The initialisation includes two auxiliary functions that the player cannot call directly.
The prose function takes a key phrase and finds the location of the heading for that key in the
actions variable. The following lines of code print the text up to the next heading indicator. The prose function describes rooms, actions, actors and so on. Using a markdown file simplifies writing the text so it is not hidden amongst code. The prose function reads keywords after double hashtags. Other headings are used for convenience.
The inventory function lists all objects that the player carries. This function is a separate verb in most adventure games, but in this game, it is integrated with other verbs.
The program simulates the player with a set of parameters, such as their location, level of health and whatever other aspect is relevant. A player can, for example, only carry a certain number of objects, can only endure two blows by a sword, and so on.
In the Landusia game, the player has a level of
health and maximum carrying
room variable keeps track of where the player is located.
The game consists of 28 'rooms'. The map is stored in a matrix which indicates the relationships between the rooms. Each direction can have one or no exit. The map can also dynamically change during the game. Specific directions can, for example, only become available after the player opens a door. A rockfall could close an entrance, and so on. The player can move around the map by issuing directional commands. These commands are the four cardinal compass directions, and up and down.
In Landusia, the first nine rooms are a forest. The blacksmith lives south of the forest. Clovar's castle lies to the south-east and the temple north-east of the forest. The connection between room 14 and 15 opens after the player uses the rope in room 14.
The descriptions of the different rooms of the game can contain objects. These objects are fixed in that their description is hard-coded into the game. A text adventure can also have movable objects. Each object can have its own properties, such as weight, and location. The location of the object can either be a room or the player (room zero). These properties are stored in another matrix.
The Landusia game has four objects: bandage, sword, flute and rope. These objects are scattered around the map and have a certain weight. The
capacity variable limits what the layer can carry simultaneously.
Actors are like objects, with the difference that they are active in the game. As the game progresses, actors take independent actions. Actors can move around the map and respond to the actions of the player. They can have properties, such as health and strength. Actors can be either friends or enemies that create a dynamic narrative.
Landusia has four actors which are stored in the same matrix a the objects. Each actor has a name, location, health and status. The actors function controls their actions.
The wizard appears in the living quarters. His only task is to give the player the flute, once they manage to reach him. After this one action, the wizard vanishes by changing his location to room 100.
When the player is in the forest, a random variable determine whether they are swooped by a crow. Another random variable determines whether the player was hurt. If the crow manages to hit the player, the code reduces the
The dragon sequence depends on the room the player is in. In room 17, the dragon has a 50% chance of hitting, while in room 18, the dragon is always sucessful. The player needs to kill the dragon with the sword (see below). The success of these fights depends on a random variable.
The blacksmith has the most complex handler of them all. He starts lying wounded on the floor. If the player does not help the blacksmith by using the bandage, he will die. If the player uses the bandage, the blacksmith is healed and move to the workshop to forge a sword for the player. The status variable controls the evolution of the blacksmith in the game.
The command interpreter asks the player for instructions, which the program then interprets. The state of the game (player, map, objects and actors) is changed, depending on the input. If the state of the game reaches a particular defined state, the game will end either successfully or miserably.
The basic principle of all command translators in interactive fiction is that they analyse the input using
regex-type approaches. The interpreter compares the player's instructions with a vocabulary and strips verbs and nouns.
For example, “take lamp” calls the take function and passes it the parameter lamp. The object lamp will then be moved from its current location to the player. The game could also check if the player still has enough carrying capacity and so on.
If the player enters a command that the interpreter cannot parse, then a negative response follows. The better the text adventure, the higher the likelihood that the game understands the command. Traditional text adventures use elaborate command interpreters to maximise the level of realism of the simulated world. This text adventure uses a minimised vocabulary and each of the verbs has its own function.
The look function takes a room number as input and displays the prose related to the room. The next section lists the available passages and objects in the room. The final part of the look function displays any objects that the player is carrying.
Take and Put
These two functions allow a player to take an object or place it on the ground. The weight of the object is added or subtracted to the carrying capacity. When the object is not present in the room or not carried by the player, the code responds appropriately.
These functions cannot respond to objects mentioned in the room descriptions or actors, such as trees or the dragon. This can be easily remedied by creating two types of objects, moveable and immovable.
The wait function does nothing. Waiting might be useful to let other actors do their thing. The wait function has a dummy parameter because there is none, but the main loop assigns one to each verb. This verb is needed to give the blacksmith enough time to forge the sword.
The use function starts by checking whether the player actually has the object in their possession. The remainder of the function contains instructions for each of the four objects. The bandage will either heal yourself or the blacksmith. The sword is not very useful in this context. The flute activates the magical teleportation by changing the
room variable. Finally, using the rope changes the map by opening the connection between rooms 14 and 15. This action also changes the prose so that the room descriptions are enhanced.
This last function is conditional upon having the sword. If the player has no sword, then their fists are the weapon of choice. If an opponent is present in the same room as the player, they have a 60% chance of hitting the opponent. The sword has a higher strength than a fist fight. The actor function controls the return hits.
Expanding the Game
This game is modular in structure so that it should be easy to enhance this game or to write a completely different game. They key to writing a good adventure is simplicity. It is easy to get carried away with writing complex command structure that the player has to guess are available.
Feel free to leave a message below if you find bugs or like to enhance the prose or code.
A lot has changed in computing since the heyday of text adventures. Although these games provided an illusion of freedom to act in a simulated world, the player is always limited by what the writer intended them to do.
Perhaps natural language processing, complex Markov chains and text-generating deep learning methods can generate the ultimate text adventure. Imagine a text adventure where the computer understands the basic structure but freely develops the prose and interactions.