"does the player mean" rule doesn't seem to fire

I wrote some code for hinting objects which seems like it should work. And it does, with a modification. But I’m confused why the original code does not, in fact, work.

A solution to 'Does the player mean' disambiguation (finicky) gives some idea of how to work around things in general with “does the player mean” and it helped me figure out code that works.

My objective here is to try to guess which object the player wants to hint. Objects in “done-room” are placed there once the game determines you are done with them, or they vanish–for instance, if you eat an item successfully, it would go there.

r1 is a room. r2 is eat of r1. done-room is a room.

the blue circle is a thing in r2.
the blue square is a thing in done-room.
the yellow circle is a thing.
the yellow square is a thing in r2.

does the player mean hinting an off-stage thing (called th) (this is the dpm1 rule):
	say "1. [th].";
	it is very unlikely.

does the player mean hinting a thing (called th) in done-room (this is the dpm2 rule):
	say "2. [th].";
	it is unlikely.

does the player mean hinting a thing (called th) (this is the dpm3 rule):
	say "3. [th].";
	if location of th is done-room, it is unlikely;
	if th is off-stage, it is very unlikely;


[the dpm1 rule is listed first in the does the player mean rules.]
[the dpm2 rule is listed first in the does the player mean rules.]
[the dpm3 rule is listed first in the does the player mean rules.]
[the dpm3 rule is not listed in the does the player mean rules.]
[the dpm2 rule is not listed in the does the player mean rules.]
[the dpm1 rule is not listed in the does the player mean rules.]

test h with "h blue/h yellow/h circle/h square".

The code above successfully disambiguates.

  1. why does the dpm3 rule fire first? This happens with or without labeling rule names.
  2. If I delete dpm3, then whether I put dpm1 or dpm2 first, I get disambiguation problems. The does the player mean rules seem to only try dpm1 or dpm2.
  3. if I don’t delete dpm3, but I have dpm1 or dpm2 first, I still see “3. (item checked)”
  4. if I remove dpm2 and dpm1 from the rulebook, dpm3 successfully disambiguates.

The code works, but I don’t understand why the dpm3 works better than the dpm2/dpm1. They seem equivalent to me.

Thanks!

1 Like

A rulebook halts when a rule within it returns a definite result. The does the player mean rulebook works like any other in this respect.

A rule’s preamble must evaluate true in order for its body to be executed. Otherwise, it will be skipped.

The does the player mean rulebook has no default result. That means that if a rule does not have a definite result, the rulebook will continue to evaluate additional rules.

The dpm3 rule contains two if statements that can produce a definite result. It does not have a guaranteed result, so if neither if condition is true, it will have no result.

The dpm1 and dpm2 rules have unconditional definite results. If either rule is run, it will halt the rulebook.

You can inspect the does the player mean rulebook’s ordering in the Index. I see that the ordering priority (ignoring other rules included via the Standard Rules) as dpm2, dpm1, then dpm3.

The does the player mean rules are checked once for each object in scope that is a potential match to the player’s input, in order to find out whether that object gets a bonus or penalty for automatic disambiguation.

Given the way that you’ve constructed the preamble for dpm2, it probably doesn’t mean what you think it means. For the code:

does the player mean hinting a thing (called th) in done-room (this is the dpm2 rule):

the in done-room part will apply to the player’s location, not the location of th. You might instead want:

does the player mean hinting a thing (called th) that is in done-room (this is the dpm2 rule):

Your example doesn’t include the definition for the hinting action. I used:

Hinting is an action applying to one visible thing. Understand "h [any thing]" as hinting.

The Understand line for the action’s grammar allows any thing in the game to be in scope. If yours is different, the following may not apply.

