I7 - Answering Someone About Something

Hello All,

I would like to allow the player to use , to talk with an NPC. This works with text after the comma, as it is considered answering it that. However, it is also considered answering it that when a thing, instead of text, is used. I can use indexed text with “After reading a command” to change it, but that won’t allow the player to give the person instructions to do something. I also tried making a new action, but that didn’t seem to work either (it is still considered answering it that).

So, how can I allow a command like Bob, the pen, where “pen” is a thing?

Thanks,
Neil

Section 16.10 (Commands consisting only of nouns) in the Manual suggests that this is non-trivial. For all I know, you’ll have to hack the I6 parser (or Ron Newcomb’s I7 remake of it, see his Original Parser extension).

You’ll probably need to take a look at “Parser Letter H” in “Parser.i6t” of The Inform Template Code. This is where the “answering it that” action is generated.

So I hacked something up that didn’t require delving into Parser Letter H, involving a couple of undocumented features at least one of which is probably a bug, but it’s exhibiting some odd behavior which I wonder if one of the more savvy people can explain. Though I expect the reaction of savvy people will be more like “My god what have you done?” following by burning my code and sprinkling holy water on the computer.

[code]Attic is a room. Bob is a man in Attic. A stuffed owl, a stuffed chair, a curio, and a crate of papers are in Attic.

Instead of answering Bob that when the topic understood matches “[thing]”:
convert to request of Bob to perform the taking action with the noun and nothing.

Unsuccessful attempt by Bob taking:
say “[The person asked] can’t take [the noun] because of [the reason the action failed].”

Persuasion rule: persuasion succeeds.[/code]

OK, so what’s going on here? Well, as Genstein points out, when you match a topic against something with a bracketed text substitution in quotes, the thing that matches the bracketed text substitution gets stored as the noun. Which can be a pain if you want to use the original noun, and in fact is filed as a bug, but here we want to be able to recover that thing so it’s good that it’s stored in the noun. (Incidentally, if you say “[something]” instead of “[thing]” it won’t compile.)

Now, it might seem that the thing to do when this matches is “try Bob taking the noun.” But if we do this, and Bob can’t take the noun, then the unsuccessful attempt rules won’t fire, because as section 12.5 of the documentation explains, unsuccessful attempt rules only fire when someone asks someone to do something. (Also, we’d be bypassing the persuasion rules, which you might not want to do.) But there’s a “convert to request” feature in the Standard Rules, used to change “ask bob for curio” into the equivalent of “bob, give me curio,” which we can use so we’re actually requesting that Bob take the noun. The phrase is defined with two nouns, so we have to include “nothing” as the second noun.

So far so good, maybe. Here’s the problem:

I have no idea why this is happening (as you can see, it works as it should if you include “get”), but disambiguation seems not to work. It may be less trouble to hack Parser Letter H than to get it to work, but I thought I’d toss this out to see if someone had an idea.

(Also, this is my 2000th post woo.)

Hmm, I was trying to come up with a nicer way of doing this that might support disambiguation of a sort and that doesn’t depend on a probable bug, but it isn’t compiling:

Before answering Bob that: let what Bob might want be a list of things; repeat with the item running through visible things: if the topic understood matches "[item]": add the item to what Bob might want; if the number of entries in the what Bob might want is 1: convert to request of Bob to perform the taking action with entry 1 of the what Bob might want and nothing; otherwise if the what Bob might want is not empty: say "Bob says, 'That could mean any of [what Bob might want], and there's no disambiguation, sorry." instead.

The bug report says:

Any reason this doesn’t compile? I suppose the answer is “Because you can’t use temporary values in topic tokens like that” but that would be depressing.

While the noun is set, the object it is set to seems to be undefined.

Try this.

if the topic understood (exactly)? matches the text "[item]"

Hm, can you elaborate? When I include a debug line to print the noun, and I type “Bob, stuffed owl,” it tells me that the noun is the stuffed chair (as you’d expect). And when I just type “stuffed” the disambiguation prompt comes up and I wind up with an “answering it that” action, without the debug line ever firing.

This checks to see if the name of the item is contained completely in the topic understood, which is the reverse of what we want:

Also I’m pretty sure that doing stuff with indexed text would deprive us of the full power of the Understand rules, at least not without doing stuff that would be a lot less effort than convincing someone to hack parser letter H for me. The original approach does let us do more stuff; for instance this:

[code]Attic is a room. Bob is a man in Attic. A stuffed owl, a stuffed chair, a curio, and a crate of papers are in Attic.

Color is a kind of value. The colors are brown, red, gray, blue, yellow, orange, green, purple, black, and white.

A thing has a color. The color of a thing is usually brown. Understand the color property as describing a thing. The owl is gray. The curio is black.

