Better guessing of what object is intended

Suppose we have four keys: “key”, “iron key”, ruby key", and “gold key”. Now the player types TAKE KEY. The parser will then ask which is meant:

Which do you mean, the key, the iron key, the ruby key, or the gold key?

If the player types KEY, then this causes the question to be asked again. Ordinarily I’d say that the author should avoid naming things like this, but I just want to solve this and be done with it. I can get the short_name of the object being considered into an array. From there I can address each individual character. I can also print the word or words that the player typed when trying to take an object. In this example, print (address) x; will resulting in key being printed. However, and this is my big sticking point, I cannot figure out how to address the individual characters in x. If I can accomplish this, then I can compare these two strings and have the parser pick the “key” if the player says TAKE KEY rather than return an irritating disambiguation question like above.

Speaking as the one who maintains the Standard Library, I should know how to do this, but I don’t.

In case anyone’s curious, I’m trying to solve How to choose an object from another object with the same noun, but with an adjective. (#108) · Issues · David Griffith / inform6lib · GitLab

1 Like

You have to print the dictionary word to an array. Then you can inspect the characters in the array.

You mean using PrintToBuffer()? I’ve been fiddling around with that and can’t seem to figure out exactly how to make that work.

This

print (address) x, " ";
y = PrintToBuffer(mybuffer, MYBUFFER_LEN, x);
print "^";

results in HelloFatal error: Call to non-routine when PrintToBuffer() is called. Relevant code in parser.h is this:

[ PrintToBuffer buf len a b c d e;
    print_anything_result = 0;
    @output_stream 3 buf;
    switch (metaclass(a)) {
      String:
        print (string) a;
      Routine:
        print_anything_result = a(b, c, d, e);
      Object,Class:
        if (b)
            print_anything_result = PrintOrRun(a, b, true);
        else
            print (name) a;
    }
    @output_stream -3;
    if (buf-->0 > len) RunTimeError(14, len, "in PrintToBuffer()");
    return buf-->0;
];

The metaclass of x is Routine according to metaclass().

PrintToBuffer() (TARGET_ZCODE) does not allow to print a dictionary word in an array .
Edit:

#Ifdef TARGET_ZCODE;
[ PrintDictWord w   i;
   @output_stream 3 StorageForShortName;
   print (address) w;
   @output_stream -3;
   for (i = 0 : i < StorageForShortName-->0 : i++)
      print (char) StorageForShortName->(i+WORDSIZE);
];
#Ifnot; ! TARGET_GLULX
[ PrintDictWord w;
   PrintToBuffer(StorageForShortName, 160, w);
   PrintFromBuffer(StorageForShortName);
];
#Endif;

Since it’s impossible in Z-code to tell if a value is a dictionary word or a string, PrintToBuffer() can’t be expected to handle both, unless you add an argument to say “this is a dictionary word”.

Anyway, you could also encode a string of characters as a dictionary word, which would allow you to compare a word from a string to a dictionary word.

Personally, I’d write a parse_name routine for iron key, ruby key and gold key, which makes them require an adjective in the presence of the featureless key. Takes five minutes and solves the problem.

Object -> iron_key "iron key"
   with name 'iron' 'key';

Object -> ruby_key "ruby key"
   with name 'ruby' 'key';

Object -> gold_key "gold key"
   with name 'gold' 'key';

Object -> key "key"  
   with
	   parse_name [;
	   	if (NextWord() == 'key') return 2;
	   	return 0;
	   ];

A bit off-topic but:

When I played through the early MDL Zorks I encountered a nasty bug. (it’s important to know that once a treasure is deposited in the trophy case, it’s not possible to retrive it.)

In the early, 285 point, version there is a treasure called “diamond trident”. When the game was expanded to 387 points, the coal mine area was added with a new “diamond” treasure. The “diamond trident” was renamed to the “crystal trident”, but still had the synonym “diamond” associated with it. The “diamond” had no adjectives or synonyms.

So if you deposit the “crystal trident” in the trophy case first it is impossible to deposit the “diamond” thereafter.

There is also all the cakes of different colors and the “Eat Me” cake. The “Eat Me” cake has the adjective “eatme” associated with it, which isn’t entirely obvious… I saw one walkthrough that used the sack to hide all the colored cakes to be able to type “eat cake” without disambiguaty. :grinning:

1 Like

You return “2” as if it was a score here, which it is not. This may still work for some cases, depending on the implementation details of the parser, but you shouldn’t depend on it. And it most definitely won’t work for commands where the key doesn’t come last in the sentence, like “put key in sack”.

I think you mean “crystal trident”?

Of course. I’ve edited the post.

1 Like

That’s what I thought! Is there a simple way to do this?

parse_name must return how many words it matched, not how good the match is.

You can’t solve this issue with a parse_name for the featureless key. Create a parse_name for the other keys instead, something like:

Class AdjectiveKey
  with 
    parse_name [ w1 w2;
      w1 = NextWord(); w2 = NextWord();
      if(w1 == self.adjective) {
        if(w2 == 'key') return 2;
        return 1;
      }
      if(w1 == 'key' && ~~TestScope(FeaturelessKey)) return 1;
    ];

AdjectiveKey RubyKey "ruby key"
  with
    adjective 'ruby';

As for a solution that the library can supply, I can’t see a clean way to do it. With some objects having a name property and some a parse_name routine, the library can’t know if a certain match is perfect or not, unless you extend the protocol for parse_name so that it can also set a global to signal how good the match is.

The object name is just as messy. It may be the object name string, or a short_name routine which may use any conditions it likes to print the object name. There may also be adjectives mentioned in the description which are not in the object name but can help to describe the object more precisely. So the short name of the ruby key may be “red key” but the description says it has a large ruby on it, and “ruby key” or “red key” should be considered a perfect match, but there’s no way for the library to know that.

Another option would be to number the options, so the parser asks “Which do you mean, (1) the key, (2) the iron key or (3) the ruby key?” and the player can type either an adjective or a number to answer the question. But maybe this breaks mimesis.

Of course, the game author could also write a ChooseObjects routine to help the parser out here.

Why everything is so complicated? It’s daunting. I wonder if there was a good old time where everything was easy.

3 Likes

Nope!

1 Like

Federick: IMVHO Numbering the options should be confined to talking to NPC… (yes, I like the RPG method here…)

On the disambiguation, the best handling of the infinite loop should be having the noun as default adjective (I’m not sure of having explained well… the featureless (that is, no adjective) noun should be considered the default when there’s more keys in scope); of course, only one key must be “featureless”, because well, the “you mean the key, the key, the key ?” infinite loop is always lurking like a grue, ready to jump on the unwary Imp… :smiley:

Hope to have explained well my opinion on disambiguation, and
Best regards from Italy,
dott. Piergiorgio.

What I want to do here is accomplish a very narrow task of comparing the single-word noun supplied by the player with the short names of possible matches before the parser decides whether or not to ask a disambiguation question. I can access the individual letters in the player-supplied noun. My core consternation is how to access the individual letters of a string that’s printed with print (address) foo;. Doing this with a parse_name routine isn’t what I have in mind, since I want the Library to behave like this for all objects. So far, @auraes’s PrintDictWord() looks like it might be the solution. More testing…

Sounds to me like you don’t really need to handle dictionary words at all here. You want to compare a word that the player has typed (which is stored with one ZSCII character per byte in an array) to the short name of the object (which you can print with one ZSCII character per byte to an array). No dictionary words involved.

This tries to match an object p_obj to input word number p_word_no. Only tested for Z-code. It prints the length of the word typed and the short name. If they are different, it returns. Then it compares them character by character, but only up to a maximum of nine characters, since the player may have skipped typing the last part of a word longer than the dictionary resolution. And it prints every character that matches, until it finds a character that doesn’t match.

Array temp -> 100;

[ MatchWord p_obj p_word_no len_name len_input start_input i;
	@output_stream 3 temp;
	print (name) p_obj;
	@output_stream -3;
	len_name = temp --> 0;
	if(len_name > 9) {
		for(i = 9 : i < len_name : i++) if(temp -> (WORDSIZE + i) == 32) rfalse;
		len_name = 9;
	}
	len_input = (parse + 2) -> (4 * p_word_no + 2);
	if(len_input > 9) len_input = 9;
	print "len_name=",len_name, "^";
	print "len_input=",len_input, "^";
	if(len_name ~= len_input) rfalse;

	start_input = (parse + 2) -> (4 * p_word_no + 3);
	for(i = 0 : i < len_name : i++) {
		print "objname: ", (char) temp -> (WORDSIZE + i), "^";
		print "typed: ", (char) buffer -> (start_input + i), "^";
		if(temp -> (WORDSIZE + i) ~= buffer -> (start_input + i))
			rfalse;
	}
	print "MATCH!";
	rtrue;
];
1 Like

That’s what I do. It’s pretty simple, but it’s not general purpose. Essentially, what you’re trying to do is come up with the best match, rather than any match. Let’s look at the following three scenarios:

Scenario 1: Only the key is in scope. If you enter ‘key’, then that’s a 100% match, so the key is a match.

Scenario 2: Only the iron key is in scope. If you enter ‘key’, then that’s a 50% match, but there is no other key in scope, so the iron key is a match.

Scenario 3: The key and the iron key are in scope. If you enter ‘key’, then that’s a 100% match on the key and a 50% match on the iron key, so the key is the best match.

I can’t see why you need to start looking at individual characters. You only need to do a match on the number of unique words in the input that are matched against the name list vs the number of words in the name list. Or am I being naive?

The problem with that approach is that the name list can be large and the player doesn’t necessarily see it. That is, the author could wind up giving different keys synonyms like “rusty”, “corroded”, “cold”, “heavy”, “elaborate”, “long”. It would be surprising if this changed the parsing of “get key”. Especially since you often wind up adding synonyms late in development (after playtesting).

I think there’s a confusion here between the name list (which can be large and contain long-shot synonyms) and the printed name (which is presented directly to the player). There’s an argument for taking both into account, but the Inform parser has never done it because it’s so much extra work.

Historically, the parser does check whether two objects have exactly the same name list. That usually means the author has a class (“gold coins”) with a bunch of objects which are meant to be indistinguishable. In that case, they probably also have the same printed name – the parser doesn’t check that but it’s a pretty safe assumption.

1 Like