Author Topic: How to use Qualifying Adjectives with the parser?  (Read 36399 times)

0 Members and 1 Guest are viewing this topic.

Offline gumby

Re: How to use Qualifying Adjectives with the parser?
« Reply #30 on: November 21, 2010, 05:15:39 AM »
After another 3-4 hours, current score is....

Character arrays/kernel string functions: 3
me: 0

I'm getting irritated...
In the Great Underground Empire (Zork port in development)
Winter Break 2012 Rope Prop Competition

Offline gumby

Re: How to use Qualifying Adjectives with the parser?
« Reply #31 on: November 21, 2010, 09:04:07 AM »
Ah!  6 hours did it.  Brain death complete.

Hey MI, looks like X-Mas came a bit early this year for the both of us!  I think this problem is solved.  Here is the complete solution for the 'basket of goodies' problem:

First, modify the User.sc (you should be able to find the (existing) outer if statement)
Code: [Select]
   (if( (self:getInput(pEvent)) and Parse(@inputStr pEvent))
   = teststr @inputStr
   (if( == STRINGS_EQUAL StrCmp(teststr "take basket of goodies")) 
                  findAndReplace(teststr "basket of goodies" "basketofgoodies")
                  = pEvent (Event:new())
                   Parse(@findAndReplaceStr pEvent)
            )
            (send pEvent:type(evSAID))
            (self:said(pEvent))
   )

Also add these new procedure in the User.sc
Code: [Select]
(procedure public (getChar inputStr index)
   (var indexChar)
   Format(indexChar "%c" StrAt(inputStr index) )
   return(indexChar)
)


(procedure public (findAndReplace sourceStr findStr replaceStr)
  (var i, findStrIndex, returnStrIndex)

  StrCpy(@findAndReplaceStr "")
  = findStrIndex 0
  = returnStrIndex 0
  (for (=i 0) (<i StrLen(sourceStr)) (++i)
    (if (<> StrAt(sourceStr i) StrAt(findStr findStrIndex) )
          StrCat(@findAndReplaceStr getChar(sourceStr i))
          = findStrIndex 0
  ) (else
    (while (== StrAt(sourceStr (+ i findStrIndex)) StrAt(findStr findStrIndex))
       ++findStrIndex
  )

  (if (== findStrIndex (+ StrLen(findStr) 1))
  StrCat(@findAndReplaceStr replaceStr)
  = i (+ i findStrIndex)
  ) (else
             StrCat(@findAndReplaceStr getChar(sourceStr i))
             ++returnStrIndex
             = findStrIndex 0      
  )
  )
  )
)

Then add a new global variable in the Main.sc script
Code: [Select]
  findAndReplaceStr    // This is for holding our modified parsed input

Oh, and don't forget to add 'basketofgoodies' into the vocab - otherwise the User.sc won't compile.  Any time you've got a new 'compound noun' to add, just throw additional find-and-replace logic in the User.sc & also update the vocab.

Now you can do this in your room scripts:
Code: [Select]
        (if(Said('take/basketofgoodies'))
            Print("You got the goodies basket!")
        )     

Improvements can be made here, obviously we don't really want to have to specify every possible Said string - what I should do is fix that 2nd if statement that determines if the user types 'take basket of goodies' & change it to just detect if the words 'basket of goodies' are in it.  Guess I'll do that next...
In the Great Underground Empire (Zork port in development)
Winter Break 2012 Rope Prop Competition

Offline MusicallyInspired

Re: How to use Qualifying Adjectives with the parser?
« Reply #32 on: November 21, 2010, 10:05:05 AM »
Nice! I'll give this a try later today if I have time! Sweet!
Brass Lantern Prop Competition

Offline gumby

Re: How to use Qualifying Adjectives with the parser?
« Reply #33 on: November 21, 2010, 04:26:42 PM »
Uh-oh.  After executing the findAndReplace function, if navigation of the ego is attempted, the game crashes after a few steps are taken (ego gets totally corrupted on-screen & game locks up).  Hmmmm...
In the Great Underground Empire (Zork port in development)
Winter Break 2012 Rope Prop Competition

Offline gumby

Re: How to use Qualifying Adjectives with the parser?
« Reply #34 on: November 21, 2010, 04:55:41 PM »
All fixed!   I've also added a 'contains string' function so we can use 'basket of goodies' in ANY context.

User.sc - note that the 'getChar' function is removed - it was causing the crashing
Code: [Select]
(if( (self:getInput(pEvent)) and Parse(@inputStr pEvent))
= teststr @inputStr
(if (containsStr(teststr "basket of goodies"))
               findAndReplace(teststr "basket of goodies" "basketofgoodies")
               = pEvent (Event:new())
               Parse(@findAndReplaceStr pEvent)
         )

(send pEvent:type(evSAID))
(self:said(pEvent))
)

...

