Manipulating the Parser - Part 1 - 'Complex' Nouns

From SCI Wiki
Jump to: navigation, search

By Gumby

Update: The handling of a 'complex' noun can (in fact) be handled correctly with the parser by making each word involved a noun & utilizing the semantic operator. See the Said() string documentation for more details.

Overview:
When it comes to extremely complicated user input I have resorted to circumventing the parser. The techniques implemented here should work for number of oddball problems you may run across while working with the parser.

Problem:
Let's first look at the problem with 'complex' nouns. There is no way to implement the user input command of 'take basket of goodies', the problem lying with treating 'basket of goodies' as a single noun so that we can successfully parse it.

Quick solution run-down:
We will create a new noun in the vocabulary for 'basket of goodies', but we cannot have spaces in our word. Therefore, we will simply remove all whitespace between the words, resulting in 'basketofgoodies'. Upon each user input we will search for the phrase 'basket of goodies' and replace it with 'basketofgoodies'. In all of our coding we will now be able to use our new concatenated word just like any other 'simple' noun.

Step-By-Step:

1. Create a new text resource file & put the full text of the nouns to be handled there
   Examples:  basket of goodies, pieces of eight, pile of bodies
2. Add new nouns to the vocab.000 file according to what you put in the text resource, without any whitespace
   Examples:  basketofgoodies, piecesofeight, pileofbodies
3. In the game.sh file, define a new constant COMPLEXNOUN_TEXT and assign it the number corresponding to your text resource file above
4 Create a new script file called ManipulateParse.sc and include the following code
Code:
(define ANYWHERE     0)
(define STARTS_WITH  1)
(define ENDS_WITH    2)
 
(include "sci.sh")
(include "game.sh")
/******************************************************************************/
(script 972)  // You may need to change the script number if the number is already in use
/******************************************************************************/
(use "main")
 
(procedure public (manipulateParse inputStr pEvent) 
    (var i, complexNounFull[50], complexNounStripped[50], newInputStr)
 
    // Copy the input string into a new variable so we don't clobber the original
    // (in case the user presses F3 we want to see the what was actually typed)
    StrCpy(newInputStr inputStr)
 
    // Do the 'complex' noun substitutions
    = i 0
    (do
      GetFarText( COMPLEXNOUN_TEXT i @complexNounFull)
      (if (containsStr(newInputStr @complexNounFull ANYWHERE))
         findAndReplace(@complexNounFull @complexNounStripped " " "")                                 
         findAndReplace(newInputStr newInputStr @complexNounFull @complexNounStripped)
      )                               
      ++i
    ) while ( > StrLen(@complexNounFull) 0)  
 
    // Parse our 'new' input string, replacing the original parse
    Parse(newInputStr pEvent)
)
 
(procedure public (findAndReplace sourceStr destStr findStr replaceStr)    
  (var i, findStrIndex, char, findAndReplaceStr[50])
 
  StrCpy(@findAndReplaceStr "")
  = findStrIndex 0
  (for (=i 0) (<i StrLen(sourceStr)) (++i)
     (if (<> StrAt(sourceStr i) StrAt(findStr findStrIndex) )
        Format(char "%c" StrAt(sourceStr i))
        StrCat(@findAndReplaceStr char)
        = findStrIndex 0
     ) (else
        (while (== StrAt(sourceStr (+ i findStrIndex)) StrAt(findStr findStrIndex) and < findStrIndex StrLen(findStr))
       ++findStrIndex
    )
        (if (== findStrIndex StrLen(findStr))    
        StrCat(@findAndReplaceStr replaceStr)
        = i (+ i (- findStrIndex 1))
        = findStrIndex 0
        ) (else
            Format(char "%c" StrAt(sourceStr i))
            StrCat(@findAndReplaceStr char)
            = findStrIndex 0             
        )
     )
  )    
  StrCpy(destStr @findAndReplaceStr)
)
 
(procedure public (containsStr sourceStr findStr matchType)
  (var i, findStrIndex)
 
   = findStrIndex 0
  (for (=i 0) (<i StrLen(sourceStr)) (++i)
     (if (== StrAt(sourceStr i) StrAt(findStr findStrIndex) )
        (while (== StrAt(sourceStr (+ i findStrIndex)) StrAt(findStr findStrIndex) and < findStrIndex StrLen(findStr))
       ++findStrIndex
    )
    (if(== findStrIndex StrLen(findStr))
       return TRUE
    ) (else
           = findStrIndex 0             
        )
     )  
     (if (== matchType STARTS_WITH)
    return FALSE
     )
  )    
  return FALSE
)
5. Add the following code into the User.sc script
Code:
(use "ManipulateParse") // add this near the top
 
// Then find this code block below & insert the line for the 'manipulateParse' procedure call
    (if( (self:getInput(pEvent)) and Parse(@inputStr pEvent))							
          manipulateParse(@inputStr pEvent)
	  (send pEvent:type(evSAID))
	  (self:said(pEvent))
    )
6. Don't forget to recompile the User.sc & ManipulateParse.sc scripts. You can now use logic like this in your room scripts (or where-ever):
Code:
  (if(Said('move/pileofbodies'))
      Print("Probably better to just leave those poor souls alone."
  )    
  (if(Said('eat/basketofgoodies'))
      Print("You eat the basket of goodies, and promptly toss your cookies!")	
  )			
  (if(Said('take/piecesofeight'))
      Print("You pick up the pieces of eight without dropping a single one...")
  )