Inform 7 is a powerful system for creating interactive fiction, as I have mentioned before. The “natural” language syntax can be both liberating and confusing. One aspect of I7 that has confounded me is the concept of relations.

In the abstract, relations are the ways in which objects relate to each other. The most intuitive examples of relations are:

The tea is in the cup.  [containment relation] 
The cup is on the desk. [support relation]

Here the relationship between tea and cup and cup and desk are denoted by the prepositions “in” and “on”.

Relations tie things together in various ways. There are mutual one to one relations, one way one to one relations, one to many, one to many groups, etc. See the I7 docs for the full set of these.

However, relations on their own aren’t particularly useful. Relations shine in making new I7 verbs robust and interesting. In the docs, there is this example of making a machine that transmutes a bag of jewels into various other things. The example works as advertised, but does contain a subtle bug that prevents “scaling” this code to move into different values of the same state.

In the example code, a thing can be made into other classes of things through the new verb “to become.”. In the example, these classes of things are edible, valuable and dangerous. The example defines exactly one thing of each type of class: the bag of jewels is valuable; the bag of gunpowder is dangerous; the bag of jelly beans is (nominally) edible.

If one attempts to add additional concrete things to each class (e.g. the bag of broken glass is dangerous) the given example code does not work. This is because the new form of the object is evaluated twice: once during the “carry out” stage when the “insert” rule is run and once when the report rule is evaluating the string “[the new form of the noun].”

You might wonder why this bug exists at all. I am far from an expert in I7, but it seems that the decide rule for “new form” is called explictly in the carry out rule and then implicitly for the string interpolation during report. Because the decide rule picks a random concrete example of a class from the available list created by the relation, the effect is that that the new form reported by the report rule may not be the thing that appears in the player’s inventory.

To fix this, I present the following code. It creates a new player property called “lastChange” that holds the new form of the noun that is generated in the carry out rule. This property is then referred to in the report rule and is added to the player’s inventory. This could have also been solved with a global variable called “lastChange,” but I think this is cleaner.

[Setup the world]
Workshop is a room.
The bag of jewels is carried by the player. 
The player has a thing called lastChange.

[Define the transmutation machine]
The machine is fixed in place in the workshop.

Transmutation relates things to each other in groups.  
The verb to become (it becomes, they become, it became)
implies the transmutation relation.

Definition: a thing is transmutable if it becomes more than one thing.

[Override standard rulebook]
Procedural rule when inserting something into the machine:
   Ignore the can't insert into what's not a container rule. 

Check inserting something which is not transmutable into the machine:
    instead say "You can't transmute that."

[ State changes:
   edible => valuable
   valuable => dangerous
   dangerous => edible
]

To decide which thing is new form of (obj - edible thing): 
   decide on random valuable thing which becomes obj.
To decide which thing is new form of (obj - valuable thing): 
   decide on random dangerous thing which becomes obj.
To decide which thing is new form of (obj - dangerous thing): 
   decide on random edible thing which becomes obj. 

Carry out inserting something into the machine:
   remove the noun from play;
   now lastChange of the player is the new form of the noun;
   now the player carries the lastChange of the player.

[This causes the decide rule to execute twice]
Report inserting something edible into the machine:
   say "The machine spits out [the lastChange of the player].";
   rule succeeds.

Report inserting something valuable into the machine:
   say "The machine produces [the lastChange of the player].";
   rule succeeds.

Report inserting something dangerous into the machine:
   say "The machine coughes out [the lastChange of the player].";
   rule succeeds.

[Define classes of objects]
A thing can be valuable.
A thing can be dangerous.
A thing can be edible.

[Instantiate examples of each class]
The bag of jewels is a valuable thing.
The bag of gold is a valuable thing.
The bag of jelly beans is an edible thing.
The bag of apples is an edible thing.
The bag of gunpowder is a dangerous thing.
The bag of broken glass is a dangerous thing.

[Give a start point to the state machine]
The bag of jewels becomes the bag of gold, the bag of gunpowder, 
the bag of broken glass and the bag of jelly beans.