Numbered Disambiguation weirdness

Hi all – I’m looking for someone wise in the ways of Inform’s parser! :slight_smile: Aaron Reed’s Numbered Disambiguation Choices extension misbehaves, and neither Aaron nor I understand why. Here is a sample game:

[code]Include Numbered Disambiguation Choices by Aaron Reed.

Paradise is a room.

An apple of Eden is a kind of thing.
An apple of Hell is a kind of thing.

The table is a supporter in Paradise.

The player carries an apple of Eden and an apple of Hell.

Test me with “put apple on table / 2”.[/code]
The test command leads to an “I didn’t understand that sentence” error. The problem only occurs when (a) you use a command that includes a second noun (or at least is more complicated than “drop apple”, which works fine), and (b) you choose a number that is not “1”.

Aaron’s reaction:

Although a rewrite of the extension would of course be appreciated, I’m also happy with just a hint that would enable me to fix the weird behaviour of the current extension. (It’s rather relevant to Kerkerkruip’s “put scroll in analyser” command, I’m afraid.)

I haven’t looked at it for long, but this part looks like it could assign the same number more than once:

add macguffin to the list of disambiguables, if absent; now the disambiguation id of macguffin is the number of entries in list of disambiguables;

Do the numbers print out correctly?

Yes, the numbers do print out correctly.

(There is also a ‘bug’ in the extension where the numbers come out weird if the list of disambiguables contains rooms and other non-things, but that only happens in not-for-release mode when using commands like “showme” and “gonear”.)

TRACE 4 shows that the parser somehow loses track of the number of words it has read. At the end of the following quote it skips word 4 and obviously believes that word 5 is “on” (whereas it should be a noun).
Actually, I don’t understand what this means in terms of functionality. I can just see that womething goes wrong there.

I think this came up before. But IIRC, the bug appears in code written by ni, so there’s no easy way to make a direct fix—you would have to workaround it in the template layer like Felix suggested in that thread.

Oh! Yes, there was a slightly familiar feel to that thing …

Hm, OK. Diving into the template layer is not my idea of fun, but maybe I’ll be brave enough to try. Thanks for the help!

The problem there was that the I6 function “TryNumber” (which deals with the number in “take 2 apples”) was interfering with the numerical choices in Numbered Disambiguation Choices. You can easily fix this like so.

