Contemplating improvements to the built-in testing commands

I often find myself wishing that the debugging commands (particularly RULES and RELATIONS, maybe also SHOWME) were more versatile.

Some examples:

  • Some rulebooks might be called a lot, such as deciding the concealed possessions or deciding the scope. If you’re trying to debug something unrelated to concealment or scope, it would be a great help if such rulebooks could just be skipped over – ie, don’t show them even when rule debugging is enabled.
  • On the other hand, sometimes you might want to debug a specific rulebook, so it would be nice if you could ask to show the rules only for that one rulebook.
  • If there are a lot of relations in the game, it would be much easier to be able to select one and list it, rather than arbitrarily listing every relation. The show relation phrase does help with that a bit, but it’s not a command, so if you run into an issue and want to check the relation, you need to rebuild and replay.
  • Besides which, listing every relation doesn’t list every relation. What if you want to chart the containment relation? It’s not possible. Even the show relation phrase doesn’t work, despite it being a well-defined relation that you can easily iterate over. Calculated relations can’t be charted either. (Sometimes it wouldn’t make sense to do so, but if they’re relating objects, there’s no reason why you can’t loop over every object to test what it relates to.)

In the case of RULES, I noticed that you can define a rule based rulebook, which means you could in theory do something like this:

Silly little debug-hiding example
Debug is a rule based rulebook.
Debug the standard looking under rule: rule fails.

Include (-
[ DB_Rule R N blocked;
	if (R==0) return;
	! Begin modification
	FollowRulebook((+ debug rulebook +), R, true);
	if(RulebookFailed()) return;
	! End modification
	print "[Rule ~", (RulePrintingRule) R, "~ ";
	#ifdef NUMBERED_RULES; print "(", N, ") "; #endif;
	if (blocked == false) {
		"applies.]";
	}
	print "does not apply (wrong ";
	if (blocked == 1) print "scene";
	if (blocked == 2) print "action";
	if (blocked == 3) print "actor";
	if (blocked == 4) print "context";
	print ").]^";
];
-) replacing "DB_Rule".

If you run the game and try looking under something, it no longer announces that the standard looking under rule was triggered. However, it instead announces that the Debug the standard looking under rule was triggered, so as-is it’s not very useful.

It would be more useful if it could skip an entire rulebook, but that doesn’t work[1] I have a feeling the above approach wouldn’t extend well to rulebooks (you’d probably have to override FollowRulebook, which is a bit of a crazy endeavour, as it’s a huge routine).

It’s possible to debug rules for only one specific rulebook as follows:

Single-rulebook debugging

Rule-debugging is a truth state that varies.
Rule-debugging variable translates into I6 as “debug_rules”.
First deciding the concealed possessions of rule: now rule-debugging is true.
Last deciding the concealed possessions of rule: now rule-debugging is false.

But that’s not accessible as a command, so you’d need to rebuild to use it. Plus, it breaks normal RULES if active.

Does anyone know a way to filter RULES to include or exclude certain ruleb ooks?


For RELATIONS, it’s relatively easy to provide phrases that chart arbitrary relations, even the spatial ones. For example:

Sample relation charter
An object can be marked for listing.
To chart relation (R - an equivalence relation of objects):
	say "Tracing [the R] (grouping):[line break]";
	let singular elements be a list of objects;
	clear marked for listing objects;
	repeat with current target running through all objects:
		if the current target is not relation-testable, next;
		if the current target is marked for listing, next;
		let L be a list of objects;
		repeat with T running through the list of objects which relate to the current target by R:
			now T is marked for listing;
			add T to L;
		if the number of entries in L is 1:
			add the current target to singular elements;
		otherwise if L is not empty:
			say "  [L in brace notation][line break]";
	clear marked for listing objects;
	if singular elements is not empty:
		say "  and [singular elements] are singular";

The main difficulty in this case comes in understanding the relation in the parser, so that you can type something like SHOWREL CONTAINMENT to chart the containment relation, or SHOWREL LOCK-FITTING to chart the lock-fitting relation, and so forth. Currently the best I can come up with for this is to manually enumerate every relation in a table and assign it a name for understanding.

There’s also a difficulty in that the kinds a relation relates are part of the type, so, for example, the above sample charter won’t work on relations dealing with kinds of value, which means the number of charting phrases could rapidly get out of hand. Hopefully you can get away with only considering “object” and “value” as the types (using kind variables to accept any kind of value that’s not an object), but I haven’t investigated that; even then, however, you need to consider every combination of “object” and “value” (though, admittedly, there are only 4 possible combinations there). And this also means multiple tables are required if that’s the method to assign a relation a name. (Hopefully just 4 tables is sufficient…)

In putting that together, I also noticed that the empty adjective doesn’t work on the spatial relations, which is kinda weird since it should be pretty easy to check if those relations are empty.

Anyway, I’ve been wondering: is there any better way to understand a relation than building a huge table?

The primary issue with the table is that you need to manually add every new relation, including ones from extensions. I’d like it if there were a way to automatically account for any relation that’s defined. This would probably require having an adjective like iterable or such to determine whether a given relation can be charted (I think only relations of numbers or texts to numbers or texts are truly unchartable, though generalizing it to any relation of a non-enumerable kind to a non-enumerable kind would probably be the way to go).


Then there’s the SHOWME command. I frequently find myself wanting to know whether a particular adjective applies to a particular object, and the SHOWME command should logically be the optimal place to get that information. But SHOWME only shows properties, not arbitrary adjectives. (Many adjectives are property values, so it often works; but many adjectives are calculated definitions instead.)

Unfortunately, I don’t believe adjectives are enumerable, so there might be no way to generically support this… but is there maybe a way to add a specific dynamic adjective to SHOWME’s output?*