Instead of answering Bob that when the topic understood matches “[thing]”:
say “DEBUG: the noun is [noun].”;
convert to request of Bob to perform the taking action with the noun and nothing.

Unsuccessful attempt by Bob taking:
say “[The person asked] can’t take [the noun] because of [the reason the action failed].”

Persuasion rule: persuasion succeeds.[/code]

allows “Bob, gray” to be parsed as a request for Bob to pick up the (gray) stuffed owl.

Congrats! Seems you’re also the first and only person so far on el forum to hit the 2000 mark!

Basically, the noun is set by an I6 function called “ConsultNounFilterToken(object)”. However, it pretty much sets the noun to object every time without any checking whatsoever. So while noun is set, it will be whatever the last object was that passed through that function.

This is because of this phrase.

To decide if (S - a snippet) matches (T - a topic): (- (SnippetMatches({S}, {T})) -).

It calls “ParseToken” directly which in turn calls “NounDomain”, bypassing “Parser__parse” which is the usual route for standard parsing. When “NounDomain” needs to disambiguate via the “asking which do you mean” activity, it returns the “REPARSE_CODE” after gluing in the extra disambiguation words if necessary, assuming that “ParseToken” will relay this to “Parser__parse” to reparse the command. However, since “ParseToken” was called directly through the aforementioned phrase, the phrase interprets this as a failed match and the rule fails to fire.

Try this.

[spoiler][code]“Test”

Include (-

Global returnvalue;
Global reparsetoken;

-) after “Definitions.i6t”.

Include (-

! This is an actual specified object, and is therefore where a typing error
! is most likely to occur, so we set:

oops_from = wn;

! So, two cases.  Case 1: token not equal to "held" (so, no implicit takes)
! but we may well be dealing with multiple objects

! In either case below we use NounDomain, giving it the token number as
! context, and two places to look: among the actor's possessions, and in the
! present location.  (Note that the order depends on which is likeliest.)

if (token ~= HELD_TOKEN) {
	i = multiple_object-->0;
	#Ifdef DEBUG;
	if (parser_trace >= 3) print "  [Calling NounDomain on location and actor]^";
	#Endif; ! DEBUG
	l = NounDomain(actors_location, actor, token);
	if (l == REPARSE_CODE) {                  ! Reparse after Q&A
		if (reparsetoken) {
			jump TryAgain2;
		}
		else {
			return l;
		}
	}
	if (indef_wanted == INDEF_ALL_WANTED && l == 0 && number_matched == 0)
		l = 1;  ! ReviseMulti if TAKE ALL FROM empty container

	if (token_allows_multiple && ~~multiflag) {
		if (best_etype==MULTI_PE) best_etype=STUCK_PE;
		multiflag = true;
	}
	if (l == 0) {
		if (indef_possambig) {
			ResetDescriptors();
			wn = desc_wn;
			jump TryAgain2;
		}
		if (etype == MULTI_PE or TOOFEW_PE && multiflag) etype = STUCK_PE;
		etype=CantSee();
		jump FailToken;
	} ! Choose best error

	#Ifdef DEBUG;
	if (parser_trace >= 3) {
		if (l > 1) print "  [ND returned ", (the) l, "]^";
		else {
			print "  [ND appended to the multiple object list:^";
			k = multiple_object-->0;
			for (j=i+1 : j<=k : j++)
				print "  Entry ", j, ": ", (The) multiple_object-->j,
					  " (", multiple_object-->j, ")^";
			print "  List now has size ", k, "]^";
		}
	}
	#Endif; ! DEBUG

	if (l == 1) {
		if (~~many_flag) many_flag = true;
		else {                                ! Merge with earlier ones
			k = multiple_object-->0;            ! (with either parity)
			multiple_object-->0 = i;
			for (j=i+1 : j<=k : j++) {
				if (and_parity) MultiAdd(multiple_object-->j);
				else            MultiSub(multiple_object-->j);
			}
			#Ifdef DEBUG;
			if (parser_trace >= 3)
				print "  [Merging ", k-i, " new objects to the ", i, " old ones]^";
			#Endif; ! DEBUG
		}
	}
	else {
		! A single object was indeed found

		if (match_length == 0 && indef_possambig) {
			! So the answer had to be inferred from no textual data,
			! and we know that there was an ambiguity in the descriptor
			! stage (such as a word which could be a pronoun being
			! parsed as an article or possessive).  It's worth having
			! another go.

			ResetDescriptors();
			wn = desc_wn;
			jump TryAgain2;
		}

		if ((token == CREATURE_TOKEN) && (CreatureTest(l) == 0)) {
			etype = ANIMA_PE;
			jump FailToken;
		} !  Animation is required

		if (~~many_flag) {
			single_object = l;
			returnvalue = l;
		}
		else {
			if (and_parity) MultiAdd(l); else MultiSub(l);
			#Ifdef DEBUG;
			if (parser_trace >= 3) print "  [Combining ", (the) l, " with list]^";
			#Endif; ! DEBUG
		}
	}
}

else {

! Case 2: token is "held" (which fortunately can't take multiple objects)
! and may generate an implicit take

	l = NounDomain(actor,actors_location,token);       ! Same as above...
	if (l == REPARSE_CODE) {
		if (reparsetoken) {
			jump TryAgain2;
		}
		else {
			return l;
		}
	}
	if (l == 0) {
		if (indef_possambig) {
			ResetDescriptors();
			wn = desc_wn;
			jump TryAgain2;
		}
		etype = CantSee(); jump FailToken;            ! Choose best error
	}

	! ...until it produces something not held by the actor.  Then an implicit
	! take must be tried.  If this is already happening anyway, things are too
	! confused and we have to give up (but saving the oops marker so as to get
	! it on the right word afterwards).
	! The point of this last rule is that a sequence like
	!
	!     > read newspaper
	!     (taking the newspaper first)
	!     The dwarf unexpectedly prevents you from taking the newspaper!
	!
	! should not be allowed to go into an infinite repeat - read becomes
	! take then read, but take has no effect, so read becomes take then read...
	! Anyway for now all we do is record the number of the object to take.

	o = parent(l);
	if (o ~= actor) {
		#Ifdef DEBUG;
		if (parser_trace >= 3) print "  [Allowing object ", (the) l, " for now]^";
		#Endif; ! DEBUG
	}
	single_object = l;
	returnvalue = l;
} ! end of if (token ~= HELD_TOKEN) else

! The following moves the word marker to just past the named object...

wn = oops_from + match_length;

-) instead of “Parse Token Letter D” in “Parser.i6t”.

Include (-

[ SnippetMatchesWithSetNoun snippet topic_gpr rv;
wn=1;
if (topic_gpr == 0) rfalse;
if (metaclass(topic_gpr) == Routine) {
(+ disambiguation check +) = true;
rv = (topic_gpr)(snippet/100, snippet%100);
(+ disambiguation check +) = false;
if (rv ~= GPR_FAIL) {
noun = rv;
rtrue;
}
else if (returnvalue ~= GPR_FAIL) {
noun = returnvalue;
rtrue;
}
rfalse;
}
RunTimeProblem(RTP_BADTOPIC);
rfalse;
];

-).

To reparse the command: (- reparsetoken = true; -).

To decide if (S - a snippet) matches (T - a topic) while setting the noun: (- (SnippetMatchesWithSetNoun({S}, {T})) -).

Disambiguation check is a truth state that varies. Disambiguation check is false.

The first command is an indexed text that varies. The last command is an indexed text that varies.

After asking which do you mean when disambiguation check is true (this is the advanced disambiguation processing when asking which do you mean rule): now the first command is the player’s command.

After reading a command when disambiguation check is true (this is the advanced disambiguation processing when reading a command rule):
now the last command is the player’s command;
now disambiguation check is false;
now the last command is “[unpunctuated word number 3 in the last command] [unpunctuated word number 2 in the last command]”;
change the text of the player’s command to last command;
reparse the command.

A persuasion rule: persuasion succeeds.

Unsuccessful attempt by Bob taking: say “[The person asked] can’t take [the noun] because of the [reason the action failed].”.

Check answering Bob that when the topic understood matches “[thing]” while setting the noun (this is the convert to taking where possible rule): try bob taking the noun instead.

Attic is a room. Bob is a man in Attic. A stuffed owl, a stuffed chair, a curio, and a crate of papers are in Attic.

Test me with “bob, stuffed owl / bob, drop owl / bob, stuffed / owl / bob, stuffed / chair / bob, get stuffed owl / bob, drop owl / bob, stuffed / owl”.[/code][/spoiler]

It fixes the two issues mentioned above.

Hope this helps.

Thanks! I feel slightly abashed about this, actually.

This looks cool, and thanks for the explanation, but it isn’t actually working for me; all the “bob, thing” commands are getting processed as answering it that. I don’t know enough (any) I6 to figure out why, though. (It just took me a few minutes to figure out where disambiguation check was getting set to true.)

My attempts to use Ron’s I7 parser to hack Parser Letter H were unsuccessful; I tried adding something about if the usages of the verb include being a noun at what seemed like a strategic place, and not only did it not parse the “Bob, thing” commands properly, it somehow converted you-can-also-see paragraph to “You can also see five men here.” I have no idea how trying to tweak the parser messed up the looking action.

Curious! This is the output I get.

[spoiler][code]Test
An Interactive Fiction
Release 1 / Serial number 130217 / Inform 7 build 6G60 (I6/v6.32 lib 6/12N) SD

Attic
You can see Bob, a stuffed owl, a stuffed chair, a curio and a crate of papers here.

test me
(Testing.)

[1] bob, stuffed owl
Bob picks up the stuffed owl.

[2] bob, drop owl
Bob puts down the stuffed owl.

[3] bob, stuffed
Which do you mean, the stuffed owl or the stuffed chair?

[4] owl
Bob picks up the stuffed owl.

[5] bob, stuffed
Bob picks up the stuffed chair.

[6] chair
That’s not a verb I recognise.

[7] bob, get stuffed owl
Bob can’t take the stuffed owl because of the can’t take what’s already taken rule.

[8] bob, drop owl
Bob puts down the stuffed owl.

[9] bob, stuffed
Bob picks up the stuffed owl.

[10] owl
That’s not a verb I recognise.

[/code][/spoiler]

You might want to try out this version, which has extra I6 debugging text lines.

[spoiler][code]“Test”

Include (-

Global returnvalue;
Global reparsetoken;

-) after “Definitions.i6t”.

Include (-

! This is an actual specified object, and is therefore where a typing error
! is most likely to occur, so we set:

oops_from = wn;

! So, two cases.  Case 1: token not equal to "held" (so, no implicit takes)
! but we may well be dealing with multiple objects

! In either case below we use NounDomain, giving it the token number as
! context, and two places to look: among the actor's possessions, and in the
! present location.  (Note that the order depends on which is likeliest.)

if (token ~= HELD_TOKEN) {
	i = multiple_object-->0;
	#Ifdef DEBUG;
	if (parser_trace >= 3) print "  [Calling NounDomain on location and actor]^";
	#Endif; ! DEBUG
	print "[TEST - BEFORE NOUN DOMAIN - ", (the) noun, "]^";
	l = NounDomain(actors_location, actor, token);
	print "[TEST - AFTER NOUN DOMAIN - ", (the) noun, "]^";
	print "[TEST - NOUN DOMAIN RETURN - ", (the) l, "]^";
	if (l == REPARSE_CODE) {                  ! Reparse after Q&A
		if (reparsetoken) {
			jump TryAgain2;
		}
		else {
			return l;
		}
	}
	if (indef_wanted == INDEF_ALL_WANTED && l == 0 && number_matched == 0)
		l = 1;  ! ReviseMulti if TAKE ALL FROM empty container

	if (token_allows_multiple && ~~multiflag) {
		if (best_etype==MULTI_PE) best_etype=STUCK_PE;
		multiflag = true;
	}
	if (l == 0) {
		if (indef_possambig) {
			ResetDescriptors();
			wn = desc_wn;
			jump TryAgain2;
		}
		if (etype == MULTI_PE or TOOFEW_PE && multiflag) etype = STUCK_PE;
		etype=CantSee();
		jump FailToken;
	} ! Choose best error

	#Ifdef DEBUG;
	if (parser_trace >= 3) {
		if (l > 1) print "  [ND returned ", (the) l, "]^";
		else {
			print "  [ND appended to the multiple object list:^";
			k = multiple_object-->0;
			for (j=i+1 : j<=k : j++)
				print "  Entry ", j, ": ", (The) multiple_object-->j,
					  " (", multiple_object-->j, ")^";
			print "  List now has size ", k, "]^";
		}
	}
	#Endif; ! DEBUG

	if (l == 1) {
		if (~~many_flag) many_flag = true;
		else {                                ! Merge with earlier ones
			k = multiple_object-->0;            ! (with either parity)
			multiple_object-->0 = i;
			for (j=i+1 : j<=k : j++) {
				if (and_parity) MultiAdd(multiple_object-->j);
				else            MultiSub(multiple_object-->j);
			}
			#Ifdef DEBUG;
			if (parser_trace >= 3)
				print "  [Merging ", k-i, " new objects to the ", i, " old ones]^";
			#Endif; ! DEBUG
		}
	}
	else {
		! A single object was indeed found

		if (match_length == 0 && indef_possambig) {
			! So the answer had to be inferred from no textual data,
			! and we know that there was an ambiguity in the descriptor
			! stage (such as a word which could be a pronoun being
			! parsed as an article or possessive).  It's worth having
			! another go.

			ResetDescriptors();
			wn = desc_wn;
			jump TryAgain2;
		}

		if ((token == CREATURE_TOKEN) && (CreatureTest(l) == 0)) {
			etype = ANIMA_PE;
			jump FailToken;
		} !  Animation is required

		if (~~many_flag) {
			print "[TEST - BEFORE SINGLE - ", (the) single_object, "]^";
			single_object = l;
			returnvalue = l;
			print "[TEST - AFTER SINGLE - ", (the) single_object, "]^";
		}
		else {
			if (and_parity) MultiAdd(l); else MultiSub(l);
			#Ifdef DEBUG;
			if (parser_trace >= 3) print "  [Combining ", (the) l, " with list]^";
			#Endif; ! DEBUG
		}
	}
}

else {

! Case 2: token is "held" (which fortunately can't take multiple objects)
! and may generate an implicit take

	l = NounDomain(actor,actors_location,token);       ! Same as above...
	if (l == REPARSE_CODE) {
		if (reparsetoken) {
			jump TryAgain2;
		}
		else {
			return l;
		}
	}
	if (l == 0) {
		if (indef_possambig) {
			ResetDescriptors();
			wn = desc_wn;
			jump TryAgain2;
		}
		etype = CantSee(); jump FailToken;            ! Choose best error
	}

	! ...until it produces something not held by the actor.  Then an implicit
	! take must be tried.  If this is already happening anyway, things are too
	! confused and we have to give up (but saving the oops marker so as to get
	! it on the right word afterwards).
	! The point of this last rule is that a sequence like
	!
	!     > read newspaper
	!     (taking the newspaper first)
	!     The dwarf unexpectedly prevents you from taking the newspaper!
	!
	! should not be allowed to go into an infinite repeat - read becomes
	! take then read, but take has no effect, so read becomes take then read...
	! Anyway for now all we do is record the number of the object to take.

	o = parent(l);
	if (o ~= actor) {
		#Ifdef DEBUG;
		if (parser_trace >= 3) print "  [Allowing object ", (the) l, " for now]^";
		#Endif; ! DEBUG
	}
	single_object = l;
	returnvalue = l;
} ! end of if (token ~= HELD_TOKEN) else

! The following moves the word marker to just past the named object...

wn = oops_from + match_length;

-) instead of “Parse Token Letter D” in “Parser.i6t”.

Include (-

[ SnippetMatchesWithSetNoun snippet topic_gpr rv;
wn=1;
if (topic_gpr == 0) rfalse;
if (metaclass(topic_gpr) == Routine) {
(+ disambiguation check +) = true;
print "[TEST - BEFORE SNIPPET - ", (the) noun, “]^”;
rv = (topic_gpr)(snippet/100, snippet%100);
!print "[TEST - AFTER SNIPPET - ", (the) noun, “]^”;
(+ disambiguation check +) = false;
print "[TEST - SNIPPET RETURN 1 - ", (the) rv, “]^”;
print "[TEST - SNIPPET RETURN 2 - ", (the) returnvalue, “]^”;
if (rv ~= GPR_FAIL) {
noun = rv;
rtrue;
}
else if (returnvalue ~= GPR_FAIL) {
noun = returnvalue;
rtrue;
}
rfalse;
}
RunTimeProblem(RTP_BADTOPIC);
rfalse;
];

