TL;DR - Remapping nouns to topics does not like long plurals
Ran into an interesting bug/artifact during testing what I THOUGHT was a stable GlobalRemap refactoring of my bespoke SearchFor verb. As a reminder, I wanted to enable something like >SEARCH PILE OF PAPERS FOR DEED
but preserve functionality like >LOOK UP DEED IN REGISTRY
or, problematically, its alternate form >SEARCH REGISTRY FOR DEED
What this amounts to is navigating the overlapping vocabularies with different verb types (TIAction for SEARCHFOR and TopicTAction for CONSULTABOUT).
Here is my refactored GlobalRemapping solution, which tries to accomodate incomplete commands. (ie >SEARCH FOR DEED What would you like to search for that?
)
/*
* SearchFor Action, to be able to find stuff
* Note, IOBJ checks but DOBJ executes
*/
modify Thing
dobjFor(SearchFor) { // allow all
preCond = [touchObj]
verify() { }
check() { }
action() {
if (gIobj.isIn(self)) {
"You found {it dobj/him}! ";
gIobj.discover();
nestedAction(Take, gIobj);
}
}
}
iobjFor(SearchFor) {
preCond = []
verify() {
if ((gDobj != nil) && !gDobj.ofKind(Consultable)) {
if (isIn(gActor))
illogical('{You/he} {are} already holding {that iobj/him}. ');
else if (canBeSeen)
illogical('{It\'s iobj/he\'s} right here<<if !isDirectlyIn(getOutermostRoom())>>,
<<location.objInPrep>> <<location.theName>><<end>>! ');
else if ((!ofKind(Hidden) && seen) || discovered)
illogical('{You/he} already know{s} where {that iobj/he} {are}. ');
} // else let Consultable handle it
}
check() {
if (!isIn(gDobj) && !gDobj.ofKind(Consultable))
failCheck(gDobj.cannotBeFoundHereMsg);
}
}
cannotBeFoundHereMsg = 'It is unlikely {that iobj/he} could be found here. '
;
/*
* Remap SearchFor to ConsultAbout if the nominal DOBJ is a Consultable
*/
consultableSearchForRemap : GlobalRemapping
getRemapping(issuingActor, targetActor, action) {
if (action.ofKind(SearchForAction) ) {
local dobjIsConsultable = (instanceWhich(Consultable, {x: action.canDobjResolveTo(x) }));
if (dobjIsConsultable) {
local newAction = ConsultAboutAction.createActionInstance();
newAction.setOriginalAction(action);
local dobj = action.dobjMatch;
local topic = newAction.reparseMatchAsTopic(action.iobjMatch, issuingActor, issuingActor);
newAction.setObjectMatches(dobj, topic);
return [issuingActor, newAction];
} }
return nil;
}
;
// HERE IS THE WEIRD PART
// post-remap, some new Resolve results do not set nounSlotCount
// (default was nil), which threw error
// most sensitive when iobjMatch included plurals!
modify rankByVerbStructure
comparePass2(a, b)
{
a.noteNounSlots(0); // only sets to 0 if currently nil
b.noteNounSlots(0);
return inherited(a,b);
}
;
/*
* Does not allow remap when prompted to complete with DOBJ: SEARCHWHATFOR
* ie disallows >find sword -> 'What do you want to search for it?' -> ><consultable>
* instead, since are assured IOBJ will be a Thing, can just replace with impunity
*/
modify Consultable
dobjFor(SearchFor) {
action() { replaceAction(ConsultAbout, self, gIobj); }
}
;
// accepts all Hidden objects as possible, including default intangible one below
//
class IobjResolverSearchFor : IobjResolver
objInScope(obj) { return ((obj.ofKind(Hidden) && obj.isKnown) || inherited(obj)); }
;
DefineTIAction(SearchFor)
actionAllowsAll = nil
createIobjResolver (issuingActor, targetActor) { //see below
return new IobjResolverSearchFor(self, issuingActor, targetActor);
}
;
VerbRule(SearchFor)
('dig' | 'dig' 'in' | 'li' | 'lb' | 'lt' | 'search' | //'lu'
| ('look' | 'l' | 'search' ) ('in' | 'inside' | 'on' | 'under' | 'behind' | 'through') ) singleDobj 'for' singleIobj
| ('find' | ('search' | 'dig' | 'look' | 'l') 'for') singleIobj
('in' | 'inside' | 'on' | 'under' | 'behind') singleDobj : SearchForAction
verbPhrase = 'search/searching (what) (for what)'
omitIobjInDobjQuery = true
askDobjResponseProd = forSingleNoun
;
VerbRule(SearchWhatFor)
('find' | ('search' | 'dig'| 'look' | 'l') 'for') singleIobj : SearchForAction
verbPhrase = 'search/searching (what) (for what)'
whichMessageTopic = DirectObject
construct()
{
/* set up the empty direct object phrase */
dobjMatch = new EmptyNounPhraseProd();
dobjMatch.responseProd = inSingleNoun;
}
;
modify VerbRule(ConsultAbout)
'consult' singleDobj ('on' | 'about') singleTopic
// | 'search' singleDobj 'for' singleTopic
//| 'search' 'in' singleDobj 'for' singleTopic
| (('look' | 'l') ('up' | 'for')
// | 'find'
// | 'search' 'for'
| 'lu' | 'read' 'about' )
singleTopic ('in' | 'on') singleDobj
| ('look' | 'l') singleTopic 'up' 'in' singleDobj
:
defaultForRecursion = true // to ensure CA is chosen during remap, not CWA
;
replace VerbRule(ConsultWhatAbout)
[badness 500] (('look' | 'l') ('up' | 'for')
// | 'find'
// | 'search' 'for'
| 'lu' | 'read' 'about') singleTopic
| ('look' | 'l') singleTopic 'up' : ConsultAboutAction
// changed from default to accomodate SEARCHFOR reusing this vocab
verbPhrase = 'look/looking up (what) (in what)'
whichMessageTopic = DirectObject
construct()
{
/* set up the empty direct object ploohrase */
dobjMatch = new EmptyNounPhraseProd();
dobjMatch.responseProd = inSingleNoun;
}
;
Ok, that’s a mouthful, should probably be a library of some kind. Note commented out ConsultAbout vocabulary, essentially giving SearchFor first rights to the vocab.
This was working great, until I inadvertently tested it with a long word that ALSO had some plurals. An object that existed in the world shelves
ALSO was used for plurals elsewhere. Granted that isn’t very interesting to consult a Consultable about, but my theory was should not core dump if tried!
It did core dump though, when I tried >FIND SHELVES IN ENCYCLOPEDIA
I traced the error deep into the Verb resolution process, after it had successfully recognized the Consultable that needed verb remapping. What happens is, after successfully reparsing the iobjs as topics (finding about 3 of them), it creates and sorts attendant special purpose CommandRanking
objects. The intent here is for the game to test each of these items and pick the one the player actually intended. There are 22 separate criteria, sorted into two phases to do this!
The 21st Criteria is called rankByVerbStructure
which prioritizes high noun counts over low. The idea is that >DETACH WIRE FROM BOX
is more likely to mean iobj = box (nounCount =2) than dobj = ‘wire from box’ (nounCount = 1).
This nounCount value gets set by the Verb, typically. (IAction = 0, TAction = 1, TIAction = 2, etc) Except when plurals were involved, it sometimes didn’t get set at all for reasons I could not determine… though apparently only during remaps? The default value is nil
, and gets fed to a simple subtraction comparator (is a -b >0? <0?), which coredumps on nil. It seems like the REAL error is ‘why didn’t nounCount get set right?’ I found I could get usable results (since my use case pretty much guaranteed uniform noun counts when correctly analyzed) with this snippet from above:
// HERE IS THE WEIRD PART
// post-remap, some new Resolve results do not set nounSlotCount
// (default was nil), which threw error
// most sensitive when iobjMatch included plurals!
modify rankByVerbStructure
comparePass2(a, b)
{
a.noteNounSlots(0); // only sets to 0 if currently nil
b.noteNounSlots(0);
return inherited(a,b);
}
;
Can’t help feel I’m patching something that could be deeper. Anyone played with this kinda thing before?