ainslec / adventuron-issue-tracker

Adventuron Issues Tracker
4 stars 0 forks source link

GET BOTTLE is converted to GET WATER #277

Open warrigal24 opened 3 years ago

warrigal24 commented 3 years ago

I have an empty bottle and some inconspicuous water. When I GET WATER with the empty bottle, it's supposed to get swapped with the bottle of water, but I can't GET BOTTLE in the first place, because it's converted to GET WATER. Debugging code indicates that noun1 is wrongly converted to 'water', rather than 'bottle'. Why is Adventuron so obsessed with changing my input to something other than what I typed and looking at objects that aren't in scope?

Sample input is below.

>GET BOTTLE
Expected: You get the bottle.
Actual: You don't have anything to carry the water in.

Sample code is below.

start_at = room001

game_settings {
   add_standard_prepositions = false
   auto_pluralize_nouns = false
   enable_standard_all_behaviour = false
   experimental_auto_propogate_known = true
   experimental_new_parser = true
   experimental_new_scoping = true
   header_length_limit = 32
   rewind_enabled = true
   rollback_enabled = true
}

locations {
   room001 : location "You're inside a building, a well house for a large spring.";
}

objects {
   water : scenery "some water" at = "room001" conspicuous = "false";
   bottle : object "a small bottle" at = "room001" {experimental_matching_text_sequences = ["small bottle", "bottle"]}
   bottled_water : object "a bottle of water" {experimental_matching_text_sequences = ["water in the bottle", "water in bottle", "bottle of water", "bottled water", "water", "bottle"]}
}