-).

To reparse the command: (- reparsetoken = true; -).

To decide if (S - a snippet) matches (T - a topic) while setting the noun: (- (SnippetMatchesWithSetNoun({S}, {T})) -).

Disambiguation check is a truth state that varies. Disambiguation check is false.

The first command is an indexed text that varies. The last command is an indexed text that varies.

After asking which do you mean when disambiguation check is true (this is the advanced disambiguation processing when asking which do you mean rule): now the first command is the player’s command.

After reading a command when disambiguation check is true (this is the advanced disambiguation processing when reading a command rule):
now the last command is the player’s command;
now disambiguation check is false;
now the last command is “[unpunctuated word number 3 in the last command] [unpunctuated word number 2 in the last command]”;
change the text of the player’s command to last command;
reparse the command.

A persuasion rule: persuasion succeeds.

Unsuccessful attempt by Bob taking: say “[The person asked] can’t take [the noun] because of the [reason the action failed].”.

Check answering Bob that when the topic understood matches “[thing]” while setting the noun (this is the convert to taking where possible rule): try bob taking the noun instead.

Attic is a room. Bob is a man in Attic. A stuffed owl, a stuffed chair, a curio, and a crate of papers are in Attic.

Test me with “bob, stuffed owl / bob, drop owl / bob, stuffed / owl / bob, stuffed / chair / bob, get stuffed owl / bob, drop owl / bob, stuffed / owl”.[/code][/spoiler]