[spoiler][code]Include (-

[ Descriptors o x flag cto type n;
ResetDescriptors();
if (wn > num_words) return 0;

for (flag=true : flag :) {
	o = NextWordStopped(); flag = false;

   for (x=1 : x<=LanguageDescriptors-->0 : x=x+4)
		if (o == LanguageDescriptors-->x) {
			flag = true;
			type = LanguageDescriptors-->(x+2);
			if (type ~= DEFART_PK) indef_mode = true;
			indef_possambig = true;
			indef_cases = indef_cases & (LanguageDescriptors-->(x+1));

			if (type == POSSESS_PK) {
				cto = LanguageDescriptors-->(x+3);
				switch (cto) {
				  0: indef_type = indef_type | MY_BIT;
				  1: indef_type = indef_type | THAT_BIT;
				  default:
					indef_owner = PronounValue(cto);
					if (indef_owner == NULL) indef_owner = InformParser;
				}
			}

			if (type == light)  indef_type = indef_type | LIT_BIT;
			if (type == -light) indef_type = indef_type | UNLIT_BIT;
		}

	if (o == OTHER1__WD or OTHER2__WD or OTHER3__WD) {
		indef_mode = 1; flag = 1;
		indef_type = indef_type | OTHER_BIT;
	}
	if (o == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {
		indef_mode = 1; flag = 1; indef_wanted = INDEF_ALL_WANTED;
		if (take_all_rule == 1) take_all_rule = 2;
		indef_type = indef_type | PLURAL_BIT;
	}
	if (allow_plurals) {
		wn--;
		if (NextWordStopped() ~= -1 && signal == 0) {
			!print signal, " TEST 1^";
			n = TryNumber(wn-1);
		}
		else {
			!print signal, " TEST 2^";
			n=0;
			signal = 0;
		}
		if (n == 1) { indef_mode = 1; flag = 1; }
		if (n > 1) {
			indef_guess_p = 1;
			indef_mode = 1; flag = 1; indef_wanted = n;
			indef_nspec_at = wn-1;
			indef_type = indef_type | PLURAL_BIT;
		}
	}
	if (flag == 1 && NextWordStopped() ~= OF1__WD or OF2__WD or OF3__WD or OF4__WD)
		wn--;  ! Skip 'of' after these
}
wn--;
return 0;

];

[ SafeSkipDescriptors;
@push indef_mode; @push indef_type; @push indef_wanted;
@push indef_guess_p; @push indef_possambig; @push indef_owner;
@push indef_cases; @push indef_nspec_at;

Descriptors();

@pull indef_nspec_at; @pull indef_cases;
@pull indef_owner; @pull indef_possambig; @pull indef_guess_p;
@pull indef_wanted; @pull indef_type; @pull indef_mode;

];

-) instead of “Parsing Descriptors” in “Parser.i6t”.

Include (-

Global signal;

-) after “Definitions.i6t”.

The disambiguation flag is a truth state that varies. The disambiguation flag variable translates into I6 as “signal”.[/code][/spoiler]

Now all you have to do is set the disambiguation flag to true when disambiguating and false otherwise.

However, this fix doesn’t solve the “weirdness” above. I’ll take a look into it, since I’m familiar with diving head first into the world that is the I6 template layer.

This seems like a minimal case, demonstrating that the problem isn’t anything particular to Numbered Disambiguation Choices; you can get it simply by understanding a number (greater than one) as a thing:

Lab is a room. The player carries a flask. Understand "2" as the flask. The table is a supporter in lab. Test me with "put flask 2 on table/put 2 flask on table".

I managed to figure out what the problem was. It seems that the word number is incremented one too many times and it tries to see if “table” is a valid preposition rather than “on”, which fails giving the error message “I didn’t understand that sentence.”. You can fix this by adding this.

[spoiler][code]Include (-

[ Descriptors o x flag cto type n;
hold = 0;
ResetDescriptors();
if (wn > num_words) return 0;

for (flag=true : flag :) {
	o = NextWordStopped(); flag = false;

   for (x=1 : x<=LanguageDescriptors-->0 : x=x+4)
		if (o == LanguageDescriptors-->x) {
			flag = true;
			type = LanguageDescriptors-->(x+2);
			if (type ~= DEFART_PK) indef_mode = true;
			indef_possambig = true;
			indef_cases = indef_cases & (LanguageDescriptors-->(x+1));

			if (type == POSSESS_PK) {
				cto = LanguageDescriptors-->(x+3);
				switch (cto) {
				  0: indef_type = indef_type | MY_BIT;
				  1: indef_type = indef_type | THAT_BIT;
				  default:
					indef_owner = PronounValue(cto);
					if (indef_owner == NULL) indef_owner = InformParser;
				}
			}

			if (type == light)  indef_type = indef_type | LIT_BIT;
			if (type == -light) indef_type = indef_type | UNLIT_BIT;
		}

	if (o == OTHER1__WD or OTHER2__WD or OTHER3__WD) {
		indef_mode = 1; flag = 1;
		indef_type = indef_type | OTHER_BIT;
	}
	if (o == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {
		indef_mode = 1; flag = 1; indef_wanted = INDEF_ALL_WANTED;
		if (take_all_rule == 1) take_all_rule = 2;
		indef_type = indef_type | PLURAL_BIT;
	}
	if (allow_plurals) {
		if (NextWordStopped() ~= -1) { wn--; n = TryNumber(wn-1); } else { n=0; wn--; }
		if (n == 1) { indef_mode = 1; flag = 1; }
		if (n > 1) {
			indef_guess_p = 1;
			indef_mode = 1; flag = 1; indef_wanted = n;
			indef_nspec_at = wn-1;
			indef_type = indef_type | PLURAL_BIT;
		}
	}
	if (flag == 1 && NextWordStopped() ~= OF1__WD or OF2__WD or OF3__WD or OF4__WD) {
		hold=1;
		wn--;  ! Skip 'of' after these
	}
}
wn--;
if (hold == 1) {
	hold=0;
	wn--;
}
return 0;

];

[ SafeSkipDescriptors;
@push indef_mode; @push indef_type; @push indef_wanted;
@push indef_guess_p; @push indef_possambig; @push indef_owner;
@push indef_cases; @push indef_nspec_at;

Descriptors();

@pull indef_nspec_at; @pull indef_cases;
@pull indef_owner; @pull indef_possambig; @pull indef_guess_p;
@pull indef_wanted; @pull indef_type; @pull indef_mode;

];

-) instead of “Parsing Descriptors” in “Parser.i6t”.[/code][/spoiler]

Hope this helps.

Thanks, that last piece of code seems to work perfectly! (After I add the line “Global hold = 0;”.)

Oops! :blush: That was meant to be a function variable! I seem to have forgotten to declare it at the start of the function! This is what it should be!

[spoiler][code]Include (-

[ Descriptors o x flag cto type n hold;
hold = 0;
ResetDescriptors();
if (wn > num_words) return 0;

for (flag=true : flag :) {
	o = NextWordStopped(); flag = false;

   for (x=1 : x<=LanguageDescriptors-->0 : x=x+4)
		if (o == LanguageDescriptors-->x) {
			flag = true;
			type = LanguageDescriptors-->(x+2);
			if (type ~= DEFART_PK) indef_mode = true;
			indef_possambig = true;
			indef_cases = indef_cases & (LanguageDescriptors-->(x+1));

			if (type == POSSESS_PK) {
				cto = LanguageDescriptors-->(x+3);
				switch (cto) {
				  0: indef_type = indef_type | MY_BIT;
				  1: indef_type = indef_type | THAT_BIT;
				  default:
					indef_owner = PronounValue(cto);
					if (indef_owner == NULL) indef_owner = InformParser;
				}
			}

			if (type == light)  indef_type = indef_type | LIT_BIT;
			if (type == -light) indef_type = indef_type | UNLIT_BIT;
		}

	if (o == OTHER1__WD or OTHER2__WD or OTHER3__WD) {
		indef_mode = 1; flag = 1;
		indef_type = indef_type | OTHER_BIT;
	}
	if (o == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {
		indef_mode = 1; flag = 1; indef_wanted = INDEF_ALL_WANTED;
		if (take_all_rule == 1) take_all_rule = 2;
		indef_type = indef_type | PLURAL_BIT;
	}
	if (allow_plurals) {
		if (NextWordStopped() ~= -1) { wn--; n = TryNumber(wn-1); } else { n=0; wn--; }
		if (n == 1) { indef_mode = 1; flag = 1; }
		if (n > 1) {
			indef_guess_p = 1;
			indef_mode = 1; flag = 1; indef_wanted = n;
			indef_nspec_at = wn-1;
			indef_type = indef_type | PLURAL_BIT;
		}
	}
	if (flag == 1 && NextWordStopped() ~= OF1__WD or OF2__WD or OF3__WD or OF4__WD) {
		hold=1;
		wn--;  ! Skip 'of' after these
	}
}
wn--;
if (hold == 1) {
	hold=0;
	wn--;
}
return 0;

];

[ SafeSkipDescriptors;
@push indef_mode; @push indef_type; @push indef_wanted;
@push indef_guess_p; @push indef_possambig; @push indef_owner;
@push indef_cases; @push indef_nspec_at;

Descriptors();

@pull indef_nspec_at; @pull indef_cases;
@pull indef_owner; @pull indef_possambig; @pull indef_guess_p;
@pull indef_wanted; @pull indef_type; @pull indef_mode;

];

-) instead of “Parsing Descriptors” in “Parser.i6t”.[/code][/spoiler]

Hope this helps.

That’s fantastic! I had the same problem. It wasn’t a game breaker, but it was a bug I probably couldn’t fix on my own.

Unfortunately you seem to have broken ‘get all’, and possibly other things involving ‘all’.

Ouch! Didn’t see that one coming! Here’s the fix!

[spoiler][code]Include (-

[ Descriptors o x flag cto type n hold;
hold = 0;
ResetDescriptors();
if (wn > num_words) return 0;

for (flag=true : flag :) {
	o = NextWordStopped(); flag = false;

   for (x=1 : x<=LanguageDescriptors-->0 : x=x+4)
		if (o == LanguageDescriptors-->x) {
			flag = true;
			type = LanguageDescriptors-->(x+2);
			if (type ~= DEFART_PK) indef_mode = true;
			indef_possambig = true;
			indef_cases = indef_cases & (LanguageDescriptors-->(x+1));

			if (type == POSSESS_PK) {
				cto = LanguageDescriptors-->(x+3);
				switch (cto) {
				  0: indef_type = indef_type | MY_BIT;
				  1: indef_type = indef_type | THAT_BIT;
				  default:
					indef_owner = PronounValue(cto);
					if (indef_owner == NULL) indef_owner = InformParser;
				}
			}

			if (type == light)  indef_type = indef_type | LIT_BIT;
			if (type == -light) indef_type = indef_type | UNLIT_BIT;
		}

	if (o == OTHER1__WD or OTHER2__WD or OTHER3__WD) {
		indef_mode = 1; flag = 1;
		indef_type = indef_type | OTHER_BIT;
	}
	if (o == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {
		indef_mode = 1; flag = 1; indef_wanted = INDEF_ALL_WANTED;
		if (take_all_rule == 1) take_all_rule = 2;
		indef_type = indef_type | PLURAL_BIT;
	}
	if (allow_plurals) {
		if (NextWordStopped() ~= -1) { wn--; n = TryNumber(wn-1); } else { n=0; wn--; }
		if (n == 1) { indef_mode = 1; flag = 1; }
		if (n > 1) {
			indef_guess_p = 1;
			indef_mode = 1; flag = 1; indef_wanted = n;
			indef_nspec_at = wn-1;
			indef_type = indef_type | PLURAL_BIT;
		}
	}
	if (flag == 1 && NextWordStopped() ~= OF1__WD or OF2__WD or OF3__WD or OF4__WD) {
		hold=1;
		wn--;  ! Skip 'of' after these
	}
}
wn--;
if (hold == 1) {
	hold=0;
	if (indef_mode ~= 1) wn--;
}
return 0;

];

[ SafeSkipDescriptors;
@push indef_mode; @push indef_type; @push indef_wanted;
@push indef_guess_p; @push indef_possambig; @push indef_owner;
@push indef_cases; @push indef_nspec_at;

Descriptors();

@pull indef_nspec_at; @pull indef_cases;
@pull indef_owner; @pull indef_possambig; @pull indef_guess_p;
@pull indef_wanted; @pull indef_type; @pull indef_mode;

];

-) instead of “Parsing Descriptors” in “Parser.i6t”.[/code][/spoiler]

Hope this helps.

Just inserted the proposed fix into Kerkerkruip, and it seems to break numbered disambiguation again (though TAKE ALL does work).

Gah! Didn’t check it well enough! This should work!

[spoiler][code]Include (-

[ Descriptors o x flag cto type n hold signal;
hold = 0;
signal = 0;
ResetDescriptors();
if (wn > num_words) return 0;

for (flag=true : flag :) {
	o = NextWordStopped(); flag = false;

   for (x=1 : x<=LanguageDescriptors-->0 : x=x+4)
		if (o == LanguageDescriptors-->x) {
			flag = true;
			type = LanguageDescriptors-->(x+2);
			if (type ~= DEFART_PK) indef_mode = true;
			indef_possambig = true;
			indef_cases = indef_cases & (LanguageDescriptors-->(x+1));

			if (type == POSSESS_PK) {
				cto = LanguageDescriptors-->(x+3);
				switch (cto) {
				  0: indef_type = indef_type | MY_BIT;
				  1: indef_type = indef_type | THAT_BIT;
				  default:
					indef_owner = PronounValue(cto);
					if (indef_owner == NULL) indef_owner = InformParser;
				}
			}

			if (type == light)  indef_type = indef_type | LIT_BIT;
			if (type == -light) indef_type = indef_type | UNLIT_BIT;
		}

	if (o == OTHER1__WD or OTHER2__WD or OTHER3__WD) {
		indef_mode = 1; flag = 1;
		indef_type = indef_type | OTHER_BIT;
		signal = 1;
	}
	if (o == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {
		indef_mode = 1; flag = 1; indef_wanted = INDEF_ALL_WANTED;
		if (take_all_rule == 1) take_all_rule = 2;
		indef_type = indef_type | PLURAL_BIT;
		signal = 1;
	}
	if (allow_plurals) {
		if (NextWordStopped() ~= -1) { wn--; n = TryNumber(wn-1); } else { n=0; wn--; }
		if (n == 1) { indef_mode = 1; flag = 1; }
		if (n > 1) {
			indef_guess_p = 1;
			indef_mode = 1; flag = 1; indef_wanted = n;
			indef_nspec_at = wn-1;
			indef_type = indef_type | PLURAL_BIT;
		}
	}
	if (flag == 1 && NextWordStopped() ~= OF1__WD or OF2__WD or OF3__WD or OF4__WD) {
		hold=1;
		wn--;  ! Skip 'of' after these
	}
}
wn--;
if (hold == 1) {
	hold=0;
	if (signal == 0) {
		wn--;
	}
	else {
		signal = 0;
	}
}
return 0;

];

[ SafeSkipDescriptors;
@push indef_mode; @push indef_type; @push indef_wanted;
@push indef_guess_p; @push indef_possambig; @push indef_owner;
@push indef_cases; @push indef_nspec_at;

Descriptors();

@pull indef_nspec_at; @pull indef_cases;
@pull indef_owner; @pull indef_possambig; @pull indef_guess_p;
@pull indef_wanted; @pull indef_type; @pull indef_mode;

];

-) instead of “Parsing Descriptors” in “Parser.i6t”.[/code][/spoiler]

Hope this helps.

That seems to do it–thanks!

–Erik

Thanks. :slight_smile: