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:
To keep it simple, just install everything from the Devel
and Python
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: May 31, 2024