Disambiguation without unnecessary questions?

Warning, lot of text ahead.

I thought it was better to open a new thread, to not drive the original one further off topic.

So, this post is about resolving ambiguities when parsing player input. More precisely, resolving without asking unnecessary questions to the player. Unnecessary as in the parser could have at least narrowed it down a bit further, based on the current game progress.

Suppose we have 2 boxes, red and green and 2 balls, also red and green. Now, “put ball into box” gives 4 possibilites. Most parses will detect there are multiple possible matches and will ask the player which ball and which box and will then continue and hand over 2 objects (the box and ball) and an action (put into) to the interpreter.

Now let’s consider the following scenario:

[code]

x red box
The red box is locked and you don’t have a key to unlock it.
put ball into box
Which box do you mean, the red box or the green box?
red
Which ball do you mean, the red ball or the green ball?
green
The red box is locked.[/code]
It would be nice if the parser would exclude the red box as a possible hit for ‘box’ in the player input because it knew that the player already examined the red box and found it to be locked.
Likewise, if the player carries one ball and the other one is on the floor, the parser might conclude that the player means the ball being carried. But if there’s only 1 ball and it’s on the floor, the parser will match it, because the story might have a rule to pick it up first and then put it into the box.

So, what I’m looking for is sort of a framework (thanks, matt w) to model additional knowledge/rules for the parser. And if we could assign a weight to each rule (thanks again, matt w), the parser could even pick the object that qualifies best to further limit the number of possible hits.
The rules could be written in the framework by the author and it could be part of the game source, so different games can have different parser rule sets but still use the same parser.

I’ve been thinking how something like this could be implemented (the framework that is, in the parser source code).
Let’s assume most player inputs consist of an action, some nouns with each some adjectives and some prepositions (e.g. “put ball with white dots in green box”). The high level description of a sentence being

It will then work as follows:

  • the parser will match the player input to a first set of hits for object 1 and object 2, by comparing nouns, adjectives and prepositions from the player input to object descriptions from the game.
  • if an object has 0 hits, parsing ends and the parser will print an error message (“You don’t see that here.”)
  • if an object has 1 hit, that’s ok and parsing ends for that object as it has been uniquely identified.
  • if an object has 2 or more hits, the parser will consult the framework with parsing rules and retrieve the rules for the action.
  • each rule is assessed for each individual hit for the object and if the rule matches, the hit gets awarded the points for that rule.
  • next, the hit with the most points “wins” and is considered to be the only match for the object.
  • if multiple hits have the same score, the parser will ask the “which do you mean” question to the player for the final match.
  • finally, for each object, 1 hit is handed over to the interpreter to work with.

I’m still thinking about what the parser rules must look like and what they will do and what they won’t do. Currently I have the following:

  • Each action has a ruleset, which may consist of several rules.
  • The action will likely have different rules for different prepositions. E.g. “put in” -> skip if not a container; “put up” -> skip if not a supporter, etc
  • From the previous bullet it follows that rules may check the status of a possible hit. E.g. is it lockable, is it a supporter, is it takeable, has it been examined by the player, etc.

Finally, I think it is important to realize that these parse rules must only be used for the purpose of disambiguation.
To illustrate:
There are 2 rocks, 1 weighs 2 pounds and the other 1000 pounds. With “Lift rock”, the parser could find a rule for the lift action and limit the possible hits to the rock of 2 pounds.
If there’s one rock of 1000 pounds, “lift rock” will give one hit and the parser must pass this on to the interpreter without checking disambiguation rules. The interpreter will likely print an error message about the rock being too heavy. There was no ambiguation here, only an impossible command.

I appreciate any feedback on this.

Thanks for reading.

There are ways to do this, but they aren’t implicitly done by Inform. I think in general, the software doesn’t want to make assumptions the author may not want.

I don’t know if this code works specifically, but you should be able to write something like:

[code]Definition: a thing is unnoticed if it is not examined.

Does the player mean doing something to an unnoticed thing: it is very unlikely.[/code]

