Match text against object in Inform 7?

I would like to be able to take some text or a snippet and determine if it matches/describes a particular object. I other words, I would like to be able to take a portion of what the player typed, match/compare that against an object of my choice, and determine (yes/no) wether the parser would match that snippet against my chosen object or not. I understand that MatchTextAgainstObject might be useful here, but I am not familiar with Inform 6. I have seen some (parser) extensions that might be able to give me what I want, but I don’t need anything so extensive. Fingers crossed that someone can provide a few lines of Inform 6 code to do the job… thanks up front.

In what context are you trying to do this? Off the top of my head, you could define a new action for the purpose of capturing the player’s input – that way, you can piggyback off the existing parser machinery without bothering with I6 code.

I need this in a “After reading a command:” statement. I need to do this before the parser looks at what the player typed.

I think this will work:

After reading a command when the player's command includes "[rock]": say "Ixnay on the ockray!"; reject the player's command.

as long as the rock is in scope. It doesn’t seem to work for me when the rock isn’t visible. That may not be a problem. You could conceivably add the rock to scope if you need to:

After deciding the scope of the player: place the rock in scope.

…now, it seems that this clonks “take all” which may not be desirable:

[code]Cave is a room.

The rock, the coal, and the stick are in the cave.
Understand “stone/pebble” as the rock.

After reading a command when the player’s command includes “[rock]”:
say “Ixnay on the ockray!”;
reject the player’s command.[/code]

“take all” yields “Ixnay on the ockray!” even though the command didn’t strictly mention the rock.

There are definitely some things going on here that I don’t understand, so handle with care.

Thanks, that’s helpful, but it doesn’t seem to work when I try to iterate over a list like this:

After reading a command:
	repeat with item running through L:
		if the player's command matches "[item]":
			say "Match made."

Any thoughts? I need to be able to match a snippet against items in a list of things.

OK, now I have a bunch of thoughts that won’t be immediately helpful, but might inspire someone (coughdraconiscough) to dive into the I6 and make something useful.

It used to be that you could do something like this:

After reading a command: if the player's command includes "[thing]":

and the thing that got matched would get stored as the noun. (By the way, note that when you’re matching a snippet against a topic “matches” means exactly matches and “includes” means includes, while when you’re matching a text against a text “matches” means is included in and “exactly matches” means exactly matches. This confuses me no end but is impossible to fix at this point without breaking lots of code.)

Which would’ve allowed you to try this:

After reading a command: repeat with item running through L: if the player's command includes "[thing]" and the noun is the item: say "Match made."

The problem is that this was a bug, because you don’t want to overwrite the noun when you make a change like this. See this bug report, this thread, and this uservoice suggestion.

This was apparently taken care of by a change to ConsultNounFilterToken, which preserves the noun as it should be, but also makes it impossible as far as I can tell to access the thing that actually got matched as the “[thing]” token.

Now, my guess is that it should be possible to take the new ConsultNounFilterToken:

[ ConsultNounFilterToken obj sn rv; if (token_filter ofclass Routine) { sn = noun; noun = obj; rv = indirect(token_filter); noun = sn; return rv; } if (obj has (token_filter-1)) rtrue; rfalse; ];

and make a new I6 global variable that could be accessed from I7 as “the most recently matched thing,” which gets assigned somewhere in there. I think maybe you’d have to set this to obj at some point? Possibly conditional on rv? A problem is that I have no idea what “indirect” is doing, or basically what anything else is going on. So I can’t really make something that works there. But if someone who knows a bit more about the internals wants to try, that might be a possible approach.

Then if that I6 hook got put in, you could try

After reading a command: repeat with item running through L: if the player's command includes "[thing]" and the most recently matched thing is the item: say "Match made."

But you’d need someone to do the I6 hook.

(By the way there is an extension Objects Matching Snippets which I think is designed for cases like this, but I’m not sure if it’s updated for the newest versions of Inform.)

“indirect(x)” is just an old-fashioned way of writing “x()”. An idiom left over from Inform 5 if not earlier.