on_command {
   : gosub "test_parser";
   // Water
   : if (is_present "water") {
      : match "examine water" {
         : print "It's crystal clear.";
         : done;
      }
      : match "drink water" {
         : print "Very refreshing.";
         : done;
      }
      : match "get water" {
         : if (is_carried "bottled_water") {
            : print "The bottle is already full of water.";
            : done;
         }
         : if (!is_carried "bottle") {
            : print "You don't have anything to carry the water in.";
            : done;
         }
         : swap o1 = "bottle" o2 = "bottled_water";
         : print "The bottle is now full of water.";
         : done;
      }
   }

   // Bottle
   : if (is_present "bottle") {
      : match "examine bottle" {
         : print "It's just a small empty bottle.";
         : done;
      }
      : match "empty bottle" {
         : print "It's already empty.";
         : done;
      }
      : match "fill bottle" {
         : if (is_present "water") {
            : swap o1 = "bottle" o2 = "bottled_water";
            : print "You fill the bottle with water.";
            : done;
         }
         : print "There's nothing to fill it with.";
         : done;
      }
   }
   // Bottle of water
   : if (is_present "bottled_water") {
      : match "examine bottle;examine water" {
         : print "It's just a small bottle full of water.";
         : done;
      }
      : match "drink water" {
         : swap o1 = "bottled_water" o2 = "bottle";
         : print "You guzzle down the water until it's all gone.";
         : done;
      }
      : match "empty bottle;pour water" {
         : swap o1 = "bottled_water" o2 = "bottle";
         : print "You pour out the water and it soaks into the ground.";
         : done;
      }
      : match "fill bottle" {
         : print "It's already full of water.";
         : done;
      }
   }
   // Drop
   : match "drop *" {
      : disambiguate_s1 "carried";
      : if (noun1_is "all") {
         : do_all "inventory_notworn";
      }
      : if (!is_carried(s1())) {
         : print {("You're not carrying any " + original "noun1" + ".")}
         : done;
      }
      : if (is_carried (s1()) && !is_worn (s1())) {
         : drop quiet = "true";
         : print {("You " + original "verb" + " " + definite(d(s1())) + ".")}
         : done;
      }
   }
   // Get
   : match "get *" {
      : disambiguate_s1 "present";
      : if (noun1_is "all") {
         : do_all "current_location_objects";
      }
      : if (is_carried(s1())) {
         : print {("You're already carrying " + definite(d(s1())) + ".")}
         : done;
      }
      : if (!is_present(s1())) {
         : print {("You can't see any " + original "noun1" + " here.")}
         : done;
      }
      : if (!is_pocketable (s1())) {
         : print "You can't carry any more. Try dropping something.";
         : done;
      }
      : if (is_beside (s1())) {
         : get quiet = "true";
         : print {("You " + original "verb" + " " + definite(d(s1())) + ".")}
         : 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

Related to issue #198

ainslec commented 3 years ago

Reformatted for ease of review ....

start_at = room001

locations {
   room001 : location "You're inside a building, a well house for a large spring.";
}

objects {
   water : scenery "some water" at = "room001" conspicuous = "false";
   bottle : object "a small bottle" at = "room001" {
      experimental_matching_text_sequences = [
         "small bottle",
         "bottle"
      ]
   }
   bottled_water : object "a bottle of water" {
      experimental_matching_text_sequences = [
         "water in the bottle",
         "water in bottle",
         "bottle of water",
         "bottled water",
         "water",
         "bottle"
      ]
   }
}

on_command {
   : if (true) { : debug_sentence; }

   // Water
   : if (is_present "water") {
      : match "examine water" {
         : print "It's crystal clear.";
         : done;
      }
      : match "drink water" {
         : print "Very refreshing.";
         : done;
      }
      : match "get water" {
         : if (is_carried "bottled_water") {
            : print "The bottle is already full of water.";
            : done;
         }
         : if (!is_carried "bottle") {
            : print "You don't have anything to carry the water in.";
            : done;
         }
         : swap o1 = "bottle" o2 = "bottled_water";
         : print "The bottle is now full of water.";
         : done;
      }
   }

   // Bottle
   : if (is_present "bottle") {
      : match "examine bottle" {
         : print "It's just a small empty bottle.";
         : done;
      }
      : match "empty bottle" {
         : print "It's already empty.";
         : done;
      }
      : match "fill bottle" {
         : if (is_present "water") {
            : swap o1 = "bottle" o2 = "bottled_water";
            : print "You fill the bottle with water.";
            : done;
         }
         : print "There's nothing to fill it with.";
         : done;
      }
   }

   // Bottle of water
   : if (is_present "bottled_water") {
      : match "examine bottle;examine water" {
         : print "It's just a small bottle full of water.";
         : done;
      }
      : match "drink water" {
         : swap o1 = "bottled_water" o2 = "bottle";
         : print "You guzzle down the water until it's all gone.";
         : done;
      }
      : match "empty bottle;pour water" {
         : swap o1 = "bottled_water" o2 = "bottle";
         : print "You pour out the water and it soaks into the ground.";
         : done;
      }
      : match "fill bottle" {
         : print "It's already full of water.";
         : done;
      }
   }
   // Drop
   : match "drop *" {
      : disambiguate_s1 "carried";
      : if (noun1_is "all") {
         : do_all "inventory_notworn";
      }
      : if (!is_carried(s1())) {
         : print {("You're not carrying any " + original "noun1" + ".")}
         : done;
      }
      : if (is_carried (s1()) && !is_worn (s1())) {
         : drop quiet = "true";
         : print {("You " + original "verb" + " " + definite(d(s1())) + ".")}
         : done;
      }
   }
   // Get
   : match "get *" {
      : disambiguate_s1 "present";
      : if (noun1_is "all") {
         : do_all "current_location_objects";
      }
      : if (is_carried(s1())) {
         : print {("You're already carrying " + definite(d(s1())) + ".")}
         : done;
      }
      : if (!is_present(s1())) {
         : print {("You can't see any " + original "noun1" + " here.")}
         : done;
      }
      : if (!is_pocketable (s1())) {
         : print "You can't carry any more. Try dropping something.";
         : done;
      }
      : if (is_beside (s1())) {
         : get quiet = "true";
         : print {("You " + original "verb" + " " + definite(d(s1())) + ".")}
         : done;
      }
   }
}
ainslec commented 3 years ago

Analysis (not yet resolved this in code yet).

The reason this appears to be happening, is that the matching_text_sequences are being substituted for a single noun prior to logical sentence processing. In this case, the "bottle" alias for the"bottled_water" object id maps to "water" noun.

The water noun then during adjective noun scanning maps to the "water" object id.

The resolution to this is to resolve the text sequences during the adjective noun scanning stage.

Problems to be resolved are what happens if the same text sequence applies to two objects. In that case, then the usual known/universal matching should be used, and disambiguation should take care of everything else.

This late resolution will likely take a little more cpu time, but adventuron has mountains of cpu time, and this will resolve the issue.

ainslec commented 3 years ago

Now the problem has morphed via input

> GET BOTTLE
> FILL BOTTLE
> DROP BOTTLE
... does not work

New code snippet

start_at = room001

game_settings {
   add_standard_prepositions = false
   auto_pluralize_nouns = false
   enable_standard_all_behaviour = false
   experimental_auto_propogate_known = true
   header_length_limit = 32
}

locations {
   room001 : location "You're inside a building, a well house for a large spring.";
}

objects {
   water : scenery "some water" at = "room001" conspicuous = "false";
   bottle : object "a small bottle" at = "room001" noun="bottle" {
      experimental_matching_text_sequences = [
         "small bottle",
         "bottle"
      ]
   }
   bottled_water : object "a bottle of water" noun="water" {
      experimental_matching_text_sequences = [
         "water in the bottle",
         "water in bottle",
         "bottle of water",
         "bottled water",
         "water",
         "bottle"
      ]
   }
}

on_command {
   : if (true) {:debug_sentence;}
   // Water
   : if (is_present "water") {
      : match "examine water" {
         : print "It's crystal clear.";
         : done;
      }
      : match "drink water" {
         : print "Very refreshing.";
         : done;
      }
      : match "get water" {
         : if (is_carried "bottled_water") {
            : print "The bottle is already full of water.";
            : done;
         }
         : if (!is_carried "bottle") {
            : print "You don't have anything to carry the water in.";
            : done;
         }
         : swap o1 = "bottle" o2 = "bottled_water";
         : print "The bottle is now full of water.";
         : done;
      }
   }

   // Bottle
   : if (is_present "bottle") {
      : match "examine bottle" {
         : print "It's just a small empty bottle.";
         : done;
      }
      : match "empty bottle" {
         : print "It's already empty.";
         : done;
      }
      : match "fill bottle" {
         : if (is_present "water") {
            : swap o1 = "bottle" o2 = "bottled_water";
            : print "You fill the bottle with water.";
            : done;
         }
         : print "There's nothing to fill it with.";
         : done;
      }
   }
   // Bottle of water
   : if (is_present "bottled_water") {
      : match "examine bottle;examine water" {
         : print "It's just a small bottle full of water.";
         : done;
      }
      : match "drink water" {
         : swap o1 = "bottled_water" o2 = "bottle";
         : print "You guzzle down the water until it's all gone.";
         : done;
      }
      : match "empty bottle;pour water" {
         : swap o1 = "bottled_water" o2 = "bottle";
         : print "You pour out the water and it soaks into the ground.";
         : done;
      }
      : match "fill bottle" {
         : print "It's already full of water.";
         : done;
      }
   }
   // Drop
   : match "drop *" {
      : disambiguate_s1 "carried";
      : if (noun1_is "all") {
         : do_all "inventory_notworn";
      }
      : if (!is_carried(s1())) {
         : print {("You're not carrying any " + original "noun1" + ".")}
         : done;
      }
      : if (is_carried (s1()) && !is_worn (s1())) {
         : drop quiet = "true";
         : print {("You " + original "verb" + " " + definite(d(s1())) + ".")}
         : done;
      }
   }
   // Get
   : match "get *" {
      : disambiguate_s1 "present";
      : if (noun1_is "all") {
         : do_all "current_location_objects";
      }
      : if (is_carried(s1())) {
         : print {("You're already carrying " + definite(d(s1())) + ".")}
         : done;
      }
      : if (!is_present(s1())) {
         : print {("You can't see any " + original "noun1" + " here.")}
         : done;
      }
      : if (!is_pocketable (s1())) {
         : print "You can't carry any more. Try dropping something.";
         : done;
      }
      : if (is_beside (s1())) {
         : get quiet = "true";
         : print {("You " + original "verb" + " " + definite(d(s1())) + ".")}
         : done;
      }
   }
}

One of the problems is that the sample code checks for specific noun, and the noun is no longer a unique identifier if it has been mapped.

//  Does this want to match bottled water
: match "examine water" {
}
ainslec commented 3 years ago

Design Thoughts

Sentence Tokenizer Changes

When resolving initial s1 & s2....

To be calculated when the sentence is processed ....

Word Calculation Cleansing

  1. Omit articles
  2. Omit prepositions
  3. Eliminate internal special characters

(e.g. an eye-of-newt becomes "eye" "newt")

logical sentence

Always look up the most specific subject first. Then the other subject can be calculated from the remaining entities.

Rationalized Noun will correspond to the noun associated with the object id.

Add Topics

Topics should be added, which allow us to put nouns into global scope. Therefore all words refer to a unique entity or topic.

Update Match Command

Update match command to be able to reference individual entity / topic ids, but alow be able to reference other special values, such as traits.

(eventually we will add add pattern handlers to deal with this - this is just the first cut.

: match "get [water_bottle]" {

}

Be sure to examine the following parts:

Check out everything that touches ... LogicalSentenceElementOverrides

Especially

sentenceTokenizer.explodeLogicalSentence sentenceTokenizer.handleSentenceChunk

ainslec commented 3 years ago

More Notes + Sample Code

start_at   = room1
redescribe = auto_beta

game_settings {
   experimental_new_parser = true
}

locations {
   room1 : location "From here you can go inside <(enter)<_dir>>[south] the room2. ";
   room2 : location "You are in room2. You exit the room to go to room1." ;
   room3 : location "You are in room3." ;
}

connections {
   from, direction, to = [
      room1, enter, room2
      room1, south, room3
   ]
}

objects {

   // Per Object ... 
   //    Set of adjectives + aliases
   //    Set of nouns + aliases

   // When matching, tally the adjectives, tally the nouns.
   // Score the match
   // Needs at least one noun to match
   // The more specific scores highest.
   // Exact match is highest
   // Everything is treated as noun, unless adjectives defined elsewhere (id scanning, or vocabulary section)
   // In the case of orange orange, the same word is specified twice, so the earlier word is an orange adjective, the later word is an orange noun.
   // Adjectives can have different synonyms
   // Think about word category.
   // an orange orange = fruit
   // eye-of-newt = ingredient
   // torch = light

   orange_orange : object "an orange orange" at = "room1";
   orange_tie    : object "an orange tie" at = "room1";
   bottle_1      : object "a bottle of water" at = "room1" ;
   bottle_2      : object "a bottle of wine" at = "room1" ;
   eyeofnewt     : object "eye-of-newt"  at = "room1";
   earl            : scenery "the earl of york"  at = "room1";
   jackbox     : object "a jack in the box"  at = "room1";
   torch         : object "torch"  at = "room1";
   torch_1       : object "a lit torch" at = "room1" ;
   dogbone    : object "a dog and bone" at = "room1" ;
   torch_2       : object "a torch (on)"  at = "room1";
   torch_3       : object "a switched on torch"  at = "room1";
   torch_4       : object "a switched-on torch"  at = "room1" msg = "fdsfdfdsdsfsd" ;
   // TODO :: Dog and Bone
}

on_command {

   : match "get *"  {
      : disambiguate_s1 "beside";
      : debug_sentence;
      : get ;

   }

   : match "x"  {
      : match "x *"  {
         : disambiguate_s1 "present";
         : debug_sentence;
         : gosub "examine";
      }

      : match "x -"  {
         : if (count "_current_location" == 0) {
            : print "Nothing obvious to look at.";
         }
         : else_if (count "_current_location" == 1) {
            : print "Nothing obvious to look at.";
            : iterate "_current_location"  {
              : set_subject1 (item());
              : gosub "examine";
            }
         }
         : else {
            : iterate "_current_location"  {
               : add_choice text -> (d(item())) payload -> (item()) ;
            }
            : choose "Examine what?" ;
            : if (chosen() != "") {
               : set_subject1 (chosen());
               : gosub "examine";
            }
            : else {
               : print "Nothing obvious to examine.";
            }
         }
      }
   }

}

subroutines {

   examine : subroutine {
      : if (s1() != "unknown") {
         : if (emsg ("*") != "") {
            : print (emsg ("*"));
         }
         : else {
            : print "You notice nothing special.";

         }
      }
   }

}

themes {
   my_theme : theme {
      theme_settings {
         text_decorations= [ directions, directions_subtract ]
      }
      lister_exits {
         exit_list_style   = verbose
         //is_list_enter    = false
      }

      lister_objects {
         list_type = list
      }

      colors {
         exit_list_item_pen      = 14
      }
   }
}
ainslec commented 3 years ago

Pre-existing registered prepositions = [of] Pre-existing registered adjectives = [] Pre-existing registered nouns = [newt, swimmy]

get eye of newt

Seperate into different categories, so that difference aliases can be applied to each category.

All words are scanned as nouns (except internal prepositions), all items are lower cased, with punctuation removed.

A noun doesn't have to be in the dictionary to be scanned as a neo noun. A neo noun exists atop of the existing parser (for now), but the existing dictionaries are used for working out if something is an adjective or preposition.

s1_neo_adjectives = [ - ] s1_neo_nouns = [eye, newt] s1_neo_word_sequence = [eye, of, newt]

Need s2, s3, s0, variants.

When scanning the entity heirarchy, a similar cleansing operation is done, then a scoring is applied. If there is a precise sequence match or if more words match, then there is no ambiguity. If there are multiple matches, then s1 will contain multiple elements ready for disambiguation.