How To

Mammoth Cave
 
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.

Sound familiar? If you’re an old-school gamer, you’ve probably seen these words hundreds of times. They’re the starting point for Colossal Cave Adventure, the first major computer game. It was created in 1977 by Willie Crowther and Don Woods and quickly became a phenomenon in those pre-GUI, pre-Internet years. What helped the game spread from one data center to another was the fact that the source code was freely available.

The last version of the code from the original authors was written in 1995, and if you’re like us, you're hearing this story and wondering where that code is now. With the encouragement and permission of the authors, open source pioneer Eric S. Raymond has ported the code to his GitLab account. In this article, we’ll show you how to build and run that code.

Before we get started, though, a brief promo for more awesome things: this article and the associated video and Docker image were inspired by the Command Line Heroes podcast, Season 2, Episode 1.

This episode of the podcast covers Colossal Cave Adventure along with more info on the history of gaming. Check out the show, available at your favorite podcasteria. (A big thanks to Saron and the Command Line Heroes gang for telling us the story and piquing our interest in the code.)

Let's get started.

Cloning the repo and building the code

The code lives at https://gitlab.com/esr/open-adventure

 

From here, of course, you need to clone the repo:

git clone https://gitlab.com/esr/open-adventure.git

Once you've cloned the repo, switch to that directory and run make...which will almost certainly fail because there are a couple of libraries that may not be installed on your machine. 

doug@dtidwell-mac:~/Developer/open-adventure $ make
./make_dungeon.py
Traceback (most recent call last):
  File "./make_dungeon.py", line 13, in <module>
    import sys, yaml
ImportError: No module named yaml
make: *** [dungeon.h] Error 1

The crucial libraries are the Python YAML library and libedit. To complicate things, the way you install those libraries is different on Linux, the Mac, and Windows.

Linux

We'll tackle Linux first. Use the install manager for your distro to install the package python-yaml:

[doug@rhel-7 open-adventure]$ sudo yum -y install python-yaml
Loaded plugins: langpacks, product-id, search-disabled-repos, subscription-manager
Resolving Dependencies
--> Running transaction check
---> Package PyYAML.x86_64 0:3.10-11.el7 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

========================================================================================================================
 Package                 Arch                    Version                      Repository                           Size
========================================================================================================================
Installing:
 PyYAML                  x86_64                  3.10-11.el7                  rhel-7-server-rpms                  153 k

Transaction Summary
========================================================================================================================
Install  1 Package

Total download size: 153 k
Installed size: 630 k
Downloading packages:
PyYAML-3.10-11.el7.x86_64.rpm                                                                    | 153 kB  00:00:00     
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : PyYAML-3.10-11.el7.x86_64                                                                            1/1 
  Verifying  : PyYAML-3.10-11.el7.x86_64                                                                            1/1 

Installed:
  PyYAML.x86_64 0:3.10-11.el7                                                                                           

Complete!

Next, install the package libedit-devel:

[doug@rhel-7 open-adventure]$ sudo yum -y install libedit-devel
Loaded plugins: langpacks, product-id, search-disabled-repos, subscription-manager
Resolving Dependencies
--> Running transaction check
---> Package libedit-devel.x86_64 0:3.0-12.20121213cvs.el7 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

========================================================================================================================
 Package                 Arch             Version                           Repository                             Size
========================================================================================================================
Installing:
 libedit-devel           x86_64           3.0-12.20121213cvs.el7            rhel-7-server-optional-rpms            33 k

Transaction Summary
========================================================================================================================
Install  1 Package

Total download size: 33 k
Installed size: 52 k
Downloading packages:
libedit-devel-3.0-12.20121213cvs.el7.x86_64.rpm                                                  |  33 kB  00:00:00     
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : libedit-devel-3.0-12.20121213cvs.el7.x86_64                                                          1/1 
  Verifying  : libedit-devel-3.0-12.20121213cvs.el7.x86_64                                                          1/1 

Installed:
  libedit-devel.x86_64 0:3.0-12.20121213cvs.el7                                                                         

With those in place, you should be able to type make and build the code: 

