Shooting at and shooting with

Now here is a nice parser puzzle for you all. It is a modified version of Technological Terror from the official documentation.

What I would like is SHOOT GUN to be understood as “shoot with the gun at something” when I am holding the gun, but to be understood as “shoot at the gun with something” if I am not. Basically I want both SHOOT GUN and SHOOT RIFLE to be a sensible command to defend ourselves against the killer robot in the example.

No amount of “Does the player mean” or “Rule for supplying a missing second noun” or new grammar lines seems to get this right. Does anyone have an idea?

[code]“Technological Terror mk.2”

A pistol is a kind of thing.
The Decomposition Ray Gun is a pistol carried by the player.

[First we need to define our shooting action:]

Shooting it with is an action applying to one thing and one carried thing.

Check shooting something with something:
if the player is not carrying a pistol, say “You are pathetically unarmed!” instead;
if the second noun is not a pistol, say “[The second noun] does not fire.” instead;
if the noun is the second noun, say “Nice trick if you can do it!” instead;
if the noun is the player, say “That would be disastrous!” instead.

[Next, some grammar to allow the player to use this action:]

Understand “shoot [something] with [something]” as shooting it with.
Understand “shoot [something] at [something]” as shooting it with (with nouns reversed).

Carry out shooting something with something:
say “ZAP! [The noun] twinkles out of existence! [if something is part of the noun][The list of things which are part of the noun] clatter to the ground! [end if][paragraph break]”;
now every thing which is part of the noun is in the location;
now the noun is nowhere.

The Deathbot Assembly Line is a room. “Here is the heart of the whole operation, where your opponents are assembled fresh from scrap metal and bits of old car.” The dangerous robot is a person in the Assembly Line. “One dangerous robot looks ready to take you on[if the robot carries the laser rifle] with its laser rifle[end if]!” A robotic head, a drill arm, a needle arm, a crushing leg and a kicking leg are parts of the dangerous robot.

The Laser Rifle is a pistol carried by the dangerous robot.

Instead of examining something when something is part of the noun:
say “[The noun] consists of [a list of things which are part of the noun]. [if the noun carries something][The noun] carries [a list of things carried by the noun].[end if]”

Test me with “x robot / shoot gun / shoot rifle”.[/code]

You could try defining a new single-noun action whose only job is to hand off to the appropriate double-noun action.

[code]Ambiguously shooting is an action applying to one thing. Understand “shoot [something]” as ambiguously shooting.
Instead of ambiguously shooting when the player carries the noun:
if the player can see the dangerous robot:
let target be a random part of the dangerous robot; [you’ll want to design your own targeting code here]
say “(at [the target])[command clarification break]”;
try shooting the target with the noun;
otherwise:
say “You don’t have a target.”

Instead of ambiguously shooting when the player does not carry the noun:
if the player carries a pistol (called the gun):
try shooting the noun with the gun;
otherwise:
say “You are pathetically unarmed.”[/code]

I haven’t tested this, so it might not work. But generally, if rules for supplying a noun/second noun aren’t working right, it’s often easiest to bypass them by defining a separate action and having it call the action you want. Although (unlike a supplying the second noun rule that fails) it counts as a completed action, so you might want to make sure that it isn’t calling Every Turn rules you don’t want. (For instance, if you have a rule that makes the robot kill the player if they don’t shoot it right away, and you set things up so the response to SHOOT RIFLE is something along the lines of “You’ll have to say what to shoot it at,” make sure that you don’t proceed to run the rule to kill the player after telling them to clarify their command. That would be rude.)

Thanks a lot! With some minor modifications, I get this:

The trouble is that “targeting code”. Automatically choosing a pistol we are carrying is fine for “shoot rifle”, as long as I carry one and only one, but I’m afraid I really want the “shoot gun” to work as an ordinary command, having the parser ask “What do you want to shoot the Decomposition Ray Gun at?” and use the player’s reply. Is there any way this could possibly be accomplished? Sorry if I keep moving the goalposts.

[code]Ambiguously shooting is an action applying to one thing. Understand “shoot [something]” as ambiguously shooting.

Instead of ambiguously shooting something when the player carries the noun:
if the player can see the dangerous robot:
let target be a random thing which is part of the dangerous robot; [you’ll want to design your own targeting code here]
say “(at [the target])[command clarification break]”;
try shooting target with the noun;
otherwise:
say “You don’t have a target.”