Has anyone else tried ways of improving or augmenting the built-in debug commands? Other than the one Zed gave me awhile ago to add timing info to RULES (I’m still using that, though it’s only occasionally helpful), or the collapsing duplicate objects in SHOWME from a recent thread.


  1. To be clear, you can totally define a Debug rule that takes a rulebook rather than a rule, but the rules in that rulebook are still announced – in other words, it doesn’t do anything. In fact, that rule is never called – I guess DB_Rule is only called for single rules, not for rulebooks.
    Also, as an aside, I couldn’t find a way to define a Debug rule applying to the debug rulebook. ↩︎

Probably won’t be a surprise that I have.

One can… but it’s pretty painful. Here’s an example of exclusion:

lab is room.

excluded-rules is a list of rules that varies.

debug-val is a kind of value. debug #0 specifies a debug-val.
debug is a debug-val that varies.
The debug variable translates into Inter as "debug_rules".
debug-off is always debug #0.
debug-on is always debug #1.
debug-on-steroids is always debug #2.
first after starting the virtual machine: now debug is debug-off.

after doing anything (this is the did something rule): say "I did something."; continue the action.

instead of thinking:
  say "jumping with debug on.";
  now debug is debug-on;
  try jumping;
  say "jumping with debug on and `did something` excluded.";
  add the did something rule to excluded-rules;
  try jumping;

Include (-
[ DB_Rule R N blocked q;
        if (R==0) return;
        if (LIST_OF_TY_FindItem((+ excluded-rules +), R)) return;
        print "[Rule ~", (RulePrintingRule) R, "~ ";
        #ifdef NUMBERED_RULES; print "(", N, ") "; #endif;
        if (blocked == false) "applies.]";
        print "does not apply (wrong ";
        if (blocked == 1) print "scene";
        if (blocked == 2) print "action";
        if (blocked == 3) print "actor";
        if (blocked == 4) print "context";
        print ").]^";
];
-) replacing "DB_Rule".

test me with "think".

Including a particular rulebook is sort of easy. For the Foo rulebook you’d need just First foo: now debug is debug-on. What’s harder is turning debug back off when you’re done if a Foo rule succeeds or fails. But with some inconvenient contrivances, one could manage it.

That appears to only exclude a single rule, not an entire rulebook – just like my concept above, though it’s more usable than my concept.

Oh, good point, I missed this issue, My concept above doesn’t work properly if a rule suceeds, fails, or stops.

Yes. A rule “doesn’t know” what rulebook(s) it’s in or whether a given invocation of it is due to a rulebook being followed.

Here’s the bit in FollowRulebook in Rulebooks.i6t that checks whether it’s handling a whole rulebook or just a rule:

        if ((rulebook >= 0) && (rulebook < NUMBER_RULEBOOKS_CREATED)) {

If this is true, rulebook is a valid index into the rulebook array, so it’s really a rulebook, not an individual rule.

                rv = rulebooks_array-->rulebook;

We lookup the individual rulebook. Entries in rulebooks_array are either the EMPTY_RULEBOOK constant or a function corresponding to the whole rulebook. If the latter, this function hard-codes the invocation of each rule in the rulebook, in order. There isn’t an array of them anywhere.

                if (rv ~= EMPTY_RULEBOOK) { 
                        if (rulebook ~= ACTION_PROCESSING_RB) MStack_CreateRBVars(rulebook);

Actions need some infrastructure, so if these are the action-processing rules we build it.

                        if (say__p) RulebookParBreak(no_paragraph_skips);

Setup for either magically doing the right thing in regard to line breaks or making the author cry.

                        rv = rv(no_paragraph_skips);

Ta da, we invoke the rulebook.

                        if (rulebook ~= ACTION_PROCESSING_RB) MStack_DestroyRBVars(rulebook);

Destructor for action-processing overhead.

                } else {
                        rv = 0;
                }

(the EMPTY_RULEBOOK case)

        } else {

Now we’re in the individual rule case.

                if (say__p) RulebookParBreak(no_paragraph_skips);
                rv = rulebook();

The rule is itself a function, so we call it.

                if (rv == 2) rv = reason_the_action_failed;

We have stopped, either in success or failure; we don’t care. (Actual success or failure is stored in the latest_rule_result array.) The compiler hard-codes reason_the_action_failed manipulation into action-related stuff; that’s why I had to re-implement its equivalent from scratch to make reason the rule stopped

                else if (rv) rv = rulebook;

for any other non-zero result, we set rv to the rulebook. If rv is 0, the rule neither failed nor succeeded.

        }

As an example, here’s the top of the Startup Rules function…

