Disambiguation Between Direction Adjectives [Adv3Lite]

I feel like multiple bad decisions led to this point, but I’m not sure what the better solution would have been, so here’s where I’m stuck.

Let’s say you have two walls of interest in a room: The east wall and west wall.

This room only has two exits: north and south.

The vocab for the east wall is: 'east[weak] wall;e[weak]'

The vocab for the west wall is: 'west[weak] wall;w[weak]'

I’m marking the directions as [weak] to avoid a situation where attempts to travel in a direction instead match one of these walls (at least, outside of disambiguation checks, which I’m getting to in a moment).

Here’s the problem I have. Let’s say the player tries EXAMINE WALL. The game will ask for disambiguation:

The east wall, or the west wall?

The problem is if the player clarifies by simply typing in EAST, the parser will understand that as an action:

You can’t go that way.

So, I’m already modifying TravelAction for other purposes, as well as other parts of the action cycle. Is there anything else I could check or modify, to prevent directional actions during disambiguation checks, or somehow give the disambiguation check more priority over travel actions?

I’m also not too versed on how the disambiguation system works, so I don’t know where to even start for my own research.

Thank you!

1 Like

On workaround that seems to work is to define the following StringPreParser:

StringPreParser
    doParsing(str, which)
    {
        if(which == rmcDisambig)
        {
            if(str.toLower() is in ('e' ,'east'))
                str = 'eastx';
            
            if(str.toLower() is in ('w' ,'west'))
                str = 'westx';

        }   
        
        /* return the original string unchanged */
        return str;
    }    
;

And then that ‘eastx’ or ‘westx’ as (non-weak) adjectives to the east and west walls respectively.

This is easier than trying to delve into the disambiguation mechanism, even if it is a bit of a kludge. It’s possible it may cause problems I haven’t thought of, so you’d need to test it, but it’s probably worth a try.

This should change the player’s input from, for example, ‘east’ to ‘eastx’ only in response to a disambiguation prompt

BTW, I don’t think ‘east’ and ‘west’ need to be weak tokens in your code, unless you’ve encountered a problem with them not being weak.

6 Likes

Excellent!! I’ll implement this, and get some testing done! I shall report back as soon as I can!

EDIT: @Eric_Eve, you absolute gem! This is exactly what I was looking for! Oh my god, this is going to be such an improvement to my game! There are a lot of objects that only make sense to describe according to what side of the room they’re on, so this particular disambiguation problem has been happening everywhere!

This is literally a night and day difference! Thank you again!

This only had a minor hiccup with my PEEK actions (PEEK EAST vs PEEK EAST PORTHOLE), but I just gave the PEEK THRU X action a slight badness rating, so now PEEK EAST prioritizes TravelConnectors over objects with “east” or “eastx” in their adjectives.

This also should never really come up, but it’s an edge case I felt I should iron out, anyways.

Works great!!

1 Like

Joey, I’ve solved that problem without a preparser , and also with generic capacity to bypass other verbs than east and west. Because it can also happen in a situation like this: you’ve defined a Hook verb, or Saddle, or Water, and you try

>hang cloak
What do you want to hang it on?
>hook
What do you want to hook?

Let me know if you have any troubles with your current solution… preparser may well be all you need.

2 Likes

I might swing back around after this game is done. I don’t use any verbs in this game that could be mistaken for adjectives, other than traveling actions. Feel free to post the solution you’ve got, though! I’m just in the final high-intensity phase of finishing up this SpringThing game.

2 Likes

@jbg I’ll second that. StringPreParser is my hammer in the ‘every problem looks like a nail’ world I live in. Wouldn’t mind seeing the alternative.

Since I lean on StringPreParser too much, I also tend to overkill on the string testing. I would probably attack the above either with a localStrCopy.findReplace(' ','') before testing to get rid of leading/trailing whitespace, or match my standard PreParser processing with

StringPreParser
	doParsing(str, which) {
        local dirAdjEastRegex = R'<NoCase>^<Space>*(e|east)<Space>*$';
        local dirAdjWestRegex = R'<NoCase>^<Space>*(w|west)<Space>*$';

        if(which == rmcDisambig) {
    		if (rexMatch(dirAdjEastRegex, str)) str = 'eastx';
	    	if (rexMatch(dirAdjWestRegex, str)) str = 'westx';
        }
		return str;
	}
;

Now, I did not actually try the above, so there may be some gotchas to fire and adjust from.

Tangentially, I have not run across the documentation of the ‘which’ parameter, and this thread is actually my first exposure to its use! Looks helpful! Is it documented somewhere I have somehow missed until now?

2 Likes

@jjmcc There are a few places where the parser calls StringPreParser.runAll, which will pass either rmcCommand, rmcDisambig or rmcAskObject etc. to the doParsing which parameter…

1 Like

This is the first I’ve heard of StringPreParser, lol. (takes notes)

1 Like

Sorry Joey, I don’t think my solution can plug into the Mercury parser without me dissecting it to figure out where it executes a similar functionality…

1 Like

@jjmcc , regarding the other verbs I mentioned, these are the snippets I changed.
Add a dontPreempt property to Action. Then you have to replace tryAskingForObject(...)
You’ll copy-paste it, then modify this block to read:

if (cmdMatchList != [] )
        {       
            local ac = cmdMatchList[1].resolveFirstAction(issuingActor,targetActor);
            if(ac && ac.dontPreempt)  /* pass */ ;
            else if (!match.isSpecialResponseMatch)     {
                throw new ReplacementCommandStringException(str, nil, nil);   }
        }

I’m really curious now, because this paragraph doesn’t deal with the ‘east/west’ disambig, and yet my game does not trip up on those (it will pick the east window instead of executing a travel command to the east). I can’t remember what else I may have changed regarding those, but I’m curious enough now I might go try to sniff it out…

LATER:
It seems that adv3 handles the directions out of the box… it doesn’t seem to execute a travel command if you enter ‘east’ after a disambig prompt. I believe BasicResolveResults.ambiguousNounPhrase won’t recast the input as a fresh command unless it fails to find any matching between the input and the objects to be disambiguated…

@inventor200 , it looks like there’s a super simple solution for you.

modify Question
   priority = true
;

With that bit, the parser will always try to match input to disambiguation or missing object vocab first, and only parse input as a fresh command if nothing matches.
If you’re really bent on letting disambiguation vocab trump a fresh command only in certain cases, you’ll probably have to look at where the Question objects are created, and maybe use a subclass or make priority a conditional method.

2 Likes

It at least seems that this way you won’t have to continue giving objects ‘eastx’ vocab and whatnot…

1 Like

Also provided this works in Adv3Lite.

Disambiguation comes from the Question object? Is this like an action of some kind? I really have no idea how the disambiguation system works.

Also I ran out of meds and am having problems getting the next prescription, so I won’t be able to report back much until I’m medicated again.

Yes, this is Lite-specific. I didn’t read exhaustively, but I understand the parser to create a subclass of a Question object when it prepares to ask the player a disambig or missing object question. The object stores some information that the parser uses, and the priority property is something that the parser looks at to determine if the disambig input should be first matched to objects or first examined to see if it is a fresh command.
As stated, if you set priority to true on all Question instances, the parser will always match ‘east’ to your east window – because it’s disambiguating – instead of casting ‘east’ as a new command.

1 Like

I really appreciate you explaining that step-by-step for me.

I’m assuming the reason why it’s not priority = true by default, then, is because there are situations where the player would rather just ignore the question and explore an alternative immediately?

Apparently the status quo would rather give priority to letting the player abandon the current question and go right into a fresh command…

1 Like

Ohhhh, huh.

I feel like the experience might, overall, be better if that was true by default, and maybe implement a way to handle “nevermind” as a possible player response, lol.

Which do you mean, the east window or the west window?

>NEVERMIND

Ah, very well!

1 Like

I believe that even with priority = true the parser will still allow recasting as a command if the new input doesn’t match a disambig object. I think by a long shot it should be true by default…

1 Like

Yes, the parser will still accept recasting as a command under those circumstances (at least it did when I tested it just now).

I’m inclined to agree with you that this should be the default, so I’ll change ParseErrorQuestion accordingly for the upcoming version 1.6.1 update (unless anyone has a violent objection to my doing so).

3 Likes

Okay, I’ve added

modify ParseErrorQuestion {
    priority = true
}

to my code, and it works like a charm! (Upgrading to 1.6.1 after SpringThing is over)

Also, can confirm that they don’t need to be [weak] tokens. Not sure where I got that assumption from…

2 Likes