A Text Adventure Game in CLISP

Shaaz Ahmed
The Software Firehose
8 min readAug 9, 2016

--

In this post, I’ll discuss a simple command-line text adventure game, called “The Wizard’s Adventure”. The game’s code base is really small (< 200 lines), and these few lines of code have been split into modules of even fewer lines based on function.

By the end of this article, I hope you end up having a good idea about:

  • The game loop, and a way to implement it
  • The different functional blocks one has to code while designing a game and how these blocks work together
  • Separating logic such as UI actions, game state and game scene describers

The code is available at github.com/shaaza/the-wizard-game. It’s written in Common Lisp (CLISP), which you may not be familiar with. However, I feel it’s very well commented and structured, and the purpose of each block of code can be understood even by someone who doesn’t understand the language. You can also read this post without referring to the code.

The game:

While talking about creating games, I like to use the MDA approach. Mechanics give rise to Dynamics that create Aesthetics, i.e. the experience of the game. In this article, we are mostly concerned about the mechanical aspects of the game.

So what’s this game about?

In this game, the player is a wizard’s apprentice. The player explores the wizard’s house. In the complete version of the game, the player will be able to solve puzzles and win a magical donut.

Here’s a screenshot that takes us through the initial stages of gameplay:

A screenshot of the gameplay

The game loop:

As discussed in the article on modelling worlds, most games have the same conceptual main loop[1]:

while (game isn't over):
get user input
respond to user input
show stuff to user

The game loop in our case is implemented in the form of the game’s REPL, i.e. the game Read-Eval-Print Loop.

In our game, read, eval and print are separate functions called game-read, game-eval and game-print, all housed under one enclosing function called game-repl. Thus, game-repl calls game print, which takes as argument the resuts of game-eval, which in turn takes as argument game-read. We can express this like so, using Python equivalent code (hence it’s easier to understand):

def game-repl():
game-print(game-eval(game-read (read-user-input()))
game-repl()

What can we infer from the above snippet?

  • The function game-repl calls itself recursively, creating an infinite loop.
  • The expressions are evaluated in normal order, i.e. each function is called starting from the innermost to outermost, right to left.
  • Thus the order in which the functions are called is as follows:
1. read-user-input() : Takes what the user types in the command line interface and passes it to the next function game-read as an argument.2. game-read(): Takes the string of text typed by user, and converts it into a command understandable by the next function, game-eval.3. game-eval(): Takes the command, checks if it is an allowed one, and executes it. This function returns whatever was returned by the command execution.4. game-print(): Takes the result of the command execution, and prints it in a readable format for the player.5. game-repl(): The REPL is called again, and the process repeats, starting from 1.

After reading what each function does, you’ve probably been thinking that most of the functionality of the game lies in the game-eval function.

You’re absolutely right. The ‘commands’ executed by game-eval handle all of the game’s defined logic. In fact, at the program level, game-eval() itself takes the user input as an argument, responds to the user input and returns the output to show the user. The other components of the REPL merely provide an interface with the user — game-read() converts user input into the program’s language, and game-print() converts the program’s language into the user output, in the user’s language.

File structure:

How does game-eval process user input and return the required output? It uses various other functions and variables to make this possible. These functions and variables are organised as follows into files:

  • Game UI Actions: (game-uis.lisp)
    user inputs passed to eval are commands, which are various functions defined in this file. Each of these functions in some way or the other, change the game state, which are variables defined in the next file.
  • Game State: (game-state.lisp)
    This file contains a set of mutable data-structures that represent the state of the game — in our case, where the player is in the world, different aspects of the character’s state such as objects he has collected. The game state thus includes the things that change as the player plays the game. In terms of software, this game state consists merely of variable definitions.
  • Game Initialization Data: (game-data.lisp)
    This file consists of game initialization data. The different places and their descriptions, the objects there and the different pathways.
  • Scene Descriptors: (scene-descriptors.lisp)
    This file contains functions that process the variables in game-data along with game-state to describe the scene that can be shown to the user for his/her understanding — i.e. to re-render the game world.

What do these files actually contain?

If you haven’t already looked through the GitHub code, you may be longing for a more concrete idea of what each file contains and how the functions are implemented.

Game UI Actions:
This file contains a function for each of the actions that a user can carry out. In our simple version of the game, these are also the commands that the user can type to interact with the game world.

1. look: describe the current place
2. walk direction: walk and follow a path (east, west etc.)
3. pickup object: pickup an object in the current place
4. inventory: describe what the player is carrying
5. weld subject object: weld subject to the object if allowed
6. dunk subject object: dunk subject into object if allowed.
7. splash subject object: splash subject onto onto object

It also contains a variable called *allowed-commands*, which contains a list of the commands that the user is allowed to execute, i.e. the above commands. This is for security purposes, to ensure that the user cannot type other commands in the REPL.

Game State:

* location: where the player is (i.e. which node)
* inventory: what the player is carrying (i.e. objects)

Game Data:
We use a space graph to represent the places in the game, i.e. a graph consisting of nodes (places) and edges (pathways). We use a Lisp a-list (association list) for these data.

* nodes: contains the places and their description as an a-list
* edges
: contains the description of pathways to and from nodes
* objects
: contains the list of objects that can be picked up
* object-locations
: contains the objects and their location (i.e. which node)

Scene Descriptors:
These functions describe the data in game-data and game-state in human-readable formats, i.e. as a sentence in the English language as output in the user’s console.

* describe-location: describes any given node in readable form.
* describe-paths
: describes a given edge (path) with direction and point of entry
* describe-objects
: describes the objects and where they lie, given a node/place
* objects-at

How do these components work together?

  1. game-eval calls one of the the game UI action functions which look at the current state of the game (game-state variables) and the game world (game-data variables) and change it in the required ways.
  2. Then the scene descriptors are called which describe the game state and game world in human-readable form and pass it to game-print to be displayed as output

Here’s a flowchart to visualize that:

Flowchart representing how the different components work together

Insights and Extensions

Now that you have an idea how this game was structured, let’s look at interesting features and functions that we can add. The best part of having a good architecture is that it can be modified or extended easily without changing the overall structure or schema. In fact, I find architecting solutions exciting for the very reason that you get to design structures which make it easy to assimilate new features without having to redesign the structure to accommodate these features.

Obviously, adding new actions in the game is as trivial as adding a new a game UI action, and corresponding variables in game-state if required. The game world can be extended by changing the game-data variables.

Here are a few other interesting things you could do to make the software structure of this game interesting.

  • Diffing scene graph
    In games where the output consists of many visual elements whose rendering is slow (for e.g.: using SVG in the DOM to render graphics), then one can make the rendering faster by rendering only the things that have changed and leave the things that haven’t changed as is. This algorithm that finds what has changed is called a diffing algorithm, and would have to be reasonably fast. The diffing is done on a scene graph (or a virtual DOM in the case of SVG via DOM) which is a data structure that represents the visual state of the screen.
  • Functional game
    As described in detail in my article on Modelling Worlds using FP, one could easily figure out how to design games that do not change state, or have side-effects. Thus, like the game-data variables, even the game-state variables would then become immutable. Multiple performance optimizations can be carried out to make this somewhat practical.
  • New forms of user input
    What if we wanted to use the arrow keys instead of typing walk east, walk north and so on? We could easily modify game-read to listen for such user input and convert it into these commands that game-eval understands.
  • Metaprogramming
    In the original version of the book in which this game appeared, the author teaches you to create a macro that allows you to add game functions during run-time! I.e., Game UI Actions like weld, dunk and splash are created by a ‘function’ while the game runs —a runtime feature generation of sorts.

Concluding Remarks

Building this basic game taught me a lot about Lisp and architectures, very early on in my programming experience.

I started building this game while learning Lisp using Conrad L. Barski’s book Land of Lisp, which taught me about various programming paradigms apart from the CLISP syntax by having me code games, web servers and other applications in CLISP in a hands-on manner. I found the book extremely fun to work through.

Thus, the rights to the concept of this game (The Wizard’s Adventure) lies entirely in the hands of the author of that book, and over time I have simply extended it to learn about the software structure and game mechanics of a text-game.

--

--

Shaaz Ahmed
The Software Firehose

Every reader should ask himself periodically “Toward what end, toward what end?” — but do not ask it too often lest you pass up the fun of programming. — Perlis