Game not disambiguating

Hey all–

So I have several pieces of paper in my game. The printed name of all of them is “piece of paper.” If the player is carrying a piece of paper and is in a location with another piece of paper, and you X PAPER, the game isn’t prompting the player to disambiguate. It’s simply picking the piece of paper in the location. Why isn’t it disambiguating and asking the player “which do you mean”?

Disambiguation is the thing I’m trying to understand now in general, since weird things happen. I’ve read through all the examples in the docs, but they all seem to be about how to better control the standard “Which do you mean” reply, and my trouble is I’m not getting that reply at all.

Can y’all throw out some reasons why this would happen so I can pinpoint the problem? I can’t really post any code, since I have no idea where this is occurring.

2 Likes

No answers here but I’m curious to see the replies.

Feel like I’ve needed to do a fair amount of disambiguation tuning but never understood how I created those situations.

1 Like

I have some “instead of examining” rules, and I just changed those to “check examining” to see if the dastardly “instead of” was causing the problem, but no. Which is a shame, because I like to blame things on “instead of.”

Me too. I’m putting some effort into trying to understand this better, because it’s a bit of a black box.

1 Like

I will let more brainy people to replay the technical question, and you have been surely thinking on it before arriving that point but as a general advice…

Is it wise to bring players to potentially ambiguous situations? ¬.¬

So far, my cheap two cents! : - D

Generally it is not wise. But short of making the pieces of paper different colors or shapes, I didn’t see a way out of it. And I thought the game would simply ask “which do you mean” and the player could disambiguate. Of course, that’s not how it played out, because I am a magnet for trouble.

There’s only one area where this is a problem, and it’s not a huge one, but I hate it anyway.

1 Like

I would argue that a primary appeal of parser games is ambiguity. It is hard to separate ambiguity and possibility.

And even if the scenario does not present the player with ambiguity, implementors may need to use nouns that are of a kind, etc

For me it’s been competing “does the player mean” statements. I get them working but feel sort of bad about it

Well, that’s helpful. I looked through the “does the player mean” rules I have, and I have one that probably is the problem:

Does the player mean doing something to something carried:
		it is unlikely.

I don’t remember why I had this, but I certainly ran into a problem that caused me to put it in. When I remove this, now when you X PAPER, the game picks the one you’re holding, but it’s still not asking “Which do you mean.”

A fruitful suggestion, though.

ETA: I just remembered why I put that in: because when you type SIT or ENTER or CLIMB , etc without supplying a noun, the game always picked something carried, which was really weird. I have a theory that the parser will disambiguate by first picking something carried, and if you aren’t carrying anything, it will pick an NPC.

1 Like

It seems like the parser calculates some sort of “distance “ to a thing and compares them — things you hold are closer than things you don’t, things in containers are farther than those that aren’t, etc.

I don’t know if you can override these calculations and I’m doubtful that it’s a good idea.

In general, while writing my game I’m learning that more-or-less identical objects are kind of a pain.

Amen to that. I did my best to make sure the papers wouldn’t overlap, but there is an area where it can’t be helped. The lesson is learned, though. I won’t be doing anything like this again!

My big frustration is that I can’t create the behavior in a test project. Or (more likely) I don’t understand it well enough to create it in test.

So the behavior pops up once or twice (not every time) in my 50k-word (and counting) game and I can’t chase it down. Would like to head off this kind of behavior rather than react to it.

It also can depend on the verb. If the player types TAKE PIECE OF PAPER, it makes sense for the parser to choose one not carried.

Also this is probably a “duh” thing, but are there multiples in scope that should be disambiguated? If there’s only one piece of paper available, the parser will choose that.

1 Like

Hopefully a code wizard will pop in with a lecture for us.

I just realized how bone-stupid my setup is: even if things were working like I want, because both pieces of paper are called “piece of paper” the parser would just ask “which do you mean: the piece of paper or the piece of paper?” SOOOOO helpful. So I tweaked the name of the one in the location, but of course that doesn’t help with the bigger problem.

1 Like

You need to make “piece of paper” a kind of thing, then name them differently “a scribbled note is a piece of paper” “a scrawled combination is a piece of paper”…

I just can’t communicate worth a hill of beans these days. Blargh.

So here’s the sitch:

In one location, there is a piece of paper that you can’t get until the end of the game. Throughout the game, you will find other pieces of paper and may tote them about. Once you’ve used a piece of paper, it disappears. There are no other places in the game where 2 pieces of paper can overlap. But any carried paper, if you try to examine it in the location with the unavailable paper, causes the problem where the parser isn’t asking “Which do you mean”.

They of course all have different private names, but all have “piece”, “paper” as synonyms.

I was truly hoping not to have to do that, for reasons unimportant here. If I have to, I will, but I’d like the damned parser to just ask in this one location if I can manage it.

could you “does the player mean… it is very unlikely” to both items in that room?

does the player mean taking the tall frob in room1: it is very unlikely.
does the player mean taking the small frob in room1: it is very unlikely.

Doesn’t really explain anything but might balance things out.

That was a great idea, but no dice. X PAPER still examines the carried piece of paper. Sigh. I may have to do as Hanon suggests. I wanted the pieces of paper to all look the same, but I suppose subtle differences in names wouldn’t kill me.

This, annoyingly, comes down to a deeply-buried behavior built into the parser.

There’s a I6 routine ScoreMatchL that looks through the list of things matched by the player’s command, and decides if one of them is strictly better than the rest. If so, it decides on that one instead of asking a question.

The scoring function gives a bonus of 60 if the thing it’s considering is in the “best location”, and a bonus of 40 if that thing is in the “next-best location”. For most actions, the “best location” is being held by the actor, while the “next-best location” is the actor’s location. (The exceptions are taking and removing it from, where these two are reversed.)

Since the paper in your hand is in the best location, and the paper on the floor is in the next-best location, the one in your hand gets 20 more points and wins. If you want to see these numbers for yourself, use “trace 4” in game, then look for the line that starts with “Scoring match list:”.

Curiously enough, there’s a switch in the code that’s supposed to make this configurable (PREFER_HELD). But this switch is always turned on by the parser kit itself, making it useless.

I do wonder what would happen if this “location bonus” was turned off unless specifically requested. Unfortunately, my quick attempt to do this managed to crash the I6 compiler with no error message. I’ll let you know if I manage to fix that.

3 Likes

Ah, there we go. Yes, this does end up producing the expected behavior: the game asks if you meant the one in your hand or the one on the ground.

The side effects of this would be, as expected, that the game asks a lot more disambiguation questions. But I don’t think it should break anything horribly. Want me to throw it in an extension?

1 Like

I’d love to see some code for this. If it isn’t asking too much, I think quite a few people would like some explanation for why that code works, as well.

Aight! Here’s the code for it on 6M62. (The code for 10.x would be almost the same, you just need to specify ParserKit instead of Parser.i6t.)