Instead of ambiguously shooting something when the player does not carry the noun:
if the player carries a pistol (called the gun):
say “(with [the gun])[command clarification break]”;
try shooting the noun with the gun;
otherwise:
say “You are pathetically unarmed.”[/code]

I’ve been experimenting with

after reading a command when the player's command matches "shoot [something]":
	[conditionals go here]
	replace the player's command with "<desired command>".

but I haven’t found a convenient way to access the thing understood so that I can determine if it’s a pistol, reference it in the replaced command, etc.

From a quick look at the I6 code, it seems that ParseToken is called and the text is actually matched to an object, but this object isn’t surfaced to I7.

Well, thanks for having a go at it. Hopefully I won’t spoil the game for anyone if I mention that this is about a bug that was reported in Counterfeit Monkey. I think the reported issue can be considered solved for now, but it is one of those spots where you create two new problems for every one you fix.

Hmmkay, well, this is very fiddly stuff, but I think I have it. (Also there was some stupid bugs I created by forgetting whether the underlying action was “shooting it at” or “shooting it with,” but I think I bopped them.)

There are a couple of changes spread throughout, so I’ll explain them…

First of all, if Inform hits the Understand line for “shoot [something]” at all, it seems to want the supplying a second noun activity to run, which short-circuits the “What do you want to shoot?” question. So we need to restrict that Understand line so it doesn’t run when we want to be shooting the noun at something. Fortunately, that’s going to to be true only when the noun is a pistol carried by the player, so we can make a new adjective for everything that isn’t a carried pistol and restrict the shortened Understand line to those. (I don’t know if this will be bad for performance.)

Then we want to make sure that the shortened Understand line ask “What do you want to shoot the gun at?” rather than “What do you want to shoot the gun with?”–which seems to require that the shoot at Understand line comes first in the source code, so Inform finds it as the first possible match. So I switched those.

Finally, Inform is overenthusiastic about choosing the only thing that you’re holding if you’re only holding one thing. That means that, if the player isn’t carrying anything else, the game will interpret “shoot gun” as shooting the gun at the gun without asking the player for a second noun. I took care of this by giving the player a rock. In Counterfeit Monkey the inventory should be choc-a-bloc anyway so this shouldn’t be a problem.

Post-finally, I’m not sure there needs to be quite so many conditions on my Rule for supplying a second noun, but it shouldn’t hurt, since whenever those conditions aren’t fulfilled the noun isn’t unwielded and the supplying a second noun activity shouldn’t be running anyway.

Thus:

[code]“Technological Terror mk.2”

A pistol is a kind of thing.
The Decomposition Ray Gun is a pistol carried by the player. The player carries a rock.

[First we need to define our shooting action:]

Shooting it with is an action applying to one thing and one carried thing.

Check shooting something with something:
if the player is not carrying a pistol, say “You are pathetically unarmed!” instead;
if the second noun is not a pistol, say “[The second noun] does not fire.” instead;
if the noun is the second noun, say “Nice trick if you can do it!” instead;
if the noun is the player, say “That would be disastrous!” instead.

[Next, some grammar to allow the player to use this action:]

Understand “shoot [something] at [something]” as shooting it with (with nouns reversed).

Understand “shoot [something] with [something]” as shooting it with.

Carry out shooting something with something:
say “ZAP! [The noun] twinkles out of existence! [if something is part of the noun][The list of things which are part of the noun] clatter to the ground! [end if][paragraph break]”;
now every thing which is part of the noun is in the location;
now the noun is nowhere.

The Deathbot Assembly Line is a room. “Here is the heart of the whole operation, where your opponents are assembled fresh from scrap metal and bits of old car.” The dangerous robot is a person in the Assembly Line. “One dangerous robot looks ready to take you on[if the robot carries the laser rifle] with its laser rifle[end if]!” A robotic head, a drill arm, a needle arm, a crushing leg and a kicking leg are parts of the dangerous robot.

The Laser Rifle is a pistol carried by the dangerous robot.

Instead of examining something when something is part of the noun:
say “[The noun] consists of [a list of things which are part of the noun]. [if the noun carries something][The noun] carries [a list of things carried by the noun].[end if]”

Test me with “x robot / shoot gun / shoot rifle”.

Definition: a thing is unwielded if it is not a carried pistol.

Understand “shoot [something unwielded]” as shooting it with.

