While messing around with different ways to do disambiguation, it occurred to me that it might sometimes help clarify things for players unfamiliar with parser game conventions to be able to do the following:
>OPEN GREEN
(assuming you mean the green box, rather than Reverend Green or the bowling green)
You open the green box.
However, this looks like it would be extremely tricky to do in Inform. The clarifying the parser’s choice activity takes place long after parsing the command, which means that the list of alternative items which were considered is gone at that point. I guess one could possibly save the items in some other list just for this purpose, but it would require rewriting Adjudicate() in CommandParserKit (and my I6 is too rusty to figure out how to do this). Is this even possible?
Opening a NPC, reverend or not, is, to put it mildly, rather only for the splatter horror subgenre, so I’m safe to assume that is an issue in the world model…
dark humor aside, indeed I think that at least the obvious disambiguations should exclude a priori illogical options (easy with Open, excluding from the disambiguation list everything in scope without the “is a container” attribuite)
Sorry if I wasn’t clear. I’m perfectly comfortable with using Does the player mean rules to cue the parser to infer an appropriate choice in this situation. The question was about how to produce the more verbose form of the clarification message, rather than the default (the green box).
The mechanism to change that text is the Rule for clarifying the parser’s choice of… (you can search ‘rule for clarifying’ in the Inform docs)
Here’s a demo of the overall mechanism. There are two green things in it. If you type ‘get green’, I coded it to favour the apple, then it prints a custom clarification message.
an apple is a kind of thing.
a green apple is in lab. a green umbrella is in lab.
Does the player mean taking the green apple: it is likely.
Rule for clarifying the parser's choice of a thing (called the target):
say "[The target]: The superior choice.";
say line break;
This is just a demo of the programming only really good for this apple situation. It’s neither a good nor sensible demo. It would go around saying ‘The superior choice’ at times that would annoy the player. It’s also not listing the other things, which is what you wanted. That would require some more fiddly coding that gathered up the list of disambiguation objects and printed them all out. That in itself could (I’ll stretch and say ‘would’) be quite annoying if the list gets even a little bit long.
Note that rules for clarifying the parser’s choice can be applied to particular situations, rather than everything. Which is more often how they’re used. So you can write them for specific objects.
e.g.
Rule for clarifying the parser's choice of the green umbrella:
say "Ooooh, taking the umbrella instead of the apple! You rebel!";
Being more specific than the other rule, this rule would function for the umbrella… but won’t in this demo because, in this demo, there’s no way to provoke Inform into printing a clarification message for the umbrella. ‘green’ will go to the apple, and ‘umbrella’ requires no clarification; there’s only one.
Anyway, someone may arrive with some more explicit code about printing out the disambiguation list, but I’d argue this is not actually desirable. You could easily write a slightly nicer general message about the player choosing the target, than just printing its name in brackets as happens now, and that’s probably where I’d stop. But I’m not stopping you.
[First a neat little utility shared by Draconis that is useful for printing "or" lists; see BBA #3.]
Include (-
Global listing_parameter;
-).
To decide which K is the (name of kind of value K) being listed: (- listing_parameter -).
To set the (name of kind of value K) being listed to (V - K): (- listing_parameter = {V}; -).
To push the listing parameter: (- @push listing_parameter; -).
To pull the listing parameter: (- @pull listing_parameter; -).
To print a/the/-- list of (T - text) for (D - description of values of kind K), prefacing with is/are and/or disjunctive:
let N be the number of D;
if prefacing with is/are:
say "[if N <= one]is[else]are[end if] ";
if N is zero:
say "nothing";
stop;
let J be 1;
push the listing parameter;
repeat with V running through D:
set the K being listed to V;
say T;
if J is N: [Final one]
next;
else if J is N minus 1: [Second-to-last]
if N > 2 and the serial comma option is active: [Only print a serial comma if there are three or more items]
say ",";
say " [if disjunctive]or[else]and[end if] ";
else: [Any other]
say ", ";
increment J;
pull the listing parameter.
[Now some quick delving into parser internals...]
To decide which number is parameter count:
(- (parameters) -).
To decide which object is noun-to-be:
(- (parser_results-->INP1_PRES) -).
To decide which object is second-noun-to-be:
(- (parser_results-->INP2_PRES) -).
To decide whether the noun text names (O - object):
(- (Refers({O}, match_from)) -).
[And presto!]
Definition: a thing is noun-name-matched if the noun text names it and it is not the noun-to-be.
To specify rather than (T - thing):
say "(assuming that you mean [the T] rather than ";
print the list of "[the thing being listed]" for noun-name-matched things, disjunctive;
say ")[command clarification break]".
For clarifying the parser's choice of something:
if parameter count is 2:
specify rather than the second-noun-to-be;
otherwise:
specify rather than the noun-to-be.
Thank you, the mad science option was exactly what I was hoping for! Although I’m sorry to say I hadn’t twigged that printing a disjunctive list was also going to be needed, and that was the bulk of the code!
I’d been blinded by thinking that I needed to save the list of alternatives considered during parsing rather than just checking again after the fact whether there are other things that match the noun text. Seeing which bits of parser internals need exposing also helps me with a couple of other similar issues I was working through. Thanks again!
Ok, this is the part that seems to be sorcery to me. Your provided code seems to handle clarifying the choice of both the noun and the second noun, but I can’t possibly see how this knows which one it should be looking at. If you don’t mind indulging my curiosity, I’d love to hear an explanation!
(I think one also needs a definition of second-noun-name-matched, to avoid including the name of the correct second noun in the list of possibilities excluded, and it should probably also only mention things that are in scope, but I think I know how to do both of those.)
Yes. The proof-of-concept was rather cursorily tested, as these things tend to be.
quick fix
To decide which object is the noun-of-interest:
if parameter count is:
-- 1: decide on noun-to-be;
-- 2: decide on second-noun-to-be;
-- otherwise: decide on nothing.
Definition: a thing is noun-name-matched if the noun text names it and it is not the noun-of-interest. [MODIFIED]
Scope and visibility are kind of welded together in Inform, so changing the description fed to print the list... to visible noun-name-matched things should cover it, if you’re only worried about the player’s actions.
As for how it works: Refers() is defined in the template code (see Parser.i6t) and is routinely used by the parser when resolving nouns. The exposed globals are parts of the “workspace” used by the parser when trying to decipher a player command, necessary here because parsing is not complete when the clarifying the parser's choice activity happens. Note that the wording noun text has no special meaning to the compiler with respect to the noun global variable, so there may be less sorcery than there appears to be.
So match_from is updated by the parser itself to point to either the first word of the noun or the first word of the second noun just before it carries out the clarifying the parser’s choice activity? Meaning that ‘to decide whether the noun text names…’ finds the right value there at the points where you’re using it, even if it would behave unpredictably if you used the phrase at other points?
An I right in thinking that this only checks the first of the words the player used to refer to the noun?
I’m not asking you to code up a solution since you’ve already gone above and beyond in this thread, but for multi-word matching, is there a variable somewhere that stores the number of words matched by the noun or similar?
There is a global called match_length that might serve the purpose. It should at that point hold the number of words of player input matched to the noun in question.
Just in case anyone else is actually interested in this functionality, here’s an essentially complete version which handles first and second noun and correctly deals with cases where more than one word is needed to disambiguate. Basically all of the credit for this goes to @otistdog.
A lot of work for a minor effect
"I really wanted those clarification messages"
Section 1 - Disjunctive lists
Include (-
Global listing_parameter;
-).
To decide which K is the (name of kind of value K) being listed: (- listing_parameter -).
To set the (name of kind of value K) being listed to (V - K): (- listing_parameter = {V}; -).
To push the listing parameter: (- @push listing_parameter; -).
To pull the listing parameter: (- @pull listing_parameter; -).
To print a/the/-- list of (T - text) for (D - description of values of kind K), prefacing with is/are and/or disjunctive:
let N be the number of D;
if prefacing with is/are:
say "[if N <= one]is[else]are[end if] ";
if N is zero:
say "nothing";
stop;
let J be 1;
push the listing parameter;
repeat with V running through D:
set the K being listed to V;
say T;
if J is N: [Final one]
next;
else if J is N minus 1: [Second-to-last]
if N > 2 and the serial comma option is active: [Only print a serial comma if there are three or more items]
say ",";
say " [if disjunctive]or[else]and[end if] ";
else: [Any other]
say ", ";
increment J;
pull the listing parameter.
Section 2 - Clarifying choices
To decide which number is the parser's parameter count:
(- (parameters) -).
To decide which object is noun-to-be:
(- (parser_results-->INP1_PRES) -).
To decide which object is second-noun-to-be:
(- (parser_results-->INP2_PRES) -).
To decide which object is the parser's noun-of-interest:
if the parser's parameter count is:
-- 1: decide on noun-to-be;
-- 2: decide on second-noun-to-be;
-- otherwise: decide on nothing.
Include (-
[ CheckName obj rv ;
@push wn;
wn = match_from;
rv = TryGivenObject(obj);
@pull wn;
return rv;
];
-).
To decide whether the relevant noun text names (O - object):
(- (CheckName({O}) == CheckName((+ parser's noun-of-interest +))) -).
Definition: a thing is noun-name-matched if the relevant noun text names it and it is not the parser's noun-of-interest.
To specify rather than (T - thing):
say "(assuming that you mean [the T] rather than ";
print the list of "[the thing being listed]" for visible noun-name-matched things, disjunctive;
say ")[command clarification break]".
For clarifying the parser's choice of something:
if the parser's parameter count is 2:
specify rather than the second-noun-to-be;
otherwise:
specify rather than the noun-to-be.
Section 3 - Sandbox
Lab is a room.
A useful-key is a kind of thing.
A large green ball is here.
A small green ball is here.
An openable lockable container called the small red box is here.
An openable lockable container called the small green box is here.
A useful-key called the small green key is here. The small green key unlocks the small green box.
A useful-key called the small red key is here. The small red key unlocks the small red box.
A man called Reverend Green is here.
The small green unmentionable secret is nowhere.
Does the player mean closing the small green box: it is likely.
Does the player mean locking the small green box with the small green key: it is likely.
Test me with "close green / lock green box with green"
The only thing not handled is situations where both the first and second noun were inferred, because Inform handles the clarification message there using a different mechanism - but given the combinatorial explosion that could result, I’m not quite sure what you’d want to display there anyway.
I actually have an I7 extension that allows you to change the parser clarification message in that situation!
It’s not really a different mechanism as there’s a single I6 function that prints the inferred message, regardless of the number of inferred nouns involved.
I wouldn’t call two inferred nouns a “combinatorial explosion”!
You’d just handle it in the same way you would for a message in an action with two nouns (“You throw [the noun] at [the second noun].”).
Doesn’t Emily Short already have an extension that does that? Complex listing I believe.
> OPEN GREEN
(Assuming you mean the green box, not Reverend Green or the bowling green)
so the combinatorial explosion I was worried about was:
> UNLOCK GREEN WITH GREEN
(Assuming you mean unlocking the green box with the green key, not the green key with the green box or the green box with the bowling green or the green box with Reverend Green or the green key with the bowling green or Reverend Green with the green key or ...)
Fair enough. Might be time to give the extension an update. Also might be worth turning your own version into a standalone extension. Seems like it has come in mighty handy!
Oh okay, I see what you mean now. It was in reference to the enhanced clarification message. In that case, I agree that wouldn’t be the wisest idea.