Include (-
Constant SCORE__CHOOSEOBJ = 1000;
Constant SCORE__IFGOOD = 500;
Constant SCORE__UNCONCEALED = 100;
Constant SCORE__BESTLOC = 60;
Constant SCORE__NEXTBESTLOC = 40;
Constant SCORE__NOTCOMPASS = 20;
Constant SCORE__NOTSCENERY = 10;
Constant SCORE__NOTACTOR = 5;
Constant SCORE__GNA = 1;
Constant SCORE__DIVISOR = 20;

Constant PREFER_HELD;
[ ScoreMatchL context its_owner its_score obj i j threshold met a_s l_s;
!   if (indef_type & OTHER_BIT ~= 0) threshold++;
    if (indef_type & MY_BIT ~= 0)    threshold++;
    if (indef_type & THAT_BIT ~= 0)  threshold++;
    if (indef_type & LIT_BIT ~= 0)   threshold++;
    if (indef_type & UNLIT_BIT ~= 0) threshold++;
    if (indef_owner ~= nothing)      threshold++;

    #Ifdef DEBUG;
    if (parser_trace >= 4) print "   Scoring match list: indef mode ", indef_mode, " type ",
      indef_type, ", satisfying ", threshold, " requirements:^";
    #Endif; ! DEBUG

    #ifdef PREFER_HELD;
    a_s = SCORE__BESTLOC; l_s = SCORE__BESTLOC;
    if (action_to_be == ##Take or ##Remove) {
        a_s = SCORE__NEXTBESTLOC; l_s = SCORE__BESTLOC;
    }
    context = context;  ! silence warning
    #ifnot;
    a_s = SCORE__NEXTBESTLOC; l_s = SCORE__BESTLOC;
    if (context == HELD_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN) {
        a_s = SCORE__BESTLOC; l_s = SCORE__NEXTBESTLOC;
    }
    #endif; ! PREFER_HELD

    for (i=0 : i<number_matched : i++) {
        obj = match_list-->i; its_owner = parent(obj); its_score=0; met=0;

        !      if (indef_type & OTHER_BIT ~= 0
        !          &&  obj ~= itobj or himobj or herobj) met++;
        if (indef_type & MY_BIT ~= 0 && its_owner == actor) met++;
        if (indef_type & THAT_BIT ~= 0 && its_owner == actors_location) met++;
        if (indef_type & LIT_BIT ~= 0 && obj has light) met++;
        if (indef_type & UNLIT_BIT ~= 0 && obj hasnt light) met++;
        if (indef_owner ~= 0 && its_owner == indef_owner) met++;

        if (met < threshold) {
            #Ifdef DEBUG;
            if (parser_trace >= 4)
            	print "   ", (The) match_list-->i, " (", match_list-->i, ") in ",
            	    (the) its_owner, " is rejected (doesn't match descriptors)^";
            #Endif; ! DEBUG
            match_list-->i = -1;
        }
        else {
            its_score = 0;
            if (obj hasnt concealed) its_score = SCORE__UNCONCEALED;

            if (its_owner == actor) its_score = its_score + a_s;
            else
                if (its_owner == actors_location) its_score = its_score + l_s;
                else
                    if (its_owner ~= compass) its_score = its_score + SCORE__NOTCOMPASS;

            its_score = its_score + SCORE__CHOOSEOBJ * ChooseObjects(obj, 2);

            if (obj hasnt scenery) its_score = its_score + SCORE__NOTSCENERY;
            if (obj ~= actor) its_score = its_score + SCORE__NOTACTOR;

            !   A small bonus for having the correct GNA,
            !   for sorting out ambiguous articles and the like.

            if (indef_cases & (PowersOfTwo_TB-->(GetGNAOfObject(obj))))
                its_score = its_score + SCORE__GNA;

            match_scores-->i = match_scores-->i + its_score;
            #Ifdef DEBUG;
            if (parser_trace >= 4) print "     ", (The) match_list-->i, " (", match_list-->i,
              ") in ", (the) its_owner, " : ", match_scores-->i, " points^";
            #Endif; ! DEBUG
        }
     }

    for (i=0 : i<number_matched : i++) {
        while (match_list-->i == -1) {
            if (i == number_matched-1) { number_matched--; break; }
            for (j=i : j<number_matched-1 : j++) {
                match_list-->j = match_list-->(j+1);
                match_scores-->j = match_scores-->(j+1);
            }
            number_matched--;
        }
    }
];
-) instead of "ScoreMatchL" in "Parser.i6t".

This is a huge chunk of impenetrable I6. But there’s only one line that’s important in here:

a_s = SCORE__BESTLOC; l_s = SCORE__BESTLOC;

By default, this line sets a_s (“actor score”, the bonus for being held by the actor) to SCORE__BESTLOC (60 points), and l_s (“location score”, the bonus for being in the location) to SCORE__NEXTBESTLOC (40 points).

I’ve changed it to instead set both of them to SCORE__BESTLOC. This means there’s no longer any difference between something being held, and being in the location, as far as the parser’s preferences go.

Now, there’s still a special exception in the parser that changes the a_s and l_s values if you’re taking or removing it from. I think this could also be scrapped, and handled by “does the player mean” rules. But for now I’m leaving it in, since it doesn’t do any harm.

If this proves useful, I’ll make sure it works in 10.x too and put it in an extension.

4 Likes