(procedure public (findAndReplace sourceStr findStr replaceStr)

  (var i, findStrIndex char )

  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))
       ++findStrIndex
  )
  (if (== findStrIndex (+ StrLen(findStr) 1))
  StrCat(@findAndReplaceStr replaceStr)
  = i (+ i findStrIndex)
  ) (else
             Format(char "%c" StrAt(sourceStr i))
             StrCat(@findAndReplaceStr char)
             = findStrIndex 0      
  )
  )
  )
)

(procedure public (containsStr sourceStr findStr)

  (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))
       ++findStrIndex
  )
  (if (== findStrIndex (+ StrLen(findStr) 1))
  = i (+ i findStrIndex)
  return TRUE
  ) (else
             = findStrIndex 0      
  )
  )
  )
  return FALSE
)


And now, in the room scripts we can do this:
Code: [Select]
(if(Said('take/basketofgoodies'))
       Print("You got the goodies basket. It's pretty heavy...")
)         
(if(Said('eat/basketofgoodies'))
       Print("You eat the whole basket of goodies, and promptly toss your cookies!")
)

The last improvement that I see is to perhaps put the mapping of 'basket of goodies' to 'basketofgoodies' in a text resource, or a global array, or something so that we can define and iterate through all the 'complex nouns', rather than have to repeat the if-blocks over-and-over for each one.

Anyone got any ideas on that one?  Here is the specific section of code that needs 'generalization':
Code: [Select]
(if (containsStr(teststr "basket of goodies"))
               findAndReplace(teststr "basket of goodies" "basketofgoodies")
               = pEvent (Event:new())
               Parse(@findAndReplaceStr pEvent)
         )

(if (containsStr(teststr "pieces of eight"))
               findAndReplace(teststr "pieces of eight" "piecesofeight")
               = pEvent (Event:new())
               Parse(@findAndReplaceStr pEvent)
         )

         ... etc, etc, etc...
In the Great Underground Empire (Zork port in development)
Winter Break 2012 Rope Prop Competition

Offline Collector

Re: How to use Qualifying Adjectives with the parser?
« Reply #35 on: November 22, 2010, 03:13:54 AM »
What about recognizing different phrases the user might input, such as "goodies basket", basket of stuff, etc.? Would you repeat the same function for every possibility? If so, would that lead to heap errors? Perhaps it could be modified in a way that you could list all possible strings within the same function.
KQII Remake Pic

Offline gumby

Re: How to use Qualifying Adjectives with the parser?
« Reply #36 on: November 22, 2010, 10:38:22 AM »
What about recognizing different phrases the user might input, such as "goodies basket", basket of stuff, etc.? Would you repeat the same function for every possibility? If so, would that lead to heap errors? Perhaps it could be modified in a way that you could list all possible strings within the same function.
Yeah, every single combination of words that we would want to 'collapse' (i.e. basket of goodies to basketofgoodies) would have to be set up, which would result in a pretty big (and possibly heap error-prone) User.sc script.

However, your statement made me think of just generalizing this even more.  Maybe we could just look for the keyword 'of' and grab the words on either side of it & collapse it.  Then it would be just one statement.  I'm thinking something like this:

Code: [Select]
(if (containsStr(teststr " of "))  // Make sure you have the leading & trailing space so we truly are matching the 'of' word (avoid matching to 'roof' for example)
               findAndReplace(teststr " of " "of")  // Replace ' of ' with just 'of', which would effectively collapse our word
               = pEvent (Event:new())
               Parse(@findAndReplaceStr pEvent)
         )

Hey, this might work great!  However, now 'of' cannot be used in ANY other context.  The use of the word 'of' in the user input would always result in a collapse.  But that might be ok in this case.  Thoughts??  Oh, and can anyone think of other words we want to do this to?
In the Great Underground Empire (Zork port in development)
Winter Break 2012 Rope Prop Competition

Offline Collector

Re: How to use Qualifying Adjectives with the parser?
« Reply #37 on: November 22, 2010, 02:31:32 PM »
Remember, not all modifiers will use "of". A player could type "take goodies basket." What if you replaced the string with a variable that could be replaced by all possible strings? Does the SCI scripting include an "OrIf" that could allow a list of  all of the possible strings that fit the variable.

Perhaps a better way would be to treat the modifier as another noun, i.e. either "take goodies" or "take basket" would also place the item in the inventory.
KQII Remake Pic

Offline gumby

Re: How to use Qualifying Adjectives with the parser?
« Reply #38 on: November 22, 2010, 03:05:35 PM »
Remember, not all modifiers will use "of". A player could type "take goodies basket."
Yeah, hadn't considered phrases that don't utilize a modified (or if the user omitted it).

What if you replaced the string with a variable that could be replaced by all possible strings? Does the SCI scripting include an "OrIf" that could allow a list of  all of the possible strings that fit the variable.
I don't think there is an OR operation that works against a set of data (or a string of delimited values).  Also, I'd probably have to change the 'findAndReplace' function call, because the parameters would need to be correlated which we can't do with OR logic.