With this version, I get this output!

[spoiler][code]

Test
An Interactive Fiction
Release 1 / Serial number 130217 / Inform 7 build 6G60 (I6/v6.32 lib 6/12N) SD

Attic
You can see Bob, a stuffed owl, a stuffed chair, a curio and a crate of papers here.

test me
(Testing.)

[1] bob, stuffed owl
[TEST - BEFORE SNIPPET - Bob]
[TEST - BEFORE NOUN DOMAIN - Bob]
[TEST - AFTER NOUN DOMAIN - the stuffed chair]
[TEST - NOUN DOMAIN RETURN - the stuffed owl]
[TEST - BEFORE SINGLE - nothing]
[TEST - AFTER SINGLE - the stuffed owl]
[TEST - SNIPPET RETURN 1 - the stuffed owl]
[TEST - SNIPPET RETURN 2 - the stuffed owl]

Bob picks up the stuffed owl.

[2] bob, drop owl
[TEST - BEFORE NOUN DOMAIN - nothing]
[TEST - AFTER NOUN DOMAIN - nothing]
[TEST - NOUN DOMAIN RETURN - the stuffed owl]
[TEST - BEFORE SINGLE - nothing]
[TEST - AFTER SINGLE - the stuffed owl]

Bob puts down the stuffed owl.