For command >H BLUE:

  1. Only the blue circle and blue square can match player input, so the DTPM rules will run only on those things.
  2. The blue circle is not off-stage and the player is not in done-room, so the preambles for dpm1 and dpm2 evaluate false. The preamble for dpm3 evaluates true for the blue circle but neither if condition evaluates true, so there is output from the say statement in the rule’s body but no change in the blue circle’s scoring.
  3. The blue square is in done-room. While dpm1 and dpm2 are both skipped for it (as with the blue circle), the first if condition of dpm3 evaluates true, so the rule ends with a definite result penalizing the blue square.
  4. Because the blue square was penalized and the blue circle was not, the blue circle is chosen via automatic disambiguation.

For command >H YELLOW:

  1. Only the yellow circle and yellow square can match player input, so the DTPM rules will run only on those things.
  2. The yellow square is not off-stage and the player is not in done-room, so the preambles for dpm1 and dpm2 evaluate false. The preamble for dpm3 evaluates true for the yellow square but neither if condition evaluates true, so there is output from the say statement in the rule’s body but no change in the yellow square’s scoring. (It’s a minor mystery as to why this object is evaluated first, but the ordering doesn’t affect the outcome here.)
  3. The yellow circle is off-stage but not in done-room. The preamble for dpm2 evaluates false, so the rule is skipped. The preamble for dpm1 evaluates true, so the rule is processed. The dpm1 rule provides a definite result of very unlikely, applying a penalty to the yellow circle and also halting execution of DTPM rules for this object.
  4. Because the yellow circle was penalized and the yellow square was not, the yellow square is chosen via automatic disambiguation.

Command >H CIRCLE works similarly to >H YELLOW.

Command >H SQUARE works similarly to >H BLUE.

6 Likes

Thanks very much for the explanation. Just to check,

does the player mean hinting a thing (called th) (this is the dpm2 rule):
	if th is in done-room, it is unlikely.

Does indeed work.

I guess I committed the Inform version of a dangling modifier here!

Your explanation also made something click from years ago. I was trying to pile up a bunch of rules, and I wound up with an ugly hack that worked. But putting more in the body of the rules would’ve been easier.

For some reason, when I first programmed Inform, I put nothing in the body of “does the player mean” rules because I assumed you couldn’t. Even though I know better, I can still forget to be specific at times.

1 Like

The condition:

th is in done-room

checks that th is “on the floor” of done-room.

The similar construction <object> is a thing in done-room checks for the same condition, so:

When play begins:
    if blue square is a thing in done-room, say "YES."

will result in the output being printed.

However, the condition in <room> means something special in the context of a rule preamble. It doesn’t automatically bind to a preceding object like it does in the if condition above. I’m pretty sure this is to facilitate rules that look like:

Check jumping in Low-Ceilinged Room:
    say "You'd bonk your head." instead.

If you go back to your original code and try >GONEAR DONE-ROOM before your test commands, you’ll see that dpm2 is run.

2 Likes

It’s worth noting that, across I7’s history, this syntax has been getting more restricted. Originally “in [room]” was a condition like any other, so you could have rules apply “when in [room]” or whatever. But this was considered unnecessarily confusing, because it’s not the same as “when the player is in [room]” (it’s instead equivalent to “when the location of the player is [room]” or “when the player is enclosed by [room]”).

So a few versions ago, “when in [room]” was removed. But “in [room]” as a condition on an action-based rule (i.e. “instead of waiting in [room]”) was considered too valuable to remove in the same way; it’s nicely concise and usually clear to a reader, and much more elegant than “when the location is [room]”. This means it can still be used in the preambles of action-based rules, as long as you don’t have a “when” or “while” before it.

And “does the player mean” rules are, of course, action-based; “does the player mean eating the bread” is (as far as the compiler is concerned) exactly the same structure as “before eating the bread” or “instead of eating the bread”. It’s conceivable that you’d want to use an equivalent of “waiting in [room]” with them (“does the player mean reading a sign in the Library”) and so “in” retains its unusual meaning in this context.

2 Likes

And to elaborate even a little bit further, for the people who like technical details, there are six built-in ways an object can be referenced in an action pattern (an “action pattern” is the description of an action you can use in a rule like “instead of eating an animal”). Authors can add more of these, which is how you can have “going from”, “going to”, “going with”, “going by”, and “going through”: those five prepositions are all added to the “going” action in the Standard Rules.

