Forest edge

Paths lead into the forest to the west and northwest. Also, a well-marked path extends east.

> go west

This is a dimly lit forest, with large trees all around.

> go west

Behind House

> enter the white house


> go west

Living Room

> open the coffin

The gold coffin opens. A sceptre, possibly that of ancient Egypt itself, is in the coffin. The sceptre is ornamented with jewels.

> take the sceptre


> put the gold coffin in the trophy case


> go east
> go east
> go east
> go east

And that is the power of prayer right there: it teleports you to the forest with the gold coffin. (It also stops the trap door from shutting behind you from now on, which is very handy.) In unrelated news, I have always wondered why the authors of Zork chose the British spelling of sceptre instead of the more common in the United States spelling “scepter”. Perhaps because it seems more archaic? A nice touch.

We’ve decoded the major data structures — or, at least, gotten a start on them. There’s rather a lot more to do with objects, but we’ll come back to that later. Let’s really get into the heart of the story file now: the code.

Every instruction in the Z-machine instruction set has the same parts:

  • An opcode, which gives the fundamental purpose of the instruction
  • Between 0 and 7 operands

Many instructions have in addition:

  • a conditional branch target
  • where to store the result computed

and two instructions are followed by zstring-encoded text.

It is pretty clear that this instruction set was designed for concision in common cases, but it is still pretty easy to understand.

Over the next few episodes I’m going to quote from the Z-machine specification and show what code I wrote to implement each bit of the spec. Before we get into that though, a subtle but extremely interesting feature of OCaml that I have not yet discussed.

I want my instruction decoding function to have the following header:

let decode story (Instruction address) =

Of course I have created yet another wrapper type around integers, this one representing “I have a pointer to an instruction”. I am going to have to be reading a lot of bytes out of the story in this very long, complicated method. I think it is a bit silly to have to write

Story.read_byte story addr

every time. OCaml lets me do this inside the decode function:

  let read_byte = Story.read_byte story in

Examine that carefully. Doesn’t read_byte take two operands, a story and an address? It looks like we have only partially applied arguments to the function here!

Well… yes and no.

I have misled you when I said that we were defining functions that take some number of operands. In fact, all functions in OCaml take exactly one value as a parameter, and return exactly one value as a result. (Assuming that they return at all; a function could of course simply failwith.) When you say

let adder a b = 
  a + b

this is just a short way of writing

let adder a =
  let nested_function b = 
    a + b in

In fact there is an even shorter syntax for that which I have not yet mentioned; OCaml supports lambda expressions, the same as C#. It would be more accurate to say that this is the same as:

let adder a = fun b -> a + b

Or, as a commenter points out, we can go even farther:

let adder = fun a -> fun b -> a + b

What does adder 1 give you? It gives you back a function which takes an argument, names it b, adds 1 to it, and returns the result.

In C# we would say

Func<int, int> Adder(int a) 
  return b => a + b;   

or in C# 6:

Func<int, int> Adder(int a) => b => a + b;   

Or go full lambda:

Func<int, Func<int, int>> adder = a => b => a + b;

Note how much more concise OCaml is in all forms. Type inference for the win!

So anyways, we are perfectly within our right to partially apply the Story.read_byte method with one argument; that will give us back what is logically a nested function closed over that argument. Now we can read bytes without specifying what story to read from; the function already knows.

I think that’s enough for today. Next time on FAIC: we’ll actually get started on decoding instructions.

6 thoughts on “Forest edge

  1. The C# code is wrong, the return type should be just Func<int, int>. (The whole Adder could be assigned to a variable of type Func<int, Func<int, int>>.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s