Understand "think", "think [text]" as newthinking.
If I make a command like this that can take text, but doesn’t have to, how can I check if the player supplied no topic? I don’t want to use the ‘mistake’ mechanism or make a second action here.
Weirdly enough, I can’t find a pure-I7 way to do this. But it’s an oversight that’s easy enough to remedy with a tiny bit of I6.
Definition: a snippet is empty if I6 routine "SnippetEmpty" says so (it contains no words).
Include (-
[ SnippetEmpty s ;
return (s == 0);
];
-).
Now you can just check “if the topic understood is empty”.
(Well, you can do it with pure I7, by checking “if the number understood is 0”, since an empty snippet corresponds to the number 0. But breaking type-safety by looking at the wrong “the ___ understood” is a great way to make your code opaque and unreadable.)
Here’s a pure I7 option (that would rapidly become less attractive if the command verb had multiple synonyms): if the player's command exactly matches the text "think".
And I think this would work as a function-less I6 inclusion alternative:
To decide if no topic was specified: (- (num_words == 1) -).
If you’re dipping down to I6, I would rather check consult_words or parsed_number (i.e. the topic understood); this won’t work for an Understand line like “think about [text]”.
… I’ve been using this check but it seems to be unreliable. I’ve got a carry out rule that starts with ‘unless the topic understood is empty:’ (goal - detecting unnecessary extra words in this case)
The first time you supply a topic, the test works. But then if you use the same command again any time later and don’t supply a topic, it gives a false positive and believes you did. Attempt to print the topic/snippet says *** Run-time problem P39: Attempt to say a snippet value which is currently invalid: words 2 to 2
This is in 6M62.
This command is out of world, so I probably can use other solutions if empty topics can’t be detected reliably in general. Contrary to my preferences of 10 months ago. I’ve changed my mind in that time – deal with it!
topic understood is only valid after successfully parsing a topic token.
At other times it could have any value, or be invalid, because it’s an alias for the I6 variable parsed_number. This is used for parsing any number, float, time, or other non-object token.
If the only understand line corresponding to the action used a [text] token, the parser wouldn’t accept that command alone, so you must also have an understand "command" as action-out-of-worlding, right?
Oh, good, I found the snippet validity testing code I had (that hasn’t seen much of any testing. But you could see if this helps…
fooming is action out of world applying to one topic.
understand "foom [text]" as fooming.
understand "foom" as fooming.
fooming action has a text called the topic-text.
setting action variables for fooming:
if the topic understood is valid, now the topic-text is the substituted form of "[the topic understood]";
else now the topic-text is "".
carry out fooming: say "topic-text: [topic-text]".
Include (-
[ SnippetValid sn start end len;
len = sn % 100;
if (len < 1) rfalse;
start = sn / 100;
if (sn < 1) rfalse;
if ((start + len - 1) > (players_command % 100)) rfalse;
rtrue;
]
-)
To decide if (sn - a snippet) is valid: (- SnippetValid({sn}) -).
To decide if (sn - a snippet) is invalid: (- (~~SnippetValid({sn})) -);
edited: I’ve finally gotten in the habit of using I6 condition definitions when apprropriate but the above was old code. So, an alternative:
Definition: a snippet is valid rather than invalid if I6 condition "SnippetValid(*1)" says so (it is valid).
The problem is that if the understand "foom" grammar line matches, topic understood (parsed_number) is not necessarily set to zero.
(Arguably the compiler shouldn’t allow this. It’s not like the “supplying a missing noun” case where noun is either valid or nothing.)
You could add a rule to zero parsed_number at the start of parsing. I think that would work okay for this case. But you could imagine a more complicated grammar
understand "foom" as fooming.
understand "foom [number]" as num-fooming.
understand "foom [color]" as color-fooming.
understand "foom [text]" as text-fooming.
…where the value of parsed_number is jerked around a lot as the parser tries out diferent grammar lines. You shouldn’t really rely on it outside of an action with a known token type.
If you don’t want to go the separate actions route, I think Draconis was on the right track mentioning the consult_* globals. These are not normally reset each parsing cycle, but they are only set when a topic is parsed, so there is no interference from non-topic tokens as is seen with parsed_number.
Consult from is a number that varies. The consult from variable translates into I6 as "consult_from".
Consult words is a number that varies. The consult words variable translates into I6 as "consult_words".
To decide whether no topic was provided:
if consult from is zero and consult words is zero, decide yes;
decide no.
Before reading a command: [to clear these manually]
now consult from is zero;
now consult words is zero.
Report new thinking:
say "<response with topic>."
First report new thinking when no topic was provided:
say "<response without topic>.";
rule succeeds. [halt report rules]
Hm. Well, I think I understand a bit more about the nature of the problems involved than I did back in the halcyon days of December 2022.
I am all the time rounding up actions and vetting them for whether they can be used or blocked at certain times, or coralling them all to one action at a particular time. I repeat – this happens all the time in my game!
This is easiest to do if variations of an action can all be described in one way. You don’t want to have to remember to block both ‘thinking’ and ‘newthinking’, etc. Using the mistake mechanism is OK as part of this, because it activates before the usual rulebook process.
So yes, you can subgroup actions to deal with this. You can say ‘thinking is being thoughtful. newthinking is being thoughtful’. Then check for ‘being thoughtful’ in a before or instead rule. This solves a lot of problems.
Though, you still now have two actions that need to be programmed to survive any situation, instead of just one. Some actions seem to play well with my brain and handling/supplying missing nouns. Others don’t, or I really want to split them into two. For instance I have talking, and talking generally which is the nounless version. A benefit of doing it this way is Inform will never pick a dumb random target when no noun is supplied if you’ve got that nounless version in place.
Going back to the original case, ‘thinking’ / ‘newthinking’, I now realise I don’t have to worry about coralling actions at critical moments here because these Thought commands are out of world actions in my game. So I’m happy to split them up. I forget what my concerns were in the beginning, but they lay somewhere amongst all the stuff I just typed through.
Oh, in that case, the logistical problem is easy to solve.
Thinking vaguely is an action applying to nothing.
Before thinking vaguely: try thinking about "nothing" instead.
Now any rules that block thinking about will also block thinking vaguely (since it immediately redirects to thinking about, which then runs through all the usual Before, Instead, etc blocks).