Rule for supplying a missing second noun while shooting when the player carries a pistol (called the firearm) and the noun is not the firearm:
say “(with [the firearm])[command clarification break]”;
now the second noun is the firearm. [/code]

Also, if it’s the scene I’m thinking of, man I hated that scene.

Ok, well I hacked something together that exposes the thing understood during snippet matching.

I don’t recommend that you use this technique in Counterfeit Monkey, but I’m posting it in case people find it interesting.

A lot of the I6 stuff is unchanged from the template and there because I couldn’t get I7 to replace the single routine that I wanted to replace. See the comments for details.

(matt w has posted something more moderate than this in the interim.)

"Technological Terror mk.2"

A pistol is a kind of thing.
The Decomposition Ray Gun is a pistol carried by the player.
The player carries a feather.

[First we need to define our shooting action:]

Shooting it with is an action applying to one thing and one carried thing.

Check shooting something with something:
	if the player is not carrying a pistol, say "You are pathetically unarmed!" instead;
	if the second noun is not a pistol, say "[The second noun] does not fire." instead;
	if the noun is the second noun, say "Nice trick if you can do it!" instead;
	if the noun is the player, say "That would be disastrous!" instead.

[Next, some grammar to allow the player to use this action:]

Understand "shoot [something] with [something]" as shooting it with.
Understand "shoot [something] at [something]" as shooting it with (with nouns reversed).

Carry out shooting something with something:
	say "ZAP! [The noun] twinkles out of existence! [if something is part of the noun][The list of things which are part of the noun] clatter to the ground! [end if][paragraph break]";
	now every thing which is part of the noun is in the location;
	now the noun is nowhere.

The Deathbot Assembly Line is a room. "Here is the heart of the whole operation, where your opponents are assembled fresh from scrap metal and bits of old car." The dangerous robot is a person in the Assembly Line. "One dangerous robot looks ready to take you on[if the robot carries the laser rifle] with its laser rifle[end if]!" A robotic head, a drill arm, a needle arm, a crushing leg and a kicking leg are parts of the dangerous robot.

The Laser Rifle is a pistol carried by the dangerous robot.

[We define a second robot so that we can test disambiguation.]

The hostile robot is a person in the Assembly Line. "A hostile robot hops towards you[if the hostile robot carries the cannon] wielding a shock cannon[end if]!" A triangular head, a titanium monopod, and a long copper arm and a short plastic arm are parts of the hostile robot.

The shock cannon is a pistol carried by the hostile robot.

Instead of examining something when something is part of the noun:
	say "[The noun] consists of [a list of things which are part of the noun]. [if the noun carries something][The noun] carries [a list of things carried by the noun].[end if]"

Does the player mean shooting a pistol (called P) with P: it is very unlikely.
Does the player mean shooting something enclosed by the player with: it is unlikely.

[If the player has typed "shoot [something]" we potentially replace the command with a less ambiguous one depending on the thing understood (see below).  Even with the thing understood being available, this is clunky and suffers from a potential mismatch between the names by which an object is understood and its printed name.]
after reading a command when the player's command matches "shoot [something]":
	if the player carries the thing understood and the thing understood is a pistol:		
		replace the player's command with "[the player's command] at";
		[It would be nice if disambig clarification would say "(at the foo)" instead of "(the foo)" here.]
		continue the action;
	if the player carries a pistol (called P):
		say "(with [the P])[line break]";
		replace the player's command with "[the player's command] with [the printed name of P in lower case]";
	
[ Proof of concept for obtaining the thing understood from the I6 level.

For now, we only do this in a very limited set of snippet matching scenarios. It would be better to set parsed_thing deeper in the parser so that the thing understood can be used in a wider variety of situations. We also punt on multiple objects.

We only want to replace SnippetMatches, but, even when we ensure that the Replace directive appears prior to the definition of SnippetMatches in Parser.i6t, I7 defies us and uses the original one anyway. So, instead, we replace the entire Snippets section of Parser.i6t using "include (- -) instead of "Snippets" in "Parser.i6t". Unfortunately, this inflates the length of the program signficantly.

The only other change is to SnippetIncludes, where we replace j--) with j-- ) so as not to confuse the IDE's syntax highlighter.]

The thing understood is a thing that varies.
The thing understood variable translates into I6 as "parsed_thing".

