Bubbling Beaker Awards (Award #6, Sep 29 2023)

Each week, I will choose an outstanding example of I7 mad science and link to it here.

The “Rules”1

To qualify as “mad science,” a winning post should almost certainly:

  • contain compilable code
  • achieve the original poster’s goal better than any other posted solution

A winning post should also preferably:

  • use a technique never before conceived (or at least never before demonstrated on the forum)
  • deploy I6 inclusions, techniques from later chapters of Writing with Inform, or “hacks” intended to skirt compiler limitations
  • provoke gasps of alarm, cries of outrage and/or sinister chuckles

The winning entry need not be accepted as the solution to the posted problem – it is taken as a given that the general populace may fail to appreciate the genius on display. Additionally, the winner need not be an MSC member, though preference will be given to members in order to minimize the risk of accidental insult.


Anyone, member or not, is free to send a direct message the group to nominate a particular post (preferably on a Wednesday or Thursday). If MSC members make a substantial show of support for a particular candidate, that week’s award will be further distinguished as Peer Revued™.

Note that for the inaugural post, two awards were given because it seemed fitting that the first post to earn the award (numbered zero for technical reasons) should be publicly recognized.2

Winners’ Privileges

Winners are invited to discuss the details of their winning invention here – including how it works, why it works, and (by way of cautionary tale) the circumstances of its inspiration and conception – for the edification of users of Inform and to promote the mad science ethos at large.

>FOOTNOTE 1: As with all things mad science, the prescribed rules are entirely optional and likely to be ignored, scorned and/or forgotten whenever convenient.

>FOOTNOTE 2: Yes, this means that first winner managed to earn the award over 4 months before it even existed. “Impossible” results are the hallmark of mad science at its best!

>CREDITS: The Bubbling Beaker Awards® header image and the mad-scientists flair icon image were created by FLACRabbit (author of A Matter of Heist Urgency).



I wonder if this award survives the heavyweight champion of IF coding, Zarf…
Surely I’ll watch the proceedings :slight_smile: excellent idea, aside the proviso above…

Best regards from Italy,
dott. Piergiorgio.


To start the discussion of Award #0 (given to @Zed): His award-winning post deals with exploring the I6 implementation of relations and verbs. He showed that in some cases there are specific I6 structures set up for reversed relations, and showed that one of these could be accessed indirectly as the meaning of a verb that has been assigned to it.

The floor is open for him to speak more about it, but I will note that for 6M62 the Rel_Record_* structures produced seem to be in almost all respects identical to that of the “unreversed” or “forward” version of the relation. (I say “almost all” because it can differ in what’s produced for at least the RELS_TEST, RELS_ASSERT_TRUE, and RELS_ASSERT_FALSE tasks.)

As he points out, these I6 relation structures are produced only when an active-voice, single-word verb is used for the meaning of a reversed relation (a fact which sheds more light on the discussion of the meaning of verbs found in WWI 14.9 Verbs as values).


To start the discussion of Award #1 (given to @drpeterbatesuk): His award-winning post demonstrates a workaround for matching an object to command input via a property of the object holding a topic. Care to say anything more about it, drpeterbatesuk?


This was one of those triggering situations where it seemed as though the compiler should take a simple phrase in its stride, but it was rejected in disgust with an error message that was itself a little misleading:

Problem. You wrote ‘Understand the colour-range property as describing a swatch’ : but that property is of a kind which I can’t recognise in typed commands, so that it cannot be understood as describing or referring to something. I can understand either/or properties, properties with a limited list of named possible values, numbers, times of day, or units; but certain built-into-Inform kinds of value (like snippet or rulebook, for instance) I can’t use.

See the manual: 17.15 > 17.15. Understanding things by their properties

‘that property is of a kind which I can’t recognise in typed commands’ was something of a surprise, since a topic is very much something one might expect the parser to recognise in typed commands.

To elaborate, the word ‘topic’ has a confusing usage in Inform, as it refers to two entirely different entities. The more commonly encountered one is found in action definitions such as:

Conversing is an action applying to one thing and one topic.

Here, ‘topic’ refers to a sequence of unparsed words, a part of a typed command that could be any text at all and which the parser is not expected to process. Seasoned Informers will realise that part of a typed command is usually now referred to as a ‘snippet’, not a ‘topic’- but the use of the latter term here has historic roots in actions using snippets to represent topics of conversation. e.g.

Understand "converse with [someone] about [text]" as conversing.

Here, just to add to the confusion, the ‘topic’ or (more properly) ‘snippet’ referred to in the action definition is in Understand... phrases represented by a [text] token- which again really represents not a text as in Inform’s text kind, but part of a typed command (here, the part coming after “converse with [someone] about”), in other words a snippet.

However, after parsing, just to add insult to injury, the snippet the parser matched to the [text] token must be referred to as ‘the topic understood’- e.g.

Instead of conversing with Mr Darcy:
    say "Mr Darcy clearly has no interest in [the topic understood].".

Having got this other historic meaning of ‘topic’==‘snippet’ out of the way, what is a topic in the sense of Inform’s topic kind? It is neither a text or a snippet but a routine to match one or more words against a snippet. What does that mean? Recall that a snippet is a sequence of words in the player’s command. A snippet has no textual meaning separate to the typed command because in itself a snippet consists simply of an encoding of two numbers- a starting point and a number of words. The encoding is (start-word)*100+(number of words). So a snippet encoded as 204 means the second to fifth words (inclusive) of the typed command. The snippet representing the full typed command is 100+(length of command in words) and this snippet has the special name ‘the player’s command’.

Topics are usually represented in Inform in a way that at first sight looks confusingly like a text- i.e. something within double quotes. e.g.

If the player's command includes "red", replace the matched text with "green".

Here, the player's command is a snippet, in this case the full typed command; the innocuous-looking “red” is actually a word(s)-> snippet matching routine (a topic); ‘the matched text’ is a snippet representing the position and word(s) where the “red” word(s)-> snippet matching routine made a match in the typed command, and “green” is a text, which will be inserted into the typed command in place of the snippet represented by ‘the matched text’.

So, if we type ‘open the red book’, the player’s command starts as a snippet encoded as 104, the routine compiled by the (topic) “red” searches through the player’s command and matches the third word, so ‘the matched text’ becomes the snippet encoded as 301; this snippet, i.e. the third word in the typed command is then replaced by the text “green” and the typed command ends as ‘open the green book’.

Although a topic like “red” really does look like a common-or-garden text, despite being compiled to an I6 routine, more commonly a topic will embody a more complex search routine than just 'find the word “red” '. e.g.

After reading a command:
    If the player's command includes "red/yellow/blue", replace the matched text with "green".

means that the i6 routine compiled by “red/yellow/blue” will match any one of the words ‘red’, ‘yellow’, or ‘blue’.

And we can go further, by including in topics the same kinds of token that are used in Understand… phrases.

A colour is a kind of value. Some colours are red, orange, yellow, green, blue, indigo, violet.
After reading a command:
    If the player's command includes "[colour]", replace the matched text with "green".

means that the I6 routine compiled by “[colour]” will match the name of any one of the colour values we have defined and replace it with “green”.

And we can even find the word(s) matched by the [colour] token by ‘the colour understood’:

After reading a command:
    If the player's command includes "[colour]", replace the matched text with "green".
        say "I replaced '[the colour understood]' with 'green' and the command is now [the player's command]."

So, it seems plain that a topic held by a property ought to be the sort of thing that the parser can use to match against words in a typed command and it’s a surprise and a disappointment that the compiler is not set up to generate a suitable routine to do this.

However, one thing the compiler is coy about in the error message given at the start of this post is that the text kind is one of the kinds that can be ‘understood as describing or referring to something’ when held in a property. (Another, perhaps unexpected and at the same time less useful kind that can be used in this way is the scene kind).

So I realised this gave a backdoor to allow an ‘After reading a command’ routine to apply a topic grabbed from a topic property of a certain object to the player’s command, then, if there was a match, to put the matched text into a text property of the same object- a property that had been set up to be ‘understood as describing or referring to something’. In parsing the command, now whatever had been matched by the topic property would be matched by the text property, and thereby be recognised as describing or referring to the object.

This partially overcomes the limitation of using a text property to be understood as describing or referring to something, which is that such a property can only match a single literal plain word or sequence of words, not one of alternative words, or tokens. So in a text property, “red/blue/green” will match only the actual text ‘red/blue/green’ rather than ‘red’ or ‘blue’ or ‘green’ and “[colour]” will match only the actual text ‘[colour]’ rather than ‘(any sequence of words that is the name of a defined colour)’.

I have done some more work on the technique to allow more than one word in the command to be matched (as published to date, it will match for example ‘take red handkerchief’ or ‘take all green’ but not ‘take red green handkerchief’ (which requires two separate text properties to hold both “red” and “green” in order that the command can be matched) or ‘take red and green handkerchief’ (which the parser wants to interpret as take '(something) red' AND 'the green handkerchief') rather than ‘take the handkerchief that is both red and green’- to resolve this latter ambiguity requires some rather more substantial hacking of the command).

I have also almost finished working up an alternative approach- as suggested by Otis- whereby a relation is used in place of a property to be ‘understood as describing or referring to something’. To simplify setup by use of a table, as required by the original OP, the colours of things are defined as lists:

Table of Haberdashery
name                colour-range (list of things)
handkerchief        {red,yellow,green}
necktie             {blue,red,white}

and the initial relations swatch<=>colour are created from those lists when play begins. Watch this space!

PS I have submitted an improvement request for topic properties to be ‘understood as describing or referring to something’, hopefully in due course making my efforts redundant…


A little bit more about topics ‘under the hood’
Eagle-eyed investigators may have noticed that ‘topic’ is not a kind listed in the IDE kinds index. This is because it doesn’t fully fit into Inform 7’s kinds paradigm.

As mentioned above, a topic is compiled to an I6 routine, of a type called in I6-speak a ‘General Parsing Routine’ or GPR.

A GPR takes over parsing from a given word (or the current word the parser is up to) in the typed command (held in the I6 global variable wn (for word number)) and then tries to match one or more words from that point onward, returning one of

(i) one of a number of constants according to what kind of word(s) it matched (e.g. GPR_NUMBER)- also updating wn to show where it got up to in the typed command when it finished matching.
(ii) an object, if it matched words describing an object (also with an updated wn)
(iii) the constant GPR_FAIL to indicate that it matched no words (in which case wn is undefined)

If a topic property is created for a kind in I7, the default value for that property is compiled as a GPR called DefaultTopic(), which simply returns the constant GPR_FAIL.

The closest that a topic could be fitted into the I7 kinds paradigm would probably be to say it is a ‘phrase (number, number) → number’, the number parameters being the start word number and an (optional) specified number of words that must be matched and the return value number being the I6 integer representing one of the above valid return values- a GPR return value constant or an object.

For more detail on GPRs, see the I6 bible- the Inform Designer’s Manual 4th edition (DM4)

Here’s a simple GPR taken from the DM4:

[ OnAtorIn;
if (NextWord() == 'on’ or ’at’ or ’in’) return GPR_PREPOSITION;
return GPR_FAIL;

Which duplicates the effect in I7 of the on/at/in part of Understand "Gaze on/at/in [something]" as gazing.

Here is a simple GPR compiled from an I7 topic property, “red/green/blue” :

[ Consult_Grammar1 range_from range_words original_wn group_wn v w rv;
    (wn = range_from);
    (original_wn = wn);
    (group_wn = wn);
    (wn = group_wn);
    if ((NextWordStopped() ~= 'red')) {
        jump class_1_1_2;
    jump class_1_1_end;
    (wn = group_wn);
    if ((NextWordStopped() ~= 'green')) {
        jump class_1_1_3;
    jump class_1_1_end;
    (wn = group_wn);
    if ((NextWordStopped() ~= 'blue')) {
        jump Fail_1;
    if ((((range_words == 0)) || (((wn - range_from) == range_words)))) {
        return rv;
    (wn = original_wn);
    return GPR_FAIL;

NextWordStopped() returns the wn’th word from the typed command, and increments wn, or returns -1 if wn was beyond the end of the typed command so no valid word could be returned.

Note that because they are used in a topic, ‘red’, ‘green’ and ‘blue’ have been added to the dictionary.


It looks like Zed is choosing to invoke his right to remain silent, which is perhaps a useful habit for any mad scientist. To serve the purpose of edification of the Inform user base, I will explain what I learned from Zed’s post:

  • A reversed relation uses the same storage as the “forward” relation on which it is based.
  • When your I7 source uses a verb mapped to a reversed relation, it generates calls to the same handler routine as the “forward” version.

For example, given the setup:

Admiration relates various people to various things. The verb to admire means the admiration relation. The verb to be well-regarded by means the reversed admiration relation. The verb to inspire means the reversed admiration relation.

Bob admires the player.

When play begins:
	if Bob admires the player, say "Bob admires player.";
	if the player is well-regarded by Bob, say "Player is well-regarded by Bob."; [passive voice reversed]
	if the player inspires Bob, say "Player inspires Bob."	[active voice reversed]

the passage of generated I6 for the rule body is (6M62 version):

! [2: if bob admires the player]
if ((((RGuard_T_2(I126_bob,player)))))
{! [3: say ~Bob admires player.~]
    say__p=1;! [4: ~Bob admires player.~]
    ParaContent(); print "Bob admires player."; new_line; .L_Say3; .L_SayX3;}
! [5: if the player is well-regarded by bob]
if ((((RGuard_T_2(I126_bob,player)))))
{! [6: say ~Player is well-regarded by Bob.~]
    say__p=1;! [7: ~Player is well-regarded by Bob.~]
    ParaContent(); print "Player is well-regarded by Bob."; new_line; .L_Say4; .L_SayX4;}
! [8: if the player inspires bob]
if ((((RGuard_T_2(I126_bob,player)))))
{! [9: say ~Player inspires Bob.~]
    say__p=1;! [10: ~Player inspires Bob.~]
    ParaContent(); print "Player inspires Bob."; new_line; .L_Say5; .L_SayX5;}

As can be seen, all three use the same wrapper routine RGuard_T_2(), which makes use of handler routine for the admiration relation, and all three use Bob as the lefthand member of the relation and the player as the righthand member. The relation corresponding to meaning of inspire goes unused.

This change in the mapping of subject/object to left/right is the only actual meaning of the “reversal” in a reversed relation. This is a place where one must mind the gap between Informese and normal English. For example, the code:

Chaining relates various things to various things. The verb to chain forward means the chaining relation. The verb to chain backward means the reversed chaining relation.

does not create a single relation to track a bi-directional graph between things as might be naively expected. Any edge between two things is one-way, and the “reversed” relation just allows you to declare that edge using the target as the subject and the origin as the object. In other words A chains forward B. and B chains backward A. both create the link AB while neither creates a link BA. Assuming that you want all edges to be bi-directional, then declaring Chaining relates things to each other. may be better.

Some of the Recipe Book examples are actually technical notes in disguise. There are several on the topic of relations that are worth studying, including RB Ex 223 Formal syntax of sentences, RB Ex 243 Mathematical view of relations and RB Ex 244 Graph-theory view of relations.


In case anyone was wondering (which I doubt), properties declared as phrases yielding a kind that would otherwise work to understand something as- e.g.

A swatch has a phrase nothing -> text called calculated-text.
Understand the calculated-text property as describing a swatch.

are, unsurprisingly, similarly dismissed. :slight_smile:

1 Like

How about if you first wrap it inside a text substitution within a text property? @Zed showed a neat trick at Referring to things by computed properties - #4 by Zed that may work here.


This week’s prize, Bubbling Beaker Award® #2, is presented to CrocMiam (Florian Cargoët). He is not a mad scientist, just very clever.

His award-winning post shows how to add the ability to sort lists according to the outcome of a phrase returning a number, which is particularly useful when one wants to sort a list of objects based on a computed, dynamic number value which is not being stored as a property of the objects.

@CrocMiam, as an award winner you are invited to lead a discussion about the technical aspects of your invention. If you’re so inclined, the floor is open!


I’m honoured!
I’m willing to dive into the technical details but I don’t think I’ll have the time this weekend.

1 Like

Ah yes- the greatest gift to mad science that just keeps on giving- namely (one) answer to that age-old Inform 7 riddle:

Q. When is a text not a text?

A. When it’s a text with substitution(s).

(… or when it’s a snippet)
(… or when it’s a topic)


As Zed pointed out- although a text with substitutions is not really a text but rather a routine that returns a text, they are stored in a form that superficially mirrors the format of simple texts without substitutions, and this seems enough for the compiler to ‘allow’ them to be understood as describing something…

That gives us an ‘in’ to use I7 and/or I6 routines as we like to manipulate the returned text that will be used to understand the object holding the property…

In this case, by looking at the object’s topic property…

Mad science incoming…


That’s really very neat!

No, it’s a routine that prints text! It doesn’t return anything. (Okay, it returns true.)

But it is still a first-class text under I7’s rules.

Well OK, I know, I was leaning toward giving a simplified view of the world because in phrases such as

    let txt be "this is [some substituted text]".

despite what’s going on behind the scenes the text with substitution (by which I mean a double-quoted I7 construction like "this is [some substituted text]") doesn’t noticeably print anything, but it does syntactically look like a routine that returns a text. That’s how I find it conceptually easiest to understand how they behave in I7.

The associated ‘To say’ phrase producing the substitution itself (i.e the bit between square brackets)

To say some substituted text: say "semantically confusing".

has more the feel of a printing routine- which gives an oblique hint to some of the ‘under the hood mechanics’ of texts with substitutions.

At its lowest I6 level, (at least nowadays) a text with substitution(s) compiles variously depending on context to a sequence of one or more in-line printing statements or routines, or to a pointer to a word array whose second element is a routine that prints text, or to a function that returns either said pointer or a ‘standard’ text type (of which there are several flavours) expanded from the routine in the array it points to, or something like that, but I guess going too deeply into the weeds of these things is for most purposes making things more complicated than is helpful.

Much as classical mechanics isn’t really exactly right, but for most purposes it’s a more helpful way of thinking about the world than quantum mechanics.

1 Like

To I7, it’s just a mild-mannered unsubstituted text value…

xy is initially "[yourself]".

but beneath those glasses there lies… an I6 routine!

[ call_U2010;
    (say__p = 1);

that runs whatever arbitrary code you might have included in a to say phrase you might have written, including printing text (optionally), and returns true.

But if you squint at let x be the substituted form of xy, which assigns a dynamically generated text value to x, and give your metaphors a healthy stir, that quack starts sounding at least a little duck-like to me.


heh, or

      let mylocaltextvariable be z;

after which, depending on the nature of z, mylocaltextvariable might end up as either a dynamically-created text or a pointer (indirectly) to the same routine as z (indirectly) points to…

1 Like

Regarding Bubbling Beaker Award® #2, although CrocMiam has not yet taken the floor, I will give a brief overview because this week’s award is coming up fast.

CrocMiam’s invention creates some variations on the I6 routines called LIST_OF_TY_Sort() and ListCompareEntries(), which are found in the Lists.i6t template file. The normal versions of these routines are designed to use a property as the basis of the sort, while his variations are designed to make use of a routine as the basis. The routine that will be used for sorting is generated from an author-created to decide which number... phrase, which must be given a parenthesized name, as in:

To decide which number is the name length of (O - thing) (this is name length):
	decide on the number of characters in the printed name of O.

The phrases for sorting found his original post are quite narrow in range, to match the specific problem that was posted. A slightly more generalized version would be:

To sort (L - a list of things) in/into (ph - phrase thing -> number) order:
	(- LIST_OF_TY_SortBy({-lvalue-by-reference:L}, 1, {ph}); -).

To sort (L - a list of things) in/into reverse (ph - phrase thing -> number) order:
	(- LIST_OF_TY_SortBy({-lvalue-by-reference:L}, -1, {ph}); -).

It is important that the kind matches up between the different places in the code specifying the “arguments” for phrases – in the preceding examples they are all the thing kind. (The phrases can be generalized further, if desired, but I’ll leave that aside.)

Of note is the fact that the declarations for the sorting phrases make use of a syntax which causes them to operate “by reference” instead of “by value.” This ensures that the sorting will be done on the list that is supplied as an argument, instead of a sorting a copy of the list that just gets thrown away after the sort has been completed.

EDIT: I would also add that Zed came up with something similar for sorting according to author-defined phrases at Sorting scenes - #6 by Zed.


Thank you, I couldn’t find the time to write.

Another thing of note is the technique for calling an I7 phrase from I6.

The I7 phrase is passed to I6 with the {ph} syntax but ph is not directly a reference to the function. It’s an array: [info about kinds; a reference to the actual function; text containing the name of the I7 phrase]. For instance, the phrase:

To decide which number is attack points of (m - monster) (this is attack points):

would give:

Array DK5_phrase_monster____number --> [ PHRASE_TY; 2; NUMBER_TY; ];
Array closure_data_U21 --> [ DK5_phrase_monster____number; call_U1966; "attack points"; ];
[ call_U1966 t_0;
    // …

ph is closure_data_U21 and ph-->1 is call_U1966, the function we’re interested in.

To call it: (ph-->1)(arg1, arg2, …)


This week’s prize, Bubbling Beaker Award® #3, is presented to @Draconis (Daniel Stelzer). As with last week’s award, the winner is not a mad scientist, though he contributes to mad science discussions with suspicious regularity. His award-winning post creates an alternate list writer routine designed to more flexible than the built-in one. His invention was inspired by a method used by zarf in Hadean Lands but adds innovations of its own.

Draconis, you gave a pretty good overview of your invention already on the original post, but is there anything that you would like to say by way of acceptance speech (or perhaps as opening arguments for the defense)?