(Tads 3) Remove all vocabulary on a Thing at runtime. how?

There’s plenty of routines to modify the command vocabulary of a Thing. You can remove nouns, remove adjectives, add more words to the ones that are already there. But what I can’t seem to find is a good way to efficiently REPLACE the vocabulary of a thing without it getting really really messy.

What I’d like to do is something along these lines (pseudocode) :

[code]PokerCard: Thing

… stuff ommitted for brevity …

rank = ‘king’ // Multiple instances of this class will exist with other strings here. This is an example value to override.
suit = ‘hearts’ // Multiple instances of this class will exist with other strings here. This is an example value to override.

faceUpVocab { return ‘<> (of) <>’; }
faceDownVocab = ‘poker card’;

isFaceUp = nil

changeFaceUpness( newVal )
{
isFaceUp = newVal;
if( isFaceUp ) {
removeOldVocabForMe(); // This is the routine I need help implementing.
initializeVocabWith( faceUpVocab );
// I also put a lot of stuff here to change the display name and desc() but I’m ommitting
// it to keep the example short. What matters here is the vocabulary change.
} else {
removeOldVocabForMe(); // This is the routine I need help implementing.
initializeVocabWith( faceDownVocab );
// I also put a lot of stuff here to change the display name and desc() but I’m ommitting
// it to keep the example short. What matters here is the vocabulary change.
}
}

… etc …
;
[/code]
So the idea is that every time the card flips from face up to face down or visa versa, I want to completely remove the old vocabulary and give it brand new vocabulary. The goal is to make it so that if the card is face-down on top of a deck of cards, you can only refer to it in a generic way as “take card from deck” or “take poker card from deck” but you absolutely must not be allowed to say “take king of hearts” or “look at king of hearts” while the card is still face down.

I’m trying to implement this because of the potential exploit where a player who isn’t supposed to know what the card is yet decides to type things and watch the kinds of errors he gets. If you type “look at king” and the face down card has the noun “king” attached to it but the action is stopped with a custom verification routine because it’s face down, you’ll still get a very different response than if the face down card isn’t a king and the parser says it can’t find any such thing as a king. Thus the player can cheat by typing several such guesses until one “hits”. Since the parser’s complaint doesn’t cause any time to pass in game turns, there’s no cost at all to doing this and it’s very hard to trap that it’s happening.

So, in more precise terms, why I am having a problem doing this is:

1 - initializeVocabWith() only adds MORE words, it doesn’t take words away.

2 - The only method I found to remove words requires explicitly knowing what they were and removing them one at a time, using cmdDict.removeWord( obj, str, vocabType ). So this isn’t very practical for a generic solution to use on any and all instances of subclasses of playing card that users of this library will make.

3 - Although there are means to iterate over words in the dictionary like cmdDict.findWords() and cmdDict.foreachWord() and pull all the words fitting certain criteria out of the dictionary so I can then use cmdDict.removeWord on them, and that would work, I fear it would be very inefficient because those methods only work across the entire dictionary, not just on one object. There doesn’t seem to be a way to give it “stuff referring to this object” as the search criterion. You have to search based on knowing the word already in order to find the objects it refers to, and I’m trying to search in the opposite direction. I know the object and want to find the words for it.

The dictionary type has a native code implementation so you may find that the forEachWord method is fast enough, horrible as it sounds.

For true efficiency I would set up a reverse lookup table in PreInit, using cmdDict.forEachWord to populate it with objects as keys and a list of (word, property) pairs as values. The PreInit phase was designed for this sort of heavy lifting, and there’s no performance impact outside of compile time.

Then you can remove all object vocab easily, and when you add it back, you can add the corresponding entry to your table to keep things in sync.

How can it do them up front during compilation when the words are being inserted and deleted from the dictionary on the fly at runtime?

If all the cards are defined at compile time, or created dynamically during PreInit, you will have the base vocabulary set as part of PreInit. Actually there’s probably a PreInit object that turns the vocabWords property into cmdDict entries; you can modify that to also populate a reverseDict table.

Then you could override the initializeVocabWith method to also update reverseDict when you make changes to vocab at runtime.

With reverseDict there, you can extend cmdDict with a removeVocabFor(obj) method that calls removeWord for each vocab item listed for the object in reverseDict, then removes the object from reverseDict.

Is that helpful or am I missing part of the picture?

In the end, I think it’s just a lot easier to say that for items of type PlayingCard, the author is expected to supply the vocabulary with the adjectives and nouns split out in separate properties. Then the object remembers its own words, like so:

instead of:
faceUpVocab = ‘3 (of) (hearts) poker cardcards’
faceDownVocab = 'poker card
cards’

it would have to be this:
faceUpAdj = [‘3’,‘poker’]
faceUpWeak = [‘of’,‘hearts’]
faceUpNown = [‘card’]
faceUpPlural = [‘cards’]
faceDownAdj = [‘poker’]
faceDownWeak =
faceDownNown = [‘card’]
faceDownPlural = [‘cards’]

That way the object can flip its own vocabulary because it knows how to ask the dictionary what to remove, in the terms the dictionary is expecting.

This may seem like asking a lot of the author, but keep in mind that most of this is programmatic in the base class, and the cards are generated by an algorithm rather than by 52 individual object blocks.