[3] bob, stuffed
[TEST - BEFORE SNIPPET - Bob]
[TEST - BEFORE NOUN DOMAIN - Bob]

Which do you mean, the stuffed owl or the stuffed chair?

[4] owl
[TEST - AFTER NOUN DOMAIN - the stuffed chair]
[TEST - NOUN DOMAIN RETURN - <routine 10000>]
[TEST - BEFORE NOUN DOMAIN - the stuffed chair]
[TEST - AFTER NOUN DOMAIN - the stuffed chair]
[TEST - NOUN DOMAIN RETURN - the stuffed owl]
[TEST - BEFORE SINGLE - nothing]
[TEST - AFTER SINGLE - the stuffed owl]
[TEST - SNIPPET RETURN 1 - <illegal object number -1>]
[TEST - SNIPPET RETURN 2 - the stuffed owl]

Bob picks up the stuffed owl.

[5] bob, stuffed
[TEST - BEFORE SNIPPET - Bob]
[TEST - BEFORE NOUN DOMAIN - Bob]

[TEST - AFTER NOUN DOMAIN - the stuffed chair]
[TEST - NOUN DOMAIN RETURN - the stuffed chair]
[TEST - BEFORE SINGLE - nothing]
[TEST - AFTER SINGLE - the stuffed chair]
[TEST - SNIPPET RETURN 1 - the stuffed chair]
[TEST - SNIPPET RETURN 2 - the stuffed chair]

Bob picks up the stuffed chair.

[6] chair
That’s not a verb I recognise.

[7] bob, get stuffed owl
[TEST - BEFORE NOUN DOMAIN - nothing]
[TEST - AFTER NOUN DOMAIN - nothing]
[TEST - NOUN DOMAIN RETURN - the stuffed owl]
[TEST - BEFORE SINGLE - nothing]
[TEST - AFTER SINGLE - the stuffed owl]

Bob can’t take the stuffed owl because of the can’t take what’s already taken rule.

[8] bob, drop owl
[TEST - BEFORE NOUN DOMAIN - nothing]
[TEST - AFTER NOUN DOMAIN - nothing]
[TEST - NOUN DOMAIN RETURN - the stuffed owl]
[TEST - BEFORE SINGLE - nothing]
[TEST - AFTER SINGLE - the stuffed owl]

Bob puts down the stuffed owl.

[9] bob, stuffed
[TEST - BEFORE SNIPPET - Bob]
[TEST - BEFORE NOUN DOMAIN - Bob]

[TEST - AFTER NOUN DOMAIN - the stuffed chair]
[TEST - NOUN DOMAIN RETURN - the stuffed owl]
[TEST - BEFORE SINGLE - nothing]
[TEST - AFTER SINGLE - the stuffed owl]
[TEST - SNIPPET RETURN 1 - the stuffed owl]
[TEST - SNIPPET RETURN 2 - the stuffed owl]

Bob picks up the stuffed owl.

[10] owl
That’s not a verb I recognise.

[/code][/spoiler]

The parser works in mysterious ways!

Hi,

		Thanks for the suggestions. I am I6 illiterate, so hacking it isn't an option. I did, however, read the relevant parts in Ron Newcomb's Inform Parser. It seems like answering it that is redirected from a parser error rule. With this in mind, I thought of this:
		
		Code:
		
		Asked actor is an indexed text variable.

After reading a command when the player’s command includes “[Stoolie]”:
let t be indexed text;
let t be the player’s command;
if t matches the regular expression “.,.” begin;
replace the regular expression “\p” in t with “”;
now asked actor is word number one in t;
replace word number one in t with “”;
change the text of the player’s command to t;
end if.

After doing this, we are left with either some text, a thing, or an action. Each one can be handled with either a before rule (if it’s a thing or an action) or changing the rule for printing a parser error when the latest parser error is the noun did not make sense in that context error (for text-only).

The posts on this subject have been very instructive.

Neil

before ruoned

Here’s my output with the debug code:

Curiouser and curiouser!