Caveat: I’ve written code like this and Inform sometimes blithely ignores it, so someone might be able to explain more specifically how to do it. (Possibly Inform has an easier time not including an “unlikely” outcome than it does proactively choosing a “likely” thing?)

With your rock example, it’s easy if you’ve defined to Inform some parameters.

[code]Definition: a thing is massive if the weight of it is greater than 30 pounds.

Does the player mean taking a massive thing: it is very unlikely.[/code]

(Edit: In the above, you will need to have defined everything has a weight, and possibly that the weight of a thing is usually 0. )

Note that this isn’t an Inform question per se, but a question about designing a new system–but it’s still worth looking at what Inform does here as a model.

What Inform does is something like this: As it’s attempting to parse the command, every time it finds a spot for a noun, it goes through every thing that could match the words the player typed to refer to the noun. If there’s more than one thing, it consults a rulebook called the “Does the player mean” rules to try to break the tie. The “does the player mean” rules assign a certain likelihood to every thing that could be in that spot.

So, in Hanon’s example… well, let’s say we have code like this. (It should be pretty easy to read even if you don’t know Inform!)

[code]A thing has a number called the weight. The weight of a thing is usually 1.

Definition: a thing is massive if the weight of it is greater than 30.

Does the player mean taking a massive thing: it is very unlikely.

Cave is a room. A small rock is in the cave. A large rock is in the cave. The weight of the large rock is 50.

Instead of taking a massive thing: say “That’s too heavy to lift.”[/code]

This will work as you want. If the player types “take rock,” the parser finds that there are two things that match “rock,” and then it consults the Does the Player Mean rules to discover that the large rock is dispreferred because it’s massive. So it interprets the command as taking the small rock. But if the player types “take large rock,” then the parser finds that there’s only one thing that matches “large rock,” and it just interprets the command as taking the large rock without trying to bother with the Does the Player Mean rules.

A complication here, though, is that the Does the Player Mean rules depend on what the action is. Here we want to make sure that massive things are dispreferred when taking, but not when examining. That’s why the Does the Player Mean rule refers specifically to taking a massive thing. So when the parser is doing disambiguation it needs to know what action it’s trying to perform… but it also doesn’t really know what action it’s trying to perform until it’s done with the parsing.

The way this works is with what’s called grammar lines. When the Inform parser sees that your command starts with “take,” it consults a list of grammar lines (which in the code begin with “Understand”) to see what actions it might be. The Understand lines for take are (with the code simplified):