(Side note: adding new action variables like this is pretty rare but sometimes extremely useful. This is how Counterfeit Monkey, for example, can have rules about word-manipulation “creating” a specific thing or type of thing. Emily Short added a new action variable “matched as ‘creating’”, which means it can now be tested in action specifications.)

The three most basic ones are the actor, the noun, and the second noun. These are used when you write “instead of Alice waiting”, “instead of climbing the fish”, and “instead of giving something to the large insect”.

The other three are involving, in, and in the presence of. These ones are less common but still useful: involving checks if an object is either the actor, the noun, or the second noun, in checks if an object is the location (or sometimes a region containing the location), and in the presence of checks if an object is visible.

It’s the second of these, the in clause, that’s becoming relevant here.

2 Likes

oh good grief. when X in Y and when X is in Y don’t mean the same thing. I hadn’t known this one.

It looks like you can compile if X in Y as a plain conditional, too, outside of a rule preamble, but so far as I’ve seen it never returns true.

Not quite true- to be exact, it checks whether an object is in scope for the actor (usually an object in scope will be visible and vice versa but not always- see here)

1 Like

‘if X is in Y’ compiles to ‘if (Y == ContainerOf(X))’
‘if X in Y’ adds an opaque condition to the compiled I6: ‘if (Y == ContainerOf(X) && (self == X))’

After a conditional action pattern including ‘in’, self is set to equal the noun,such that after ‘instead of eating the banana in the Lab’ self == banana. I’m not sure what the purpose of this is, but it does mean that ‘if X in Y’ becomes true in the following example:

Lab is a room.

The banana is in the Lab.

Instead of eating the banana in the Lab:
	if banana in Lab:
		say "xyzzy.";
	if banana is in Lab:
		say "plugh."

yields the following I6