“indirect” is a very strange creature. I cannot seem to find it mentioned in the I6 documentation anywhere, DM4 nor TM. But it’s basically a way to dereference a function pointer. Given the address of a routine, and up to six arguments, it calls the routine with those arguments and returns its output.

ConsultNounFilterToken’s job is to test whether a certain object passes the “token filter”, which is what makes [something] different from [a weapon] in an Understand line. In the latter case, the token filter is a routine which returns true if the noun is a weapon, and false otherwise. (In the former case, the filter is nothing.)

When you ask Inform whether the player’s command matches “[the table]”, it (after a couple levels of indirection) calls the routine MatchTextAgainstObject. This routine’s job is to check whether a snippet matches a particular object, and call MakeMatch if so. But there’s no point in doing any difficult parsing if we already know that the object can’t match, due to a token filter.

So before the object parsing starts, it calls ConsultNounFilterToken to ask whether the particular object is even valid in this case. In the case of “[the table]”, the token filter is “is the object the table?”, and the object in question is indeed the table. So ConsultNounFilterToken returns true.

However, until recently, ConsultNounFilterToken also trashed the noun. This isn’t a problem in normal parsing (since the noun is then filled in properly later), but if you asked the parser to check something specific, that normal parsing didn’t happen. So the object under consideration remained in the noun.

I’m not sure what the best way is of relaying this information out. If you rely on ConsultNounFilterToken, it shouldn’t be called when you match against [something], since there is no filter there. (TODO: …is it?)

What you really want, it sounds like, is a way to know which objects the parser looked at during processing, even if they were eventually removed from consideration for whatever reason?

In that case, you could make NounDomain set workflag on everything it considers. Then look at things which are “marked for listing” later on.

For the more general case, TryGivenObject could store its argument into “the item described” whenever matching is successful. This is probably the more useful scenario.

I will look into this more.

Aww, I thought that this could be done relatively simply by redirecting the old stuff that used to trash the noun.

I don’t have Inform installed on the computer I’m on right now, but I can test the old noun-trashing stuff in 6G60 through Playfic. In 6G60, this:

Cave is a room. Bob is a man in Cave. A spoon is in Cave. Instead of asking Bob about "[thing]", say "'Not sure,' says [the noun]."

trashes the noun in the same way; “Ask bob about spoon” yields “Not sure,’ says the spoon.”

 Cave is a room. Bob is a man in Cave. A spoon is in Cave.
Instead of asking Bob about "[any thing]", say "'Not sure,' says [the noun]."[/code]

does not trash the noun. And, as before, "[thing]" only matches things that are in scope. For whatever reason, this:

[code] Cave is a room. Bob is a man in Cave. A spoon is a thing in Cave.
Instead of asking Bob about "[something]", say "'Not sure,' says [the noun]."

fails with the confusing error message:

Again, this is all in 6G60.

Also, gabbagabbahey, what exactly is your use case here? As you can tell, it’s a little hard to accomplish what you want in a straightforward fashion, so if there’s a workaround it might be nice to try to find one.

…I looked at Objects Matching Snippets and the extant version, version 2, makes absolutely no sense to me whatsoever. Like, it depends on a relation “identified with” and that relation doesn’t appear to be defined anywhere. BUT, version one, which can still be found here, seems like it might serve your purposes; it does a clever workaround where you can check whether a snippet contains a specific item by loading the item into a global variable and creating a filter that checks that variable. (You have to move the line “Objects Matching Snippets ends here,” which is in the wrong place.)

You know, looking at over, I think the problem with version 2 is that the code there is supposed to go after the version 1 code, not to replace it. But I still can’t get the example to work, and I’m not sure what the code in version 2 does or why it is necessary. Oh well.

All right, I’ve discovered a working version of Objects Matching Snippets on my computer and posted it on the old thread. With this you can do this:

After reading a command: repeat with item running through the interesting list: if the name of the item appears in the player's command: say "Match made on [item]."

and it will do what you want. (I suspect that in a game with lots of objects this will get real slow, or at least if there are many things on the interesting list.) It appears to break when the name of the object is followed by “and,” for whatever that’s worth.

It works! It even works with aliases and calling a thing only by a ‘describing’ property. I even understand how it works. Thanks matt w!