Understand "take inventory" as taking inventory. Understand "take [things]" as taking. Understand "take off [something]" as taking off. [that is, taking off something you're wearing] Understand "take [something] off" as taking off. Understand "take [things inside] from [something]" as removing it from. Understand "take [things inside] off [something]" as removing it from.

Inform will go through these lines until it finds something that can be parsed to match the syntax of the command. As soon as it finds a match for one of the Understand lines, it will do any disambiguation it needs to do based on the action that corresponds to the Understand line it matches.

So when Inform reads “take rock” it first tries to match the command to the first Understand line, “take inventory.” That doesn’t match because the command doesn’t have the word “inventory.” So then it tries to match “take [things].” At this point it knows that everything after the word “take” has to name the thing it’s talking about (or things, because the player can type “take all”), and that the action it’s trying to parse is “taking”. So when it finds that “rock” matches both the small rock and the large rock, it consults the Does the Player Mean rules with the knowledge that the player is taking, finds the small rock as a match, and doesn’t bother with the rest of the Understand lines.

On the other hand, if you typed “take rock off,” it would reject the “take inventory” line, reject the “take [things]” line (because “rock off” can’t be understood as the name of a thing), reject the “take off [something]” line (because “off” isn’t right after “take”), and when it reaches the “take [something] off” line it will try to match “rock” to something. Then when it finds that both the small rock and large rock match, it can’t find anything in the Does the Player Mean rules about the taking off action, so it asks which rock you mean. (And then tells you you’re not wearing it.)

Note that this means the Does the Player Mean lines only work for disambiguating things, not actions. This code involving a popular bug spray whose brand name is Off:

[code]The Outdoors is a room. Some Off spray is in the Outdoors. The Off spray is singular-named. The Off spray is wearable.

After wearing the Off spray: say “You spray the Off spray all over yourself.”

Instead of taking off the Off spray: say “You can’t take the Off spray off, it’s sprayed all over you.”

Does the player mean taking the worn Off spray: It is very unlikely.[/code]

yields this output:

What happens in the last three lines is that the parser always tries to match “take [things]” first, finds that “off spray” and “off off spray” and “off spray off” match the Off spray (the Inform parser lets you repeat any words in the object’s name in any order), and decides it’s understood the command, without bothering to check whether the command matches any of the later Understand lines like “take [thing] off” or “take [off] thing.” The Does the Player Mean rule does nothing because the

I think in the other thread I said something about not putting the burden on the author to choose object names carefully, but in this case the author totally deserved what happens.

…another thing about this is that, as we were talking about in the other thread, this model doesn’t always work great for two-noun commands, because it only disambiguates one noun at a time. And one of the ways Inform tries to deal with this is with the [things inside] token that you can see above, which tells the parser to look at the other noun to see if you can get a good pair; and I was going to tell you that the [things inside] token is a big mess and something you shouldn’t emulate it. But my attempt to get a simple example of “things inside” is running into weird problems, and I’ve already written a novel, so I’ll stop for now.

1 Like

This is more a general meta-comment on disambiguation in parser, rather than a continuation of the above discussion.

I’ve been thinking about this alot for my latest WIP - in that I started with a couple of puzzle systems that I originally felt required a significant level of work to help the player disambiguate between similar objects. After a whole bunch of work to make a set of rules that checked the state of the various objects and try to pre-parse the player’s command to sensibly refer to the thing that Inform thinks they’re trying to refer to based on some overly complicated rules, I’ve come to the conclusion that disambiguation is a kind of white-elephant problem.

It is better, I feel, as an author, to try and minimise disambiguation requirements through world-building rather than complicated rules and whatnot.

In the above example, red box and green box. Hmmmm… ‘steam-trunk covered in red, patterned leather’ and ‘green jade trinket box’.

Red ball and Green ball… ‘Bright red beachball, only partially inflated’ and ‘chalky sphere of green bath soap’

It’s something this, that I’ve been thinking about a lot. There are very few situations I can think of where, with the imaginative application of language, names and a re-look at what an object can be in parser that would mean you can’t, as an author, help the player not even know that they need to disambiguate.

I think the point is that as I was play-testing my game and getting annoyed as a player by disambiguation requirements - thinking about this, about how lots of similar objects needed to be actioned with lots of other similar object made me realize that the puzzle itself suffered from IPS (Irritating Puzzle Syndrome) and the process of re-imagining the objects made me re-think the puzzle system into something else entirely.

Just some thoughts.

Ade.

If this thread isn’t Inform-specific, then I’ll speak up on Hugo’s behalf as well and explain how it works. Hugo already makes the assumptions suggested.

First, every object has a “parse_rank” property that determines if it gets priority over other, similarly-named objects (you can also redirect actions to this or that object depending on what action is being taken). The default value of an object’s parse_rank is 0, giving them all equal footing. The FindObject routine checks parse_rank values among all possible objects, and the object last referred to gets a +1 so the red box would come out ahead in >PUT BALL IN BOX.

Of course, it also depends on what actions are being taken as some verbs rely on certain factors (are they held? are they unheld? and other stuff). Some of this is set in stone by the grammar definitions. Some of it is nudged along with higher priority parser ranking in FindObject (for example, if the action is OPEN/CLOSE, an unopenable object’s parse ranking is subtracted by one). “>EXAMINE RED BOX. GET BOX. GET BOX.” will result in the player getting both boxes without a disambiguation question.

Hugo’s >PUT OBJECT IN OBJECT grammar, by default, forces the first object to be held, so Hugo would just go with the held red ball in any case. Getting it to even consider unheld items involve using Hugo’s “Checkheld” system which is usually used for stuff like “>THROW BOOK (automatically picking up the book first…) You throw the book.” You then would put in a replacement to the following routine to make an exception for object that does not need to be held.

replace DoPutIn_CheckHeld ! See above re: ImplicitTakeForDrop { if object = water and xobject = goblet return Perform(&DoPutin, water, goblet) else return CallVerbCheckHeld(&DoPutIn, location) }

So, then a combination of the Inform and Hugo approach seems like something to go for? Give each object a likelihood score (parse rank) and use author definable rules per action (does the player mean) to increase or decrease the likelihood score.

Out of curiosity, how would Hugo handle a sentence like

[code]

hit the man with the hammer
You don’t have a hammer.

OR

hit the man with the hammer
What do you want to hit the man with the hammer with?

OR

…[/code]

Yes, Hugo would have this behavior. Trying to refer to characters by their possessions would be incredibly difficult.

Sorry, I realize now that my question was not clear.

The object would be defined as " the man with the hammer" (and probably also as “man”), he does not necessarily carry a hammer. Maybe “man with a mission” would be a better example.

In general, I want to allow object names with syntax , like “hall of fame”, “gallery of paintings”, “jack of all kinds”.

Would your parser try to map to an object, to another object and then link to the action by ? Or would it try to map to one object and perform the action on that object?

I want mine to try both and then go for the one that yields a usable result, if any (usable as in not an “you don’t see that here” message).

Hope this clarifies it.

Hugo really only has a concept of adjectives and nouns. In some of those cases, defining all of the words as adjectives might suffice (since Hugo expects the noun to be listed last). “hall of fame” would need the adjectives “hall” and “fame”, as “of” is defined as a “removal” by hugolib.h (which means it is ignored from parser input).

A good added precaution would be to have the PreParse routine look for applicable phrases and convert them to easier-to-interpret commands. “jack of all trades” could be turned to “jack-of-all-trades”. This is all done “behind the curtain” so the safe word or phrase doesn’t even have to make sense, and it’ll work from the player’s perspective.

Well, sticking purely to evaluating one object at a time has limitations. To go with the example I was trying to construct in Inform, consider this:

Shrine is a room. A blue pedestal is in Shrine. A bust of Venus is on the blue pedestal. A red pedestal is in Shrine. A bust of Cupid is on the red pedestal.

Remember the Understand line

Understand "take [things inside] from [something]" as removing it from.

Inform has a “removing it from” action which in theory can be understood when you type “remove from ,” and which lets you take noun1 so long as it’s in or on noun2. Now I’m going to start talking about how I want the Inform parser to work, as opposed to how it does work.

Let’s say you type:

What should the parser do? Well, it can figure out what “Cupid” is, and there are two possibilities for “pedestal”; but the only one that makes any sense is the red pedestal, because that’s the one that Cupid is on. So it would be good to be able to write a rule that tells the parser to look back, figure out that you’re taking the bust of Cupid, and figure that when you’re taking the bust of Cupid from noun2, it should prefer noun2 to be something that the bust of Cupid is on.

Inform is actually pretty good at figuring out one noun when it knows the other, though this example is currently bugged. But it can’t make a guess when it has to resolve both nouns at once; in this example:

[code]Kitchen is a room.

A fruit is a kind of thing. A lime, a coconut, and an orange are fruits in the Kitchen. Understand “fruit” as a fruit.

Does the player mean inserting the lime into the coconut: it is very likely.

Instead of inserting the lime into the coconut:
say “You put the lime in the coconut and drink them both up.”[/code]

it won’t realize that if you type “put fruit in fruit” then the only combination that makes sense is the lime and the coconut–it won’t be able to guess that you mean the coconut until the player tells it that they meant the lime. (This is terrible authoring, of course, and what Ade said is good advice, but there are times where I feel like you can’t help it–I made a game about making tea that involved a tea cup, tea leaves, a tea strainer, a tea pot, and tea, which each made sense with various actions only in various combinations–you would want to put the tea leaves in the tea strainer, the tea strainer in the tea pot, and the tea in the tea cup.)

The thing is that this might be really difficult to program–for every Understand line and for every Does the Player Mean rule you have to come up with a tentative parsing and then see whether the command can be parsed so as to match that rule. Inform does in a few special cases try to do a lookahead where one noun is parsed in terms of the other noun but from what I’ve been able to tell this involves a lot of extra programming just to deal with this case and is pretty fragile. So I would definitely understand if you just didn’t want to deal with these cases.

I’ll add that Inform does allow you to refer to people by their possessions (I think I put an example of this in the other thread); you can set it so “with [x]” refers to a person that carries x. This was probably pretty hard to implement.

The blue-sky, resource-unconstrained parser would work something like this:

(1) Create a list of all possible interpretations of the command. This includes all grammar lines and all objects in scope. If there are several colored balls, then TAKE BALL would give <take: red-ball>, <take: green-ball>, etc. If the player has an “inventory sheet”, then TAKE INVENTORY would result two possibilities: , <take: inv-sheet>.

We do not consider world state at this stage, except in deciding scope. (Do not rule out <take: inv-sheet> just because the player already carries the sheet.) If the player is visualizing the wrong world-state, we want our response to explain the mistake.

For conditional object names (“the ball in the box”, “the man wearing the hat”) we ignore the conditional phrase – parse as “the ball”, “the man” with annotations that we’ll consider later. (Same reasoning – we want to be able to explain the mistake.) To stretch a case, PUSH X IN Y could result in <insert-into: X, Y> and <push: X> both being considered.

(2) Sort. Go down the entire list and assign likelihoods. At this stage, we look at conditional phrase annotations and assign a big penalty for the ones that fail. (Any entry including “the man wearing the hat” will go to the bottom of the list if all men in scope are hatless.)

Nothing is eliminated from the list at this stage.

(3) If there’s a single best candidate interpretation, pick it. Done.

(4) If there is a group of equally good candidate interpretations above a threshold, print a disambig query. What threshold? The level where interpretatations are grammatical but don’t match the world state. Depending on the game, there may also be a threshold of commands which match the world state but the protagonist categorically refuses to do.

Printing a query can get messy! If all the entries in the group look like <take: …> then we can use a traditional “Which do you want to take…?” query. But if the group is heterogenous, we’ll have to do something clever. This is where numbered disambiguation starts to look really good.

(5) If the entire list is below your threshold, it’s time to complain. Again, this gets messy. Error messages could look like “Steve isn’t wearing a hat”; “No man here is wearing a hat”; “You’re already carrying the sword”; “That isn’t alive” (the Inform special case for ASK DOOR ABOUT HOMOIOUSIS).

Or maybe, as noted, there could be a threshold range that produces “You refuse to jump off the cliff,” or even “You refuse to jump off any of the cliffs here.”

The above is only a sketch. I haven’t accounted for incomplete commands (TAKE, or PUT SWORD IN). Those could be handled by allowing the parser to assume an implicit ALL at the end of the input.

Here’s an idea for including “You don’t see any such thing” in the model: include an invisible no-such-thing object in scope. It can match any string of words but is considered to have a world-state conditional phrase of (false). So TAKE SWORD will match it and add <take: no-such-thing> to the stage-1 list. But that entry will be sorted to the bottom at stage 2, no matter what. It will only become relevant if we reach stage 5, and then we can print “You don’t see any such thing.”

Further obvious possibilities: treat everything the player has ever seen as in-scope, with world-state conditions about unreachability. Then TAKE SWORD can result in “You left the Elvish sword in the living room.”

This is where it’s important that the model only prints disambig queries for entries above the threshold of could-possibly-work. You don’t want to print “Which do you mean, the Elvish sword that you left in the living room or the broadsword that got melted down in the furnace?”

Also note that TAKE SWORD still matches <take: no-such-thing>, but that’s even farther down the list than <take: elvish-sword>. This is why stage 2 involves only sorting, no elimination.

When you have multiple hits for noun1 and in order to resolve it you need noun2, which also happens to have multiple hits, then that’s an issue. Even worse when resolving noun2 also depends on noun1. In that case we need sort of a trial and error approach

I was thinking about simply trying all possible combinations of hits for noun1 and noun2: iterate through all possible hits for noun2 and in each iteration try all possible hits for noun1. Apply the parsing rules for each combination, assign the scores and hope that one combination wins.

So, for the fruits we would have 9 possibilities, each with initially, say, 10 points

put lime in orange, 10
put coconut in orange, 10
put orange in orange, 10
put lime in coconut, 10
put coconut in coconut, 10
put orange in coconut, 10
put lime in lime, 10
put coconut in lime, 10
put orange in lime, 10

Now we need some parsing rules that the author can enter in your framework :slight_smile:

We can never put something in itself, so

if equal(noun1, noun2) then score(-100)

This gives (sorted):
put lime in orange, 10
put coconut in orange, 10
put lime in coconut, 10
put orange in coconut, 10
put coconut in lime, 10
put orange in lime, 10
put orange in orange, -90
put coconut in coconut, -90
put lime in lime, -90

We can only put something in a container

if testflag(noun2.f_container) then score(+10) if testflag(noun2.f_open) then score(+5) # extra bonus when the container is also open endif endif
This gives:
put lime in coconut, 25
put orange in coconut, 25
put lime in orange, 10
put coconut in orange, 10
put coconut in lime, 10
put orange in lime, 10
put coconut in coconut, -80
put orange in orange, -90
put lime in lime, -90

Now there are 2 winners.
We could add another parsing rule which would rule out the orange

if testflag(noun1.f_in_harry_nilssen_lyrics) and testflag(noun2.f_in_harry_nilssen_lyrics) then score(+5)

If we forget about this last rule then we get:

[code]

put fruit in fruit
What do you want to put in the coconut, the lime or the orange?
lime[/code]

After this point, the scores don’t matter anymore because we have unique hits for noun1 and noun2. If the player would have typed ‘coconut’ instead of lime or orange then ‘put coconut in coconut’ would have been sent to the interpreter which would have thrown an error.

I know a little but about Inform 7 (I dissected the Bronze sources for my project). If you make the coconut a container, wouldn’t Inform be able to figure it out to the level of lime or orange?

For the pedestal and bust scenario I think “take bust from pedestal” is pretty hopeless. Unless we use the Hugo approach and set a flag for the last examined object and hope it’s either a bust or a pedestal.

Options are:
take venus from blue pedestal, 10
take cupid from blue pedestal, 10
take venus from red pedestal, 10
take cupid from red pedestal, 10

Rule1: if testflag(noun2.f_supporter) then score(+5) Rule2: if testflag(noun1.f_takeable) then score(+5) Rule3: if testflag(noun1.f_last_examined) or testflag(noun2.f_last_examined) then score(+1) Rule 4: if contains(noun2, noun1) then score(+5)

After rule 1:
take venus from blue pedestal, 15
take cupid from blue pedestal, 15
take venus from red pedestal, 15
take cupid from red pedestal, 15

After rule 2:
take venus from blue pedestal, 20
take cupid from blue pedestal, 20
take venus from red pedestal, 20
take cupid from red pedestal, 20

Suppose they examined the blue pedestal last, then after rule 3
After rule 3:
take venus from blue pedestal, 16
take cupid from blue pedestal, 16
take venus from red pedestal, 15
take cupid from red pedestal, 15

And after rule 4:
take venus from blue pedestal, 21
take cupid from red pedestal, 20
take cupid from blue pedestal, 16
take venus from red pedestal, 15

But honestly, if I were in the shrine and someone would instruct me to take the bust from the pedestal, I would kindly ask them to be a little more specific…

Well, what should happen for “take bust from pedestal” is that the parser asks “Which do you mean, the bust of Venus or the bust of Cupid?” or “Which do you mean, the red pedestal or the blue pedestal?” And then when you answer, it automatically selects the matching one. And this is what happens currently, although it’s happening for the wrong reasons. I’m pretty sure this has to do with the special parsing that Inform uses for “take… from…”, which is designed to ensure that the player can type “TAKE ALL FROM TABLE” and get all and only the things that are on the table.

I suppose in the new blue-sky parser the parser could ask for entire action names when the actions differ at more than one place. So you would get a tie between taking venus from the blue pedestal and taking cupid from the red pedestal, and the parser could ask “Which do you mean, taking the bust of Venus from the blue pedestal or the bust of Cupid from the red pedestal?”

In cases that don’t involve the removing from action and its weird grammar Inform is doing pretty well, it seems. For instance this:

[code]Stadium is a room. A Yankee fan is a person in Stadium. A Red Sox fan is a person in Stadium.

The player carries a Derek Jeter card and a David Ortiz card.

Instead of showing the Derek Jeter card to the Yankee fan, say “The Yankee fan is impressed.”

Instead of showing the David Ortiz card to the Red Sox fan, say “The Red Sox fan is impressed.”

Does the player mean showing the Derek Jeter card to the Yankee fan: it is very likely.

Does the player mean showing the David Ortiz card to the Red Sox fan: it is very likely.[/code]

yields

(apologies if you’re not a honkbal fan)

–long text warning –

Well, I gave it a try. For who’s interested, below I’ll share some detail on what I did, with an example.

I must give a little background info on my parser. It uses what I call action records. The player’s command line input is translated to an action record with information on the action , actor, objects involved, etc. Ambiguities occurs when the parsers tries to convert user input to an action record. There are 3 candidates for ambiguity: actor, subject and specifier (don’t remember why they are called like that, I made it up some 20 years ago and never changed it).
So in “man, put cube in bowl”: actor = man, subject = cube, specifier = bowl.

I made s ample story with ambiguity.
It’s one room with:

3 Actors:

  • red man with a mission
  • green man with a mission
  • blue man with a mission

5 subjects:

  • red cube
  • green cube
  • blue cube
  • orange cube
  • pink cube

4 specifiers:

  • red bowl
  • green bowl
  • blue bowl
  • orange bowl

From an ambiguity point of view, the worst command possible would be “man, put cube in bowl”. This would give 3x5x4=60 possible combinations. Let’s take this as an example.

Here’s how the parser handles it.

  1. It starts with creating all possible action records (60 in this case). Each action record has a score field that is set to 0.

2.Next, the parser examines the structure of the user command and checks with the appropriate verb if disambiguation rules exist for this structure. In this example it will query the ‘put’ verb for structure "someone, put in ". If there are no rules, it will skip the next step and go to step 4.

  1. Disambiguation rules have syntax IF THEN score(). Each of the 60 action records is now ran by the rules and if it complies the score field is increased by number. In we can relate actor, subject and specifier, so resolving one can depend on the value of the others. For example, if - for same reason - it is less likely to put the blue cube in the blue bowl, a rule might be defined as :
    IF equal(o_subject, o_blue_cube) AND equal(o_spec, o_blue_bowl) THEN score(-10)
    The 3 action records with blue cube and blue bowl will each get subtracted 10 points.

  2. Now the parser will check if there is one single highest score. If so, this action record will be passed on to the interpreter for execution and the parsing has ended.

  3. If there are more winning action records (e.g. in case of no rules they will all win), the parser will check actor/subject/specifier hits, pick one that is ambiguous and ask the player which one they mean.

  4. Now that the player has resolved 1 ambiguity, the parser will start from point 1 again, but now with less action records.

That’s it.

I considered to print all winning action records as a numbered list of player commands and let the player pick one, but in my personal opinion it would interrupt the game play too much. So I went for the approach to ask the player to resolve one ambiguity and then repeat the whole process with the new information.

Some sample code and output:

We have following disambiguation rules:

[code]"%o_actor, get %o_subject"
disambiguation_rules

it’s less likely to want to get something you already have

if owns(o_actor, o_subject) then score(-5)

end_rules

“%o_actor, drop %o_subject”
disambiguation_rules

it’s more likely to want to drop something that you are carrying

if owns(o_actor, o_subject) then score(5)

end_rules

“%o_actor, put %o_subject in %o_spec”
disambiguation_rules
# it’s more likely to put something that you are carrying
if owns(o_actor, o_subject) then score(5)
# it’s very likely that the blue man wants to put the blue cube in something
if equal(o_actor, o_blue_man) and equal(o_subject, o_blue_cube) then score(25)
end_rules[/code]

Sample output1:

[code]> l
Room

There’s a red man with a mission here.
There’s a blue man with a mission here.
There’s a green man with a mission here.
There’s a red bowl here.
There’s a green bowl here.
There’s a blue bowl here.
There’s an orange bowl here.
There’s a red cube here.
There’s a green cube here.
There’s a blue cube here.
There’s an orange cube here.
There’s a purple cube here.

get cube
Which cube do you mean?
The red cube, the green cube, the blue cube, the orange cube or the purple cube?

red
red cube: taken.

get green cube, orange cube
Green cube: taken.
Orange cube: taken.

i
You are carrying:
a red cube
a green cube
an orange cube

drop cube
Which cube do you mean?
The red cube, the green cube or the orange cube?

orange
orange cube: dropped.

drop cube
Which cube do you mean?
The red cube or the green cube?

red
red cube: dropped.

drop cube
Green cube: dropped.

transcript
Turning off transcript mode.[/code]

Sample output 2:

[code]> l
Room

There’s a red man with a mission here.
There’s a blue man with a mission here.
There’s a green man with a mission here.
There’s a red bowl here.
There’s a green bowl here.
There’s a blue bowl here.
There’s an orange bowl here.
There’s a red cube here.
There’s a green cube here.
There’s a blue cube here.
There’s an orange cube here.
There’s a purple cube here.

red man, get red cube, green cube
Red cube: taken.
Green cube: taken.

red man, put cube in green bowl
Which cube do you mean?
The red cube or the green cube?

red
The red cube is now in the green bowl

red man, put cube in green bowl
The green cube is now in the green bowl

man, put cube in red bowl
[Taking the blue cube first]
The blue cube is now in the red bowl

green man, put cube in bowl
Which cube do you mean?
The blue cube, the red cube, the green cube, the orange cube or the purple cube?

orange cube
Which bowl do you mean?
The red bowl, the green bowl, the blue bowl or the orange bowl?

orange bowl
[Taking the orange cube first]
The orange cube is now in the orange bowl

l
Room

There’s a red man with a mission here.
There’s a blue man with a mission here.
There’s a green man with a mission here.
There’s a red bowl here.
There is a blue cube in the red bowl.
There’s a green bowl here.
There is a red cube in the green bowl.
There is a green cube in the green bowl.
There’s a blue bowl here.
There’s an orange bowl here.
There is an orange cube in the orange bowl.
There’s a purple cube here.

transcript
Turning off transcript mode.[/code]

Thanks for reading!

This is incredibly interesting! If (or when) you have something that a third party can play around with, I’d love to take a look at this stuff.

I really like the part where you narrow things down one ambiguity at a time. Inform processes disambiguation requests by sticking the player’s response back into the original command and reparsing it, which I think often has the same response but which has some other problems.

Also, this works better than the Inform parser when there’s a tie; in Inform, if A, B, and C are possible ways of reading the command, and the does the player mean rules give a tie between A and B, then the disambiguation will still ask the player to choose between A, B, and C.

Sure. Windows versions + docs are ready. I can give you these whenever you want, Linux in a couple of days.

…aw shoot, I use a Mac. Maybe this should inspire me to finally do one of those things that lets me run another system. Anyway, keep us posted about this, I will follow it with interest.

Well, I wrote all source code myself (apart from the Glk libraries for the Glk version). It’s plain C and I use the code::blocks IDE to make builds. So, maybe with cb on a Mac it will build from the source code. I believe there are Glk libraries for the Mac so building a Glk Mac version might work as well.