Zaytun Handbook

Introduction

A text adventure is a game made entirely of words. It describes where you are, you type what you want to do, and it tells you what happened — and that is the whole of the interface. There is nothing to look at, nothing to aim at and nothing to react to in a hurry; what the game asks of a player is attention, curiosity and patience.

Zaytun is a language for writing them. You do not write the loop that reads a command, or the code that tracks where everything is, or the parser that works out what the player meant. You write the world — the rooms and what is in them, the people and what they say, the things that happen when somebody does something — and the compiler turns that description into a file that plays.

The result is small in a deliberate way. There is one kind of thing you are making, so the language knows what a room is, what an item is, and what it means to carry one; you never build those out of anything more primitive. Most of a Zaytun game reads like a description of the game rather than like a program, and that is the point of it.

This handbook is the whole of the language. It is written for somebody who can program a little and has never written a game, and for somebody who has written ten and wants to know what this one calls things. You do not need to have played a text adventure to follow it, and you do not need to read it in order — How to read this handbook, at the end of this section, says which parts repay being read through and which are there to look things up in.

The rest of this section is the short version of everything: what Zaytun is, why it exists, what writing well for its players means in practice, the handful of ideas the language is built on, and what it deliberately does not do.

What Zaytun is

A small language for writing text adventures, and two programs that play what it compiles.

A game is a description of a world — rooms, the things in them, the people in them, and what happens when a player does something. You write that description in one or more .zaytun files, compile it, and the result is a single file that plays in a terminal or in a browser:

game.zaytun  --[ zaytunc ]-->  game.zaytunb  --[ zaytunplay / the browser player ]-->  a game

Here is a whole one. Two rooms, one lamp, one way to win:

title: "The Lamp"
start: cellar

room cellar "The Cellar" {
  description: "Stone walls, and a stair going up into the dark."
  item lamp
  exit up -> hall
}

room hall "The Hall" {
  description: "Bare boards, and a door standing open on the night."
  exit down -> cellar
  exit out -> { if has(lamp) { finish "You walk out with the lamp lit. THE END." }
                else { print "It is too dark out there without a light." } }
}

item lamp "a brass lamp" { examine: "Dented, and still full of oil." }

That is not a fragment or an outline — compile it and it plays. Most of what the language adds to this is more of the same kind of thing: people to talk to, creatures to fight, numbers to track, and blocks that run when something happens.

Three programs, each usable on its own. zaytunc compiles and never plays. zaytunplay plays in a terminal and has no compiler in it. The browser player is a third program that plays the same file with nothing installed. The two players are independent implementations of one runtime, held byte-for-byte identical by an automated check that runs a seeded game through both and diffs the transcripts — so a game behaves the same wherever it is played, and that is a thing this project tests rather than a thing it hopes.

Why Zaytun exists

The short answer is accessibility, and it is the reason the project exists rather than a feature added to it afterwards.

Most games are built to be looked at. What the player needs to know is in the picture: where things are, what just happened, what is about to. If you cannot see the picture you are usually not playing a harder version of that game — you are not playing it.

Why text. A game made of words is different, and the difference is not that it is a lesser substitute. The whole state of the world arrives as text, and text works equally well read on a screen, enlarged, spoken aloud by a screen reader, or felt on a braille display. Nothing has to be translated, because nothing was visual to begin with.

What a text game actually runs on is reading, imagination, choice and patience, and none of those are distributed according to eyesight. A blind player and a sighted player sitting down to the same text game are playing the same game — not an accessible version of it, and not a reduced one. The same one.

Text is not a fallback here. It is the medium, and it happens to be the most accessible medium there is.

What that asks of you: almost nothing, which is the point. A game written in this language is accessible by construction, because it has no pictures to describe and no interface to label. The whole of it is words going out and words coming back.

What is left is craft, and it is the ordinary craft of writing well — which happens to be the same work as making the game playable by ear. Writing for a player who is listening, below, is what that comes to in practice.

The name. زيتون — zaytūn — is the Arabic word for the olive, the fruit and the tree both.

The project was called Loom for most of its life: the compiler was loomc, games were .loom files, and the four bytes at the front of a compiled game said LOOM. It was a good name for what the thing looks like from the inside — a game is warp and weft, rooms crossing with the things that happen in them — but it described the making and said nothing about the thing made, and software is not short of looms.

An olive tree is slow, it is not much to look at, it is famously hard to kill, and it outlives whoever planted it; you plant one for somebody you are not going to meet. That is a fair statement of the ambition here — plain text and plain C, no dependencies to rot, and a game file you can hand to somebody in ten years and expect to open.

Writing for a player who is listening

A screen reader reads your game aloud, from the top, one line at a time, and the player cannot glance back at what has scrolled past. Everything below follows from that, and all of it is just writing well for the ear.

Put the important thing first. A reader hears a line from its beginning, and a player who already knows the room will move on before the end of it. A key glints in the lock is better than Set into the heavy oak of the door, below a tarnished plate, a key glints in the lock.

Keep the first description short and let examine carry the detail. The room description is read every time the player walks in. The paragraph about the carving on the mantelpiece is what examine mantelpiece is for, and a player who wants it will ask twice as often as one who does not will be made to sit through it.

Say what changed, not everything that is true. After an action, one line about the difference is worth five re-describing the room. The lock gives beats reprinting the door.

Use the lists the engine already prints. You see here:, Also here:, Exits: come out in a fixed shape at a fixed place, so a player learns where to listen for them. Repeating the same information inside your prose makes the game read twice.

Give everything a short name that somebody would type. aliases: costs a line and saves the player spelling out the tarnished brass candlestick every time. The title is what they hear; the aliases are what they type.

Do not lean on punctuation or layout to carry meaning. A row of dashes, an ASCII box or a column of spaces is read out as itself, or skipped, or made into noise. If a table matters, say it in sentences. print a blank line to separate blocks — that reads as a pause.

Be careful with long unbroken output. A forty-line dump is forty lines the player must sit through or interrupt. A scene that runs in several short paragraphs reads better than one that runs in one long one.

Say where you are when it changes. The engine prints the room's name on arrival; if your own prose moves the player somewhere, say so in the first line of it.

Test by ear. Turn on a screen reader, or read the transcript aloud, and play your own game through once without looking at the source. Everything awkward shows up in the first five minutes.

The ideas the language is built on

Zaytun is opinionated in a few specific ways, and knowing them in advance explains most of what the compiler will say to you.

Anything that can be decided before the game runs is decided before the game runs. Every name you write is checked: a room that does not exist, an item spelled two ways, a flag that was never declared, a subscript on something that is not a series. A misspelling is an error you see once, not a branch that silently never runs for the rest of the game's life.

A refusal says why, in the game's own words. Every guard in the language — boards if, moves if, takes if, opens if — requires the message the player is shown when it refuses. A guard with nothing to say is a locked door with no sign on it, and the compiler will not let you write one.

The player and the game are never told different things. One walk produces the Exits: line and the answer exitcount gives. One question decides whether / divides as whole numbers and what isint reports. Where a game can ask about the world, it asks the same code the player's eyes are given.

The two runtimes must agree. Anything that changes what a game prints is written twice, in C and in JavaScript, and the suite plays a seeded game through both and diffs the output. That is also why the language pins things other languages leave to the platform: round rounds a half away from zero in both, because C and JavaScript disagree about which way a negative half goes.

One file, and it keeps playing. A compiled game is a single .zaytunb with a version floor in it. A player will tell you plainly when a game is too new for it rather than misreading it, and an old game keeps working.

Small on purpose. The language does one job. When something belongs in a game rather than in the language, it stays out — see What Zaytun is not.

What Zaytun is not

Worth knowing early, so you do not go looking.

How to read this handbook

Getting started and Your first game are twenty minutes and give you something that runs.

Language reference is the long middle, one chapter per kind of thing, and it is meant to be dipped into rather than read through. If you are looking for a form rather than an idea, the two generated lists at the end of it — Statements and Expressions — name every form in the language on one line each, with a pointer to the chapter that explains it.

The player's command language is what somebody typing at your game may say. Read it once before you write much: it is short, and it will stop you inventing a verb the engine already has.

The rest is reference for when you need it — how to play a compiled game, what the compiler's complaints mean, how versions work, and the bytecode format for anyone who wants to write a third player.