§1   Routines

If you have already dipped into Chapter II and looked at how some simple games and puzzles are designed, you'll have seen that any really interesting item needs to be given instructions on how to behave, and that these instructions are written in a language of their own. Chapter I is about that language, and its examples are mostly short programs to carry out mundane tasks, undistracted by the lure of adventure.

§1.1   Getting started

Inform turns your description of a game (or other program), called the “source code”, into a “story file” which can be played (or run through) using an “interpreter”. Interpreter programs are available for very many models of computer, and if you can already play Infocom's games or other people's Inform games on your machine then you already have an interpreter. There are several interpreter programs available, in a sort of friendly rivalry. You should be able to use whichever you prefer and even the oldest, most rickety interpreter will probably not give serious trouble. A good sign to look out for is compliance with the exacting Z-Machine Standards Document, agreed by an informal committee of interested parties between 1995 and 1997. At time of writing, the current version is Standard 1.0, dating from June 1997.

Turning source code into a story file is called “compilation”, and Inform itself is the compiler. It's also supplied with a whole slew of ready-made source code called the “library” and giving all the rules for adventure games. The story of what the library does and how to work with it occupies most of this manual, but not this chapter.

It isn't practicable to give installation instructions here, because they vary so much from machine to machine, but before you can go any further you'll need to install Inform: try downloading the software for your own machine from ftp.gmd.de, which should either install itself or give instructions. A useful test exercise would be to try to create the “Hello World” source code given below, then to compile it with Inform, and finally “play” it on your interpreter. (You can type up source code with any text editor or even with a word-processor, provided you save its documents as “text only”.)

Inform can run in a number of slightly different ways, controlled by “switches”. The way to set these varies from one installation to another. Note that this chapter assumes that you're running Inform in “Strict mode”, controlled by the -S switch, which is normally set and ensures that helpful error messages will be printed if a story file you have compiled does something it shouldn't.

§1.2   Hello World

Traditionally, all programming language tutorials begin by giving a program which does nothing but print “Hello world” and stop. Here it is in Inform:

!  "Hello world" example program
[ Main;
  print "Hello world^";
];

The text after the exclamation mark is a “comment”, that is, it is text written in the margin by the author to remind himself of what is going on here. Such text means nothing to Inform, which ignores anything on the same line and to the right of an exclamation mark. In addition, any gaps made up of line and page breaks, tab characters and spaces are treated the same and called “white space”, so the layout of the source code doesn't much matter. Exactly the same story file would be produced by:

   [
      Main   ;
print
         "Hello world^"        ;
     ]
 ;

or, at the other extreme, by:

[Main;print"Hello world^";];

Laying out programs legibly is a matter of personal taste.

The exception to the rule about ignoring white space is inside quoted text, where "Hello    world^" and "Hello world^" are genuinely different pieces of text and are treated as such. Inform treats text inside quotation marks with much more care than its ordinary program material: for instance, an exclamation mark inside quotation marks will not cause the rest of its line to be thrown away as a comment.

Inform regards its source code as a list of things to look at, divided up by semicolons ;. These things are generally objects, of which more later. In this case there is only one, called Main, and it's of a special kind called a “routine”.

Every program has to contain a routine called Main. When a story file is set running the interpreter follows the first instruction in Main, and it carries on line by line from there. This process is called “execution”. Once the Main routine is finished, the interpreter stops.

These instructions are called “statements”, a traditional term in computing albeit an ungrammatical one. In this case there is only one statement:

print "Hello world^";

Printing is the process of writing text onto the computer screen. This statement prints the two words “Hello world” and then skips the rest of the line (or “prints a new-line”), because the ^ character, in quoted text, means “new-line”. For example, the statement

print "Blue^Red^Green^";

prints up:

Blue
Red
Green

print is one of 28 different statements in the Inform language. Only about 20 of these are commonly used, but the full list is as follows:

box break continue do font for
give if inversion jump move new_line
objectloop print print_ret quit read remove
restore return rfalse rtrue save spaces
string style switch while

§1.3   Routine calls, errors and warnings

The following source code has three routines, Main, Rosencrantz and Hamlet:

[ Main;
  print "Hello from Elsinore.^";
  Rosencrantz();
];
[ Rosencrantz;
  print "Greetings from Rosencrantz.^";
];
[ Hamlet;
  print "The rest is silence.^";
];

The resulting program prints up

Hello from Elsinore.
Greetings from Rosencrantz.

but the text “The rest is silence.” is never printed. Execution begins at Main, and “Hello from Elsinore” is printed; next, the statement Rosencrantz() causes the Rosencrantz routine to be executed. That continues until it ends with the close-routine marker ], whereupon execution goes back to Main just after the point where it left off: since there is nothing more to do in Main, the interpreter stops. Thus, Rosencrantz is executed but Hamlet is not.

In fact, when the above source code is compiled, Inform notices that Hamlet is never needed and prints out a warning to that effect. The exact text produced by Inform varies from machine to machine, but will be something like this:

RISC OS Inform 6.20 (10th December 1998)
line 8: Warning: Routine "Hamlet" declared but not used
Compiled with 1 warning

Errors are mistakes in the source which cause Inform to refuse to compile it, but this is only a warning. It alerts the programmer that a mistake may have been made (because presumably the programmer has simply forgotten to put in a statement calling Hamlet) but it doesn't prevent the compilation from taking place. Note that the opening line of the routine Hamlet occurs on the 8th line of the program above.

There are usually mistakes in a newly-written program and one goes through a cycle of running a first draft through Inform, receiving a batch of error messages, correcting the draft according to these messages, and trying again. A typical error message would occur if, on line 3, we had mistyped Rosncrantz() for Rosencrantz(). Inform would then have produced:

RISC OS Inform 6.20 (10th December 1998)
line 5: Warning: Routine "Rosencrantz" declared but not used
line 8: Warning: Routine "Hamlet" declared but not used
line 3: Error: No such constant as "Rosncrantz"
Compiled with 1 error and 2 warnings (no output)

The error message means that on line 3 Inform ran into a name which did not correspond to any known quantity: it's not the name of any routine, in particular. A human reader would immediately realise what was intended, but Inform doesn't, so that it goes on to warn that the routine Rosencrantz is never used. Warnings (and errors) are quite often produced as knock-on effects of other mistakes, so it is generally a good idea to worry about fixing errors first and warnings afterward.

