ainslec / adventuron-issue-tracker

Adventuron Issues Tracker
4 stars 0 forks source link

Parser matches wrong object to noun1 and s1 #257

Open warrigal24 opened 3 years ago

warrigal24 commented 3 years ago

I have a cookie box and a jack-in-the-box. As the latter has multiple words and special characters in its description, its matching noun phrases are defined using experimental_matching_text_sequences. Whenever I use a noun phrase such as 'jack-in-the-box' in a command referring to this object, the parser wrongly reduces the noun to 'box' and identifies this as the cookie box, even though the entered noun phrase is not a match for any of the noun phrases used by the cookie box.

Here's an example:

>MAKE JACK-IN-THE-BOX

Expected: You assemble the cookie box, cardboard cylinder, spring and hand puppet and make a near perfect jack-in-the-box. Actual: Santa didn't ask you to make that.

Here's the sample with debugging code to show how the original command has been parsed.

start_at = room01

traits {
   treasure_t : trait;
}

locations {
   room01 : location "You're in a factory.";
}

objects {
   cookie_box : object "a cookie box" at = "room01";
   spring : object "a spring" at = "room01";
   cardboard_cylinder : object "a cardboard cylinder" at = "room01";
   hand_puppet : object "a hand puppet" at = "room01" wearable = "true";
   jack_box : object "a jack-in-the-box" {experimental_matching_text_sequences = ["jack-in-the-box", "jack in the box", "jack-in-box", "jack in box", "jack", "box"] traits = [treasure_t]}
}

on_pre_command {
   : match "_ _" {
      : gosub "test_parser";
   }
}

on_command {
   : match "make _" {
      : if (!s1_has_trait "treasure_t") {
         : print "Santa didn't ask you to make that.";
         : done;
      }
      : if (noun1_is "box") {
         : if (!has_not_created "jack_box") {
            : print "You've already made a jack-in-the-box.";
            : done;
         }
         : if (!(is_present "cookie_box" && is_present "cardboard_cylinder" && is_present "hand_puppet" && is_present "spring")) {
            : print "You don't have all the materials you need to make a jack-in-the-box.";
            : done;
         }
         : destroy "cookie_box";
         : destroy "cardboard_cylinder";
         : destroy "hand_puppet";
         : destroy "spring";
         : create "jack_box";
         : print "You assemble the cookie box, cardboard cylinder, spring and hand puppet and make a near perfect jack-in-the-box.";
         : done;
      }
   }
}

subroutines {
   test_parser : subroutine {
      : match "_ _" {
         : mask {
            : print {("^n^verb = " + original "verb")}
            : print {("^n^preposition1 = " + original "preposition1")}
            : print {("^n^noun1 = " + original "noun1")}
            : print {("^n^preposition2 = " + original "preposition2")}
            : print {("^n^noun2 = " + original "noun2")}
            : print {("^n^s1 = " + s1())}
            : print {("^n^s2 = " + s2() + "^m^")}
         }
      }
   }
}
ainslec commented 3 years ago

I'm not a big fan of "experimental_matching_text_sequences" per object, even though I created the setting (experimental prefix is the clue when I'm out on a limb).

Also, again, you are calling "s1_has_trait" without telling adventuron what how to asscociate the noun with one particular object. In this case, you want to scan across items that might not be present or even known). Scanning across universal objects may also not be what you want as a cookie box not be makeable. In this case, use disambiguate_s1 with "universal" and with_trait "makeable_t" (I just introduced it in the snippet below).

Feel free to ask questions about the code below (latest beta required).

start_at = room01

traits {
   treasure_t : trait;
   makeable_t : trait;
}

locations {
   room01 : location "You're in a factory.";
}

objects {
   cookie_box : object "a cookie box" at = "room01";
   spring : object "a spring" at = "room01";
   cardboard_cylinder : object "a cardboard cylinder" at = "room01";
   hand_puppet : object "a hand puppet" at = "room01" wearable = "true";
   jack_box : object "a jack-in-the-box" { traits = [treasure_t, makeable_t]}
}

vocabulary {

   // Note we don't associate "box" with an object, as this would
   // mask out cookie box.

   // Instead we can rely on the disambigation_s1 if the player types "VERB box"
   : experimental_replace { text = "jack"            with = "jack box" }
   : experimental_replace { text = "jack-in-box"     with = "jack box" }
   : experimental_replace { text = "jack in box"     with = "jack box" }
   : experimental_replace { text = "jack-in-the-box" with = "jack box" }
   : experimental_replace { text = "jack in the box" with = "jack box" }
}

on_pre_command {
   : match "_ _" {
      : gosub "test_parser";
   }
}

on_command {
   : match "make _" {

      // Only disambiguate known subjects ... 
      : disambiguate_s1  {
         // Universal means that we can match on non existing / non known objects
         category   = "universal"

         // We will only match on objects with the makeable_t trait.
         with_trait = "makeable_t"
      }

      // s1_has_trait requires a call to disambiguate_s1() before testing.
      : if (!s1_has_trait "treasure_t") {
         : print "Santa didn't ask you to make that.";
         : done;
      }
      : if (noun1_is "box") {
         : if (!has_not_created "jack_box") {
            : print "You've already made a jack-in-the-box.";
            : done;
         }
         : if (!(is_present "cookie_box" && is_present "cardboard_cylinder" && is_present "hand_puppet" && is_present "spring")) {
            : print "You don't have all the materials you need to make a jack-in-the-box.";
            : done;
         }
         : destroy "cookie_box";
         : destroy "cardboard_cylinder";
         : destroy "hand_puppet";
         : destroy "spring";
         : create "jack_box";
         : print "You assemble the cookie box, cardboard cylinder, spring and hand puppet and make a near perfect jack-in-the-box.";
         : done;
      }
   }
}

subroutines {
   test_parser : subroutine {
      : match "_ _" {
         : mask {
            : print {("^n^verb = " + original "verb")}
            : print {("^n^preposition1 = " + original "preposition1")}
            : print {("^n^adjective1 = " + original "adjective1")}
            : print {("^n^noun1 = " + original "noun1")}
            : print {("^n^preposition2 = " + original "preposition2")}
            : print {("^n^noun2 = " + original "noun2")}
            : print {("^n^s1 = " + s1())}
            : print {("^n^s2 = " + s2() + "^m^")}
         }
      }
   }
}
ainslec commented 3 years ago

Is this answer acceptable?

ainslec commented 3 years ago

OK to close this?

warrigal24 commented 3 years ago

There is a workaround (as there usually is), so I don't mind if you close this. However, it doesn't address the underlying issue of an object being identified as a match when it actually isn't a match at all. By a 'match', I mean a match on the originally entered noun phrase. If I enter 'jack-in-the-box', there is no way that this should be a match for 'cookie box'. The pattern matching should be done long before the scoping and disambiguation. I still don't understand how Adventuron can get something like that so wrong. It has nothing to do with s1 and universal and traits and all that other stuff that comes after the initial pattern matching. We can discuss this offline.