That is really bizarre! Where did “Bob is thinking about (something)” come from? I don’t recognise it as a standard message.

You could try this.

[spoiler][code]“Test”

Include (-

Global returnvalue;
Global reparsetoken;

-) after “Definitions.i6t”.

Include (-

! This is an actual specified object, and is therefore where a typing error
! is most likely to occur, so we set:

oops_from = wn;

! So, two cases.  Case 1: token not equal to "held" (so, no implicit takes)
! but we may well be dealing with multiple objects

! In either case below we use NounDomain, giving it the token number as
! context, and two places to look: among the actor's possessions, and in the
! present location.  (Note that the order depends on which is likeliest.)

if (token ~= HELD_TOKEN) {
	i = multiple_object-->0;
	#Ifdef DEBUG;
	if (parser_trace >= 3) print "  [Calling NounDomain on location and actor]^";
	#Endif; ! DEBUG
	print "[TEST - BEFORE NOUN DOMAIN - ", (the) noun, "]^";
	l = NounDomain(actors_location, actor, token);
	print "[TEST - AFTER NOUN DOMAIN - ", (the) noun, "]^";
	print "[TEST - NOUN DOMAIN RETURN - ", (the) l, "]^";
	if (l == REPARSE_CODE) {                  ! Reparse after Q&A
		if (reparsetoken) {
			jump TryAgain2;
		}
		else {
			return l;
		}
	}
	if (indef_wanted == INDEF_ALL_WANTED && l == 0 && number_matched == 0)
		l = 1;  ! ReviseMulti if TAKE ALL FROM empty container

	if (token_allows_multiple && ~~multiflag) {
		if (best_etype==MULTI_PE) best_etype=STUCK_PE;
		multiflag = true;
	}
	if (l == 0) {
		if (indef_possambig) {
			ResetDescriptors();
			wn = desc_wn;
			jump TryAgain2;
		}
		if (etype == MULTI_PE or TOOFEW_PE && multiflag) etype = STUCK_PE;
		etype=CantSee();
		jump FailToken;
	} ! Choose best error

	#Ifdef DEBUG;
	if (parser_trace >= 3) {
		if (l > 1) print "  [ND returned ", (the) l, "]^";
		else {
			print "  [ND appended to the multiple object list:^";
			k = multiple_object-->0;
			for (j=i+1 : j<=k : j++)
				print "  Entry ", j, ": ", (The) multiple_object-->j,
					  " (", multiple_object-->j, ")^";
			print "  List now has size ", k, "]^";
		}
	}
	#Endif; ! DEBUG

	if (l == 1) {
		if (~~many_flag) many_flag = true;
		else {                                ! Merge with earlier ones
			k = multiple_object-->0;            ! (with either parity)
			multiple_object-->0 = i;
			for (j=i+1 : j<=k : j++) {
				if (and_parity) MultiAdd(multiple_object-->j);
				else            MultiSub(multiple_object-->j);
			}
			#Ifdef DEBUG;
			if (parser_trace >= 3)
				print "  [Merging ", k-i, " new objects to the ", i, " old ones]^";
			#Endif; ! DEBUG
		}
	}
	else {
		! A single object was indeed found

		if (match_length == 0 && indef_possambig) {
			! So the answer had to be inferred from no textual data,
			! and we know that there was an ambiguity in the descriptor
			! stage (such as a word which could be a pronoun being
			! parsed as an article or possessive).  It's worth having
			! another go.

			ResetDescriptors();
			wn = desc_wn;
			jump TryAgain2;
		}

		if ((token == CREATURE_TOKEN) && (CreatureTest(l) == 0)) {
			etype = ANIMA_PE;
			jump FailToken;
		} !  Animation is required

		if (~~many_flag) {
			print "[TEST - BEFORE SINGLE - ", (the) single_object, "]^";
			single_object = l;
			returnvalue = l;
			print "[TEST - AFTER SINGLE - ", (the) single_object, "]^";
		}
		else {
			if (and_parity) MultiAdd(l); else MultiSub(l);
			#Ifdef DEBUG;
			if (parser_trace >= 3) print "  [Combining ", (the) l, " with list]^";
			#Endif; ! DEBUG
		}
	}
}

else {

! Case 2: token is "held" (which fortunately can't take multiple objects)
! and may generate an implicit take

	l = NounDomain(actor,actors_location,token);       ! Same as above...
	if (l == REPARSE_CODE) {
		if (reparsetoken) {
			jump TryAgain2;
		}
		else {
			return l;
		}
	}
	if (l == 0) {
		if (indef_possambig) {
			ResetDescriptors();
			wn = desc_wn;
			jump TryAgain2;
		}
		etype = CantSee(); jump FailToken;            ! Choose best error
	}

	! ...until it produces something not held by the actor.  Then an implicit
	! take must be tried.  If this is already happening anyway, things are too
	! confused and we have to give up (but saving the oops marker so as to get
	! it on the right word afterwards).
	! The point of this last rule is that a sequence like
	!
	!     > read newspaper
	!     (taking the newspaper first)
	!     The dwarf unexpectedly prevents you from taking the newspaper!
	!
	! should not be allowed to go into an infinite repeat - read becomes
	! take then read, but take has no effect, so read becomes take then read...
	! Anyway for now all we do is record the number of the object to take.

	o = parent(l);
	if (o ~= actor) {
		#Ifdef DEBUG;
		if (parser_trace >= 3) print "  [Allowing object ", (the) l, " for now]^";
		#Endif; ! DEBUG
	}
	single_object = l;
	returnvalue = l;
} ! end of if (token ~= HELD_TOKEN) else

! The following moves the word marker to just past the named object...

wn = oops_from + match_length;

-) instead of “Parse Token Letter D” in “Parser.i6t”.

Include (-

[ SnippetMatchesWithSetNoun snippet topic_gpr rv;
wn=1;
if (topic_gpr == 0) rfalse;
if (metaclass(topic_gpr) == Routine) {
(+ disambiguation check +) = true;
print "[TEST - BEFORE SNIPPET - ", (the) noun, “]^”;
rv = (topic_gpr)(snippet/100, snippet%100);
!print "[TEST - AFTER SNIPPET - ", (the) noun, “]^”;
(+ disambiguation check +) = false;
print "[TEST - SNIPPET RETURN 1 - ", (the) rv, “]^”;
print "[TEST - SNIPPET RETURN 2 - ", (the) returnvalue, “]^”;
if (rv ~= GPR_FAIL) {
noun = rv;
rtrue;
}
else if (returnvalue ~= GPR_FAIL) {
noun = returnvalue;
rtrue;
}
rfalse;
}
RunTimeProblem(RTP_BADTOPIC);
rfalse;
];