Notice that Inform normally doesn't produce the final story file if errors occurred during compilation: this prevents it from producing damaged story files.

§1.4   Numbers and other constants

Inform numbers are normally whole numbers in the range −32,768 to 32,767. (Special programming is needed to represent larger numbers or fractions, as we shall see when parsing phone numbers in Chapter IV.) There are three notations for writing numbers in source code: here is an example of each.

-4205
$3f08
$$1000111010110

The difference is the radix, or number base, in which they are expressed. The first is in decimal (base 10), the second hexadecimal (base 16, where the digits after 9 are written a to f or A to F) and the third binary (base 2). Once Inform has read in a number, it forgets which notation was used: for instance, if the source code is altered so that $$10110 is replaced by 22, this makes no difference to the story file produced.

A print statement can print numbers as well as text, though it always prints them back in ordinary decimal notation. For example, the program

[ Main;
  print "Today's number is ", $3f08, ".^";
];

prints up

Today's number is 16136.

since 16,136 in base 10 is the same number as 3f08 in hexadecimal.

Literal quantities written down in the source code are called “constants”. Numbers are one kind; strings of text like "Today's number is " are another. A third kind are characters, given between single quotation marks. For instance, 'x' means “the letter lower-case x”. A “character” is a single letter or typewriter-symbol.

Just as $3f08 is a fancy way of writing the number 16,136, so 'x' is a fancy way of writing the number 120. The way characters correspond to numeric values is given by a code called ZSCII, itself quite close to the traditional computing standard called ASCII. 120 means “lower-case x” in ASCII, too, but ZSCII does its own thing with non-English characters like “é”. (You can type accented letters directly into source code: see §1.11.) The available characters and their ZSCII values are laid out in Table 2.

Inform also provides a few constants named for convenience, the most commonly used of which are true and false. A condition such as “the jewelled box is unlocked” will always have either the value true or the value false.

Once again these are numbers in disguise. true is 1, false 0.

Inform is a language designed with adventure games in mind, where a player regularly types in commands like “unlock the box”, using a fairly limited vocabulary. Writing a word like 'box' in the source code, in single-quotation marks, adds it to the “dictionary” or vocabulary of the story file to be compiled. This is a kind of constant, too: you can use it for writing code like “if the second word typed by the player is 'box', then…”.

If you need to lodge a single-letter word (say “h”) into the dictionary, you can't put it in single quotes because then it would look like a character constant ('h'). Instead, the best you can write is 'h//'. (The two slashes are sometimes used to tack a little linguistic information onto the end of a dictionary word, so that in some circumstances, see §29, you might want to write 'pears//p' to indicate that “pears” must go into the dictionary marked as a plural. In this case the two slashes only serve to clarify that it isn't a character.)

You can put an apostrophe ' into a dictionary word by writing it as ^: for instance 'helen^s'.

▲▲ In two places where dictionary words often appear, the name slot of an object definition and in grammar laid out with Verb and Extend, you're allowed to use single or double quotes interchangeably, and people sometimes do. For clarity's sake, this book tries to stick to using single quotes around dictionary words at all times. The handling of dictionary words is probably the single worst-designed bit of syntax in Inform, but you're past it now.

§1.5   Variables

Unlike a literal number, a variable is able to vary. It is referred to by its name and, like the “memory” key on some pocket calculators, remembers the last value placed in it. For instance, if oil_left has been declared as a variable (see below), then the statement

print "There are ", oil_left, " gallons remaining.^";

would cause the interpreter to print “There are 4 gallons remaining.” if oil_left happened to be 4, and so on. It's possible for the statement to be executed many times and produce different text at different times.

Inform can only know the named quantity oil_left is to be a variable if the source code has “declared” that it is. Each routine can declare its own selection of up to 15 variables on its opening line. For example, in the program

[ Main alpha b;
  alpha = 2200;
  b = 201;
  print "Alpha is ", alpha, " while b is ", b, "^";
];

the Main routine has two variables, alpha and b.

Going back to the Main routine above, the = sign which occurs twice is an example of an “operator”: a notation usually made up of the symbols on the non-alphabetic keys on a typewriter and which means something is to be done with or calculated from the items it is written next to. Here = means “set equal to”. When the statement alpha = 2200; is interpreted, the current value of the variable alpha is changed to 2,200. It then keeps that value until another such statement changes it. All variables have the value 0 until they are first set.

The variables alpha and b are called “local variables” because they are local to Main and are its private property. The source code

[ Main alpha;
  alpha = 2200;
  Rival();
];

[ Rival;
  print alpha;
];

causes an error on the print statement in Rival, since alpha does not exist there. Indeed, Rival could even have defined a variable of its own also called alpha and this would have been an entirely separate variable.

· · · · ·

That's now two kinds of name: routines have names and so have variables. Such names, for instance Rival and alpha, are called “identifiers” and can be up to 32 characters long. They may contain letters of the alphabet, decimal digits or the underscore _ character (often used to impersonate a space). To prevent them looking too much like numbers, though, they cannot start with a decimal digit. The following are examples of legal identifiers:

turns_still_to_play     room101     X

Inform ignores any difference between upper and lower case letters in such names, so for instance room101 is the same name as Room101.

§1.6   Arithmetic, assignment and bitwise operators

The Inform language is rich with operators, as Table 1 shows. This section introduces a first batch of them.

A general mixture of quantities and operators, designed to end up with a single resulting quantity, is called an “expression”. For example: the statement

seconds = 60*minutes + 3600*hours;

sets the variable seconds equal to 60 times the variable minutes plus 3600 times the variable hours. White space is not needed between operators and “operands” (the quantities they operate on, such as 3600 and hours). The spaces on either side of the + sign were written in just for legibility.

The arithmetic operators are the simplest ones. To begin with, there are + (plus), - (minus), * (times) and / (divide by). Dividing one whole number by another usually leaves a remainder: for example, 3 goes into 7 twice, with remainder 1. In Inform notation,

7/3 evaluates to 2 and 7%3 evaluates to 1

the % operator meaning “remainder after division”, usually called just “remainder”.

The basic rule is that a == (a/b)*b + (a%b), so that for instance:

13/5 == 2    13/-5 == -2    -13/5 == -2    -13/-5 == 2
13%5 == 3    13%-5 == 3     -13%5 == -3    -13%-5 == -3

