Advent of Computing - Episode 68 - Zork
Episode Date: October 31, 2021Make sure you have some extra batteries for your lamp, this episode we are delving into the depths of Zork. Written in 1977 Zork would quickly become the epitome of text based adventures, pushing asi...de all competitors. A lot of this comes down to it's simple gameplay, and the simple fact that Zork is fun to play. But lurking deeper into the game is a hidden treasure. Ya see, the other huge part of Zork's success was it's portability. That was made possible thanks to some sick programming tricks, and a virtual computer called the Z-machine. Selected Sources: https://sci-hub.se/10.1109/MC.1979.1658697 - Early article from IEEE https://web.archive.org/web/20060427000213/http://www.csd.uwo.ca/Infocom/Articles/NZT/zorkhist.html - Tim Anderson's Zork history https://archive.org/details/a2woz_Zork_I_1981_Infocom_r75 - Go play Zork Â
Transcript
Discussion (0)
You just finished work for the evening and make your way home.
You walk up to your small white house, but before going inside, you decide you should check your mailbox.
To your surprise, there's a strange, unmarked leaflet inside, no postage or anything.
You grab the leaflet, unfold it, and it reads, quote,
Zork is a game of adventure, danger, and low cunning.
In it, you will explore some of the most amazing
territory ever seen by mortal man. Hardened adventurers have ran screaming from the terrors
contained within. In Zork, the intrepid explorer delves into the forgotten secrets of a lost
labyrinth deep in the bowels of the earth, searching for vast treasures long hidden from That can't be right.
Looking up, you see that more has kind of gone wrong.
Your door is now boarded shut, and the rubber mat in front of your door reads,
Welcome to Zork.
It appears that you're not quite home.
You've stumbled into some interactive fiction.
Now, that long passage, at least the quoted part, is how the game Zork describes itself in-game.
It's an accurate description, at least as far as what the player will normally see during gameplay.
But there's a secret layer to this dungeon. What most adventurers miss out on are the
technical treasures and wonders buried just a little bit deeper inside of Zork itself.
Welcome back to Advent of Computing.
I'm your host, Sean Haas, and this is episode 68, Zork.
Today, we're wrapping up our celebration of Spook Month with a dive into the spooky fantasy world depicted inside the game, Zork.
Now, as far as text-based adventure games go, Zork is probably one of the most popular examples of the genre.
adventure games go, Zork is probably one of the most popular examples of the genre. It served as a good foundation for Infocomma, Juggernaut, and PC gaming in the 80s and early part of the 90s.
This digital empire would all start with Zork, a very strange and technically fascinating game.
Now, Zork is a text-based adventure game, but you shouldn't be tricked into thinking that
it's simple. Zork is anything but simple. It is relatively complex gameplay that covers a
sprawling world, and it's just as complex under the hood. Zork has been ported to just about
every platform you can think of. The game really has a wide reach. It even shows up inside other video games.
I think Call of Duty actually has an easter egg where you can play Zork inside that game.
A big reason for the spread is that Zork itself runs inside a virtual machine. That's right,
Zork is such a complex program that it even has its own virtualized environment. That's
not something
you really see often with any video games, let alone a text-based adventure game. It might seem
out of place for something as deceptively simple as a text interface to have a virtual machine
dedicated to it. But there is a deeper purpose to this madness. Zork's authors describe the game as a computer fantasy simulation.
It's meant to be an immersive and engrossing experience, and even decades later, the game
still retains a certain feel of wonder. From breaking into a small house, delving into the
underground empire, to being eaten by groos and other nasties that lurk in the shadows,
to being eaten by groos and other nasties that lurk in the shadows,
Zork does really have a way of pulling you in.
So grab your trusty sword and, hey, maybe you'll need a sack to put some treasure in.
Today, we're going to be looking at Zork the game, but more importantly, Zork the program.
We're going to explore how project goals influence software design, how sometimes a radically complex solution can really pay dividends,
and how the early internet made games like Zork possible. Most importantly, we'll try to parse
out what makes Zork so much fun to play decades later. It's simple to just say that ARPANET was
the predecessor to the modern internet. That's an easy tagline. Maybe if you're feeling a little
more verbose, you could talk about the difference between public versus private ownership, or maybe
the difference in communication protocol. Those are both valid approaches, but I think it misses
the really big picture. In practice, ARPANET was just cut from a different cloth. Part of it was
the size, part of it was the user base,
and part of it was timing. The internet has managed to connect just about everyone in the
world. It's become a great place to build small communities of like-minded people.
But by contrast, ARPANET was a lot closer to one community of like-minded people.
There was also this rampant type of openness to ARPANET that we don't get
online anymore. Yeah, it was a government-funded research network and it was hard to access if you
weren't in the right places, but over time that kind of just became a cover story. There weren't
really malicious actors on ARPANET. First off, being a malicious actor on ARPANET was a federal crime,
so you didn't really want to do that.
You weren't supposed to mess around with a government network.
Plus, the community was so small that it could effectively self-police
if someone was getting out of line.
So users were nearly free to come and go.
It was just sort of assumed that you knew what you were doing.
Mainframes were somewhat open to perusal, at least files could be really easily browsed.
Some systems even had their doors flung wide open for any passersby. This made the perfect
storm to allow new phenomena to spread like wildfire amongst the small community.
One of the first sparks came in 1976, and it was called Colossal
Cave Adventure. This was one of the first text-based adventure games. Now, I've kind of
gone back and forth on if Adventure was actually the first text-based adventure game or not.
There are a few earlier examples, there are some far stranger examples, but Adventure was at least
the first popular member of this species. It was
initially written by Will Crowther at BBN, a computer company based in Massachusetts. Crowther
developed Adventure on a computer at BBN that was connected to ARPANET. Soon thereafter, the game was
discovered by Don Woods, a programmer at Stanford University all the way out in California. He was
browsing the ARPANET one day, found the file,
and became instantly engrossed. In the coming months, he expanded the game further. In 1977,
this souped-up version hit the network. Nearly everyone with access to the ARPANET was talking
about Adventure. Tradition holds that the entire computer industry was set back by at least two weeks just by this game.
It's pretty easy to see why Adventure had this kind of impact.
It was just fun to play. It had a certain addictive quality to it.
In Adventure, you play as, what else, an adventurer.
Gameplay revolves around spelunking the colossal cave in search of treasure,
but a series of puzzles, hazards, and other dangerous cave dwellers stand in your way.
The twist that pulled in so many computer nerds was the scoring system.
Solving puzzles, slaying beasts, and collecting treasure,
and more importantly, returning booty to the surface, all earn you points.
The finalized game has a maximum of 350 possible points you can earn, so you have this incentive to keep scrounging for every last point. It's
really like a big immersive puzzle, and in 1977 it was a totally new experience to see on a computer.
Portability also added to Adventure's popularity. The game was fully textual. Everything
was explained to the player as a chunk of text, and all player actions were controlled by typing
in short commands. This was roughly the same kind of interface used on every computer hooked into
the ARPANET, so you didn't need anything special to experience adventure. Adding to this, the Colossal Cave
was implemented in Fortran. Everyone used Fortran, or at least if you didn't use it,
you had access to a Fortran compiler really easily. So once again, there wasn't really a
barrier to entry. All you needed were the source files, which were already spreading all around the ARPANET.
Thus, our perfect storm was constructed.
And the computer industry at large ground to a halt as programmers would disappear into the colossal cave for days at a time.
This effect was particularly profound amongst a group of hackers at MIT's Dynamic Modeling Group.
The DM group, as it was sometimes called,
was something of a special place.
The computer science-based research groups and labs at MIT in this era
were working on really cutting-edge stuff,
but they also had this unique air to them.
One of the big things brewing
inside the Dynamic Modeling Group around this time
was called the Incompatible Time-Sharing System, or ITS.
I did a whole episode about this a while back, so if you want to know more, I'd recommend checking that out.
The broad strokes are that ITS was a system designed by hackers for hackers. It incorporated
some real cutting-edge features while focusing on user freedoms. There were no passwords or
permissions on ITS. Anyone could hop onto the
system if they knew its address on the ARPANET. ITS was very much built as a playground for new
ideas. This kind of culture primed the DM group to have a very particular response when they came
into contact with Adventure. Call it a can-do attitude, or maybe just an expression of the hacker ethic.
Tim Anderson was one of the early victims of the Colossal Cave, and this is how he explained the
phenomenon. Quote, When Adventure arrived at MIT, the reaction was typical. After everyone spent a
lot of time doing nothing but solving the game, the true lunatics began to think about how they
could do it better.
Adventure was written in Fortran, after all, so it couldn't be very smart. It accepted only two-word commands, it was obviously hard to change, and the problems were sometimes not everything one
could desire. I was present when Bruce Daniels, one of the DMers, figured out how to get the last
point in Adventure by examining the game
with a machine language debugger. People were obsessed with Adventure. There's no other way
to put it. And I particularly like that last part, that one student went so far as debugging
the program just to squeeze out that last point. The bug really bit some people hard. Perhaps more interesting here
is Anderson's quip about Fortran. If you've never used Fortran, then maybe you should consider
yourself a little bit lucky. It's a strange and archaic tongue, to be sure. Besides just being
outdated, even by 1970s standards, Fortran wasn't really the best tool for text-based
anything. It was designed for handling math, numeric data, and basically nothing else.
Fortran can deal with text-based stuff. It can do a little bit of string manipulation,
but that's getting outside of its comfort zone. That's just the surface-level issue
with Fortran for this application. Once you start actually profiling the problem a little deeper,
things don't get much better. In fact, I'd say they get a lot worse.
I'm going to put the cart a little bit before the horse here, but I need to introduce one
crucial source. The article Zork, a computerized fantasy simulation
game, was published in IEEE in 1979. Besides discussing Zork, which we'll get to in a few
minutes here, the article gives a pretty comprehensive operating theory of text-based
adventure games in general. The article describes these types of games as an exercise in data management,
something akin to a hyper-intelligent database. Further, the authors posit that objects are
crucial when implementing this type of game. Now, I don't mean objects in the physical sense.
We're talking about objects the programming construct. In the simplest possible terms,
the programming construct. In the simplest possible terms, and this is a radical simplification so don't get too mad at me, objects are reusable data structures that can follow and inherit
some predefined template. Each object can contain a mix of data, executable code,
information about its relationships to other objects, and even entire sub-objects. It's
basically a really flexible format for defining a system, and it's also a really modular and
expandable framework. It's a great way to, say, model a network of interconnected rooms all
containing items. Fortran just plain doesn't support objects. At least it didn't at that
time period. I think there's a few modern versions that do kind of have object support.
You got a bunch of different data types for numbers, you got booleans, you even got arrays
of any dimensions you want. Plus, you could encode characters and strings as variables.
you want. Plus, you could encode characters and strings as variables. That's a usable set of variable types, but it's really basic. It doesn't give you a lot of advanced options. You can't,
say, make your own new data types, and you certainly can't use objects. Talking more
concretely, this inflexibility majorly impacted how Colossal Cave was written.
This is a fabulous time to mention that we still have the original source code for Adventure.
Both Crowther's original version and Woods' expanded code are readily available online.
If you like Fortran, then feel free to take a look.
Otherwise, you might want to steer clear for your own safety,
unless I guess you're looking
for something really scary to do on Halloween. Anyway, Adventure the Program is composed of two
main components, the actual code and a single data file. The code component is something like
a game engine, at least sort of. It's essentially a big and somewhat complicated state machine that models a fantasy
cave. It handles taking user inputs, giving textual feedback, and it tracks progression of states.
When I'm talking about states here, I'm just talking about a small set of simple variables.
Things like what room the player is in, what items the player is carrying, where dwarves may be prowling, and the all-important score.
There are also a handful of other relevant variables, but that's the basic gist.
The data file, which in some source code comments is called the database, contains the bulk of game data.
It describes every room in the Colossal Cave, the items that can be in rooms,
vocabulary terms, and how everything is connected. Basically, it's all the text you see during the
game of adventure. It's all stored in that one file. But calling it a database, I think,
is a little bit of a stretch. When the file is actually loaded into memory, it just takes up a bunch of separate arrays.
Everything from rooms to items has its own array index.
So you can traverse and manage the dataset, but it's all done in a kind of clunky Fortran way.
That's no knock on Crowther or Woods.
It's just how Fortran can handle large sets of
interconnected data.
I think a good example of this Fortran way of life is how Adventure handles, well, how
it handles all user interactions.
I know that's kind of a large swath, but I'm going to break out just a small bit of
this.
Traversing rooms is the most common thing you do in adventure,
and that's handled by one big table of values.
This is basically a grid that lists which keywords the player must type at certain locations
to advance to another location.
It defines that if, say, you're in a room, then typing east will move you down a hallway.
Or that saying exit will let you leave the house where the game starts.
So far, pretty simple.
All of those interconnections, the mappings of keywords to progress the states,
that's defined in the data file.
But that's only part of the game's logic.
The huge omission here, the big glaring hole, is puzzles.
Well, that part of the program,
possibly the most fun part of the program,
isn't fully defined by the data file.
This is where the inflexibility part should start to make some sense. I think a
good example from early in the game is the snake puzzle. This is your spoiler warning in case you
haven't played the first few puzzles in Adventure. Anyway, you run into a big scary snake in the cave
pretty early on in the game. The first time this happens, you die. That's kind
of how puzzles and text-based games go. You eventually figure out that in another room,
you can catch a bird in a small cage. Then, if you release it, the bird will scare away the snake.
It doesn't make a lot of sense, but hey, it works. It's kind of funny. It's kind of the shtick to these types of games.
Well, here's the weirdness. The bird is defined in the data file. It's just an item. The snake
is also at least mentioned in the data file. It's part of a room's state. Specifically,
the snake room has a state with and without the snake. But there's nothing in the data file that defines
how you get from one state to another. The actual action of unleashing the bird on the unsuspecting
snake is all defined in the game's Fortran code itself. Specifically, it's a special case of the
throw action, which gets aliased to a number of synonyms that cover the
bird situation, such as release or drop. There's a line of code that checks if you're releasing the
bird and if the snake is in the room with you, and then it handles those interactions.
To mix this up and generalize a bit, what we're seeing is a coupling between the actual game data and the program.
You could write a custom data file for Adventure, but no matter how much of the data you change,
the bird will always scare off the snake if it's dropped in the right room. That's burned into the
code. It's baked into the binary. If you wanted the player to be able to, say, eat the snake,
baked into the binary. If you wanted the player to be able to, say, eat the snake, then you would have to modify some Fortran and recompile the game. While that's not the end of the world,
it's an annoying quirk. Adding new puzzles or any kind of new functionality means blowing up
the complexity of the program. By way of example, we can look at Adventure itself. The first version, written solely by
Crowther, was only 709 lines of Fortran. Woods expanded the game significantly. He added scoring
and some general features, but crucially, he added a new slate of treasures and puzzles to solve.
This bumped Adventure up to just shy of 3,000 lines of Fortran code.
In the long term, this kind of expansion can make a project hard to maintain,
and it can make future expansion more difficult to handle.
Every time you try to add complexity to the game, you end up with more and more complicated code.
I guess this is just a long way of saying that Anderson's
disdain for Fortran was, at least in this case, warranted. It just wasn't a good tool for the job.
Plus, there was a more fitting tool that was starting to see some use inside the dynamic
modeling group. This tool was a language called MDL. The actual meaning behind those letters is a little up for
debate. Officially, it stood for Model Development Language, but it was sometimes referred to as
More Data Types Than Lisp. Personally, I like that one. But many of the programmers who worked
with the language would simply call it MUDL. The language was developed by a team of programmers
at the DM Group and MIT's better-known AI Lab. In short, MUDL is what it says exactly on the tin.
It's LISP with more data types. The syntax and structure is a little bit different,
but in general, MUDL is modeled closely off LISP. M Muddle was built to be the primary language used within the
DM group and a native option for ITS. Lisp was used as a base because the older language had
great support for strings, plus was already kind of being used as the lingua franca of artificial
intelligence. Lisp was developed at MIT, so there's also this home team advantage to be aware of.
MUDL offered everything that Fortran was lacking. MDL was adept at handling strings for one thing,
but most importantly of all, MUDL had much better support for data types. I mean,
it should go without saying, it's in the name. You could define new data types on the fly, which was a huge boon in itself.
But MDL also offered actual objects and all the features that came along with that.
And, this is the big one,
Muddle was able to treat functions as variables.
This is a little subtle, but it makes a world of difference.
Muddle lets you define a variable, or an object property for that matter, as a function.
That function can be reassigned, moved around, and executed.
This would have a profound impact on what programmers were able to do with MDL.
So this is the setup. Call it another perfect storm. We have a wildly popular
game that blows up a pretty new genre. We have a group of wildly motivated hackers obsessed with
that game, and we have a new tool that's just waiting to be exploited. All the pieces are set
for something spectacular to happen. Zork proper begins with a core group of four programmers inside the dynamic modeling group.
We've already met Tim Anderson. He's one of the key programmers.
More importantly for me, Anderson wrote up a pretty comprehensive account of the history of Zork back in 1985.
As far as I'm concerned, that's the definitive first-hand account.
1985. As far as I'm concerned, that's the definitive first-hand account.
According to Anderson's account, the first person to actually devote code to keyboard was Dave Lebling. As soon as Adventure was quote-unquote solved, Lebling set to work on creating a new
text-based game. He already had some experience writing mainframe games. Most of the Zork group
did. Lebling started by tackling the most
front-facing part of any text-based adventure game. That's the command parser. This is the
code that handles user input. It's how players actually interact with the world. Lebling's
weapon of choice for this task was, of course, muddle. This parser showed promise, and this is
where Anderson gets involved with the new adventure.
Tim and Mark Blank, another programmer, took Lebling's command parser and started using it
to build a small virtual world. As Anderson recalls, quote, Mark and I, who were both in
the habit of hacking all night, took advantage of this to write a prototype four-room game. It has long
since vanished. There was a band, a band box, a peanut room, the band was outside the door playing
Hail to the Chief, and a chamber, quote, filled with deadlines, end quote. Now, Blank is a really
strange case of someone who became enamored with programming. And I think he's illustrative of a
larger point here. In 1977, he was already enrolled in medical school in New York. But, as he put it,
that was boring. Blank was planning on having a career in medicine, but wasn't a huge fan of the
endless lectures. He worked out his schedule so that he could just skip days worth of lectures every week
to drive up to MIT and crash the DM group's lab.
Blank just found programming to be much more fun than med school.
I want to pause here to highlight this as a trend that's forming.
Zork was never an official MIT research project.
It didn't start off with funding or any research goals. Its only goal was to make a fun computer
game in the mold of Colossal Cave Adventure. Everyone involved got in on the project because
it was the most fun thing they could think of. Blank is a great example here.
He was driving back and forth between med school in New York and MIT just to program with his
friends. He wasn't doing it for money or to publish a paper, this was all in the pursuit of fun.
We can see how this attitude is leaking into the code even at this proto-Zork stage.
What's the proof of concept look like?
Is it an example of traversing data structures or rendering descriptions of simple rooms?
No, it's a tiny world full of chambers.
There's even a room full of deadlines.
Even at the beginning, Zork is shaping up to be a labor of love. It's a program full of
fun and jokes. It just happens that it was backed up with some nifty data structures and some really
sound programming by some really good programmers. According to Anderson, this proto-Zork was quickly
dropped. It showed that a new adventure-ish game could be written in MUDL, but
was left lacking. It was an early beginning. After some playtesting, this initial code was scrapped
and the team set in on a larger project. A new member, Bruce Daniels, joined the group. This
brings us up to the pack of four that's usually referred to as Zork's Implementers, or simply as Imps.
But this four-person configuration here is a little misleading, at least to begin with.
The very first version of Zork, the game written after the four-room test version,
was actually written while Leblanc was on vacation. This also brings us to another
strange bit of terminology. I've actually been using the name Zork to reference the game that was taking shape in
1977 at the DM Lab.
It's not entirely clear when that name was first slapped on the program.
You see, Zork's name came kind of by accident.
According to Anderson, quote,
Zork was a nonsense word floating around.
It was usually used as a verb, as in Zork the Fweep, and may have been derived from Zorch.
Zorch is another nonsense word implying total destruction. We tended to name our programs with
the word Zork until they were ready to be installed on the system. End quote. The game was unfinished,
so it was just being called Zork as a placeholder. The intent was to rename it later, but that didn't
work out as expected. This seems like a simple enough explanation. These nonsense technobabble
words are pretty commonly used among programmers. You can even see relatives like foo, bar, or baz show up
in textbooks. But, you know me, I can't leave well enough alone. There's a strange detail here
that's a bit of a tangent, but I certainly think it's worth addressing. This comes down to Zorch,
Zorch, as in Z-O-R-C-H. Anderson says that Zork may have been derived from Zorch. Usually,
this kind of esoteric linguistic stuff is impossible to track down, but this is a case where we actually have a really good source to work off of. In 1975, the first version of the
jargon file was written. This contains a pretty exhaustive lexicon of
hacker words and phrases, and crucially, it was mainly contributed to by programmers at MIT and
Stanford. There are also archives of the jargon file that we can consult for period-accurate
words and definitions. During the first few years of activity, we have versions released
every few months, so we can get really granular with it. The general idea that I had here is that
if Zorch was in common use around this time, then it should be in the jargon file. And, well,
it's not. Zorch doesn't show up until the 1983 version of the file, which was later published
as the Hacker's Dictionary. This is canonically called version 1.5. Stranger still, version 1.4
references Zork in a definition for another text-based game. The final puzzle piece here
is that Anderson's written history of Zork was published
in a newsletter in 1985. So, let's put all of this together. We don't have a full picture,
but we do have some kind of complicated picture. Option one, Zorch was a word really specific to the dynamic modeling group.
Most of the authors of the jargon file within MIT were from the AI lab,
so conceivably there could be some missing DM group stuff.
Now, I'm kind of of two minds on this.
References to ITS, a very DM group-specific thing,
show up in very early versions of the file, so the jargon file should
be covering this area of slang, but hey, maybe it's an oversight. Option two is that Zorch wasn't
in use until the 1980s, and Zorch was an unrelated word used by a small group of friends. This is the
one I'm kind of leaning towards, but I don't think we can ever
prove either options. Anyway, this is all just a tangent to the larger history of Zork. I just
think this is an interesting side mystery. Moving back onto topic, the implementers blazed through
the first version of Zork at really a breakneck pace. Loebling was off on vacation for only two weeks.
By the time he returned, there was a playable version of Zork. At this point, it was still a
pretty small game, but the basics were in place. There was also a new philosophy around the game
taking shape. The team of implementers wanted Zork to be an immersive experience, one where
the players could sink into a simulated
world and forget about the computer. This was actually in pretty stark contrast to Colossal
Cave Adventure. In general, all commands in Adventure are two-word sentences. Go west.
Take lamp. Open cage. You can't give very complicated commands or the game will get confused.
It's not possible to say,
Hit the troll with the rusty lamp.
That would just result in an error message.
This was a known problem, but Crowther actually ascribed Adventure's success to it,
at least in part.
He had this to say in a 1994 interview.
Quote,
And why did people enjoy it?
Because it's exactly the kind of thing that
computer programmers do. They're struggling with an obstinate system that can do what you want,
but only if you can figure out the right thing to say to it. End quote. Adventure is more like
an intricate puzzle box. It has treasures tucked inside, but you need to figure out exactly how to open it.
That's accomplished through this rigid and almost stilted dialogue.
It's primitive, no doubt that's partly thanks to being written in Fortran,
but at least according to Crowther, that made it fascinating to a lot of programmers.
The problems here should be pretty plain to see.
This is a limiting format, and it's a bit of a grueling format to play.
Programmers do this kind of problem-solving day in and day out.
You can't really escape into a game if the game is just more of your normal existence.
It also does a disservice to the roots the text-based adventure games grew out of.
does a disservice to the roots the text-based adventure games grew out of. You see, whether we're talking Adventure or Zork or any other computerized fantasy game of this era, they all
draw from the same well. Dungeons and Dragons. These games were all written by a gaggle of nerds
after all. These early adventure games drew their high fantasy settings and some of
their mechanics from tabletop adventures like those played out in D&D. This even shows up in
the text-based interfaces. A game of Dungeons & Dragons plays out like a conversation between a
group of players and a dungeon master, or DM. A player will say what they want to do, the dungeon
master then interprets that within
the framework of D&D's rules and determines how to proceed. In the case of a complicated action
like combat, for instance, the player may need to roll some dice to determine an outcome.
In more simple cases like just moving around a map, the DM will just modify the game state.
But the core gameplay all comes down to a spoken
conversation. Players have to describe what they want to do, and the DM has to make sense of it.
Adventure functions like a digital dungeon master, but with a very limited scope and
limited vocabulary. You can end up spending more time trying to work out how to talk to Adventure
than actually solving the game. Zork's implementers didn't like that. They didn't think that it would
be fun or make the game popular if you're just trying to struggle with the program.
In the 1979 IEEE article I cited earlier, the Imps laid down a general framework for what they called a computerized
fantasy simulator. According to the team, there was one aspect of the game that had to be put
first and foremost. Quote, The heart of any CFS game is the ability to mimic omniscience.
By this we mean that the game should simulate the real world sufficiently well so that the player is able to spend most of his time solving the problems rather than solving the program.
End quote.
Put another way, Zork wasn't meant to be a puzzle box.
It was meant to be an actual adventure.
That's all fine and good as a concept, but with goals this broad, you tend to run into technical road bumps.
How can you make a computer seem smart enough for an adventure to feel immersive?
How can you, quote, mimic omniscience?
In D&D, for instance, a player might say, hey, I want to run up to that troll, I'm going to punch it in the face,
then while it's reeling from the blow, I want to pick its pocket. That's not a very complicated concept for a human to deal with.
Someone just wants to go have a confrontation with a troll. You have some steps, maybe something
about a chance to hit or damage comes into play, maybe some statistics about how hard it is to
steal from a reeling troll. This is the kind of interaction where a
human dungeon master can use their better judgment. You reference some tables, you generate some kind
of outcome to feed back to the player. That's easy for us to do. If you know how to play D&D,
if you know how to be a dungeon master, you can work this out pretty quick. But how do you translate that kind of knowledge into a computer
fantasy simulation? Well, Zork takes a few different angles of attack. One that I think
gets overlooked is that Zork, and for that matter Adventure, breaks everything down into small and
simple actions. You have to step through the game one room at a time. You can't move and interact with items at the same time.
This partly comes down to how the medium is structured,
but it also limits how players can interact with the world.
Everything has to happen in turns, and each turn can only accomplish one small task.
That alone really simplifies the programmer's job.
small task. That alone really simplifies the programmer's job. The second trick is that Zork radically limits what a player can actually do. Zork is, after all, just a computer program.
There will always be a limit to the realm of possibilities. The implementers, praise be,
got around this by carefully constructing the story of Zork. Once again, from IEEE, quote,
What it can do, however, is simulate enough of the universe to appear to be more intelligent
than it really is. This is a successful strategy only because CFS games are goal-directed.
As a consequence, most players try to do only a small subset of things they might choose
to do with an object if they really had one in their possession.
End quote.
If you pick up a key, you can use it to unlock something, you can pocket it, or you can toss
it.
All those eventualities are programmed into Zork, and looking at that list, that's just
three things, that's not a lot of options.
In the real world, you might try to give it to a friend, or's just three things, that's not a lot of options.
In the real world, you might try to give it to a friend, or get a copy made, or even scrape
your name into the surface of something using the key.
But if you're deep in a dungeon and find a key in some dusty desk, you probably aren't
thinking about what you can vandalize.
So Zork just doesn't need to have code for key scratching. Once again, these limitations
can be used wisely to limit the scope of programming needed. To complement this design
philosophy, Zork is also pretty smart when it comes to processing commands. Remember that the
text parser was the first thing written for the game. One of the tricks that made Adventure so slick was that it employed
synonyms. The take action could be invoked by typing take, but also by typing grab. It's just
a quick and dirty way to make things easier on the player. Zork took this a whole nother level
above. Sure, it had synonyms for actions and objects, but that was just the start. In general, a full command in Zork is composed of an action and up to two objects.
This gives you such hits as
Take the lamp.
Or unlock the door with the key.
Thrilling.
But you don't always have to follow that pattern.
To make things more manageable, Zork's parser will
try to aggressively assume what the player wants to do. If you're trying to do an action with only
one valid object within reach, Zork can figure out the proper result. If you're in a room with
a solitary lamp, you can just type grab and Zork will reply that you now have a lamp in your inventory. This extends to
more dynamic interactions. Let's say you run into a troll and decide that you don't want to talk
anymore. You may start off by telling Zork to attack the troll. If you have more than one weapon,
then Zork will ask what weapon you want to attack with. You then further specify your sword. That's an action in
two words, so Zork now has enough information to process your request and start slashing at the
troll. Next turn, instead of typing the full ATTACK THE TROLL WITH THE SWORD, you just type
ATTACK. Zork is smart enough to infer what you mean, and the troll slashing will continue.
The whole point with this tech system
is to make things easier on the player. Inspiration here is definitely, at least in part,
coming from D&D. A human dungeon master will very readily make assumptions about what a player is
trying to do. You could very well have the exact same situation where a player just says they want
to attack, the DM has to ask for some
clarification, and then moving forward can guess what the player will do next round. They're gonna
probably attack again. For us humans with our squishy, malleable brains, this is a dead simple
process, but computers have to be programmed carefully to do something this natural.
But that's just the textual part of Zork. As we
look deeper, we get into the more interesting details. I want to pull us back to one of the
core issues of Colossal Cave Adventure. How do you add a new puzzle? The only option for that game
was to extend some of the core parts of its source code. Zork took a very different approach, one that would prove to be much more flexible.
Down at the code level, things get a little muddy, at least in this early era of Zork.
Unlike Adventure, the newer text game didn't really break itself up into a separate game
engine that read and ran a data file.
All of Zork is defined as MDL code.
At first glance, that might make it seem that Zork suffers from the same fatal flaw as Adventure,
but don't be so hasty. Zork's source code is designed to be modular. I know that's a big
buzzword, but let me explain. On the surface level, Zork is broken up into multiple
MDL files. When you get down to it, this is kind of a convenience more than anything, but I think
it does serve an important purpose. In Adventure, everything was dictated by one big Fortran file.
Anytime you needed to change something, you had to modify the core part of the game engine because there was only one part of the game engine. It's all the core.
Zork spread things out into separate code files for the parser, room definitions, vocabulary,
items, actors, and so on. When you're adding a new item, you only have to edit the item file.
This saves you from trudging around in the depths of the text
parser when you're trying to find, say, the code for handling keys. But like I said, this is the
more superficial aspect. We can go deeper, and that's where we start to see the real modularity
at play. In Zork, just about everything is defined as an object. That includes locations, items, enemies, pretty much anything you see in the game has some object backing it up.
Even actions are defined as objects themselves, which gives you a whole lot of flexibility.
Zork is built up by defining new objects and then registering them with the game engine or referencing them somewhere else. So why does the whole object orientation of Zork matter?
This all comes down to how you expand the game. For this, I want to stick with the bird and snake
puzzle from Adventure, since I just think it's a good example. Remember that in Adventure, that puzzle's solution is hard-coded
into the game's drop item handling. Zork has an analogous glob of code for the drop command and
its many synonyms. However, it's not a very large glob of code. It's pretty simple. When you tell
Zork to release the bird, it would see that as a synonym for the drop command,
so the internal drop handler is invoked.
The handler gets the birdcage object and starts making some checks.
Each object is composed of a set of flags and properties that describe the item.
First off, drop makes sure that the item is actually being carried by the adventurer,
you know, in case you're trying to
do something funny. So far, seems fine. The next step is where Zork really sets itself apart from
adventure. The drop handler never checks if the player is trying to drop the bird. That's not
drop's job. It never checks any special cases at all. Instead, it checks to see if the object has its
own handler function. Muddle objects can contain functions, their own little blobs of code that can
be executed on a whim. In Zork, each object carries with it a small function that's invoked
whenever the player tries to perform an action on that object. This allows items to override the action's default behavior. Under normal circumstances,
a dropped object will fall to the floor. But for the bird and snake puzzle, there needs to be an
override. When you drop the bird from its cage, the bird's own handler gets called. It checks if
there's a snake in the room. If so, the bird goes
forth to scare off the snake and the puzzle is solved. You get some points. Otherwise, the bird
just plops on the floor and sits there. The key element here is that Zork's core functionality
doesn't need to be changed in order to add a new puzzle. Once the drop command was implemented,
it didn't need to get retouched for every new item interaction or new puzzle. Once the drop command was implemented, it didn't need to get retouched
for every new item interaction or new puzzle. Instead, all the new features were added inside
new items, enemies, and room objects. This might sound like it's just shifting around some code,
but it had major implications. For one, it made expanding Zork much easier than expanding Adventure.
You don't need to know how Zork's core programming logic works to add a new puzzle.
This also insulated Zork from at least some snafus and bugs. Adding some bad code for a
new object will just break that object. But it won't break any other part of Zork.
just break that object. But it won't break any other part of Zork. The bottom line here is that modularity opened up new options for Zork's implementers. That allowed the game to blossom.
The game saw massive growth through the summer of 1977. By fall, ARPANET users were starting to
notice something funny going on. Zork was being developed on a machine running ITS, an operating system totally
devoid of security and barriers. So computer nerds across the ARPANET started seeing this strange
Zork process running, and they would soon fall into the underground empire themselves.
Zork spread in a somewhat similar way to Adventure, but once again, there were major
differences between the two games. Adventure was so spreadable because everyone had access to a 4chan compiler. Zork
was written in MDL, a language that at the time only ran under ITS, and there was only one mainframe
that ran ITS. To play Zork, you had to connect to the dynamic modeling group's computer. Luckily,
the aforementioned lack of security made this an easy process, and so Zork enticed untold numbers
of adventurers. But this lack of portability would be something of an Achilles heel for the game.
The next phase of Zork's development takes us deeper into the realms of true madness. This final dive
all starts with the question of portability. Within a year of its initial release, or at least
its first sighting by outsiders, Zork already had a port. You see, Zork's implementers have been
trying to guard the source code for the game since day one, either to protect
their project or to keep players from getting spoiled on solutions. The code was hidden in a
mislabeled folder on ITS, but the source was also encrypted to keep prying eyes further at bay.
However, we are talking early ARPANET. Everyone online at this point was a programmer or hacker of some stripe.
It was only a matter of time before the code escaped. In 1978, Robert Supnik, a developer at
DEC, decided that they wanted that code. At least, we believe this was the perpetrator, the name
may be a little lost to history. Anyway, through some creative use of ITS and some free computer time, he was able to
track down and then decrypt the source. Sure, it was in a language that could only be compiled for
ITS, but it gave Supnik something to go off. Anderson describes the debacle in his Zork
history like this, quote, As a result of the purloined sources at DEC, a lunatic there decided to translate Zork into Fortran.
We had always assumed this would be impossible.
Muddle is very, oops, very different from Fortran and much more complicated,
and we'd used most of its features in designing Zork.
The guy who did it was mostly a hardware person, so perhaps he didn't
know what he was up against. At any rate, shortly after the great blizzard of 78, he had a working
version implemented initially for PDP-11s. Since it was in Fortran, it could run on practically
anything, and by now it has." This is mostly a sideshow to the main story. I don't really want to do another
code deep dive into a Fortran program today, but the Fortran port is important for two big reasons.
Every mainframe had a Fortran compiler. The wild port made Zork even more popular since now
anyone near a computer could experience Zork,
even without an ARP net connection.
As 1978 drug on, Zork became more and more popular amongst a certain crowd.
The 4chan port went a long way towards proving that there was an audience for this kind of software.
Just as important, it also proved that Zork didn't necessarily need to be written in MUDL.
But the implementers were about to face another problem.
Graduation.
Zork was nominally finished around 1979,
with the final mainframe version taking up just over one megabyte of memory.
That's a lot for the time.
It was a major achievement,
and the ragtag team of programmers wanted to do more. Mark Blank, Tim Anderson, Dave Lebling,
as well as a few other programmers around the CS department wanted to somehow stick together
and keep on hacking up projects. The only viable way to do this after graduation was to start some sort of company. They would need to create a productive outlet for their collective programming obsession.
In 1979, Infocom was formed.
The initial plan was to do something about software.
From Anderson's account, it seems like the real goal was just to keep having fun and hopefully find a way to make a living out of it.
Zork wasn't their first go-to, but it soon took center stage.
Joel Perez, one of the other MIT programmers that joined Infocom, and Mark Blank figured that it might be possible to get Zork running on a home computer.
I mean, some madman had ported it to Fortran, so anything
was possible, right? The market environment of the time played heavily into this next big leap.
In the 70s, the home computing market was relatively diverse, at least more so than the
current PC monoculture. The Apple II, TRS-80, and Commodore PET made up the majority of the market,
with Altair and Altair-like computers somewhere on the periphery. So ideally, any software
manufacturer would want to court multiple platforms at once. You really wanted to be
able to sell ports of your software for all major machines in the market. The other complicating
factor for Zork specifically
was the matter of memory. All home computers of the time period had RAM sizes measured in the
tens of kilobytes if you were lucky. Zork may not have any graphics, but it's still a pretty
complicated program. It has a whole lot of text and a good amount of logic to run everything.
It was designed to run on a
mainframe, after all. So shrinking it down to run on a home computer and trying to make it portable
at the same time, well, that was going to be a challenge. But it would be a fun challenge.
Luckily, the Infocomp team, fresh from graduation, was really up to the task. Anderson recalls,
quote, Joel and Mark began some seat-of-the-pants design
work, much of it on Joel's parents' coffee table, on how much Zork could be compressed, and how to
do it in a flexible way to allow for different incompatible personal computers in the future.
They considered using some available portable tools for programming, like UCSD Pascal, but it soon became clear that
Zork had too much text in it. They finally concluded that, by inventing a programming
system specifically for Zork, they could fit about half of it into a computer with 32 kilobytes of
memory and one floppy disk drive, end quote. Calling their solution a, quote, programming system undersells
the complexity a little bit. What Blank and Barrez hashed out on a coffee table was called
the Z-Machine. It's an entire computer designed from the ground up just for running Zork. This is,
perhaps, the most complicated solution possible.
But this wasn't just flexing their degrees to show off.
There was a method to this madness.
For one, the Z-Machine isn't a real computer.
It's a virtual machine.
It's a specification for a theoretical computer that can then be implemented as software.
computer that can then be implemented as software. Think of it as a tiny software-designed computer that's built up just to run text-based adventure games. The Z-Machine has its own registers,
its own stack, its own memory space, and input-output routines. It just so happens
that everything is mediated through software instead of silicon. In a more modern parlance, we call the Z-Machine a bytecode
interpreter. You pass the Z-Machine a program, sometimes called bytecode, that's written
specifically for this virtualized computer. The Z-Machine interpreter then reads the bytecode
and figures out how to run each instruction using its host computer. In that sense, it's interpreting the bytecode for the host.
That host computer can be anything. An Apple II, a TRS-80, even a modern PC. The glue that makes
it all possible is the bytecode interpreter, or as Infocom called it, the Z-Machine Interpreter
Program, aka the ZIP. This may sound outlandish at first, but this is a pretty common practice.
The most high-profile example of a bytecode interpreter has to be Java. In the 90s and
early 1000s, Java was able to rise to prominence thanks to its wild portability. It seemed like
just about any computer could run Java. The portability was all thanks to the fact that Java compiled down to bytecode that ran on the Java virtual machine.
To spread Java to a new system, all you had to do was rewrite the JVM for that new platform and boom.
Every Java program in existence worked on that new computer.
It should be plain to see why this was such a big draw for the Zork team.
The zip was a lot less code than Zork itself, so porting the game would be considerably easier.
But how does this address size? Well, this gets us into the actual implementation details of the
Z-Machine. The short story is the Z-Machine was designed to work with objects,
so it made for more efficient bytecode. Let me unpack why that matters and why I think that's
so wild. For this to have its proper impact, I think it's important to keep in mind that
Infocom's version of Zork was targeting 8-bit home microcomputers. At the time, these machines were running either
assembly language or BASIC programs. Compared to something like MUDL, these small environments
were primitive. BASIC doesn't have objects, and assembly language doesn't offer you anything.
Talking technically, Zork was a program that used objects to manage data.
While possible to reimplement without objects, that would destroy all of the sophistication of the game's engine.
So when the shadowy implementers sat down to design the Z-Machine, they had objects in mind.
This approach of building a virtual machine for a very specific task allowed them to create the best possible environment for that task.
So the Z machine was designed for managing object data.
Specifically, it was designed to work with trees of objects.
A tree in this context is a data structure that stores data in a hierarchy, such as rooms connected to other rooms or items inside of rooms.
The immediate benefit is that working with objects on the Z-Machine is dead simple.
It has built-in instructions for moving objects in memory, traversing the object tree, and
getting and setting object properties.
It goes further, but just those four operations should give you a taste of what the Z-Machine
offers.
further, but just those four operations should give you a taste of what the Z-Machine offers.
You can accomplish really complex object manipulation using a single instruction.
That's just plain convenient. What's interesting is when we start looking at the impact of these complex instructions at scale. For example, let's take the insert object instruction.
This instruction will insert an object into a specific
location in the object tree. If my reading on the docs are correct, then it takes a grand total of
four bytes to encode that instruction and its arguments as Z machine code. For a normal computer,
this type of operation would require multiple instructions. You'd need to do something along the lines of
shuffling around pointers in memory, maybe changing some values of certain memory locations.
When all's said and done, you'd probably end up using more than four bytes of machine code.
So the Z machine helps you shave off a few bytes here and there. Moving around objects takes less
machine code. So does accessing objects and their properties. So does moving around trees and there. Moving around objects takes less machine code, so does accessing objects and their
properties, so does moving around trees and tables. Since most of Zork's code is just shuffling around
objects and manipulating data, the savings really adds up. The net result is that a Zork port for
the Z-Machine ends up being a much smaller program, even when you take the size of the Z
machine interpreter into account. This is phase one of the true madness. Anderson said that we're
dealing with an entire programming system, and he wasn't wrong. The Z machine was a totally new
computer, or rather, a totally new fake computer. It didn't have any native compilers or programming tools,
so the next step after defining the machine was to make some tools to actually use with it.
The big one here, and the final wild choice that we need to talk about,
is called the Zork Implementation Language, or ZIL. The language was developed by Mark Blank
as the final piece that would make a microcomputer Zork
possible. ZIL is essentially a remixed version of MUDL. Blank took out all the parts of MUDL
that Zork was actually using, simplified that even further, and then continued the simplification
some more. ZIL looks a lot like MUDL. The syntax is roughly the same, but ZIL is actually a pretty rudimentary
imitation of the larger mainframe language. The big difference comes down to stuff like
memory allocation and garbage collection. I know, riveting. MUDL has both, but ZIL has neither.
Blank gets away with stripping out these more advanced features because, for all the complications
going on here, Zork is a really simple program.
Like I keep saying, the backend of Zork is all about managing data.
But you aren't ever making new data, you're just interacting with a mostly static pile
of objects.
We can even see this deep inside the Z machine.
The memory map for the virtual computer is broken up into three major sections.
Global variables, the object tree, and object functions.
The object tree is, for the most part, static.
Sometimes objects will get moved around in the tree,
for instance, when you drop an item, that changes its location in the object tree. Functions are, well, just functions. That's static. You don't want that
to be any other way. That just leaves global variables, which are used to keep track of the
game's state. Those are defined on compile time, their values change, but their location and size in memory stays constant.
Once again, it's all static.
It may be a bit of a hyperbole, but ZIL seems to share more in common with a data descriptor
language like HTML or LaTeX than a general purpose language like MUDL.
When you put this all together, the Z-Machine specification, the actual Z-Machine
interpreter program, and ZIL, you get a surprising recipe for success. The Z-Machine spec was written
really soon after Infocom forms. The first interpreter was written soon after for a
DexSystem 20 that Infocom bought to use as a development system. A ZIL compiler called Zilch was next on the list.
By the end of 1979, the first big section of Zork had been ported to ZIL, and a second Z
machine interpreter had been written for the TRS-80 Model 1. This is, in no uncertain terms,
a wild pace of development. In under a year, the implementers had pulled off a really amazing feat, and here's where
it all paid off.
By the beginning of 1980, the first third of Zork, what Infocom would sell as Zork 1,
was running on the TRS-80 Model 1.
The retail release of Zork required just 32 kilobytes of RAM. By the end of 1980,
a new Z machine interpreter was written for the Apple II, and Zork was suddenly running on two
of the three most popular home computers. The IBM PC was released in August of 1981.
By 1982, it had its own port of Zork.
By 1982, it had its own port of Zork.
Alright, that brings us to the end of this episode.
At the tail end of our discussion of Zork, I think we can see a pattern formed.
At first, the idea of developing an entire virtual computer and new programming language sounds kind of laughable.
But as time went on, that choice really paid off. Now, what made this all work so well was that Zork was,
and still is, really fun to play. It would be one thing if Infocom made a highly portable game that
was just okay. But Zork is a fantastically fun game in its own right. The way I see it, Zork, especially the retail release, was an absolute labor of love.
The implementers started off just having fun programming, and they wanted to keep the fun going.
To couch this in fancier terms, Zork's implementers made effective use of abstraction.
Zork's implementers made effective use of abstraction.
Once we get to the retail release, Zork wasn't really written for the Apple II or the TRS-80 or even the IBM PC.
It was written for the Z machine.
It just so happened that Z machine code could run on many multiple platforms.
That's the technical side of things, but we can't forget the creativity at play here.
One of the huge reasons for Zork's success is that it's fun and impressive. The implementers said it best themselves.
They wanted Zork to be about solving puzzles, not solving the program.
We can easily say that Zork was heavily influenced by Adventure. That much is crystal clear. Adventure
was the whole reason Zork was created in the first place.
But when we get down to it, I think Zork is more of a response to adventure than a direct
descendant. All the way back in the dynamic modeling group, a small team of programmers
decided that Zork would be different than Adventure. That it would be better. And luckily,
they had the technical chops to make that possible.
In closing, go play some Zork.
Thanks for listening to Adrin of Computing.
I'll be back in two weeks' time with another piece of computing's past.
And hey, if you like the show, there are now a few ways you can support it.
If you know someone else who'd be interested in the story of computers,
then why not take a minute to share the show with them?
You can also rate and review on Apple Podcasts. If you want to be a super fan, you can support
Advent of Computing directly through merch or becoming a patron on Patreon. Patrons get early
access to episodes, polls for the direction of the show, and bonus content. You can find links
to everything on my website, adventofcomputing.com. If you have any comments or suggestions for a future episode,
then go ahead and shoot me a tweet.
I'm at adventofcomp on Twitter.
And as always, have a great rest of your day.