§27   Listing and grouping objects

As some day it may happen that a victim must be found
I've got a little list – I've got a little list
Of society offenders who might well be underground,
And who never would be missed
Who never would be missed!
— W. S. Gilbert (1836–1911), The Mikado

Listing objects tidily in a grammatical sentence is more difficult than it seems, especially when taking plurals and groups of similar objects into account. Here, for instance, is a list of 23 items printed by a room description in the demonstration game ‘List Property’:

You can see a plastic fork, knife and spoon, three hats (a fez, a Panama and a sombrero), the letters X, Y, Z, P, Q and R from a Scrabble set, Punch magazine, a recent issue of the Spectator, a die and eight coins (four silver, one bronze and three gold) here.

Fortunately, the library's list-maker is available to the public by calling:

WriteListFrom(object, style);

where the list will start from the given object and go along its siblings. Thus, to list all the objects inside X, list from child(X). What the list looks like depends on a “style” made by adding up some of the following:

ALWAYS_BIT Always recurse downwards
CONCEAL_BIT Misses out concealed or scenery objects
DEFART_BIT Uses the definite article in list
ENGLISH_BIT English sentence style, with commas and ‘and’
FULLINV_BIT Gives full inventory entry for each object
INDENT_BIT Indents each entry according to depth
ISARE_BIT Prints “ is ” or “ are ” before list
NEWLINE_BIT Prints new-line after each entry
NOARTICLE_BIT Prints no articles, definite or indefinite
PARTINV_BIT Only brief inventory information after entry
RECURSE_BIT Recurses downwards with usual rules
TERSE_BIT More terse English style
WORKFLAG_BIT At top level (only), only lists objects which have the workflag attribute

Recursing downwards means that if an object is listed, then its children are also listed, and so on for their children. The “usual rules” of RECURSE_BIT are that children are only listed if the parent is transparent, or a supporter, or a container which is open – which is the definition of “see-through” used throughout the Inform library. “Full inventory information” means listing objects exactly as if in an inventory, according to the rigmarole described in §26. “Brief inventory information” means listing as if in a room description: that is, noting whether objects are open, closed, empty or providing light (except that light is only mentioned when in a room which is normally dark).

The best way to decide which bits to set is to experiment. For example, a ‘tall’ inventory is produced by:

WriteListFrom(child(player),
    FULLINV_BIT + INDENT_BIT + NEWLINE_BIT + RECURSE_BIT);

and a ‘wide’ one by:

WriteListFrom(child(player),
    FULLINV_BIT + ENGLISH_BIT + RECURSE_BIT);

which produce effects like:

>inventory tall
You are carrying:
  a bag (which is open)
    three gold coins
    two silver coins
    a bronze coin
  four featureless white cubes
  a magic burin
  a spell book
>inventory wide
You are carrying a bag (which is open), inside which are three gold coins, two silver coins and a bronze coin, four featureless white cubes, a magic burin and a spell book.

except that the “You are carrying” part is not done by the list-maker, and nor is the final full stop in the second example.

The workflag is an attribute which the library scribbles over from time to time as temporary storage, but you can use it for short periods with care. In this case it makes it possible to specify any reasonable list.

▲▲ WORKFLAG_BIT and CONCEAL_BIT specify conflicting rules. If they're both given, then what happens is: at the top level, but not below, everything with workflag is included; on lower levels, but not at the top, everything without concealed or scenery is included.

EXERCISE 66
Write a DoubleInvSub action routine to produce an inventory like so:

You are carrying four featureless white cubes, a magic burin and a spell book. In addition, you are wearing a purple cloak and a miner's helmet.

· · · · ·

Lists are shorter, neater and more elegantly phrased if similar objects are grouped together. For instance, keys, books and torch batteries are all grouped together in lists printed by the game ‘Curses’. To achieve this, the objects belonging to such a group (all the keys for instance) provide a common value of the property list_together. If this value is a number between 1 and 1000, the library will unobtrusively group the objects with that value together so that they appear consecutively in any list. For instance ‘Curses’ includes the following definitions:

Constant KEY_GROUP = 1;
...
Object -> -> brass_key "small brass key"
  with ...
       list_together KEY_GROUP;

and similarly for the other keys. Alternatively, instead of being a small number the common value can be a string such as "foodstuffs". If so, then it must either be given in a class definition or else as a constant, like so:

Constant FOOD_GROUP = "foodstuffs";

(In particular, the actual text should only be written out in one place in the source code. Otherwise two or more different strings will be made, which just happen to have the same text as each other, and Inform will consider these to be different values of list_together.) Lists will then cite, for instance,

three foodstuffs (a scarlet fish, some lembas wafer and an onion)

in running text, or

three foodstuffs:
  a scarlet fish
  some lembas wafer
  an onion

in indented lists. This only happens when two or more are gathered together. Finally, the common value of list_together can be a routine, such as:

list_together [;
    if (inventory_stage == 1) {
        print "heaps of food, notably";
        if (c_style & INDENT_BIT == 0) print " ";
            else print " --^";
    } else if (c_style & INDENT_BIT == 0)
        print " (which only reminds you how hungry you are)";
],

Typically this might be part of a class definition from which all the objects in question inherit. Any list_together routine will be called twice: once, with inventory_stage set to 1, as a preamble to the list of items, and once (with 2) to print any postscript required. It is allowed to change c_style, the current list style, without needing to restore the old value and may, by returning true from stage 1, signal the list-maker not to print a list at all. The above example would give a conversational sentence-like list as follows:

heaps of food, notably a scarlet fish, some lembas wafer and an onion (which only reminds you how hungry you are)

and would also look suitable in sober tall-inventory-like columns:

heaps of food, notably --
  a scarlet fish
  some lembas wafer
  an onion

A list_together routine has the opportunity to look through the objects which are about to be listed together, by looking at some of the list-maker's variables. parser_one holds the first object in the group and parser_two holds the depth of recursion in the list, which might be needed to keep the indentation straight. Applying x = NextEntry(x,parser_two) will move x on from one object to the next in the group being listed.

The library variable listing_together is set to the first object of a group being listed, when a group is being listed, or to nothing the rest of the time. This is useful because an object's short_name routine might need to behave differently during a grouped listing to the rest of the time.

EXERCISE 67
Implement the Scrabble pieces from the example list above.

▲▲ EXERCISE 68
Implement the three denominations of coin.

▲▲ EXERCISE 69
Implement the I Ching in the form of six coins, three gold (goat, deer and chicken), three silver (robin, snake and bison) which can be thrown to reveal gold and silver trigrams.

▲▲ EXERCISE 70
Design a class called AlphaSorted, members of which are always listed in alphabetical order. Although this could be done with list_together, this exercise is here to draw the reader's attention to an ugly but sometimes useful alternative. The only actions likely to produce lists are Look, Search, Open and Inv: so react to these actions by rearranging the object tree into alphabetical order before the list-writer gets going.

REFERENCES
A good example of WriteListFrom in action is the definition of CarryingClass from the example game ‘The Thief’, by Gareth Rees. This alters the examine description of a character by appending a list of what that person is carrying and wearing.   Andreas Hoppler has written an alternative list-writing library extension called "Lister.h", which defines a class Lister so that designers can customise their own listing engines for different purposes in a single game.   Anson Turner's example program "52.inf" models a deck of cards, and an extensive list_together sorts out hands of cards.