Case-insensitive input?

In a bug report it was pointed out to me that Inform input is actually case-sensitive, and that there is quite a bit of code in Counterfeit Monkey and the extensions it uses that relies on it being all lower case.

Of course, the actual parsing is case-insensitive, but as soon as you convert the player input into a string, with something like

let N be "[the player's command]"; the resulting string will have the same case as the input, so if the player types “L R-REMOVER” or “l R-remover” , the line

replace the text " [current setting of the letter-remover]-remover" in N with " letter-remover"; will do nothing, while “l r-remover” works fine. That is particularly annoying because the tutorial usually asks the player to type L R-REMOVER, in upper case.

This is easy to work around by adding the lines

let N be "[the player's command in lower case]"; change the text of the player's command to N;in a “first after reading a command” rule, but that adds quite a bit of overhead: an increase of 15690866 opcodes in a full playthrough, or an average of 31892 opcodes per turn.

Does anyone have a better solution? Is there some Inform 6 trick that will make this faster?

Given all the stuff CM has to do, I’d guess that coverting the player’s command to lower case each turn isn’t going to be too significant. But why do you need to do it every turn? Can’t you just do it when the player is trying something funky? (I.e. do the conversion in the rules for letter-removing, etc, not in the after reading a command rules.)

Of course, those additional opcodes probably won’t be noticeable even on slower interpreters, but if we keep adding slower code it will add up, and it has become something of an obsession of mine to not let the game get slower without offsetting it somehow.

I’m currently experimenting with using a global string variable, player-command-substitute, which is set to the player’s command (in lower case) just once at the beginning of the after reading a command stage. All instances of ‘let N be “[the player’s command]”’ can then be replaced with ‘let N be the player-command-substitute’, and every ‘change the text of the player’s command to N’ with ‘now the player-command-substitute is N’. Then at the very end of the after reading a command stage the text of the player’s command is changed to this player-command-substitute variable. We’ll see if it helps. It will certainly not make the code prettier.

I confess I don’t have any real understanding of what’s going on at the virtual machine level, but I don’t see how that wins over “replace the player’s command with the player’s command in lower case.” Is it somehow easier to set a global variable than to alter the player’s command? I though the player’s command was a global variable.

“The player’s command” is a global array which is treated specially. Setting it automatically re-runs the tokenisation step, which scans through it (case-insensitively) for dictionary words.

So this plan may save cycles, but it’s hard to say for sure without testing it.

Replacing ‘let N be “[the player’s command]”’ with ‘let N be the player-command-substitute’ is probably not much of a gain, but in a couple of places I was able to use the new global variable directly instead of bothering with temporary variables (like N) at all.

So far, so good. A decrease of 6912417 opcodes in a full play-through. On the other hand, the undo file has grown by 984 bytes, presumably to store the new global string variable, which might cancel out any performance won.

EDIT: Yay, it seems I can avoid this by resetting the player-command-substitute variable to an empty string at the end of every turn.