[doug@rhel-7 open-adventure]$ make
./make_dungeon.py
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/include/editline    -c main.c
main.c: In function ‘do_command’:
main.c:1152:9: warning: implicit declaration of function ‘strncasecmp’ [-Wimplicit-function-declaration]
         if (strncasecmp(command.word[0].raw, "west", sizeof("west")) == 0) {
         ^
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/include/editline    -c init.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/include/editline    -c actions.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/include/editline    -c score.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/include/editline    -c misc.c
misc.c: In function ‘get_motion_vocab_id’:
misc.c:362:13: warning: implicit declaration of function ‘strncasecmp’ [-Wimplicit-function-declaration]
             if (strncasecmp(word, motions[i].words.strs[j], TOKLEN) == 0 && (strlen(word) > 1 ||
             ^
misc.c: In function ‘get_vocab_metadata’:
misc.c:457:5: warning: implicit declaration of function ‘strcasecmp’ [-Wimplicit-function-declaration]
     if (strcasecmp(word->raw, game.zzword) == 0) {
     ^
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/include/editline    -c saveresume.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all  -c dungeon.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all  -o advent main.o init.o actions.o score.o misc.o saveresume.o dungeon.o  -ledit -ltinfo  

You'll get a couple of warnings, which you'll probably ignore. (Hey, the code compiled, didn’t it?) You should now have an executable named advent.

Mac

On the Mac, it's a little more complicated. For the Python YAML library, just type sudo -H pip install PyYAML

doug@dtidwell-mac:~/Developer/open-adventure $ sudo -H pip install PyYAML
Collecting PyYAML
  Using cached https://files.pythonhosted.org/packages/9e/a3/1d13970c3f36777c583f136c136f804d70f500168edc1edea6daa7200769/PyYAML-3.13.tar.gz
Installing collected packages: PyYAML
  Running setup.py install for PyYAML ... done
Successfully installed PyYAML-3.13

For libedit, if you use MacPorts, sudo port install libedit does the trick. Ignore any warning error messages that come up:

doug@dtidwell-mac:~/Developer/open-adventure $ sudo port install libedit
Warning: port definitions are more than two weeks old, consider updating them by running 'port selfupdate'.
Warning: xcodebuild exists but failed to execute
Warning: Xcode does not appear to be installed; most ports will likely fail to build.
--->  Computing dependencies for libedit
--->  Cleaning libedit
--->  Scanning binaries for linking errors
--->  No broken files found.
--->  No broken ports found.

If you use Homebrew, type brew install libedit:

doug@dtidwell-mac:~/Developer/CLH/S2E1 $ brew install libedit
Updating Homebrew...
Ignoring path homebrew-cask/
To restore the stashed changes to /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask run:
  'cd /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask && git stash pop'
==> Auto-updated Homebrew!
Updated 2 taps (homebrew/core, homebrew/cask).
==> New Formulae
skopeo                                   ucloud
==> Updated Formulae
ghostscript ✔       double-conversion   libphonenumber      pdfsandwich
kubernetes-cli ✔    eslint              libspectre          phpunit
annie               folly               lnav                pqiv
apache-drill        fonttools           logentries          ripgrep
at-spi2-atk         grunt-cli           mdcat               terragrunt
atk                 imagemagick         media-info          webpack
atomicparsley       imagemagick@6       mps-youtube         xmount
bwfmetaedit         kustomize           msgpack             youtube-dl
cmdshelf            libimobiledevice    nano
==> Deleted Formulae
llvm@3.7

==> Downloading https://homebrew.bintray.com/bottles/libedit-20180525-3.1.high_s
Already downloaded: /Users/doug/Library/Caches/Homebrew/downloads/44dfc51536ebc0eede0404570dae31f5d2812eb16b2eaf3ac33ad31eb8f7714f--libedit-20180525-3.1.high_sierra.bottle.tar.gz
==> Pouring libedit-20180525-3.1.high_sierra.bottle.tar.gz
==> Caveats
libedit is keg-only, which means it was not symlinked into /usr/local,
because macOS already provides this software and installing another version in
parallel can cause all kinds of trouble.

For compilers to find libedit you may need to set:
  export LDFLAGS="-L/usr/local/opt/libedit/lib"
  export CPPFLAGS="-I/usr/local/opt/libedit/include"

For pkg-config to find libedit you may need to set:
  export PKG_CONFIG_PATH="/usr/local/opt/libedit/lib/pkgconfig"

==> Summary
?  /usr/local/Cellar/libedit/20180525-3.1: 53 files, 510KB

Be aware that whichever tool you use, you'll have to add the directory with the libedit.pc file to your PKG_CONFIG_PATH environment variable. In the screen capture above, Homebrew gives you the command to use. Simply cut and paste the export command. Be aware that MacPorts installs libedit in a different directory (/opt/local/lib/pkgconfig), so set the variable accordingly. 

With those tedious steps behind you, running make should work. The warning messages you'll get are different, but the carelessness with which you ignore them should be the same. 

doug@dtidwell-mac:~/Developer/open-adventure $ make
./make_dungeon.py
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/local/Cellar/libedit/20180525-3.1/include -I/usr/local/Cellar/libedit/20180525-3.1/include/editline  -c main.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/local/Cellar/libedit/20180525-3.1/include -I/usr/local/Cellar/libedit/20180525-3.1/include/editline  -c init.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/local/Cellar/libedit/20180525-3.1/include -I/usr/local/Cellar/libedit/20180525-3.1/include/editline  -c actions.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/local/Cellar/libedit/20180525-3.1/include -I/usr/local/Cellar/libedit/20180525-3.1/include/editline  -c score.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/local/Cellar/libedit/20180525-3.1/include -I/usr/local/Cellar/libedit/20180525-3.1/include/editline  -c misc.c
misc.c:150:18: warning: passing an object that undergoes default argument
      promotion to 'va_start' has undefined behavior [-Wvarargs]
    va_start(ap, blank);
                 ^
misc.c:142:62: note: parameter of type 'bool' is declared here
void pspeak(vocab_t msg, enum speaktype mode, int skip, bool blank, ...)
                                                             ^
1 warning generated.
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/local/Cellar/libedit/20180525-3.1/include -I/usr/local/Cellar/libedit/20180525-3.1/include/editline  -c saveresume.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all  -c dungeon.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all  -o advent main.o init.o actions.o score.o misc.o saveresume.o dungeon.o  -L/usr/local/Cellar/libedit/20180525-3.1/lib -ledit

Windows

Finally, if you’re using Windows, you’ll need to go to the Cygwin website and click the Install link: 

The Cygwin website

To keep it simple, just install everything from the Devel and Python packages.

Installing Cygwin packages

With that done, just type make and you’re all set:

doug@DOUGTIDWELL4497 ~/open-adventure
$ make
./make_dungeon.py
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/include/editline  -c main.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/include/editline  -c init.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/include/editline  -c actions.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/include/editline  -c score.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/include/editline  -c misc.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all -I/usr/include/editline  -c saveresume.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all  -c dungeon.c
cc -std=c99 -D_DEFAULT_SOURCE -DVERSION=\"1.4\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all  -o advent main.o init.o actions.o score.o misc.o saveresume.o dungeon.o  -ledit -lcurses

Notice that the Windows/Cygwin ports of gcc and make didn’t generate any errors at all.

Playing the game

Now you’re ready to play the game! Brace yourselves: Colossal Cave Adventure combines the visual appeal of the command line with all the excitement of typing. Run the advent executable to get started. First you’re asked if you want instructions. Type yes to get some background and see the words that inspired a generation of gamers:  

doug@dtidwell-mac:~/Developer/open-adventure$ advent

Welcome to Adventure!!  Would you like instructions?

> y

Somewhere nearby is Colossal Cave, where others have found fortunes in
treasure and gold, though it is rumored that some who enter are never
seen again. Magic is said to work in the cave. I will be your eyes
and hands. Direct me with commands of 1 or 2 words. I should warn
you that I look at only the first five letters of each word, so you'll
have to enter "northeast" as "ne" to distinguish it from "north".
You can type "help" for some general hints.  For information on how
to end your adventure, scoring, etc., type "info".
                 - - -
This program was originally developed by Willie Crowther.  Most of the
features of the current program were added by Don Woods.

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.

Exploring the building seems like a reasonable place to start. You can type enter building to make things explicit, or just type the word enter:

> enter

You are inside a building, a well house for a large spring.

There are some keys on the ground here.

There is a shiny brass lamp nearby.

There is food here.

There is a bottle of water here.

All of those things sound useful, so go ahead and take everything:

> get keys

OK

> get lamp

OK

> get food

OK

> get water

OK

At any time, you can use the inventory command to see what you’re carrying:

> inventory

You are currently holding the following:
Set of keys
Brass lantern
Tasty food
Small bottle
Water in the bottle

Notice that the command lists the water in the bottle separate from the bottle itself. So maybe the bottle is useful whether it has any water or not. Hmmm. And the food, whatever it is, is tasty. So we’ve got that going for us, which is nice.

Pro tip: As explained in the instructions, Colossal Cave Adventure only looks at the first five characters of a command. That means you can type inven to do the same thing:

> inven

You are currently holding the following:
Set of keys
Brass lantern
Tasty food
Small bottle
Water in the bottle

This shortcut allows you to see your inventory 44% faster. You’re welcome.

Another tip: Let’s just say that maybe it’s not a good idea to pick up everything in the building. Or maybe it is. You’ll have to play the game to find out.

Now that you’re loaded up with provisions, type leave to go back outside.

> leave

You're in front of building.

Now you can start exploring the world. Type west to, well, go west:

> west

You have walked up a hill, still in the forest.  The road slopes back
down the other side of the hill.  There is a building in the distance.

(You can also just type w.)

One of the great things about the game is that it requires your active imagination. For example, the narrative here suggests that the building in the distance is the one you just visited. It’s surprisingly compelling, especially considering that it’s a command-line game originally written 40+ years ago. To keep track of things, most players grab a sheet of paper and draw a map of the world as they go.

Typing quit and yes takes you out of the game. Unless you’ve explored the game beyond what we’ve done here, you’ll get a snarky message about your pathetic score:

> quit

Do you really want to quit now?

> y

OK

You scored 32 out of a possible 430, using 11 turns.

You are obviously a rank amateur.  Better luck next time.

To achieve the next higher rating, you need 14 more points.

Our gift to you: a Docker image

As an added bonus, we’ve created a Docker image at quay.io that contains the game and its source code:

That image lets you play the game without building the code yourself. If you have Docker installed and running on your machine, docker pull quay.io/rhdp/open-adventure downloads the image. Create a container from it, SSH into the container, run ./advent, and off you go.

Summary

Colossal Cave Adventure had an enormous impact on gamers and gaming. Forty years later, you can still see what all the fuss was/is about. Like any good game, it’s complicated and demands your concentration. If you’ve gone through the steps here (or just pulled the Docker image), you can follow the same treacherous paths as many thousands of gamers before you. Enjoy the adventure!

Last updated: February 28, 2019