DM4 §27: Listing and grouping objects

## §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.