Now I'm leaning toward putting all possible combinations in a text resource & iterating over it to get the results we want.  Dunno.
In the Great Underground Empire (Zork port in development)
Winter Break 2012 Rope Prop Competition

Offline MusicallyInspired

Re: How to use Qualifying Adjectives with the parser?
« Reply #39 on: November 22, 2010, 05:16:29 PM »
"Get Goodies Basket" is already possible at this point in the same way that "Get Silver Key" is possible. It's the addition of the word "of" that's the most problematic.
Brass Lantern Prop Competition

Offline gumby

Re: How to use Qualifying Adjectives with the parser?
« Reply #40 on: November 22, 2010, 07:36:37 PM »
But, for example, would the phrase "put goodies basket on table" work ok?  I guess if 'goodies' is classified as an adjective it would?  Yeah, I guess it would.  I'm just thinking about the '3 part maximum' that the parser splits the phrase into...
« Last Edit: November 22, 2010, 07:40:13 PM by gumby »
In the Great Underground Empire (Zork port in development)
Winter Break 2012 Rope Prop Competition

Offline gumby

Re: How to use Qualifying Adjectives with the parser?
« Reply #41 on: November 22, 2010, 10:12:41 PM »
Quick update:  The 'of' logic I previously posted with did not work.  I'm not going to bother looking into it.  I'm moving forward with implementing a text resource.
In the Great Underground Empire (Zork port in development)
Winter Break 2012 Rope Prop Competition

Offline MusicallyInspired

Re: How to use Qualifying Adjectives with the parser?
« Reply #42 on: November 23, 2010, 01:12:14 AM »
Man what an annoying fix this is. I wish Brian was here and cared enough to explain it...
Brass Lantern Prop Competition

Offline gumby

Re: How to use Qualifying Adjectives with the parser?
« Reply #43 on: November 23, 2010, 08:03:59 AM »
Man what an annoying fix this is. I wish Brian was here and cared enough to explain it...
I think the problem I was running into most recently ('of') is due to whitespace comparison (but I was doing it before and it was/is working!).  It's not a big deal and now the pain has been felt & I think I've got decent handle on string manipulation.  Hopefully we will have a full solution buttoned up in the next few days...
In the Great Underground Empire (Zork port in development)
Winter Break 2012 Rope Prop Competition

Offline gumby

Re: How to use Qualifying Adjectives with the parser?
« Reply #44 on: November 24, 2010, 04:34:11 PM »
Here is the final result.  I've moved (almost) all the logic out of the User.sc & Main.sc to keep it cleaner.  I did find out the problem I was having earlier was due to the null-termination of the strings which totally fouled up my matching process.

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)  Add the following code into the User.sc script
Code: [Select]
(use "ComplexNoun") // add this near the top, we'll create this script in step 5

// Then find this code block below & insert the line for the 'handleComplexNoun' procedure call
    (if( (self:getInput(pEvent)) and Parse(@inputStr pEvent))
          handleComplexNoun(@inputStr pEvent)
  (send pEvent:type(evSAID))
  (self:said(pEvent))
    )

 
5)  Create a new script file called ComplexNoun.sc and include the following code
 
Code: [Select]
(include "sci.sh")
(include "game.sh")
/******************************************************************************/
(script 972)             // This may need to be changed if this number is already in use
/******************************************************************************/
(use "main")
(local
   findAndReplaceStr
)

(procedure public (handleComplexNoun inputStr pEvent)
  (var i,complexNounFull[50], complexNounStripped[50])
 
  = i 0
  (do
    GetFarText( COMPLEXNOUN_TEXT i @complexNounFull)
    (if (containsStr(inputStr @complexNounFull))
  findAndReplace(@complexNounFull " " "")                             
  StrCpy(@complexNounStripped @findAndReplaceStr)
  findAndReplace(inputStr @complexNounFull @complexNounStripped)
  Parse(@findAndReplaceStr pEvent)
    )      
     ++i
  ) while ( > StrLen(@complexNounFull) 0) 
)

(procedure public (findAndReplace sourceStr findStr replaceStr)
  (var i, findStrIndex char )

  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))
    ++findStrIndex
)
         // >= comparison to handle match at end of string (null terminator)
(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      
         )
     )
  )
)

(procedure public (containsStr sourceStr findStr)
  (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))
     ++findStrIndex
         )
         (if (== findStrIndex (+ StrLen(findStr) 1))
    return TRUE
) (else
            = findStrIndex 0      
         )
      )
  )
  return FALSE
)

6)  Don't forget to recompile the User.sc script.  You can now use logic like this in your room scripts (or whereever):

Code: [Select]
  (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")
  )
In the Great Underground Empire (Zork port in development)
Winter Break 2012 Rope Prop Competition


SMF 2.0.19 | SMF © 2021, Simple Machines
Simple Audio Video Embedder

Page created in 0.037 seconds with 22 queries.