Include (-
Global parsed_thing;
[ PrintSnippet snip from to i w1 w2;
	w1 = snip/100; w2 = w1 + (snip%100) - 1;
	if ((w2<w1) || (w1<1) || (w2>WordCount())) {
		if ((w1 == 1) && (w2 == 0)) rfalse;
		return RunTimeProblem(RTP_SAYINVALIDSNIPPET, w1, w2);
	}
	from = WordAddress(w1); to = WordAddress(w2) + WordLength(w2) - 1;
	for (i=from: i<=to: i++) print (char) i->0;
];

[ SpliceSnippet snip t i w1 w2 nextw at endsnippet newlen;
	w1 = snip/100; w2 = w1 + (snip%100) - 1;
	if ((w2<w1) || (w1<1)) {
		if ((w1 == 1) && (w2 == 0)) return;
		return RunTimeProblem(RTP_SPLICEINVALIDSNIPPET, w1, w2);
	}
	@push say__p; @push say__pc;
	nextw = w2 + 1;
	at = WordAddress(w1) - buffer;
	if (nextw <= WordCount()) endsnippet = 100*nextw + (WordCount() - nextw + 1);
	buffer2-->0 = 120;
	newlen = VM_PrintToBuffer(buffer2, 120, SpliceSnippet__TextPrinter, t, endsnippet);
	for (i=0: (i<newlen) && (at+i<120): i++) buffer->(at+i) = buffer2->(WORDSIZE+i);
	#Ifdef TARGET_ZCODE; buffer->1 = at+i; #ifnot; buffer-->0 = at+i; #endif;
	for (:at+i<120:i++) buffer->(at+i) = ' ';
	VM_Tokenise(buffer, parse);
	players_command = 100 + WordCount();
	@pull say__pc; @pull say__p;
];

[ SpliceSnippet__TextPrinter t endsnippet;
	TEXT_TY_Say(t);
	if (endsnippet) { print " "; PrintSnippet(endsnippet); }
];

[ SnippetIncludes test snippet w1 w2 wlen i j;
	w1 = snippet/100; w2 = w1 + (snippet%100) - 1;
	if ((w2<w1) || (w1<1)) {
		if ((w1 == 1) && (w2 == 0)) rfalse;
		return RunTimeProblem(RTP_INCLUDEINVALIDSNIPPET, w1, w2);
	}
	if (metaclass(test) == Routine) {
		wlen = snippet%100;
		for (i=w1, j=wlen: j>0: i++, j-- ) {
			if (((test)(i, 0)) ~= GPR_FAIL) return i*100+wn-i;
		}
	}
	rfalse;
];

[ SnippetMatches snippet topic_gpr rv;
	wn=1;
	if (topic_gpr == 0) rfalse;
	if (metaclass(topic_gpr) == Routine) {
		rv = (topic_gpr)(snippet/100, snippet%100);
		if (rv ~= GPR_FAIL) {
			! This is the only significant change. We set parsed_thing to rv if rv is a thing.
			if (metaclass(rv) == Object && rv ofclass K2_thing) {
				parsed_thing = rv;
			} else {
				parsed_thing = nothing;
			}
			rtrue;
		}
		rfalse;
	}
	RunTimeProblem(RTP_BADTOPIC);
	rfalse;
];
-) instead of "Snippets" in "Parser.i6t".

Test me with "x hostile robot / shoot gun / hostile / shoot rifle / take all / shoot dangerous with feather / drop gun / shoot dangerous with feather / take gun / shoot gun".

Wow. Great work, both of you. It seems that Matt’s version gets a little less likely to shoot the rock we carry if I add the line

Does the player mean shooting something enclosed by the player with: it is unlikely.

from Vince’s code. This will be a nice improvement. I’m sure you will find fewer reasons to hate that scene from now on.

Vince’s version seems less eager to choose the weapon automatically for us. I suppose a case can be made for both variants.

Sorry, why wouldn’t you recommend your version, Vince? What problems do you think it could cause?

I’m not happy with the after reading a command rule. I don’t like the absence of the word “at” when it clarifies a target; it just says “(the foo)”.

There’s no guarantee that a pistol’s printed name is the way that it should be referred to in a command, although this may not be an issue with CM.

Including all that template code is less robust to future changes than if we stick to I7.

Fair enough. The code is in! Thanks again, both of you.

After-reading-a-command rules that manipulate individual commands tend to fail badly when the player types multiple commands on a line. E.g. “LOOK. SHOOT GUN” isn’t handled by Vince’s example.