[ call_U134 forbid_breaks rv original_deadflag;
    (original_deadflag = deadflag);

And below is its first rule. All this boilerplate is repeated over and over for each rule.

    if (debug_rules) {
        DB_Rule(INITIALISE_MEMORY_R, 0, 0);
    }
    (rv = (INITIALISE_MEMORY_R)());
    if (rv) {
        if ((rv == 2)) {
            return reason_the_action_failed;
        }
        return INITIALISE_MEMORY_R;
    }
    ((latest_rule_result-->(0)) = 0);
    if ((original_deadflag ~= deadflag)) {
        rfalse;
    }
    if (say__p) {
        RulebookParBreak(forbid_breaks);
    }

So we know when we’ve started a rulebook… but we can’t tell when we’ve finished one. Suppose we want to maintain a global of what rulebook we’re in that we’re going to push to and pop from the stack. So at the moment we know we’re in the Foo rulebook… or at least we think we know that. But there’s this Foo rule:

Foo (this is the doomed rule): follow the always successful rule.

And that rule ends in success. There isn’t a way within FollowRulebook to know that the doomed rule is in Foo or that the always successful rule isn’t, so we can’t conclude we’ve left Foo because we’ve encountered a rule that finished.

So we can contrive turning debug on or off for some individual author-created rulebook, but without changing the Inform compiler’s representation of rulebooks, there can’t be a way to include/exclude rulebooks in the general case.

Hm, I thought there was also a (software) stack of currently executing rulebooks that could be tested?

Yeah, I suppose with great pain one could directly inspect the stack to see if one were currently in a given rulebook. But if we want to include Foo (and just Foo) in debugging, that wouldn’t help us make the distinction that we should mention the doomed rule but not the always successful rule.

So, yeah, it isn’t the inability to know when a rulebook has finished that’s the big problem, it’s that we don’t know whether a given rule being run is within a given (or any) rulebook.

Ah, I figured you could look at the top of the stack to see if the currently-running rulebook was a blocked one or not, but it looks like most of the “rulebook stack” machinery is gone now. Shame.

Pushing something onto a stack at the top of FollowRulebook and popping it at the bottom doesn’t sound terribly difficult… though, that’s assuming there exists a way to instruct the compiler to rename FollowRulebook to (eg) FollowRulebookOrig so you can write a new function called FollowRulebook that does exactly that (and calls FollowRulebookOrig).

I think I saw an extension somewhere that did exactly that. Not sure if it’s supported in version 10.1 though.

Ugh, as it turns out, it’s absolutely impossible to handle non-object relations with tables as I’d noted earlier. A “relation of values” column cannot contain a “relation of scenes”, so you’d need a new set of tables for every possible kind of value, which is just ridiculous.

Any other ideas on how to handle this?

I need to somehow temporarily dodge the type system to grab a relation by its name and then forward it to the correct type-appropriate phrase (which would probably be generic via kind variables and thus work properly). But I have absolutely no clue how to do something like that.

…so, I just discovered the “leading-through” relation, which seems annoyingly useless (at runtime at least). It relates rooms and doors. You can write East of the Ballroom is the long slide. Through the long slide is the cellar. to create a one-way connection, which is nice and all, but doors created without that kind of statement will never be added to the relation.

I guess its entire point is for initial setup, but still, it wouldn’t’ve been all that hard to define its meaning at runtime too, for example “through every door is the front side of the door”. Arbitrary, sure, but slightly more useful than “most doors just aren’t in there”.

But is this relation even documented anywhere? I couldn’t find anything in a search through the official documentation, but it’s such a common word (with at least two other uses in Inform) that it would be easy to miss it.

From the Standard Rules:

The leading-through relation is built in to Inform. This has to be stored in the property “door_to”, but we don’t want to give authors direct access to this property, since its contents are strictly speaking not typesafe. (It stores a value to which a message can be sent which must return an object: but that is not always the same thing as storing an object.)

Until 2021, the storage property was called “other side”, and was useful in as much as “the other side of D” would helpfully evaluate to the location on the other side of a door. But that only worked because of a hacky exception in the code handling property evaluation which made “door_to” a special case, and this led to other problems. Instead, a new phrase “other side of (D - door)” has been added to the Standard Rules, and this avoids directly evaluating “door_to”.

Basically, it’s a relation used to make one-way doors fit properly into the room-and-direction system, but it’s deliberately left undocumented because it’s kind of hacky, and should be considered an implementation detail rather than something authors should use.

Iterating over object relations presents its own problem: you need to somehow exclude thedark, Compass, and an unknown number of value property holders from the iteration.

Dealing with the first two is easy:

Include (-
[ IsPublicObject obj i;
	if(obj == thedark) rfalse;
	if(obj == Compass) rfalse;
	rtrue;
];
-).
Definition: an object is source-public if I6 routine "IsPublicObject" says so.

And then always loop over source-public objects instead of just objects.

But how can you exclude the value property holders? Adding this line to the above routine doesn’t work:

! 200 is an arbitrary number that works in my big project.
! I have no idea how you'd find the correct number.
! This would give runtime errors in a smaller project.
for(i=0:i<200:++i) if(obj == value_property_holders-->i) rfalse;

Even though value_property_holders is clearly defined in the final I6 source, I guess it’s defined “too late” for this routine to reference it. Is there any other way to do this?

It still wouldn’t help the case described above: wanting to distinguish that the always successful rule isn’t in the Foo rules; it just happened to get followed in the course of following the Foo rules.

That said, I thought I’d take a whack at maintaining a global with the current rulebook. And a frustration of mine for a while has been that FollowRulebook has a silent vestige of much better rule-debugging output in it…

  • mentioning the names of rulebooks, not just rules
  • indenting the output to match the rulebook nesting level
  • for rulebooks that take a parameter, what parameter was passed
  • explicitly stating whether a rulebook suceeded or failed

Once upon a time I got parts of that working, but I have no idea where that code is now. So I just recreated a much better version of it. I threw in the reason the rule stopped from Equivalent of "reason the action failed" for an object-based rulebook? - #4 by Zed 'cause, well, why not? If I was doing this much violence to FollowRulebook it’s not like that much more could hurt.

I’d make a Borogove snippet of it, but Quixe’s Glkote balks at it sending output to a window with an active line input request. This is a fair cop, but I don’t feel like dealing with it now. git built with either of Cheapglk or Glkterm are okay with it; gargoyle complains about it but doesn’t refuse to run.

Lengthy code
lab is room.
bob is a person in the lab.

To decide what K is a/an/-- (v - a value) cast as a/an/-- (name of kind of value K): (- {v} -).

debug-rules is a number that varies.
the debug-rules variable translates into Inter as "debug_rules".
debug-val is a kind of value. The debug-vals are debug-off, debug-on, debug-on-steroids.
To set debug to (d - debug-val): (- debug_rules = ({d} - 1); -).
debug-setting is an action out of world applying to one debug-val.
understand "set [debug-val]" as debug-setting.
carry out debug-setting: set debug to the debug-val understood.
report debug-setting: say "Debugging set to [(debug-rules + 1) cast as a debug-val].".

foo is an object based rulebook producing an object.
foo lab: rule succeeds with result yourself.
foo yourself: rule succeeds with result lab.

llm is a rulebook producing a text.

first llm when the location is not the lab: do nothing. [ set debug-on-steroids to see this not apply ]

llm (this is the stochastic parrot rule):
let t be "";
repeat with i running from 1 to 50 begin;
  let x be a random number from 97 to 122;
  now t is "[t][x cast as a unicode character]";
end repeat;
rule succeeds with result t;

incr is a number based rulebook producing a text.

incr a number (called n): rule succeeds with result "[n + 1]".

when play begins:
set debug to debug-on-steroids;
follow the foo rules for lab;
follow the incr rules for 27;
let t be the text produced by the llm rules;

test me with "jump / bob, jump".

Include (-
[ DB_Rule R N blocked q;
	if (R==0) return;
        spaces(2 * process_rulebook_count);
	print "[Rule ~", (RulePrintingRule) R, "~ ";
	#ifdef NUMBERED_RULES; print "(", N, ") "; #endif;
	if (blocked == false) "applies.]";
	print "does not apply (wrong ";
	if (blocked == 1) print "scene";
	if (blocked == 2) print "action";
	if (blocked == 3) print "actor";
	if (blocked == 4) print "context";
	print ").]^";
];
-) replacing "DB_Rule".

Include (- Global current_rulebook; -).

To say current rulebook: (- print (RulePrintingRule) current_rulebook; -).

Include (-
Array latest_rule_result --> 4;
-) replacing "latest_rule_result".

To decide what rule is the reason the/-- rule stopped: (- latest_rule_result-->3 -).

Include (-
[ FollowRulebook rulebook parameter no_paragraph_skips rv;
  @push self;
  @push parameter_value;
  if ((Protect_I7_Arrays-->0 ~= 16339) || (Protect_I7_Arrays-->1 ~= 12345)) {
    print "^^*** Fatal programming error: I7 arrays corrupted ***^^";
    @quit;
  }
  if (parameter) {
    self = parameter;
    parameter_object = parameter;
  }
  parameter_value = parameter;
  if ((rulebook >= 0) && (rulebook < NUMBER_RULEBOOKS_CREATED)) {
    rv = rulebooks_array-->rulebook;
    if (debug_rules) {
      spaces(2 * process_rulebook_count);
      if (rv == EMPTY_RULEBOOK) {
         print "[ (";
         print (RulePrintingRule) rulebook, " is empty) ]^";
      }
      else {    
        print "[... following ";
        print (RulePrintingRule) rulebook;
        if (parameter) {
	  print " on ";
	  if (rulebook == WHEN_SCENE_BEGINS_RB or WHEN_SCENE_ENDS_RB) PrintSceneName(parameter);
          else if (metaclass(parameter)) Glulx_PrintAnything(parameter);
	  else print parameter;
        }
        print " ]^";
      }
    }
    if (rv ~= EMPTY_RULEBOOK) {
      @push process_rulebook_count;
      process_rulebook_count++;
      @push current_rulebook;
      current_rulebook = rulebook;
      if (rulebook ~= ACTION_PROCESSING_RB) MStack_CreateRBVars(rulebook);
      if (say__p) RulebookParBreak(no_paragraph_skips);
      rv = rv(no_paragraph_skips);
      if (rulebook ~= ACTION_PROCESSING_RB) MStack_DestroyRBVars(rulebook);
      @pull current_rulebook;
      @pull process_rulebook_count;
      if (debug_rules) {
        spaces(2 * process_rulebook_count);
        print "[... finished ";
        print (RulePrintingRule) rulebook, " ]^";
      }
    }
    else { ! it's an empty rulebook
       rv = 0;
    }
  }
  else { ! plain rule, not a rulebook
    if (say__p) RulebookParBreak(no_paragraph_skips);
    rv = rulebook();
    if (rv == 2) rv = reason_the_action_failed;
    else if (rv) rv = rulebook;
  }
  if (~~rv) latest_rule_result-->0 = RS_NEITHER;
  else {
    latest_rule_result-->3 = rv;
    if (debug_rules) {
      spaces(2*process_rulebook_count);
      print "[ ";
      print (RulePrintingRule) rulebook;
      print " ended";
      if (latest_rule_result-->0 ~= RS_NEITHER) {
        print " in ";
        if (latest_rule_result-->0 == RS_SUCCEEDS) {
          print "success";
          if (latest_rule_result-->1) {
            print " producing ";
            PrintKindValuePair(latest_rule_result-->1, latest_rule_result-->2);
	  }
        }	
        if (latest_rule_result-->0 == RS_FAILS) print "failure";
      }
      print " ]^";
    }
  }
  @pull parameter_value;
  @pull self;
  return rv;
];
-) replacing "FollowRulebook".

Here’s an excerpt from the test me output showing the very end of the Startup Rules, entering test me and the whole turn sequence for a jumping action (the first action taken by test me) and on into the initial steps of the subsequent turn, i.e., getting as far into the parse command rule to print the prompt for the next command.

Obviously, this is massively verbose and one is rarely going to want all of this output. I’ll have to think of options for cutting it down. But, that said, I think this gives a picture of the turn sequence and the action-processing rules that’s really useful and otherwise hard to come by.

[... finished Startup rulebook ]
[... following Turn sequence rulebook ]
  [Rule "parse command rule" applies.]
  [ (before reading a command rulebook is empty) ]
  [ (for reading a command rulebook is empty) ]
>  [ (before constructing the status line rulebook is empty) ]
  [ (after constructing the status line rulebook is empty) ]
  [ (after reading a command rulebook is empty) ]
  [Rule "declare everything initially unmentioned rule" applies.]
  [Rule "generate action rule" applies.]
  [... following Setting action variables rulebook ]
  [... finished Setting action variables rulebook ]
(Testing.)

[... finished Turn sequence rulebook ]
[ Turn sequence rulebook ended in success ]
[... following Turn sequence rulebook ]
  [Rule "parse command rule" applies.]
  [ (before reading a command rulebook is empty) ]
  [ (for reading a command rulebook is empty) ]
>  [ (before constructing the status line rulebook is empty) ]
  [ (after constructing the status line rulebook is empty) ]
[1] jump
  [ (after reading a command rulebook is empty) ]
  [Rule "declare everything initially unmentioned rule" applies.]
  [Rule "generate action rule" applies.]
  [... following Setting action variables rulebook ]
  [... finished Setting action variables rulebook ]
  [... following Action-processing rulebook ]
    [Rule "announce items from multiple object lists rule" applies.]
    [Rule "set pronouns from items from multiple object lists rule" applies.]
    [Rule "before stage rule" applies.]
    [ (Before rulebook is empty) ]
    [Rule "basic visibility rule" applies.]
    [Rule "basic accessibility rule" applies.]
    [Rule "carrying requirements rule" applies.]
    [Rule "instead stage rule" applies.]
    [ (Instead rulebook is empty) ]
    [Rule "requested actions require persuasion rule" applies.]
    [Rule "carry out requested actions rule" applies.]
    [Rule "descend to specific action-processing rule" applies.]
    [... following specific action-processing rulebook ]
      [Rule "work out details of specific action rule" applies.]
      [Rule "investigate player's awareness before action rule" applies.]
      [... following player's action awareness rulebook ]
        [Rule "player aware of his own actions rule" applies.]
      [... finished player's action awareness rulebook ]
      [ player's action awareness rulebook ended in success ]
      [Rule "check stage rule" applies.]
      [ (check jumping rulebook is empty) ]
      [Rule "carry out stage rule" applies.]
      [ (carry out jumping rulebook is empty) ]
      [Rule "after stage rule" applies.]
      [ (After rulebook is empty) ]
      [Rule "investigate player's awareness after action rule" applies.]
      [Rule "report stage rule" applies.]
      [... following report jumping rulebook ]
        [Rule "report jumping rule" applies.]
You jump on the spot.
      [... finished report jumping rulebook ]
      [Rule "last specific action-processing rule" applies.]
    [... finished specific action-processing rulebook ]
    [ specific action-processing rulebook ended in success ]
  [... finished Action-processing rulebook ]
  [ Action-processing rulebook ended in success ]

  [Rule "A first turn sequence rule" applies.]
  [... following Scene changing rulebook ]
    [Rule "scene change machinery rule" applies.]
  [... finished Scene changing rulebook ]
  [ Scene changing rulebook ended ]
  [Rule "every turn stage rule" applies.]
  [ (Every turn rulebook is empty) ]
  [Rule "timed events rule" applies.]
  [Rule "advance time rule" applies.]
  [Rule "update chronological records rule" applies.]
  [Rule "A last turn sequence rule" applies.]
  [... following Scene changing rulebook ]
    [Rule "scene change machinery rule" applies.]
  [... finished Scene changing rulebook ]
  [ Scene changing rulebook ended ]
  [Rule "adjust light rule" applies.]
  [Rule "note object acquisitions rule" applies.]
  [Rule "notify score changes rule" applies.]
[... finished Turn sequence rulebook ]
[... following Turn sequence rulebook ]
  [Rule "parse command rule" applies.]
  [ (before reading a command rulebook is empty) ]
  [ (for reading a command rulebook is empty) ]
>
complete test me output
    [... following foo rulebook on lab ]
      [Rule "foo lab" applies.]
    [... finished foo rulebook ]
    [ foo rulebook ended in success producing yourself ]
    [... following incr rulebook on 27 ]
      [Rule "incr a number ( called n )" applies.]
    [... finished incr rulebook ]
    [ incr rulebook ended in success producing 28 ]
    [... following llm rulebook ]
      [Rule "first llm when the location is not the lab" does not apply (wrong context).]
      [Rule "stochastic parrot rule" applies.]
    [... finished llm rulebook ]
    [ llm rulebook ended in success producing zwdcxdyiovcbbczqilwiluyxfpgwnylgoiobdiqipxtdqnqati ]
  [... finished When play begins rulebook ]

  [Rule "fix baseline scoring rule" applies.]
  [Rule "display banner rule" applies.]
  [ (before printing the banner text rulebook is empty) ]
  [ (for printing the banner text rulebook is empty) ]
Welcome
An Interactive Fiction
Release 1 / Serial number 250721 / Inform 7 v10.1.2 / D
  [ (after printing the banner text rulebook is empty) ]

  [Rule "initial room description rule" applies.]
  [... following Setting action variables rulebook ]
    [Rule "determine visibility ceiling rule" applies.]
  [... finished Setting action variables rulebook ]
  [... following Action-processing rulebook ]
    [Rule "announce items from multiple object lists rule" applies.]
    [Rule "set pronouns from items from multiple object lists rule" applies.]
    [Rule "before stage rule" applies.]
    [ (Before rulebook is empty) ]
    [Rule "basic visibility rule" applies.]
    [Rule "basic accessibility rule" applies.]
    [Rule "carrying requirements rule" applies.]
    [Rule "instead stage rule" applies.]
    [ (Instead rulebook is empty) ]
    [Rule "requested actions require persuasion rule" applies.]
    [Rule "carry out requested actions rule" applies.]
    [Rule "descend to specific action-processing rule" applies.]
    [... following specific action-processing rulebook ]
      [Rule "work out details of specific action rule" applies.]
      [Rule "investigate player's awareness before action rule" applies.]
      [... following player's action awareness rulebook ]
        [Rule "player aware of his own actions rule" applies.]
      [... finished player's action awareness rulebook ]
      [ player's action awareness rulebook ended in success ]
      [Rule "check stage rule" applies.]
      [ (check looking rulebook is empty) ]
      [Rule "carry out stage rule" applies.]
      [... following carry out looking rulebook ]
        [Rule "declare everything unmentioned rule" applies.]
        [Rule "room description heading rule" applies.]
lab
        [Rule "room description body text rule" applies.]
        [Rule "room description paragraphs about objects rule" applies.]
        [... following before printing the locale description rulebook on lab ]
          [Rule "initialise locale description rule" applies.]
          [Rule "find notable locale objects rule" applies.]
          [ (before choosing notable locale objects rulebook is empty) ]
          [... following for choosing notable locale objects rulebook on lab ]
            [Rule "standard notable locale objects rule" applies.]
          [... finished for choosing notable locale objects rulebook ]
          [ (after choosing notable locale objects rulebook is empty) ]
        [... finished before printing the locale description rulebook ]
        [... following for printing the locale description rulebook on lab ]
          [Rule "interesting locale paragraphs rule" applies.]
          [ (before printing a locale paragraph about rulebook is empty) ]
          [... following for printing a locale paragraph about rulebook on yourself ]
            [Rule "initial appearance on supporters rule" does not apply (wrong action).]
            [Rule "don't mention player's supporter in room descriptions rule" applies.]
            [Rule "don't mention scenery in room descriptions rule" applies.]
            [Rule "don't mention undescribed items in room descriptions rule" applies.]
            [Rule "set pronouns from items in room descriptions rule" applies.]
            [Rule "offer items to writing a paragraph about rule" applies.]
            [Rule "use initial appearance in room descriptions rule" applies.]
            [Rule "describe what's on scenery supporters in room descriptions rule" applies.]
            [Rule "describe what's on mentioned supporters in room descriptions rule" applies.]
          [... finished for printing a locale paragraph about rulebook ]
          [ (after printing a locale paragraph about rulebook is empty) ]
          [ (before printing a locale paragraph about rulebook is empty) ]
          [... following for printing a locale paragraph about rulebook on bob ]
            [Rule "initial appearance on supporters rule" does not apply (wrong action).]
            [Rule "don't mention player's supporter in room descriptions rule" applies.]
            [Rule "don't mention scenery in room descriptions rule" applies.]
            [Rule "don't mention undescribed items in room descriptions rule" applies.]
            [Rule "set pronouns from items in room descriptions rule" applies.]
            [Rule "offer items to writing a paragraph about rule" applies.]
            [Rule "use initial appearance in room descriptions rule" applies.]
            [Rule "describe what's on scenery supporters in room descriptions rule" applies.]
            [Rule "describe what's on mentioned supporters in room descriptions rule" applies.]
          [... finished for printing a locale paragraph about rulebook ]
          [ (after printing a locale paragraph about rulebook is empty) ]
          [Rule "you-can-also-see rule" applies.]
          [ (before listing nondescript items rulebook is empty) ]
        [... finished for printing the locale description rulebook ]
        [ (after printing the locale description rulebook is empty) ]

        [Rule "check new arrival rule" applies.]
      [... finished carry out looking rulebook ]
      [Rule "after stage rule" applies.]
      [ (After rulebook is empty) ]
      [Rule "investigate player's awareness after action rule" applies.]
      [Rule "report stage rule" applies.]
      [... following report looking rulebook ]
        [Rule "other people looking rule" applies.]
      [... finished report looking rulebook ]
      [Rule "last specific action-processing rule" applies.]
    [... finished specific action-processing rulebook ]
    [ specific action-processing rulebook ended in success ]
  [... finished Action-processing rulebook ]
  [ Action-processing rulebook ended in success ]
[... finished Startup rulebook ]
[... following Turn sequence rulebook ]
  [Rule "parse command rule" applies.]
  [ (before reading a command rulebook is empty) ]
  [ (for reading a command rulebook is empty) ]
>  [ (before constructing the status line rulebook is empty) ]
  [ (after constructing the status line rulebook is empty) ]
  [ (after reading a command rulebook is empty) ]
  [Rule "declare everything initially unmentioned rule" applies.]
  [Rule "generate action rule" applies.]
  [... following Setting action variables rulebook ]
  [... finished Setting action variables rulebook ]
(Testing.)

[... finished Turn sequence rulebook ]
[ Turn sequence rulebook ended in success ]
[... following Turn sequence rulebook ]
  [Rule "parse command rule" applies.]
  [ (before reading a command rulebook is empty) ]
  [ (for reading a command rulebook is empty) ]
>  [ (before constructing the status line rulebook is empty) ]
  [ (after constructing the status line rulebook is empty) ]
[1] jump
  [ (after reading a command rulebook is empty) ]
  [Rule "declare everything initially unmentioned rule" applies.]
  [Rule "generate action rule" applies.]
  [... following Setting action variables rulebook ]
  [... finished Setting action variables rulebook ]
  [... following Action-processing rulebook ]
    [Rule "announce items from multiple object lists rule" applies.]
    [Rule "set pronouns from items from multiple object lists rule" applies.]
    [Rule "before stage rule" applies.]
    [ (Before rulebook is empty) ]
    [Rule "basic visibility rule" applies.]
    [Rule "basic accessibility rule" applies.]
    [Rule "carrying requirements rule" applies.]
    [Rule "instead stage rule" applies.]
    [ (Instead rulebook is empty) ]
    [Rule "requested actions require persuasion rule" applies.]
    [Rule "carry out requested actions rule" applies.]
    [Rule "descend to specific action-processing rule" applies.]
    [... following specific action-processing rulebook ]
      [Rule "work out details of specific action rule" applies.]
      [Rule "investigate player's awareness before action rule" applies.]
      [... following player's action awareness rulebook ]
        [Rule "player aware of his own actions rule" applies.]
      [... finished player's action awareness rulebook ]
      [ player's action awareness rulebook ended in success ]
      [Rule "check stage rule" applies.]
      [ (check jumping rulebook is empty) ]
      [Rule "carry out stage rule" applies.]
      [ (carry out jumping rulebook is empty) ]
      [Rule "after stage rule" applies.]
      [ (After rulebook is empty) ]
      [Rule "investigate player's awareness after action rule" applies.]
      [Rule "report stage rule" applies.]
      [... following report jumping rulebook ]
        [Rule "report jumping rule" applies.]
You jump on the spot.
      [... finished report jumping rulebook ]
      [Rule "last specific action-processing rule" applies.]
    [... finished specific action-processing rulebook ]
    [ specific action-processing rulebook ended in success ]
  [... finished Action-processing rulebook ]
  [ Action-processing rulebook ended in success ]

  [Rule "A first turn sequence rule" applies.]
  [... following Scene changing rulebook ]
    [Rule "scene change machinery rule" applies.]
  [... finished Scene changing rulebook ]
  [ Scene changing rulebook ended ]
  [Rule "every turn stage rule" applies.]
  [ (Every turn rulebook is empty) ]
  [Rule "timed events rule" applies.]
  [Rule "advance time rule" applies.]
  [Rule "update chronological records rule" applies.]
  [Rule "A last turn sequence rule" applies.]
  [... following Scene changing rulebook ]
    [Rule "scene change machinery rule" applies.]
  [... finished Scene changing rulebook ]
  [ Scene changing rulebook ended ]
  [Rule "adjust light rule" applies.]
  [Rule "note object acquisitions rule" applies.]
  [Rule "notify score changes rule" applies.]
[... finished Turn sequence rulebook ]
[... following Turn sequence rulebook ]
  [Rule "parse command rule" applies.]
  [ (before reading a command rulebook is empty) ]
  [ (for reading a command rulebook is empty) ]
>  [ (before constructing the status line rulebook is empty) ]
  [ (after constructing the status line rulebook is empty) ]
[2] bob, jump
  [ (after reading a command rulebook is empty) ]
  [ (before deciding the scope rulebook is empty) ]
  [ (for deciding the scope rulebook is empty) ]
  [ (after deciding the scope rulebook is empty) ]
  [ (before deciding the scope rulebook is empty) ]
  [ (for deciding the scope rulebook is empty) ]
  [ (after deciding the scope rulebook is empty) ]
  [Rule "declare everything initially unmentioned rule" applies.]
  [Rule "generate action rule" applies.]
  [... following Setting action variables rulebook ]
  [... finished Setting action variables rulebook ]
  [... following Action-processing rulebook ]
    [Rule "announce items from multiple object lists rule" applies.]
    [Rule "set pronouns from items from multiple object lists rule" applies.]
    [Rule "before stage rule" applies.]
    [ (Before rulebook is empty) ]
    [Rule "basic visibility rule" applies.]
    [Rule "basic accessibility rule" applies.]
    [Rule "carrying requirements rule" applies.]
    [Rule "instead stage rule" applies.]
    [ (Instead rulebook is empty) ]
    [Rule "requested actions require persuasion rule" applies.]
    [ (Persuasion rulebook is empty) ]
Bob has better things to do.
  [... finished Action-processing rulebook ]
  [ Action-processing rulebook ended in failure ]

  [Rule "A first turn sequence rule" applies.]
  [... following Scene changing rulebook ]
    [Rule "scene change machinery rule" applies.]
  [... finished Scene changing rulebook ]
  [ Scene changing rulebook ended ]
  [Rule "every turn stage rule" applies.]
  [ (Every turn rulebook is empty) ]
  [Rule "timed events rule" applies.]
  [Rule "advance time rule" applies.]
  [Rule "update chronological records rule" applies.]
  [Rule "A last turn sequence rule" applies.]
  [... following Scene changing rulebook ]
    [Rule "scene change machinery rule" applies.]
  [... finished Scene changing rulebook ]
  [ Scene changing rulebook ended ]
  [Rule "adjust light rule" applies.]
  [Rule "note object acquisitions rule" applies.]
  [Rule "notify score changes rule" applies.]
[... finished Turn sequence rulebook ]
[... following Turn sequence rulebook ]
  [Rule "parse command rule" applies.]
  [ (before reading a command rulebook is empty) ]
  [ (for reading a command rulebook is empty) ]
>  [ (before constructing the status line rulebook is empty) ]
  [ (after constructing the status line rulebook is empty) ]

That looks amazing! While we’re on the subject, I think this is line is actually wrong:

It’s the line that was originally there, but I think that actually refers to whatever the basis of the rulebook is, which might not be an action.

This looks quite weird. Only scenes have a special way of printing? No other enumerated value needs such a thing?

I don’t see WHEN_SCENE_ENDS_RB in the Standard Rules, which makes me suspect it might be different from the When ends begins rule declared there. Could it be that this is the action-based rulebook that rules for specific scenes (as opposed to descriptions of scenes) get placed in? If so, does it somehow account for the scene ending variant as well? Or, are those actually a separate unique set of rulebooks for every scene (and thus, the RulePrintingRule doubles as printing the name of the scene as well as the ending variant, both of which are embedded in the name of the rule).

None of this really matters, mostly idle curiosity.

I guess there’s one more way this could maybe be enhanced, to show which of the named outcomes was produced for rulebooks that have named outcomes (as you could have several different types of success and failure). You can also have a named outcome that makes no decision, though I think it may be impossible to distinguish between two such outcomes on one rulebook… but if the rulebook does have such an outcome and makes no decision, it might make sense to show that named outcome.

No idea how hard that would be though. It’s a fairly small thing, anyway.


Still musing about how to chart arbitrary relations. Pondering whether it’s worth defining a spatial adjective to isolate the fixed spatial relations that are defined as part of the world model. Also sorta wondering if it’s possible to define an adjective on relations that would be true if and only if the relation is a mapping [direction] relation.

Though, I suppose in the case of mapping relations I could just say chart relation east instead of trying to identify the relation itself, which is probably good enough if this is solely for a debugging command, but probably breaks down in some edge cases if you want to call the phrase directly.

I was also thinking about an iterable or enumerable adjective on relations. After looking through the I6 definitions of relations, it looks like it could be approximated by something like (rel-->6) & RELS_SOME_CONSTANT (not sure specifically which constant would be the correct one there, or if & is specifically the correct way to test it). But that definition leaves something to be desired, so I’d want to make exceptions at least for the spatial and mapping relations and probably for any calculated enumerable-enumerable relation (I believe Inform automatically treats calculated relations as non-enumerable even though it’s trivial to enumerate them as long as the domain is enumerable; and some types are trivially enumerable as long as one side is enumerable).

…so now I’ve discovered that “is adjacent to” in Inform is an asymmetric relation, contrary what you’d expect. Normally in English, that would be a symmetric relation (if A is adjacent to B, then of course B is also adjacent to A), but in Inform, it seems that “if A is adjacent to B” means “if there is a connection from B to A”.

It’s not that enumerated values don’t need it, it’s that (so far as I know) there just ain’t a thing I can do about it in the general case, but I could do something about scenes. The invocations of FollowRulebook don’t pass a type. The compiler doesn’t create some nice convenient array for us to look it up in. So my code falls back to numeric equivalent in that case as a least bad choice.

good idea, I’ll look at outcomes tomorrow.

Sooo, there is a never-holding relation which sounds like it’s supposed to be the universal “null relation”. The index supports this by not specifying the kinds it relates.

But it is, in fact, a relation of values. Thus, you can’t assign it to a relation of objects variable, nor can you assign it to a relation of numbers variable. Thus, there is no universal “null relation”. Is there even any point in the never-holding relation existing then?

Also, it’s impossible to create a relation of values variable (though a relation of values table column seems to be fine, you just can’t put anything in it).

From Basic Inform:

To decide which relation of objects is meaning of (V - a verb): (- {V}(CV_MEANING) -).

The meaning of verb can returns the never-holding relation. Theoretically, Inform doesn’t allow null values , so the never-holding relation is the meaningless relation that gives meaning of something to return for verbs that aren’t related to real relations.

In case you hadn’t struck on the syntax, you can create a relation of values variable with specific kinds of value.

lab is room.

color is a kind of value. Colors are red, green, blue.

stooge is a kind of value. stooges are larry, moe, curly.

chromostoogery relates one color to one stooge.
The verb to chromostooge means the chromostoogery relation.

Green chromostooges curly.

hugger-muggery relates one narrative viewpoint to one command parser error.
the verb to hugger-mugger means the hugger-muggery relation.

second person singular hugger-muggers the I beg your pardon error.

r1 is a relation of color to stooge that varies.
r2 is a relation of a narrative viewpoint to a command parser error that varies.

when play begins:
say "[r1] / [r2].";
say "[chromostoogery relation] / [hugger-muggery relation].";

produces:

anonymous relation of colors to stooges / anonymous relation of narrative viewpoints to command parser errors.
chromostoogery relation / hugger-muggery relation.

These fail to compile.

r3 is a relation between a color and a stooge that varies. [ Nope! ]
r4 is a relation of one color to one stooge that varies. [ Nyet! ]

…so, this is cheating the type system and casting the never-holding relation, a relation of values, to a relation of objects. And not just that relation, but any relation is casted to a relation of objects. Which in turn makes meaning of utterly useless in certain cases.

Ben and Lucy are people.
Fliggling relates one thing to one number.
The verb to fliggle means the fliggling relation.
Ben fliggles 3. Lucy fliggles 42.
When play begins:
	showme whether or not the never-holding relation relates 1 to 3;
	showme whether or not the never-holding relation relates red to blue;
	showme whether or not the never-holding relation relates Lucy to Ben;
	showme whether or not the never-holding relation is empty;
	showme whether or not Ben fliggles 3;
	showme whether or not Lucy fliggles 5;
	[showme whether or not Lucy fliggles Ben;] [rightly does not compile]
	let zed be a relation of objects;
	[now zed is the fliggling relation;] [rightly does not compile]
	now zed is the meaning of verb fliggle; [whoops, this compiles]
	[showme whether or not zed relates Ben to 3;] [is logically valid yet does not compile]
	showme whether or not zed relates Lucy to Ben; [rightly false; not sure if I can artificially construct a comparison that's wrongly true]

Also, the never-holding relation is empty is false when it should absolutely be true. I can’t fix it with this statement, either:

Definition: the never-holding relation is empty: yes.

Nor with a regular assertion:

The never-holding relation is empty.

I think I may have solved the problem of excluding special objects (like value property holders) when iterating over objects. This seems to do the trick:

Definition: an object is source-public if I6 condition "(*1) ofclass K0_kind" says so (it is a public I7 object).