-).

To reparse the command: (- reparsetoken = true; -).

To decide if (S - a snippet) matches (T - a topic) while setting the noun: (- (SnippetMatchesWithSetNoun({S}, {T})) -).

Disambiguation check is a truth state that varies. Disambiguation check is false.

The first command is an indexed text that varies. The last command is an indexed text that varies.

After asking which do you mean when disambiguation check is true (this is the advanced disambiguation processing when asking which do you mean rule): now the first command is the player’s command.

After reading a command when disambiguation check is true (this is the advanced disambiguation processing when reading a command rule):
now the last command is the player’s command;
now disambiguation check is false;
now the last command is “[unpunctuated word number 3 in the last command] [unpunctuated word number 2 in the last command]”;
change the text of the player’s command to last command;
reparse the command.

A persuasion rule: persuasion succeeds.

Unsuccessful attempt by Bob taking: say “[The person asked] can’t take [the noun] because of the [reason the action failed].”.

Check answering Bob that when the topic understood matches “[thing]” while setting the noun (this is the convert to taking where possible rule): convert to request of Bob to perform the taking action with the noun and nothing instead.

Attic is a room. Bob is a man in Attic. A stuffed owl, a stuffed chair, a curio, and a crate of papers are in Attic.

Test me with “bob, stuffed owl / bob, drop owl / bob, stuffed / owl / bob, stuffed / chair / bob, get stuffed owl / bob, drop owl / bob, stuffed / owl”.

Test trace with “trace 6 / test me / trace 0”.

Test rules with “rules on / test me / rules off”.

Test actions with “actions on / test me / actions off”.[/code][/spoiler]

Here, I added extra test commands and changed “try Bob taking the noun” to “convert to request of Bob to perform the taking action with the noun and nothing”. This has no effect on the output for me, but it might for you. If all else fails try “test trace/rules/action”. It might show what the problem is.

Well, that’s in my code, which means that if it isn’t in your code I must have left some of mine in when I copy-pasted.

…yup, that’s it. I had an “Instead of answering” rule that I forgot to comment out. Carry on. This is the peril of keeping all your test code in one giant product full of commented-out layers. How embarrassing, and it provides some support for your stance against “Instead” rules. (Though it does tell us that maybe your Check rule should be a Before rule or even a rule that runs before the Before rules, because we want to be able to write rules that apply to answering proper.)

Now that I’ve started it in an absolutely fresh project, disambiguation still isn’t working, but I’ll edit this post after double-checking I’ve pasted properly (and maybe try the convert to request, because I’m not quite sure why the unsuccessful attempt rules are working). EDIT: Wait, I’m already sure the answer is that I messed up some tab stops. EDIT TWO: OK, the first disambiguation is working, and the other ones just default to the thing Bob hasn’t already picked up, so that’s good.