§6.17. Clarification and Correction
Some commands and some objects raise special challenges when it comes to working out the player's intention.
Sometimes this can be done with good rules about the assumptions Inform should make. Alpaca Farm demonstrates a USE command, always a challenge because USE can mean very different actions with different items.
There are also times when we need to ask the player for more information. Apples demonstrates how sensibly to use properties to disambiguate between similar objects, while Walls and Noses rephrases the disambiguation question when special objects are involved: examining one of the walls of the room will make the story ask "In which direction?" and EXAMINE NOSE will lead to "Whose nose do you mean, Frederica's, Betty's, Wilma's or your own?"
At other times, the player types something that is wrong in a predictable way: for instance, we might want to remove all the "with..." phrases from commands like
and merely parse the remainder of the command. (That last command may be unlikely, but novice players do quite often type commands that refer unnecessarily to body parts.) Cave-troll demonstrates how.
For catching typing errors, Cedric Knight's extension Mistype may also be of use: it provides an automatic typo-correction function that the player can turn on or off.
|
ExampleAlpaca Farm
A generic USE action which behaves sensibly with a range of different objects.
|
|
This example takes the ordering of grammar lines to its logical extreme, sorting the player's input into different categories depending on the kind and condition of the objects mentioned.
"Alpaca Farm"
Understand "use [an edible thing]" as eating.
Understand "use [a wearable thing]" as wearing.
Understand "use [a closed openable container]" as opening. Understand "use [an open openable container]" as closing.
Understand "use [something preferably held] on [a locked lockable thing]" as unlocking it with (with nouns reversed). Understand "use [something preferably held] on [an unlocked lockable thing]" as locking it with (with nouns reversed).
Understand "use [a switched off device]" as switching on.
Understand "use [something]" as using. Using is an action applying to one thing. Carry out using: say "You will have to be more specific about your intentions."
Understand "use [a door]" as opening. Understand "use [an open door]" as entering.
The Llama Pen is a room. North of the Pen is the gate. The gate is a door. North of the gate is the Rocky Path. The brown llama is an animal in the Llama Pen.
Appearance is a kind of value. The appearances are muddy, scruffy, fluffy, and dapper. The brown llama has an appearance. The brown llama is muddy. Before printing the name of the brown llama, say "[appearance] ". Before printing the name of the brown llama while grooming: say "now-[if appearance of the brown llama is less than dapper]merely-[end if]".
A grooming tool is a kind of thing. Understand "use [a grooming tool] on [something]" as grooming it with (with nouns reversed). Grooming it with is an action applying to two things. Understand "groom [something] with [something]" as grooming it with.
Carry out grooming it with:
if the appearance of the noun is less than dapper, now the appearance of the noun is the appearance after the appearance of the noun.
Report grooming it with:
say "You attend diligently to the appearance and hygiene of [the noun]."
Instead of using a grooming tool in the presence of the brown llama:
try grooming the brown llama with the noun.
The player carries some nail nippers, a slicker brush, and an apple. The apple is edible. The brush and the nippers are grooming tools. The player wears a sombrero.
The description of the nail nippers is "Ten inches long, to give you the necessary leverage to cut tough llama toenails. It still helps to soften them up by making the llama stand in a bucket of water first, though."
The description of the slicker brush is "Fine, angled soft bristles set into a broad back, perfect for removing mud from the coat of a long-woolled llama."
The industrial-strength blower is a fixed in place device in the Llama Pen. "Attached to the nearest wall, on its own movable boom, is an industrial-strength blower for doing llama hair."
Understand "use [switched off blower]" as switching on. Understand "use [switched on blower] on [brown llama]" as grooming it with (with nouns reversed). Instead of using the blower in the presence of the brown llama, try grooming the brown llama with the blower.
Test me with "use gate / use blower / use nippers / use brush / use apple / remove sombrero / use sombrero".
Whether we actually want a USE action is a subject of some theoretical debate in the IF community. On the one hand, it helps avoid guess-the-verb problems where the player cannot figure out what term to use in order to express a fairly simple idea. On the other, it encourages the player to think that all items have one and exactly one use, rather than getting him to consider the range of possibilities that arise from having a complex vocabulary.
|
ExampleApples
Prompting the player on how to disambiguate otherwise similar objects.
|
|
Inform by default detects whether two objects can be disambiguated by any vocabulary available to the player. If so, it asks a question; if not, it picks one of the identical objects at random.
Generally this produces good behavior. Occasionally, though, two objects have some distinguishing characteristic that doesn't appear in the object name. For instance, suppose we've created a class of apples that can be told apart depending on whether they've been bitten or not:
An apple is a kind of thing. Consumption is a kind of value. The consumptions are pristine and bitten. An apple has a consumption. The description of an apple is "It is [consumption]."
Understand the consumption property as describing an apple.
The player can meaningfully type
>EAT BITTEN APPLE
or
>EAT PRISTINE APPLE
but if he types
>EAT APPLE
Inform will, annoyingly, ask
Which do you mean, an apple or the apple?
This gives the player no indication of why Inform is making a distinction. So here we add a special "printing the name" rule to get around that situation:
"Apples"
Orchard is a room.
An apple is a kind of thing. Consumption is a kind of value. The consumptions are pristine and bitten. An apple has a consumption. The description of an apple is "It is [consumption]."
Understand the consumption property as describing an apple.
Before printing the name of an apple while asking which do you mean: say "[consumption] ". Before printing the plural name of an apple while asking which do you mean: say "[consumption] ".
The player carries three apples.
Instead of eating a pristine apple (called the fruit):
say "You take a satisfying bite.";
now the fruit is bitten.
Instead of eating a bitten apple (called the fruit):
say "You consume the apple entirely.";
now the fruit is nowhere.
Inform will also separate the bitten from the pristine apples in inventory listings and room descriptions, even though it's not clear why; we can improve on that behavior thus:
Before listing contents: group apples together.
Rule for grouping together an apple (called target):
let source be the holder of the target;
say "[number of apples held by the source in words] apple[s], some bitten".
Before printing the plural name of an apple (called target):
let source be the holder of the target;
if every apple held by the source is bitten, say "bitten ";
if every apple held by the source is pristine, say "pristine ".
Test me with "i / eat apple / i / eat apple / pristine / i / eat apple / pristine / i".
|
ExampleWXPQ
Creating a more sensible parser error than "that noun did not make sense in this context".
|
|
The parser error "That noun did not make sense in this context" arises instead of "You can't see any such thing" when the player uses a command that could apply to any item in the game -- that is, a command such as
Understand "go to [any room]" as going directly to.
Understand "talk about [any subject]" as discussing.
...and so on. The idea here is that "You can't see any such thing" isn't a sensible rejoinder when the player doesn't really need to be able to see the object.
Nonetheless, "That noun did not make sense..." is itself a fairly dry and uninformative response, and we may want to override it to something more appropriate for the specific kind of context in which it might appear. For instance:
"WXPQ"
WXPQ Studio is a room. "After about 2 AM, no one is listening anyway, so you can more or less make up whatever you like to fill the airwaves."
John F Kennedy, Elvis, Ralph Nader, Tony Blair, and single-origin chocolate are things.
Understand "talk about [any thing]" or "discuss [any thing]" as discussing. Discussing is an action applying to one visible thing.
Carry out discussing:
say "You babble for a while about your [one of]interest in[or]hatred of[or]passionate devotion to[or]conspiracy theory concerning[or]mother's secret love affair with[as decreasingly likely outcomes] [the noun]."
Rule for printing a parser error when the latest parser error is the noun did not make sense in that context error:
say "For once, you're at a loss for anything to say."
Test me with "discuss Elvis / discuss Kennedy / discuss chocolate / discuss narratology vs ludology debate".
Note that this solution works as simply as it does because we only have one command in the game that can apply to an "[any]" token. If we had several, we'd need to distinguish between the parser error attached to "discuss" and the parser error attached to "go to" (for instance). In that case, we might instead write something like
Rule for printing a parser error when the latest parser error is the noun did not make sense in that context error:
if the player's command includes "go":
say "There's no such place you know how to get to.";
otherwise:
say "For once, you're at a loss for anything to say."
Suppose we want our game to respond to "EXAMINE WALL" with "In which direction?", and to "EXAMINE NOSE" with "Whose nose do you mean, Frederica's, Betty's, Wilma's or your own?"
For the case of EXAMINE WALL, we need a way to determine whether every item being disambiguated is a direction. We'll start by making a "matched" adjective which will identify items being disambiguated:
"Walls and Noses"
Eight-Walled Chamber is a room. "A perfectly octagonal room whose walls are tinted in various hues."
Understand "wall" as a direction.
Definition: a direction is matched if it fits the parse list.
Definition: a room is matched if it fits the parse list.
Definition: a thing is matched if it fits the parse list.
Rule for asking which do you mean when everything matched is direction:
say "In which direction?"
Checking the parse list requires a bit of behind-the-scenes work with Inform 6. Fortunately, you don't have to understand this entirely in order to use the rest of the example:
To decide whether (N - an object) fits the parse list:
(- (FindInParseList({N})) -)
Include (-
[ FindInParseList obj i k marker;
marker = 0;
for (i=1 : i<=number_of_classes : i++) {
while (((match_classes-->marker) ~= i) && ((match_classes-->marker) ~= -i)) marker++;
k = match_list-->marker;
if (k==obj) rtrue;
}
rfalse;
];
-)
Now that we've defined our "matched" adjective, we can use it for other purposes as well -- even generating our own lists. Our second challenge was to respond to EXAMINE NOSE with "Whose nose do you mean, Frederica's, Betty's, Wilma's or your own?"
Here we need to change the way the question is worded (not "which do you mean" but "whose nose do you mean"). We also have to the names of the noses as they're printed in this particular context, so that they don't repeat the word "nose" over and over. And -- as a point of good English style -- we also want "your own" nose always to be last on the list.
For this purpose we may want to use the built-in "Complex Listing" extension, which allows us to print specially ordered lists. So:
Include Complex Listing by Emily Short.
Wilma, Betty, and Frederica are women in the Eight-Walled Chamber. Understand "lady" or "woman" as a woman. A nose is a kind of thing. A nose is part of every person.
Rule for asking which do you mean when everything matched is a nose:
prepare a list of matched things;
if your nose is an output listed in the Table of Scored Listing:
choose row with an output of your nose in the Table of Scored Listing;
now the assigned score entry is -1;
say "Whose nose do you mean, [the prepared list delimited in disjunctive style]?"
Rule for printing the name of a nose (called target) while asking which do you mean :
if everything matched is a nose:
if the target is part of a person (called owner):
if the owner is the player, say "your own";
otherwise say "[the owner][apostrophe]s";
otherwise:
make no decision.
Understand "own" or "mine" as your nose.
Test me with "x wall / north / x nose / mine".
Novice players of interactive fiction, unfamiliar with its conventions, will often try to add extra phrases to a command that the game cannot properly parse: HIT DOOR WITH FIST, for instance, instead of HIT DOOR.
While we can deal with some of these instances by expanding our range of actions, at some point it becomes impossible to account for all the possible prepositional phrases that the player might want to tack on. So what do we do if we want to handle those appended bits of text sensibly?
We could go through and remove any piece of text containing "with ..." from the end of a player's command; the problem with that is that it overzealously lops off the ends of valid commands like UNLOCK DOOR WITH KEY, as well. So clearly we don't want to do this as part of the "After reading a command..." stage.
A better time to cut off the offending text is right before issuing a parser error. At that point, Inform has already determined that it definitely cannot parse the instruction as given, so we know that there's something wrong with it.
The next problem, though, is that after we've edited the player's text we want to feed the corrected version back to Inform and try once more to interpret it.
This is where we have a valid reason to write a new "rule for reading a command". We will tell Inform that when we have just corrected the player's input to something new, it should not ask for a new command (by printing a prompt and waiting for another line of input); it should instead paste our stored corrected command back into "the player's command" and proceed as though that new text had just been typed.
Thanks to John Clemens for the specifics of the implementation.
"Cave-troll" by JDC
Section 1 - The Mechanism
The last command is a text that varies.
The parser error flag is a truth state that varies. The parser error flag is false.
Rule for printing a parser error when the latest parser error is the only understood as far as error and the player's command matches the text "with":
now the last command is the player's command;
now the parser error flag is true;
let n be "[the player's command]";
replace the regular expression ".* with (.*)" in n with "with \1";
say "(ignoring the unnecessary words '[n]')[line break]";
replace the regular expression "with .*" in the last command with "".
Rule for reading a command when the parser error flag is true:
now the parser error flag is false;
change the text of the player's command to the last command.
Section 2 - The Scenario
The Cave is a room.
The troll is a man in the cave.
The player carries a sword.
The chest is a locked lockable container in the cave.
Test me with "attack troll with sword / unlock chest with sword / attack troll as a test".
A caveat about using this method in a larger game: "parser error flag" will not automatically control the behavior of any rules we might have written for Before reading a command... or After reading a command..., so they may now fire at inappropriate times. It is a good idea to check for parser error flag in those rules as well.