The deadly secret of Aaron Reed's "Smarter Parser"

Here’s some sample code:

Include Smarter Parser by Aaron Reed.

The Room_Grassy_Clearing is a room.
The printed name is "Grassy Clearing".
The description is "The water churgles in the nearby creek but you can't see it through the forest of blackberry brambles all around you.".
Understand "grassy/-- clearing" as Room_Grassy_Clearing.

And the deadly secret (at least in Lectrote):

>look
Grassy Clearing
The water churgles in the nearby creek but you can’t see it through the forest of blackberry brambles all around you.

>try again

And… that’s it. It never returns from this command. It hangs the Lectrote window (though you can open another).

Does this happen for you in the IDE?

Any idea what’s happening here?

Obviously it’s a bug, but TRY AGAIN certainly isn’t a standard IF command. The traditional one since Zork is AGAIN. Most games don’t recognize the verb TRY.

In the IDE, the interpreter prints:

>try again
Most connecting and comparative words are not necessary.

Retrying as:
>AGAIN

Type UNDO if this isn't what you wanted to do, or CORRECT OFF to stop automatically correcting commands.

To stop these messages entirely, type NOVICE OFF.

Most connecting and comparative words are not necessary.

Retrying as:
>AGAIN
Most connecting and comparative words are not necessary.

Retrying as:
>AGAIN
Most connecting and comparative words are not necessary.

(and so on, over and over again)

Smarter Parser strips off the “TRY” from “TRY AGAIN”, as per the “stripping pointless words rule”:

A smarter parser rule when sp_normal (this is the stripping pointless words rule):
	if stripping "(anyway|instead|very|almost|this|so|just|ye)" is fruitful or stripping "(now|try|next|around|more)" is fruitful:
		identify error as stripping pointless words rule;
		if the number of words in the reborn command > 0, reparse the command;
		else reject the command. [If there are no more words to deal with.]

… and then tries the command without the stripped part. And I guess that the problem then occurs because the AGAIN command, in this case, does not really have a separate command to repeat (the system/extension presumably does not remember that the next-to-last action was LOOK, and so cannot fall back on that as the referent of AGAIN).

I don’t know what the best fix would be; depends a bit on the desired behaviour.
You could modify the rule above to avoid stripping “try”; but note that the input “PLEASE AGAIN”, for example, will lead to the same problem, because PLEASE will be stripped out by another rule.
So, it would probably be best to make an exception (thus refraining from all Smarter Parser corrections) whenever “AGAIN” occurs in a command.

1 Like

Here’s an additional related mystery (this time with Inform) I ran into trying to work around the above bug:

Understand "try again" as a mistake ("(Try using the command 'again' or it's shortcut 'g'.)")

I had the understanding that “Understand * as a mistake” treated the text literally. But…

Grassy Clearing

The water churgles in the nearby creek but you can’t see it through the forest of blackberry brambles all around you.

>try again
(Try using the command “again” or it’s shortcut “g”.)

>again
(Try using the command “again” or it’s shortcut “g”.)

What? Why is the mistake matching “again?”

I had a playtester tell me that the prose told them “Try again?” (which I’ve seen somewhere in almost every IF I’ve played). So my tester dutifully typed “try again”. And discovered the bug.

I could turn the rule off, but I kind of like that it strips out some pointless words. So I wanted to remove the stripping of “try” and “around”:

The new stripping pointless words rule substitutes for the stripping pointless words rule.
[ The stripping pointless words rule is not listed in the Smarter Parser rulebook. ]

A smarter parser rule when sp_normal (this is the new stripping pointless words rule):
	if stripping "(anyway|instead|very|almost|this|so|just|ye)" is fruitful or stripping "(now|next|more)" is fruitful:
		identify error as stripping pointless words rule;
		if the number of words in the reborn command > 0, reparse the command;
		else reject the command. [If there are no more words to deal with.]

It is not matching “again”; the system is obeying the “again” command and literally entering the previous command “try again” again, thus getting the same result as for “try again”.
You can verify that by entering “look” and “again” (which will correctly repeat the looking, not match the mistake) or, the other way, by entering a non-recognised command, which will also be repeated:

>bla
That's not a verb I recognise.

>again
That's not a verb I recognise.
2 Likes

I believe the parser offers some means of discarding the AGAIN buffer. If it doesn’t, I can hack one in.

But this would let you keep the convenience rule while avoiding the bug, without needing to manually match every possible option (PLEASE G, G PLEASE, etc).

Looks like this should do it:

To discard the/-- again buffer: (- DiscardAgain(); -).

Include (-
[ DiscardAgain ;
    #ifdef TARGET_ZCODE;
    buffer3->1 = 0;
    #ifnot;
    buffer3-->0 = 0;
    #endif;
];
-).

Just stick this phrase into any command-rewriting rules that lead to this crash. It’s simple enough that it really shouldn’t need a whole routine to do it, but it needs to be done differently on Z-machine vs Glulx because of how they arrange the buffers.

This isn’t quite ideal—it means that if you try to AGAIN after this phrase is called, the game will think you’ve never done anything, and give the relevant error (“You can hardly repeat that.”, aka the parser command internal rule response D). But it’s much better than an infinite loop.

2 Likes

It is not matching “again”; the system is obeying the “again” command and literally entering the previous command “try again” again, thus getting the same result as for “try again”.

OMG, of course. Lol.

Now ideally, I would just have the command “try again” do “again,” but I haven’t found a simple straightforward way to trigger again. Is there one?

Here’s a way to do that:

After reading a command:
	if the player's command matches "try again", replace the player's command with "again".

Cf. 18.33. Reading a command.

3 Likes

I think more extensions should come with deadly secrets.

7 Likes

Super helpful. Thanks. No more infinite loops and totally transparent to the player.

Extra helpful in my WIP as trying again is kinda critical to gameplay.

Here’s what I had as a temporary-don’t-infinite-loop workaround:

Understand "try again" as a mistake ("(Try using the command [em]again[/em] or it's shortcut [em]g[/em].)") ]

But I found playtesters were entering their own kind of infinite loop based on what the game was telling them:

>climb the tree
You can’t reach the lower branches.
>try again
(Try using the command again or it’s shortcut g.)
>again
(Try using the command again or it’s shortcut g.)
>g
(Try using the command again or it’s shortcut g.)
>again
(Try using the command again or it’s shortcut g.)

::laugh to keep from crying emoji::

2 Likes