WARNING
Dividing by zero, and taking remainder after dividing by zero, are impossible. You must write your program so that it never tries to. It's worth a brief aside here on errors, because dividing by zero offers an example of how and when Inform can help the programmer to spot mistakes. The following source code:

[ Main; print 73/0; ];

won't compile, because Inform can see that it definitely involves something illegal:

line 2: Error: Division of constant by zero
>   print 73/0;

However, Inform fails to notice anything amiss when compiling this:

[ Main x; x = 0; print 73/x; ];

and this source code compiles correctly. When the resulting story file is interpreted, however, the following will be printed:

[** Programming error: tried to divide by zero **]

This is only one of about fifty different programming errors which can turn up when a story file is interpreted. As in this case, they arise when the interpreter has accidentally been asked to do something impossible. The moral is that just because Inform compiles source code without errors, it does not follow that the story file does what the programmer intended.

Since an Inform number has to be between −32,768 and 32,767, some arithmetic operations overflow. For instance, multiplying 8 by 5,040 ought to give 40,320, but this is over the top and the answer is instead −25,216. Unlike dividing by zero, causing arithmetic overflows is perfectly legal. Some programmers make deliberate use of overflows, for instance to generate apparently random numbers.

▲▲ Only apparently random, because overflows are perfectly predictable. Inform story files store numbers in sixteen binary digits, so that when a number reaches 216 = 65,536 it clocks back round to zero (much as a car's odometer clocks over from 999999 miles to 000000). Now, since 65,535 is the value that comes before 0, it represents −1, and 65,534 represents −2 and so on. The result is that if you start with zero and keep adding 1 to it, you get 1, 2, 3, …, 32,767 and then −32,768, −32,767, −32,766, …, −1 and at last zero again. Here's how to predict an overflowing multiplication, say: first, multiply the two numbers as they stand, then keep adding or subtracting 65,536 from the result until it lies in the range −32,768 to 32,767. For example, 8 multiplied by 5,040 is 40,320, which is too big, but we only need to subtract 65,536 once to bring it into range, and the result is −25,216.

· · · · ·

In a complicated expression the order in which the operators work may affect the result. As most human readers would, Inform works out both of

3 + 2 * 6
2 * 6 + 3

as 15, because the operator * has “precedence” over + and so is acted on first. Brackets may be used to overcome this:

(3 + 2) * 6
2 * (6 + 3)

evaluate to 30 and 18 respectively. Each operator has such a “precedence level”. When two operators have the same precedence level (for example, + and - are of equal precedence) calculation is (almost always) “left associative”, that is, carried out left to right. So the notation a-b-c means (a-b)-c and not a-(b-c). The standard way to write formulae in maths is to give + and - equal precedence, but lower than that of * and / (which are also equal). Inform agrees and also pegs % equal to * and /.

The last purely arithmetic operator is “unary minus”. This is also written as a minus sign - but is not quite the same as subtraction. The expression:

-credit

means the same thing as 0-credit. The operator - is different from all those mentioned so far because it operates only on one value. It has higher precedence than any of the five other arithmetic operators. For example,

-credit - 5

means (-credit) - 5 and not -(credit - 5).

One way to imagine precedence is to think of it as glue attached to the operator. A higher level means stronger glue. Thus, in

3 + 2 * 6

the glue around the * is stronger than that around the +, so that 2 and 6 belong bound to the *.

Some languages have a “unary plus” too, but Inform hasn't.

· · · · ·

Some operators don't just work out values but actually change the current settings of variables: expressions containing these are called “assignments”. One such is “set equals”:

alpha = 72

sets the variable alpha equal to 72. Like + and the others, it also comes up with an answer: which is the value it has set, in this case 72. The other two assignment operators are ++ and --, which will be familiar to any C programmer. They are unary operators, and mean “increase (or decrease) the value of this variable by one”. If the ++ or -- goes before the variable, then the increase (or decrease) happens before the value is read off; if after, then after. For instance, if variable currently has the value 12 then:

variable++ evaluates to 12 and leaves variable set to 13;
++variable evaluates to 13 and leaves variable set to 13;
variable-- evaluates to 12 and leaves variable set to 11;
--variable evaluates to 11 and leaves variable set to 11.

These operators are provided as convenient shorthand forms, since their effect could usually be achieved in other ways. Note that expressions like

500++     (4*alpha)--     34 = beta

are quite meaningless: the values of 500 and 34 cannot be altered, and Inform knows no way to adjust alpha so as to make 4*alpha decrease by 1. All three will cause compilation errors.

· · · · ·

The “bitwise operators” are provided for manipulating binary numbers on a digit-by-digit basis, something which is only done in programs which are working with low-level data or data which has to be stored very compactly. Inform provides &, bitwise AND, |, bitwise OR and ~, bitwise NOT. For each digit, such an operator works out the value in the answer from the values in the operands. Bitwise NOT acts on a single operand and results in the number whose i-th binary digit is the opposite of that in the operand (a 1 for a 0, a 0 for a 1). Bitwise AND (and OR) acts on two numbers and sets the i-th digit to 1 if both operands have (either operand has) i-th digit set. All Inform numbers are sixteen bits wide. So:

$$10111100 & $$01010001 == $$0000000000010000
$$10111100 | $$01010001 == $$0000000011111101
           ~ $$01010001 == $$1111111110101110

§1.7   Arguments and Return Values

Here is one way to imagine how an Inform routine works: you feed some values into it, it then goes away and works on them, possibly printing some text out or doing other interesting things, and it then returns with a single value which it gives back to you. As far as you're concerned, the transaction consists of turning a group of starting values, called “arguments”, into a single “return value”:

A1, A2, A3, … → Routine → R

The number of arguments needed varies with the routine: some, like Main and the other routines in this chapter so far, need none at all. (Others need anything up to seven, which is the maximum number allowed by Inform.) On the other hand, every routine without exception produces a return value. Even when it looks as if there isn't one, there is. For example:

[ Main;
  Sonnet();
];

[ Sonnet;
  print "When to the sessions of sweet silent thought^";
  print "I summon up remembrance of things past^";
];

Main and Sonnet both take no arguments, but they both return a value: as it happens this value is true, in the absence of any instruction to the contrary. (As was mentioned earlier, true is the same as the number 1.) The statement Sonnet(); calls Sonnet but does nothing with the return value, which is just thrown away. But if Main had instead been written like so:

[ Main;
  print Sonnet();
];

then the output would be

When to the sessions of sweet silent thought
I summon up remembrance of things past
1

because now the return value, 1, is not thrown away: it is printed out.

You can call it a routine with arguments by writing them in a list, separated by commas, in between the round brackets. For instance, here is a call supplying two arguments:

Weather("hurricane", 12);

When the routine begins, the value "hurricane" is written into its first local variable, and the value 12 into its second. For example, suppose:

[ Weather called force;
  print "I forecast a ", (string) called, " measuring force ",
      force, " on the Beaufort scale.^";
];

Leaving the details of the print statement aside for the moment, the call to this routine produces the text:

I forecast a hurricane measuring force 12 on the Beaufort scale.

The Weather routine finishes when its ] end-marker is reached, whereupon it returns true, but any of the following statements will finish a routine the moment they are reached:

rfalse;       which returns false,
rtrue;       which returns true,
return;       which also returns true,
return value;       which returns ‹value›.

For example, here is a routine to print out the cubes of the numbers 1 to 5:

[ Main;
  print Cube(1), " ";
  print Cube(2), " ";
  print Cube(3), " ";
  print Cube(4), " ";
  print Cube(5), "^";
];

[ Cube x;
  return x*x*x;
];

When interpreted, the resulting story file prints up the text:

1 8 27 64 125

Any “missing arguments” in a routine call are set equal to zero, so the call Cube() is legal and does the same as Cube(0). What you mustn't do is to give too many arguments: Cube(1,2) isn't possible because there is no variable to put the 2 into.

A hazardous, but legal and sometimes useful practice is for a routine to call itself. This is called recursion. The hazard is that the following mistake can be made, probably in some much better disguised way:

[ Disaster; 
  return Disaster(); 
];

Despite the reassuring presence of the word return, execution is tied up forever, unable to finish evaluating the return value. The first call to Disaster needs to make a second before it can finish, the second needs to make a third, the third… and so on. (Actually, for “forever” read “until the interpreter runs out of stack space and halts”, but that's little comfort.)

§1.8   Conditions: if, true and false

The facilities described so far make Inform about as powerful as the average small programmable calculator. To make it a proper programming language, it needs much greater flexibility of action. This is provided by special statements which control whether or not, and if so how many times or in what order, other statements are executed. The simplest is if:

if (condition) statement

which executes the ‹statement› only if the ‹condition›, when it is tested, turns out to be true. For example, when the statement

if (alpha == 3) print "Hello";

is executed, the word “Hello” is printed only if the variable alpha currently has value 3. It's important not to confuse the == (test whether or not equal to) with the = operator (set equal to). But because it's easy to write something plausible like

if (alpha = 3) print "Hello";

by accident, which always prints “Hello” because the condition evaluates to 3 which is considered non-zero and therefore true (see below), Inform will issue a warning if you try to compile something like this. (‘=’ used as condition: ‘==’ intended?)

· · · · ·

Conditions are always given in brackets. There are 12 different conditions in Inform (see Table 1), six arithmetic and half a dozen to do with objects. Here are the arithmetic ones:

(a == b)a equals b
(a ~= b)a doesn't equal b
(a >= b)a is greater than or equal to b
(a <= b)a is less than or equal to b
(a > b)a is greater than b
(a < b)a is less than b

A useful extension to this set is provided by the special operator or, which gives alternative possibilities. For example,

if (alpha == 3 or 4) print "Scott";
if (alpha ~= 5 or 7 or 9) print "Amundsen";

where two or more values are given with the word or between. “Scott” is printed if alpha has value either 3 or 4, and “Amundsen” if the value of alpha is not 5, is not 7 and is not 9. or can be used with any condition, and any number of alternatives can be given. For example

if (player in Forest or Village or Building) ...

often makes code much clearer than writing three separate conditions out. Or you might want to use

if (x > 100 or y) ...

to test whether x is bigger than the minimum of 100 and y.

Conditions can also be built up from simpler ones using the three logical operators &&, || and ~~, pronounced “and”, “or” and “not”. For example,

if (alpha == 1 && (beta > 10 || beta < -10)) print "Lewis";
if (~~(alpha > 6)) print "Clark";

“Lewis” is printed if alpha equals 1 and beta is outside the range −10 to 10; “Clark” is printed if alpha is less than or equal to 6.

&& and || work left to right and stop evaluating conditions as soon as the final outcome is known. So for instance if (A && B) ... will work out A first. If this is false, there's no need to work out B. This is sometimes called short-cut evaluation and can be convenient when working out conditions like

if (x~=nothing && TreasureDeposited(x)==true) ...

where you don't want TreasureDeposited to be called with the argument nothing.

· · · · ·

Conditions are expressions like any other, except that their values are always either true or false. You can write a condition as a value, say by copying it into a variable like so:

lower_caves_explored = (Y2_Rock_Room has visited);

This kind of variable, storing a logical state, is traditionally called a “flag”. Flags are always either true or false: they are like the red flag over the beach at Lee-on-Solent in Hampshire, which is either flying, meaning that the army is using the firing range, or not flying, when it is safe to walk along the shore. Flags allow you to write natural-looking code like:

if (lower_caves_explored) print "You've already been that way.";

The actual test performed by if (x) ... is x~=0, not that x==true, because all non-zero quantities are considered to represent truth whereas only zero represents falsity.

· · · · ·

Now that the if statement is available, it's possible to give an example of a recursion that does work:

[ GreenBottles n;
  print n, " green bottles, standing on a wall.^";
  if (n == 0) print "(And an awful lot of broken glass.)^";
  else {
      print "And if one green bottle should accidentally fall^";
      print "There'd be ", n-1, " green bottles.^";
      return GreenBottles(n-1);
  }
];

Try calling GreenBottles(10). It prints the first verse of the song, then before returning it calls GreenBottles(9) to print the rest. So it goes on, until GreenBottles(0). At this point n is zero, so the text “(And an awful lot of broken glass.)” is printed for the first and only time. GreenBottles(0) then returns back to GreenBottles(1), which returns to GreenBottles(2), which… and so on until GreenBottles(10) finally returns and the song is completed.

▲▲ Thus execution reached “ten routines deep” before starting to return back up, and each of these copies of GreenBottles had its own private copy of the variable n. The limit to how deep you are allowed to go varies from one player's machine to another, but here is a rule of thumb, erring on the low side for safety's sake. (On a standard interpreter it will certainly be safe.) Total up 4 plus the number of its variables for each routine that needs to be running at the same time, and keep the total beneath 1,000. Ten green bottles amounts only to a total of 10 × 5 = 50, and as it seems unlikely that anyone will wish to read the lyrics to “two hundred and one green bottles” the above recursion is safe.

§1.9 Code blocks, else and switch

A feature of all statements choosing what to do next is that instead of just giving a single ‹statement›, one can give a list of statements grouped together into a unit called a “code block”. Such a group begins with an open brace { and ends with a close brace }. For example,

if (alpha > 5) {
    v = alpha*alpha;
    print "The square of alpha is ", v, ".^";
}

If alpha is 3, nothing is printed; if alpha is 9,

The square of alpha is 81.

is printed. (The indentation used in the source code, like all points of source code layout, is a matter of personal taste.†) In some ways, code blocks are like routines, and at first it may seem inconsistent to write routines between [ and ] brackets and code blocks between braces { and }. However, code blocks cannot have private variables of their own and do not return values; and it is possible for execution to break out of code blocks again, or to jump from block to block, which cannot happen with routines.


† Almost everybody indents each code block as shown, but the position of the open brace is a point of schism. This book adopts the “One True Brace Style”, as handed down in such sacred texts as the original Unix source code and Kernighan and Ritchie's textbook of C. Other conventions place the open brace vertically above its matching closure, perhaps on its own otherwise blank line of source code.

· · · · ·

An if statement can optionally have the form

if (condition)statement1elsestatement2

whereupon ‹statement1› is executed if the condition is true, and ‹statement2› if it is false. For example,

if (alpha == 5) print "Five."; else print "Not five.";

Note that the condition is only checked once, so that the statement

if (alpha == 5) {
    print "Five.";
    alpha = 10;
}
else 
    print "Not five.";

cannot ever print both “Five” and then “Not five”.

The else clause has a snag attached: the problem of “hanging elses”. In the following, which if statement does the else attach to?

if (alpha == 1) if (beta == 2)
    print "Clearly if alpha=1 and beta=2.^";
else
    print "Ambiguous.^";

Without clarifying braces, Inform pairs an else to the most recent if. (Much as the U.S. Supreme Court rigorously interprets muddled laws like “all animals must be licensed, except cats, other than those under six months old” by applying a so-called Last Antecedent Rule to ambiguous qualifications like this “other than…”.) The following version is much better style:

if (alpha == 1) {
    if (beta == 2)
        print "Clearly if alpha=1 and beta=2.^";
    else
        print "Clearly if alpha=1 but beta not 2.^";
}

· · · · ·

The ifelse … construction is ideal for switching execution between two possible “tracks”, like railway signals, but it is a nuisance trying to divide between many different outcomes this way. To follow the analogy, the switch construction is like a railway turntable.

print "The train on platform 1 is going to ";
switch (DestinationOnPlatform(1)) {
    1: print "Dover Priory.";
    2: print "Bristol Parkway.";
    3: print "Edinburgh Waverley.";
    default: print "a siding.";
}

The default clause is optional but must be placed last if at all: it is executed when the original expression matches none of the other values. Otherwise there's no obligation for these clauses to be given in numerical or any other order. Each possible alternative value must be a constant, so

switch (alpha) {
    beta: print "The variables alpha and beta are equal!";
}

will produce a compilation error. (But note that in a typical game, the name of an object or a location, such as First_Court, is a constant, so there is no problem in quoting this as a switch value.)

Any number of outcomes can be specified, and values can be grouped together in lists separated by commas, or in ranges like 3 to 6. For example:

print "The mission Apollo ", num, " made ";
switch (num) {
    7, 9: print "a test-flight in Earth orbit.";
    8, 10: print "a test-flight in lunar orbit.";
    11, 12, 14 to 17: print "a landing on the Moon.";
    13: print "it back safely after a catastrophic explosion.";
}

Each clause is automatically a code block, so a whole run of statements can be given without the need for any braces { and } around them.

If you're used to the C language, you might want to note a major difference: Inform doesn't have “case fall-through”, with execution running from one case to the next, so there's no need to use break statements.

▲▲ A good default clause for the above example would be a little complicated: Apollo 1 was lost in a ground fire, causing a shuffle so that 2 and 3 never happened, while automatic test-flights of the Saturn rocket were unofficially numbered 4 to 6. Apollo 20 was cancelled to free up a heavy launcher for the Skylab station, and 18 and 19 through budget cuts, though the Apollo/Soyuz Test Project (1975) is sometimes unhistorically called Apollo 18. The three Apollo flights to Skylab were called Skylab 2, 3 and 4. All six Mercury capsules were numbered 7, while at time of writing the Space Shuttle mission STS-88 has just landed and the next to launch, in order, are projected to be 96, 93, 99, 103, 101 and 92. NASA has proud traditions.

§1.10   while, dountil, for, break, continue

The other four Inform control constructions are all “loops”, that is, ways to repeat the execution of a given statement or code block. Discussion of one of them, called objectloop, is deferred until §3.4.

The two basic forms of loop are while and dountil:

while (condition) statement
do statement until (condition)

The first repeatedly tests the condition and, provided it is still true, executes the statement. If the condition is not even true the first time, the statement is never executed even once. For example:

[ SquareRoot n x;
  while (x*x < n) x = x + 1;
  if (x*x == n) return x;
  return x - 1;
];

which is a simple if rather inefficient way to find square roots, rounded down to the nearest whole number. If SquareRoot(200) is called, then x runs up through the values 0, 1, 2, …, 14, 15, at which point x*x is 225: so 14 is returned. If SquareRoot(0) is called, the while condition never holds at all and so the return value is 0, made by the if statement.

The dountil loop repeats the given statement until the condition is found to be true. Even if the condition is already satisfied, like (true), the statement is always executed the first time through.

· · · · ·

One particular kind of while loop is needed so often that there is an abbreviation for it, called for. This can produce any loop in the form

start
while (condition) {
    
    update
}

where ‹start› and ‹update› are expressions which actually do something, such as setting a variable. The notation to achieve this is:

for (start : condition : update)

Note that if the condition is false the very first time, the loop is never executed. For instance, this prints nothing:

for (counter=1 : counter<0 : counter++) print "Banana";

Any of the three parts of a for statement can be omitted. If the condition is missed out, it is assumed always true, so that the loop will continue forever, unless escaped by other means (see below).

For example, here is the while version of a common kind of loop:

counter = 1;
while (counter <= 10) {
    print counter, " ";
    counter++;
}

which produces the output “1 2 3 4 5 6 7 8 9 10”. (Recall that counter++ adds 1 to the variable counter.) The abbreviated version is:

for (counter=1 : counter<=10 : counter++)
    print counter, " ";

Using commas, several assignments can be joined into one. For instance:

i++, score=50, j++

is a single expression. This is never useful in ordinary code, where the assignments can be divided up by semicolons in the usual way. But in for loops it can be a convenience:

for (i=1, j=5: i<=5: i++, j--) 
    print i, " ", j, ", ";

produces the output “1 5, 2 4, 3 3, 4 2, 5 1,”.

▲▲ Comma , is an operator, and moreover is the one with the lowest precedence level. The result of a,b is always b, but a and b are both evaluated and in that order.

· · · · ·

On the face of it, the following loops all repeat forever:

while (true) statement
do statement until (false)
for (::) statement

But there is always an escape. One way is to return from the current routine. Another is to jump to a label outside the loop (see below), though if one only wants to escape the current loop then this is seldom good style. It's neatest to use the statement break, which means “break out of” the current innermost loop or switch statement: it can be read as “finish early”. All these ways out are entirely safe, and there is no harm in leaving a loop only half-done.

The other simple statement used inside loops is continue. This causes the current iteration to end immediately, but the loop then continues. In particular, inside a for loop, continue skips the rest of the body of the loop and goes straight to the update part. For example,

for (i=1: i<=5: i++) {
    if (i==3) continue;
    print i, " ";
}

will output “1 2 4 5”.

· · · · ·

The following routine is a curious example of a loop which, though apparently simple enough, contains a trap for the unwary.

[ RunPuzzle n count;
  do {
     print n, " ";
     n = NextNumber(n);
     count++;
  }
  until (n==1);
  print "1^(taking ", count, " steps to reach 1)^";
];

[ NextNumber n;
  if (n%2 == 0) return n/2;     ! If n is even, halve it
  return 3*n + 1;               ! If n is odd, triple and add 1
];

The call RunPuzzle(10), for example, results in the output

10 5 16 8 4 2 1
(taking 6 steps to reach 1)

The definition of RunPuzzle assumes that, no matter what the initial value of n, enough iteration will end up back at 1. If this did not happen, the interpreter would lock up into an infinite loop, printing numbers forever. The routine is apparently very simple, so it would seem reasonable that by thinking carefully enough about it, we ought to be able to decide whether or not it will ever finish. But this is not so easy as it looks. RunPuzzle(26) takes ten steps, but there again, RunPuzzle(27) takes 111. Can this routine ever lock up into an infinite loop, or not?

▲▲ The answer, which caught the author by surprise, is: yes. Because of Inform's limited number range, eventually the numbers reached overflow 32,767 and Inform interprets them as negative − and quickly homes in on the cycle −1, −2, −1, −2,… This first happens to RunPuzzle(447). Using proper arithmetic, unhindered by a limited number range, the answer is unknown. As a student in Hamburg in the 1930s, Lothar Collatz conjectured that every positive n eventually reaches 1. Little progress has been made (though it is known to be true if n is less than 240), and even Paul Erdös said of it that “Mathematics is not yet ready for such problems.” See Jeffrey Lagarias's bibliography The 3x + 1 Problem and its Generalisations (1996).

§1.11   How text is printed

Adventure games take a lot of trouble over printing, so Inform is rich in features to make printing elegant output more easy. During story-file interpretation, your text will automatically be broken properly at line-endings. Inform story files are interpreted on screen displays of all shapes and sizes, but as a programmer you can largely ignore this. Moreover, Inform itself will compile a block of text spilling over several source-code lines into normally-spaced prose, so:

print "Here in her hairs
       the painter plays the spider, and hath woven
       a golden mesh t'untrap the hearts of men
       faster than gnats in cobwebs";

results in the line divisions being replaced by a single space each. The text printed is: “Here in her hairs the painter plays the spider, and hath woven a golden mesh…” and so on. There is one exception to this: if a line finishes with a ^ (new-line) character, then no space is added.

You shouldn't type double-spaces after full stops or other punctuation, as this can spoil the look of the final text on screen. In particular, if a line break ends up after the first space of a double-space, the next line will begin with the second space. Since many typists habitually double-space, Inform has a switch -d1 which contracts “.  ” to “. ” wherever it occurs, and then a further setting -d2 which also contracts “!  ” to “! ” and “?  ” to “? ”. A modest warning, though: this can sometimes make a mess of diagrams. Try examining the 1851 Convention on Telegraphy Morse Code chart in ‘Jigsaw’, for instance, where the present author inadvertently jumbled the spacing because -d treated the Morse dots as full stops.

· · · · ·

When a string of text is printed up with print, the characters in the string normally appear exactly as given in the source code. However, four characters have special meanings. ^ means “print a new-line”. The tilde character ~, meaning “print a quotation mark”, is needed since quotation marks otherwise finish strings. Thus,

print "~Look,~ says Peter. ~Socks can jump.~^Jane agrees.^";

is printed as

“Look,” says Peter. “Socks can jump.”
Jane agrees.

The third remaining special character is @, occasionally used for accented characters and other unusual effects, as described below. Finally, \ is reserved for “folding lines”, and is no longer needed but is retained so that old programs continue to work.

If you want to print an actual ~, ^, @ or \, you may need one of the following “escape sequences”. A double @ sign followed by a decimal number means the character with the given ZSCII value, so in particular

@@92 comes out as “\ @@64 comes out as “@
@@94 comes out as “^ @@126 comes out as “~

· · · · ·

A number of Inform games have been written in European languages other than English, and even English-language games need accented letters from time to time. Inform can be told via command-line switches to assume that quoted text in the source code uses any of the ISO 8859-1 to -9 character sets, which include West and Central European forms, Greek, Arabic, Cyrillic and Hebrew. The default is ISO Latin-1, which means that you should be able to type most standard West European letters straight into the source code.

If you can't conveniently type foreign accents on your keyboard, or you want to make a source code file which could be safely taken from a PC to a Macintosh or vice versa, you can instead type accented characters using @. (The PC operating system Windows and Mac OS use incompatible character sets, but this incompatibility would only affect your source code. A story file, once compiled, behaves identically on PC and Macintosh regardless of what accented letters it may contain.) Many accented characters can be written as @, followed by an accent marker, then the letter on which the accent appears:

@^   put a circumflex on the next letter: a e i o u A E I O or U
@'   put an acute on the next letter: a e i o u y A E I O U or Y
@`   put a grave on the next letter: a e i o u A E I O or U
@:   put a diaeresis on the next letter: a e i o u A E I O or U
@c   put a cedilla on the next letter: c or C
@~   put a tilde on the next letter: a n o A N or O
@\   put a slash on the next letter: o or O
@o   put a ring on the next letter: a or A

A few other letter-forms are available: German ß (@ss), ligatures (@oe, @ae, @OE, @AE), Icelandic “thorn” @th and “eth” @et, a pounds-sterling sign (@LL), Spanish inverted punctuation (@!! and @??) and continental European quotation marks (@<< and @>>): see Table 2. For instance,

print "Les @oeuvres d'@AEsop en fran@ccais, mon @'el@`eve!";
print "Na@:ive readers of the New Yorker re@:elected Mr Clinton.";
print "Gau@ss first proved the Fundamental Theorem of Algebra.";

Accented characters can also be referred to as constants, like other characters. Just as 'x' represents the character lower-case-X, so '@^A' represents capital-A-circumflex.

▲▲ It takes a really, really good interpreter with support built in for Unicode and access to the proper fonts to use such a story file, but in principle you can place any Unicode character into text by quoting its Unicode value in hexadecimal. For instance, @{a9} produces a copyright sign (Unicode values between $0000 and $00ff are equal to ISO Latin1 values); @{2657} is a White bishop chess symbol; @{274b} is a “heavy eight teardrop-spoked propeller asterisk”; @{621} is the Arabic letter Hamza, and so on for around 30,000 more, including vast sets for Pacific Rim scripts and even for invented ones like Klingon or Tolkien's Elvish. Of course none of these new characters are in the regular ZSCII set, but ZSCII is configurable using Inform's Zcharacter directive. See §36 for more.

· · · · ·

The remaining usage of @ is hacky but powerful. Suppose you are trying to implement some scenes from Infocom's spoof of 1930s sci-fi, ‘Leather Goddesses of Phobos’, where one of the player's companions has to be called Tiffany if the player is female, and Trent if male. The name turns up in numerous messages and it would be tiresome to keep writing

if (female_flag) print "Tiffany"; else print "Trent";

Instead you can use one of Inform's 32 “printing-variables” @00 to @31. When the text @14 is printed, for instance, the contents of string 14 are substituted in. The contents of string 14 can be set using the string statement, so:

if (female_flag) string 14 "Tiffany"; else string 14 "Trent";
...
print "You offer the untangling cream to @14, who whistles.^";

The value specified by a string statement has to be a literal, constant bit of text. There are really hacky ways to get around this, but if you needed to then you'd probably be better off with a different solution anyway.

§1.12   The print and print_ret statements

The print and print_ret statements are almost identical. The difference is that the second prints out a final and extra new-line character, and then causes a return from the current routine with the value true. Thus, print_ret should be read as “print this, print a new-line and then return true”, and so

print_ret "That's enough of that.";

is equivalent to

print "That's enough of that.^"; rtrue;

As an abbreviation, it can even be shortened to:

"That's enough of that.";

Although Inform newcomers are often confused by the fact that this innocently free-standing bit of text actually causes a return from the current routine, it's an abbreviation which pays dividends in adventure-writing situations:

if (fuse_is_lit) { deadflag = true; "The bomb explodes!"; }
"Nothing happens.";

Note that if the source code:

[ Main;
  "Hello, and now for a number...";
  print 21*764;
];

is compiled, Inform will produce the warning message:

line 3: Warning: This statement can never be reached.
>   print 21*764;

because the bare string on line 2 is printed using print_ret: so the text is printed, then a new-line is printed, and then a return takes place immediately. As the warning message indicates, there is no way the statement on line 3 can ever be executed.

So what can be printed? The answer is a list of terms, separated by commas. For example,

print "The value is ", value, ".";

contains three terms. A term can take the following forms:

a value printed as a (signed, decimal) number
text in double-quotes printed as text
(rule)value printed according to some special rule

Inform provides a stock of special printing rules built-in, and also allows the programmer to create new ones. The most important rules are:

(char) print out the character which this is the ZSCII code for
(string) print out this string
(address) print out the text of this dictionary word
(name) print out the name of this object (see §3)

Games compiled with the Inform library have several other printing rules built-in (like print (The) …), but as these aren't part of the Inform language as such they will be left until §26.

print (string) … requires a little explanation. Of the following lines, the first two print out “Hello!” but the third prints only a mysterious number:

print (string) "Hello!";
x = "Hello!"; print (string) x;
x = "Hello!"; print x;

This is because strings are internally represented by mysterious numbers. print (string) means “interpret this value as the mysterious number of a string, and print out that string”: it is liable to give an error, or in some cases print gibberish, if applied to a value which isn't the mysterious number of any string.

· · · · ·

Any Inform program can define its own printing rules simply by providing a routine whose name is the same as that of the rule. For example, the following pair of routines provides for printing out a value as a four-digit, unsigned hexadecimal number:

[ hex x y;
  y = (x & $7f00) / $100;
  if (x<0) y = y + $80;
  x = x & $ff;
  print (hexdigit) y/$10, (hexdigit) y,
        (hexdigit) x/$10, (hexdigit) x;
];

[ hexdigit x;
  x = x % $10;
  switch (x) {
      0 to 9: print x;
      10: print "a";  11: print "b";  12: print "c";
      13: print "d";  14: print "e";  15: print "f";
  }
];

You can paste these two routines into any Inform source code to make these new printing rules hex and hexdigit available to that code. For example, print (hex) 16339; will then print up “3fd3”, and print (hex) -2; will print “fffe”. Something to look out for is that if you inadvertently write

print hex(16339);

then the text printed will be “3fd31”, with a spurious 1 on the end: because you've printed out the return value of the routine hex, which was true, or in other words 1.

§1.13   Other printing statements

Besides print and print_ret, several other statements can also be used for printing.

new_line

prints a new-line, otherwise known as a carriage return (named for the carriage which used to move paper across a typewriter). This is equivalent to

print "^"

but is a convenient abbreviation. Similarly,

spacesnumber

prints a sequence of the given number of spaces.

boxstring1› … ‹stringn

displays a reverse-video panel in the centre of the screen, containing each string on its own line. For example, the statement

box "Passio domini nostri" "Jesu Christi Secundum" "Joannem";

displays the opening line of the libretto to Arvo Pärt's ‘St John Passion’:

Passio domini nostri
Jesu Christi Secundum
Joannem

· · · · ·

Text is normally displayed in an unembellished but legible type intended to make reading paragraphs comfortable. Its actual appearance will vary from machine to machine running the story file. On most machines, it will be displayed using a “font” which is variably-pitched, so that for example a “w” will be wider on-screen than an “i”. Such text is much easier to read, but makes it difficult to print out diagrams. The statement

print "+------------+
      ^+   Hello    +
      ^+------------+^";

will print something irregular if the letters in “Hello” and the characters “-”, “+” and “ ” (space) do not all have the same width on-screen:

+------------+
+   Hello    +
+------------+

Because one sometimes does want to print such a diagram, to represent a sketch-map, say, or to print out a table, the statement font is provided:

font on
font off

font off switches into a fixed-pitch display style. It is now guaranteed that the interpreter will print all characters at the same width. font on restores the usual state of affairs.

In addition, you can choose the type style from a small set of possibilities: roman (the default), boldface, underlined or otherwise emphasized (some interpreters will use italic for this), reverse-colour:

style roman
style bold
style underline
style reverse

“Reverse colour” would mean, for instance, yellow on blue if the normal text appearance happened to be blue on yellow. An attempt will be made to approximate these effects whatever kind of machine is running the story file.

Changes of foreground and background colours, so memorably used in games like Adam Cadre's ‘Photopia’, can be achieved with care using Z-machine assembly language. See §42.

§1.14   Generating random numbers

Inform provides a small stock of functions ready-defined, but which are used much as other routines are. As most of these concern objects, only one will be given here: random, which has two forms:

random(N)

returns a random number in the range 1, 2, …, N, each of these outcomes being (in theory) equally likely. N should be a positive number, between 1 and 32,767, for this to work properly.

random(two or more constant quantities, separated by commas)

returns a random choice from the given selection of constant values. Thus,

print (string) random("red", "blue", "green", "violet");

prints the name of one of these four colours at random. Likewise,

print random(13, 17);

has a 50% chance of printing “13”, and a 50% chance of printing “17”.

Random numbers are produced inside the interpreter by a “generator” which is not truly random in its behaviour. Instead, the sequence of numbers it produces depend on a value inside the generator called the “seed”. Setting this value is called “seeding the random-number generator”. Whenever it has the same seed value, the same sequence of random values will be harvested. This is called “pseudo-randomness”. An interpreter normally goes to some effort to start up with a seed value which is unpredictable: say, the current time of day in milliseconds. Because you just might want to make it predictable, though, you can change the seed value within the story file by calling random(N) for a negative number N of your own choosing. (This returns zero.) Seeding is sometimes useful to test a game which contains random events or delays. For example if you have release 29 of Infocom's game ‘Enchanter’, you may be surprised to know that you can type “#random 14” into it, after which its random events will be repeatable and predictable. Internally, ‘Enchanter’ does this by calling random(-14).

§1.15   Deprecated ways to jump around

There are four statements left which control the flow of execution, but one should try to avoid using any of them if, for instance, a while loop would do the job more tidily. Please stop reading this section now.

Oh very well. ‘Deprecated’ is too strong a word, anyway, as there are circumstances where jumping is justified and even elegant: to break out of several loops at once, for instance, or to construct a finite state machine. It's the gratuitous use of jumping which is unfortunate, as this rapidly decreases the legibility of the source code.

The jump statement transfers execution to some named place in the same routine. (Some programming languages call this goto.) To use jump a notation is needed to mark particular places in the source code. Such markers are called “labels”. For example, here is a never-ending loop made by hand:

[ Main i;
  i=1;
  .Marker;
  print "I have now printed this ", i++, " times.^";
  jump Marker;
];

This routine has one label, Marker. A statement consisting only of a full stop and then an identifier means “put a label here and call it this”. Like local variables, labels belong to the routines defining them and cannot be used by other routines.

The quit statement ends interpretation of the story file immediately, as if a return had taken place from the Main routine. This is a drastic measure, best reserved for error conditions so awful that there is no point carrying on.

▲▲ An Inform story file has the ability to save a snapshot of its entire state and to restore back to that previous state. This snapshot includes values of variables, the point where code is currently being executed, and so on. Just as we cannot know if the universe is only six thousand years old, as creationists claim, having been endowed by God with a carefully faked fossil record, so an Inform story file cannot know if it has been executing all along or if it was only recently restarted. The statements required are save and restore:

savelabel
restorelabel

This is a rare example of an Inform feature which may depend on the host machine's state of health: for example, if all disc storage is full, then save will fail. It should always be assumed that these statements may well fail. A jump to the label provided occurs if the operation has been a success. This is irrelevant in the case of a restore since, if all has gone well, execution is now resuming from the successful branch of the save statement: because that is where execution was when the state was saved.

▲▲ If you don't mind using assembly language (see §42), you can imitate exceptions, in the sense of programming languages like Java. The opcodes you would need are @throw and @catch.

REFERENCES
If you need to calculate with integers of any size then the restriction of Inform numbers to the range −32,768 to 32,767 is a nuisance. The function library "longint.h", by Chris Hall and Francis Irving, provides routines to calculate with signed and unsigned 4-byte integers, increasing the range to about ±2,147,000,000.   L. Ross Raszewski's function library "ictype.h" is an Inform version of the ANSI C routines "ctype.h", which means that it contains routines to test whether a given character is lower or upper case, or is a punctuation symbol, and so forth. (See also the same author's "istring.h".)