I recently picked up my battered copy of Peter Killworth’s How To Write Adventure Games for the BBC Microcomputer Model B and Acorn Electron (1984) from my bookshelf. This was intended to allow the reader to write text adventure games of the style “GO NORTH”, “TAKE SWORD”, “THORIN SITS DOWN AND STARTS SINGING ABOUT GOLD” - a genre nowadays generally referred to nowadays as interactive fiction. But looking at it now reminded me of the challenges of writing these adventures then, and prompted me to explore how these have changed in the intervening decades (especially with increasingly sophisticated authoring tools) - and how these challenges relate to the concepts of accidental and essential complexity in system design.
(A PDF scan of the book is now available at https://www.mocagh.org/softguide/writeadventuregames.pdf)
The tools we had
(Public domain image from Wikimedia Commons)
The BBC Model B had 32K of RAM. An 80-track double-sided 5.25" floppy disk (i.e. the most you could fit on one disk) held 400K.
Watford Electronics had a great range of hardware and software to support the BBC microcomputer, and used to buy page after page of advertising in the magazines every month. In January 1984’s copy of The Micro User (now with an extensive online archive), they advertised a BBC Model B at £346 (equivalent to £1198 in March 2021), and the Cumana disk drive capable of working with that 400K storage would be another £310 (while the cheapest disk drive - 40-track, single-sided, 100K on a disk- would be a mere £180). And the disks from Verbatim would be £2 each for single-sided, £3 each for double-sided.
At those prices - and with apparent supply restrictions in getting the disk interface for the computer (I now learn from the detailed article in that issue of Micro User on how to hook up a disk drive, and the compatibility quirks between different vendors' disk filing systems) - disk drives were by no means universal, and the alternative was a cassette player and audio cassette tapes.
Squeezing a game into 32K
I learned a lot from Killworth’s book on how to design an entertaining adventure game - the map, the objects, the puzzles, and the descriptions - and how to design a program to implement it. (Killworth seems to have a reputation for tricky games, but he is clear about the need to be fair and provide enough clues for the observant player.)
But with the limited capabilities of the micro, he needed to find a way to fit the game into memory - and for the biggest game in the book, ROMAN, this needed some clever trickery.
For efficiency (at the cost of complexity), instead of storing the data in variables, arrays, or DATA statements managed by the BASIC interpreter, Killworth uses direct memory access.
-
To read or write individual values within the a program, BBC BASIC has three operators.
?P
gets/sets the 1-byte value stored in the byte at addressP
.!P
gets/sets a 4-byte integer in the bytes at addressesP
,P+1
,P+2
, andP+3
. And$P
gets/sets a string starting at addressP
and terminated by a carriage return (ASCII 13). -
To read or write blocks of memory to a filesystem (i.e. a tape or, if you’re lucky, a floppy disk), there are two operating system commands (denoted on the BBC Micro by the star at the beginning).
*SAVE <filename> <start> <end>
saves the block of memory between the<start>
and<end>
addresses. And*LOAD <filename> <start>
loads the contents of the specified file directly into memory beginning at the specified starting address.
BBC BASIC has no equivalent of malloc
or new
to obtain a chunk of memory we can use as our own. (Well, the NEW
command clears the current BASIC program from memory - recoverable with OLD
if you do it immediately - but that’s not quite what we want…) Instead we must look at a map of the computer’s RAM and work out where we can store it safely.
(From a remastered version of the BBC Micrcomputer System User Guide, presented in a post on the Acorn-focused Stardot forum.)
The memory from PAGE
to TOP
holds our BASIC program. LOMEM
is usually set to just above TOP
, and HIMEM
is usually set to just below the start of the memory for the graphics. The memory between LOMEM
and HIMEM
is used by BASIC as its workspace, e.g. to hold variables. Interestingly, we can specify alternative values for LOMEM
and HIMEM
(and, for that matter, for PAGE
) by setting a different value as if they were variables. So Killworth’s approach is to re-set HIMEM
to a lower value, and use the space between HIMEM
and the bottom of the graphics memory to store the game data.
And how do we populate the data in this block of memory, without recourse to arrays or DATA
statements? We write one tool (which Killworth calls DATAGEN) which lets us type in the data and stores it in the block of memory. We can then *SAVE
those blocks of memory as direct memory dumps into files. We then LOAD
our adventure game into the BASIC interpreter, and *LOAD
the data into the memory block. Then we can *SAVE
the adventure together with the block of memory as a single file - when it is LOAD
ed as a BASIC program, we will get the program and the data. (More intricacies of this approach and how everything is put together are provided in the book.)
So I also learned a lot about general programming, bit-twiddling to pack multiple flags into a single byte, and the innards of the BBC Micro. Fascinating and useful - and necessary at the time to actually make the game work - but was it really about writing adventure games?
Interlude: Accidental vs Essential Complexity
Fred Brooks’s classic 1986 paper No Silver Bullet is credited with introducing the concepts of essential complexity - the complexity which is fundamentally part of the requirements to be satisfied or the problem to be solved - as opposed to accidental complexity - complexity arising from representing the abstract solution in concrete programming languages.
In the chapter on Simplicity in Site Reliability Engineering: How Google runs production systems (2016), the distinction is presented as follows:
Essential complexity is the complexity inherent in a given situation that cannot be removed from a problem definition, whereas accidental complexity is more fluid and can be resolved with engineering effort. For example, writing a web server entails dealing with the essential complexity of serving web pages quickly. However, if we write a web server in Java, we may introduce accidental complexity when trying to minimize the performance impact of garbage collection.
Simplicity in systems helps keep them reliable and flexible - if we can’t remove the essential complexity, we would like to reduce the accidental.
Where is the essential complexity in adventure games?
In the introduction of How To Write Adventure Games…, Killworth identifies the following elements which a player sees:
a) a PLOT (maybe weak, maybe strong; but the player must have a goal or why else play the game?)
b) an AREA, or a GEOGRAPHY (usually to explore or map, but not necessarily)
c) some TOOLS for the game (usually objects or information which the player manipulates to achieve his goals)
d) a way for the player to make decisions and TELL them to the game (or INPUT them)
e) a way to TELL THE PLAYER WHAT HAPPENS.
and other key elements going on in the background:
f) any game must have a well-defined VOCABULARY (whether the player knows this from the start or discovers it during the course of the game, depends on you)
g) a way of handling ERRORS of all kinds, from unknown words to inconsiderate pressing of Escape at awkward moments
h) a way to keep track of all the things going on, where the player is, what happened to that dragon, and so on; this is known as a DATABASE structure.
i) an overall RUNNING PROGRAM to organise all the strange things that happen to the player.
If I were sat in front of a 32 KB BBC Micro and needed to write my adventure, it will need all of those. And if I were sat in front of my 2020s dual-booting Linux/Windows desktop machine with 32 GB of RAM, ready to write the adventure in the modern programming language of my choice (which for me probably means “in Python”), then it too would need them all. None of them is tied to the details of the system where I’m going to develop or run the game. So maybe this is the essential complexity of developing an adventure game?
Absent from this list is jumping through hoops to squeeze the solution into strictly limited memory. That is down to the concrete details of the system on which the game is being developed and the programming language and tools being used, so I’d consider it accidental complexity.
Now authoring tools can bear much of the load
What is the player of an adventure game interested in? The locations, the objects, the things that happen - the elements which make the puzzles and the game.
Of the list above, this could be the PLOT, GEOGRAPHY, TOOLS, and game-specific elements of the VOCABULARY, along with a definition of the “strange things that happen to the player”. Much of the rest is standard functionality similar across many adventure games (with some specific tuning depending on how mould-breaking an individual game might be) - and a programmer’s instincts should recoil at the prospect of re-implementing these for every game.
There have long been tools for writing adventure games, and reducing/eliminating the programming required. From the 1980s, I’d recalled The Quill or the Professional Adventure Writing System, although I never had the chance to try them out. There are now lots of options: the “Authoring Tools” page on the Interactive Fiction wiki reports that “The most commonly used (and talked about) systems appear to be ADRIFT, Alan, Hugo, Inform 6, Inform 7, Quest, TADS 2, and TADS 3.”
They allow the author to specify the design of the game, while the tools provide standard elements of an adventure game (e.g. a parser to interpret the player’s commands, a database to track the state of the game). Examples of two authoring systems - TADS 3 and Inform 7 - are presented below.
TADS 3: an object-oriented programming-language-style tool
TADS 3 (where “TADS” stands for the “Text Adventure Development System”) approaches interactive fiction design from a programmer’s perspective. The TADS 3 language overview states “It starts with a programming language that closely resembles C, C++, Java, and Javascript. TADS is faithful to C’s core procedural syntax, and even includes meticulously complete support for ANSI C macros. If you know C or Javascript, you pretty much already know the TADS procedural syntax.”
The example here is based on an early example in Getting Started in TADS 3, in Chapter 2: A Very Simple Game.
We define the cave as a room, with one exit to the south.
cave: Room
roomName = 'Cave'
desc = "You're inside a dark and musty cave. Sunlight
pours in from a passage to the south. "
south = startroom
;
cave
is a an object of class Room
, with properties including roomName
, desc
, and south
, and startroom
is the name of another room.
We then bring into existence three objects in the cave: a pedestal (which is a Surface
, i.e. another object can be on the pedestal, and which is a Fixture
, i.e. the player cannot take it), a small rock in the cave (not on the pedestal), and a gold skull, which is on the pedestal.
pedestal: Surface, Fixture
name = 'pedestal'
noun = 'pedestal'
location = cave
;
smallRock: Thing
name = 'small rock'
vocabWords = 'small rock'
location = cave
;
goldSkull: Thing
name = 'gold skull'
vocabWords = 'gold skull/head'
location = pedestal
actionDobjTake()
{
if (location != pedestal || /* am I off the pedestal? */
smallRock.location == pedestal ) /* or is the rock there? */
inherited; /* yes - take as usual */
else /* no - the trap goes off! */
{
"As you lift the skull, a volley
of poisonous arrows is shot from
the walls! You try to dodge the
arrows, but they take you by surprise!";
finishGameMsg(ftDeath, [finishOptionUndo]);
}
}
;
Taking the gold skull presents an Indiana-Jones-esque puzzle, encoded in the actionDobjTake
method which is called if the player tries to take the skull. If the player takes the skull without any precautions while it is still on the pedestal, the reduction in weight on the pedestal triggers a volley of death. However, if the player takes the rock and puts it on the pedestal, then they can remove the skull safely without triggering the trap - inherited
means that the normal take action is completed.
Inform 7: a semi-natural-language alternative
In contrast with the programming-language-style code in TADS 3, the Inform 7 tool allows the game designer to specify a game in (semi-)natural English. An example is below - based on my Inform 7 implementation of the MINI adventure from Killworth’s book.
The Cobwebby Room is a room. "You are in a crumbling room full of cobwebs. A passage leads west. [if the barred door is closed]A barred door blocks the way north.[otherwise]A barred door stands open to the north." A shield is here. The shield is wearable.
A barred door is north of the Cobwebby Room and south of the Treasure Chamber. The barred door is a door. It is not openable. It is scenery.
After waving (noun):
if the player is in the Cobwebby Room and the barred door is closed and noun is black rod:
say "The barred door quietly swings open.";
now the barred door is open;
otherwise:
say "Nothing happens."
The Cobwebby Room is, as it says, a room. The barred door is a door, a feature which sits between rooms (in this case, the Cobwebby Room and the Treasure Room) and which at the start of the game is closed - the description of the Cobwebby Room varies depending on whether the door is open or closed. The shield and the black rod are objects (the black rod is introduced in another room not shown here).
To open the door, the player must wave the black rod while standing in the Cobwebby Room. This is managed by the code After waving(noun)
- whenever the player waves anything, this code is run, but Nothing Happens unless the player is in the Cobwebby Room and the object being waved is the black rod.
So - where is the essential complexity of writing an adventure, really?
With the engineering effort which these authoring tools implement, the challenge which the developer of an adventure faces is less daunting. They “just” need to specify the game design - the map, objects, their interactions, and the puzzles - in terms which the authoring tool understands. And while that may still be complex, the complexity arises from the design of the individual game rather than the boilerplate functionality required by an adventure game.
Brooks identifies that one of the key advances for “software productivity, reliability, and simplicity has been the progressive use of high-level languages for programming” (emphasis mine). His explanation that “An abstract program consists of conceptual constructs: operations, data types, sequences, and communication. The concrete machine program is concerned with bits, registers, conditions, branches, channels, disks, and such” suggests that he may have been thinking of the step up from machine language or low-level programming languages to modern general-purpose languages. But these authoring tools are effectively domain-specific languages for interactive fiction, which successfully shift the essential/accidental boundary and reduce the accidental complexity of an individual game. And this should bring the benefit of improved robustness in the games produced, and flexibility to make changes or innovate.
To further illustrate what these tools can do, in a future post, I will show how the four-room adventure MINI from Peter Killworth’s book can now be implemented in Inform 7, and how this compares to the BBC BASIC version which he provides.
Comment on this article on Twitter:
How a 1984 book on writing text adventures, & the programming trickery needed to fit them into the 32K of a BBC Micro, led me to explore modern interactive fiction authoring tools and where the boundary between accidental and essential complexity now lies: https://t.co/pyBGw0V1wh pic.twitter.com/NoobOmEome
— Terry Boon (@terryboon) May 5, 2021
Sign up to the newsletter: If you would like to be notified of new posts on this blog, please sign up to the Eclectic Stacks email newsletter.