§45   Limitations and getting around them

How wide the limits stand
Between a splendid and an happy land.
— Oliver Goldsmith (1728–1774), The Deserted Village

The Z-machine is well-designed, and has three major advantages: it is compact, widely portable and can be quickly executed. Nevertheless, like any rigidly defined format it imposes limitations. This section is intended to help those few designers who encounter the current limits. Some of the economy-measures below may sound like increasingly desperate manoeuvres in a lost battle, but if so then the cavalry is on its way: Andrew Plotkin has written a hybrid version of Inform which removes almost every restriction. Although it doesn't quite have all the nooks and crannies of Inform yet working, it does allow most games to compile without difficulty to a very much larger virtual machine than the Z-machine called “glulx”.

1. Story file size.  The maximum size of a story file (in K) is given by:

V3V4V5V6V7V8
128256256512320512

Because the centralised library of Inform is efficient in terms of not duplicating code, even 128K allows for a game at least half as large again as a typical old-style Infocom game. Inform is normally used only to produce story files of Versions 5, 6 and 8. Version 5 is the default; Version 6 should be used where pictures or other graphical features are essential to the game; Version 8 is a size extension for Version 5, allowing games of fairly gargantuan proportions.

If story file memory does become short, a standard mechanism can save about 8­10% of the total memory, though it will not greatly affect readable memory extent. Inform does not usually trouble with this economy measure, since there's very seldom any need, and it makes the compiler run about 10% slower. What you need to do is define abbreviations and then run the compiler in its “economy” mode (using the switch -e). For instance, the directive

Abbreviate " the ";

(placed before any text appears) will cause the string “ the ” to be internally stored as a single ‘letter’, saving memory every time it occurs (about 2,500 times in ‘Curses’, for instance). You can have up to 64 abbreviations. When choosing abbreviations, avoid proper nouns and instead pick on short combinations of a space and common two- or three-letter blocks. Good choices include " the ", "The ", ", ", " and ", "you", " a ", "ing ", " to". You can even get Inform to work out by itself what a good stock of abbreviations would be, by setting the -u switch: but be warned, this makes the compiler run about 29,000% slower.

2. Readable memory size.  In a very large game, or even a small one if it uses unusually large or very many arrays, the designer may run up against the following Inform fatal error message:

This program has overflowed the maximum readable-memory size of the Z-machine format. See the memory map below: the start of the area marked “above readable memory” must be brought down to $10000 or less.

In other words, the readable-memory area is absolutely limited to 65,536 bytes in all Versions. Using the -D debugging option increases the amount of readable-memory consumed, and the Infix -X switch increases this further yet. (For instance ‘Advent’ normally uses 24,820 bytes, but 25,276 with -D and 28,908 with -X.) The following table suggests what is, and what is not, worth economising on.

Each …Costs …
Routine0
Text in double-quotes0
Object or class26
Common property value3
Non-common property value5
If a property holds an arrayadd 2 for each entry after the first
Dictionary word9
Verb3
Different action4
Grammar token3
--> or table array entry2
-> or string array entry1

To draw some morals: verbs, actions, grammar and the dictionary consume little readable memory and are too useful to economise on. Objects and arrays are where savings can be made. Here is one strategy for doing so.

2a. Economise on arrays.  Many programmers create arrays with more entries than needed, saying in effect “I'm not sure how many this will take, but it's bound to be less than 1,000, so I'll say 1,000 entries to be on the safe side.” More thought will often reduce the number. If not, look at the typical contents. Are the possible values always between 0 and 255? If so, make it a -> or string array and the consumption of readable memory is halved. Are the possible values always true or false? If so, Adam Cadre's "flags.h" library extension offers a slower-access form of array but which consumes only about 1/8th of a byte of readable memory per array entry.

2b. Turn arrays of constants into routines.  Routines cost nothing in readable memory terms, but they can still store information as long as it doesn't need to vary during play. For instance, ‘Curses’ contains an array beginning:

Array RadioSongs table
  "Queen's ~I Want To Break Free~."
  "Bach's ~Air on a G-string~."
  "Mozart's ~Musical Joke~."

and so on for dozens more easy-listening songs which sometimes play on Aunt Jemima's radio. It might equally be a routine:

[ RadioSongs n;
  switch (n) {
      0: return 100; ! Number of songs
      1: return "Queen's ~I Want To Break Free~.";
      2: return "Bach's ~Air on a G-string~.";
      3: return "Mozart's ~Musical Joke~.";

and so on. Instead of reading RadioSongs-->x, one now reads RadioSongs(x). Not an elegant trick, but it saves 200 bytes of readable memory.

2c. Economise on object properties.  Each time an object provides a property, readable memory is used. This is sometimes worth bearing in mind when writing definitions of classes which will have many members. For instance:

Class Doubloon(100)
 with name 'gold' 'golden' 'spanish' 'doubloon' 'coin' 'money'
           'coins//p' 'doubloons//p',
      ...

Each of the hundred doubloons has a name array with eight entries, so 1700 bytes of readable memory are consumed. This could be reduced to 300 like so:

Class Doubloon(100)
 with parse_name [;
      ! A routine detecting the same name-words
      ...
      ],

2d. Make commonly occurring properties common.  Recall that properties declared with the Property directive are called “common properties”: these are faster to access and consume less memory. If, for instance, each of 100 rooms in your game provides a property called time_zone, then the declaration

Property time_zone;

at the start of your code will save 2 bytes each time time_zone is provided, saving 200 bytes in all. (The library's properties are all common already.)

2e. Economise on objects.  In a room with four scenery objects irrelevant to the action, say a window, a chest of drawers, a bed and a carpet, is it strictly necessary for each to have its own object? Kathleen Fischer: “parse_name is your friend… a single object with an elaborate parse_name can be used to cover a whole lot of scenery.” In Kathleen's technique, it would use parse_name to record which of the words “window”, “chest”, “bed” or “carpet” was used, storing that information in a property: other properties, like description, would be routines which produced text depending on what the object is representing this turn.

2f. Reuse objects.  This is a last resort but L. Ross Raszewski's "imem.h" has helped several designers through it. Briefly, just as an array was converted to a routine in (1) above, "imem.h" converts object definitions to routines, with a minimal number of them “swapped in” as real objects at any given time and the rest – items of scenery in distant locations, for instance – “swapped out”.

3. Grammar.  There can be up to 256 essentially different verbs, each with up to 32 grammar lines. Using the UnknownVerb entry point will get around the former limit, and general parsing routines can make even a single grammar line match almost any range of syntax.

4. Vocabulary.  There is no theoretical limit except that the dictionary words each take up 9 bytes of readable memory, which means that 4,000 words is probably the practical limit. In practice games generally have vocabularies of between 500 and 2,000 words.

5. Dictionary resolution.  Dictionary words are truncated to their first 9 letters (except that non-alphabetic characters, such as hyphens, count as 2 “letters” for this purpose: look up Zcharacter in the index for references to more on this). Upper and lower case letters are considered equal. Since general parsing routines, or parse_name routines, can look at the exact text typed by the player, finer resolution is easy enough if needed.

6. Attributes, properties, names.  There can be up to 48 attributes and an unlimited number of properties, at most 63 of these can be made common by being declared with Property. A property entry can hold up to 64 bytes of data. Hence, for example, an object can have up to 32 names. If an object must respond to more, give it a suitable parse_name routine.

7. Objects and classes.  The number of objects is unlimited so long as there is readable memory to hold their definitions. The number of classes is presently limited to 256, of which the library uses only 1.

8. Global variables.  There can only be 240 of these, and the Inform compiler uses 5 as scratch space, while the library uses slightly over 100; but since a typical game uses only a dozen of its own, code being almost always object-oriented, the restriction is never felt.

9. Arrays.  An unlimited number of Array statements is permitted, although the entries in arrays consume readable memory (see above).

10. Function calls and messages.  A function can be called with at most seven arguments. A message can be called with at most five.

11. Recursion and stack usage.  The limit on this is rather technical (see The Z-Machine Standards Document). Roughly speaking, recursion is permitted to a depth of 90 routines in almost all circumstances, and often much deeper. Direct usage of the stack via assembly language must be modest.