! No specific request
! Instead of eating the banana in the Lab:
[ R_799 ;
    if ((((action ==##Eat) &&  (actor==player) && ((noun == I128_banana) && (true)) && ((real_location == I125_lab) && (true))))) { ! Runs only when pattern matches
    self = noun;
    if (debug_rules) DB_Rule(R_799, 799);
    ! [2: if banana in lab]
    if (((I125_lab == ContainerOf(I128_banana)) && (self == I128_banana)))
    {! [3: say ~xyzzy.~]
        say__p=1;! [4: ~xyzzy.~]
        ParaContent(); print "xyzzy."; new_line; .L_Say3; .L_SayX3;}
    ! [5: if banana is in lab]
    if (((I125_lab == ContainerOf(I128_banana))))
    {! [6: say ~plugh.~]
        say__p=1;! [7: ~plugh.~]
        ParaContent(); print "plugh."; new_line; .L_Say4; .L_SayX4;}
        RulebookFails(); rtrue;
        } else if (debug_rules > 1) DB_Rule(R_799, 799, 'action');
        rfalse;
];

and the transcript:

>eat banana
xyzzy.
plugh.
1 Like

Yes, rather counter-intuitively, if the actor is in an enterable container such as a cupboard a rule such as

Instead of an actor eating the banana in the cupboard...

will never fire, because the cupboard is not the actor’s location.

(also to be clear it’s the location of the actor that’s checked, not the location of the banana!)

wow, it’s even worse and weirder – it looks like x in y behaves differently depending on whether you’re in a rule whose conditions include ‘in’.

The lab is a room.
the box is a container. the box is in the lab.
the spam is in the box.
The map region of the lab is the house.
The block is a region.
The house is in the block.
Neighborhood is a region.
The block is in the neighborhood.

when play begins:
 if lab in house, say "lab in house.";
 if house in block, say "house in block.";
 if house in neighborhood, say "house in neighborhood.";
 if lab is in house, say "lab is in house.";
 if house is in block, say "house is in block.";
 if house is in neighborhood, say "house is in neighborhood.";
 if box in lab, say "box in lab.";
 if box is in lab, say "box is in lab.";
 if spam in box, say "spam in box.";
 if spam is in box, say "spam is in box.";

instead of smelling the spam in the lab:
 if lab in house, say "lab in house.";
 if house in block, say "house in block.";
 if house in neighborhood, say "house in neighborhood.";
 if lab is in house, say "lab is in house.";
 if house is in block, say "house is in block.";
 if house is in neighborhood, say "house is in neighborhood.";
 if box in lab, say "box in lab.";
 if box is in lab, say "box is in lab.";
 if spam in box, say "spam in box.";
 if spam is in box, say "spam is in box.";

instead of smelling the box in the lab:
 if lab in house, say "lab in house.";
 if house in block, say "house in block.";
 if house in neighborhood, say "house in neighborhood.";
 if lab is in house, say "lab is in house.";
 if house is in block, say "house is in block.";
 if house is in neighborhood, say "house is in neighborhood.";
 if box in lab, say "box in lab.";
 if box is in lab, say "box is in lab.";
 if spam in box, say "spam in box.";
 if spam is in box, say "spam is in box.";

=>

 
lab is in house.
house is in block.
house is in neighborhood.
box is in lab.
spam is in box.
 
Welcome
An Interactive Fiction
Release 1 / Serial number 220714 / Inform 7 v10.1.0 / D
 
lab
You can see a box (in which is a spam) here.
 
>smell spam
lab is in house.
house is in block.
house is in neighborhood.
box is in lab.
spam in box.
spam is in box.
 
>smell box
lab is in house.
house is in block.
house is in neighborhood.
box in lab.
box is in lab.
spam is in box.
 
> 

I think those results are expected based on my previous observation that ‘X in Y’ being true depends on both Y==ContainerOf(X) and self==X being true, and that after rule ...ing ...X...in... self is set to equal X.

Outside of such a rule it’s not clear under what other circumstances in I7 self==X (in I6, self would normally be a referent to a method’s object).

What the purpose is in I7 of making self==X a requirement in the context of the conditional X in Y, I can’t immediately see… The I6 compilation of many (perhaps all) rules conditional on an action pattern (e.g. Check an actor taking) starts by setting self equal to noun, but I still can’t see how that links in to a rational use in I7 for `X in Y’

EDIT:
Consequently, modifying your code to:

instead of smelling the spam:
 if lab in house, say "lab in house.";
 if house in block, say "house in block.";
 if house in neighborhood, say "house in neighborhood.";
 if lab is in house, say "lab is in house.";
 if house is in block, say "house is in block.";
 if house is in neighborhood, say "house is in neighborhood.";
 if box in lab, say "box in lab.";
 if box is in lab, say "box is in lab.";
 if spam in box, say "spam in box.";
 if spam is in box, say "spam is in box.";

instead of smelling the box:
 if lab in house, say "lab in house.";
 if house in block, say "house in block.";
 if house in neighborhood, say "house in neighborhood.";
 if lab is in house, say "lab is in house.";
 if house is in block, say "house is in block.";
 if house is in neighborhood, say "house is in neighborhood.";
 if box in lab, say "box in lab.";
 if box is in lab, say "box is in lab.";
 if spam in box, say "spam in box.";
 if spam is in box, say "spam is in box.";

produces the same transcript…

i.e. it’s not the case that ‘x in y behaves differently depending on whether you’re in a rule whose conditions include ‘in’’ - the 'in' is irrelevant and unnecessary; it’s the fact that it’s a rule conditional on matching an action pattern (leading to self==noun) that appears to count.

EDIT: to emphasise: for ...an actor ...ing X in Y i.e. when in Y is used as a condition to an action pattern such as instead of the gorilla eating the banana in the Lab, in checks whether Y is the location of the actor. Conversely, leaving aside the self==X weirdness, X in Y as a condition elsewhere checks whether Y is the container of X, not its location. All this really feels as though it might be a compiler bug (legacy code?) - outside of conditions to action patterns X in Y, if it compiles at all, should probably produce equivalent I6 to X is in Y.