Pre-release
Adventure.js Docs Downloads
Score: 0 Moves: 0
// verifySentence.js

(function () {
  /*global adventurejs A*/
  "use strict";

  var p = adventurejs.Parser.prototype;

  /**
   * After parse, verify each word in sentence.
   * @memberOf adventurejs.Parser
   * @method adventurejs.Parser#verifySentence
   */
  p.verifySentence = function Parser_parseSentence() {
    let this_turn = this.input_history[0];
    this.game.log(
      "log",
      "high",
      "verifySentence.js > " + this_turn.input,
      "Parser"
    );

    let count = {
      noun: 0,
      prep: 0,
      verb: 0,
      adverb: 0,
      adjective: 0,
      direction: 0,
      exclusion: 0,
      unknown: 0,
      string: 0,
      phrase: 0,
    };
    let firstverb;

    for (
      let position = 0;
      position < this_turn.parsed_sentence.length;
      position++
    ) {
      let this_word = this_turn.parsed_sentence[position];
      let last_word = this_turn.parsed_sentence[position - 1];
      let next_word = this_turn.parsed_sentence[position + 1];
      let msg = "";

      this.game.debug(
        `F1203 | verifySentence.js | ${this_word.type}/${this_word.word} `
      );

      // ----------------------------------------
      // verb
      // ----------------------------------------

      if (this_word.type === "verb") {
        // we don't accept double verbs except for "oops verb"
        if (
          last_word &&
          last_word.type === "verb" &&
          last_word.word !== "oops"
        ) {
          // too many verbs
          this.game.debug(
            `F1198 | verifySentence.js | input found two verbs in a row`
          );
          msg += this.getUnparsedMessage(this_turn.input);
          this.game.print(msg);
          return false;
        }
        count.verb++;
        this_turn.verified_sentence_structure += "verb ";
        this_turn.verified_sentence["verb" + count.verb] = {
          verb: this_word.word,
          verb_properties: this_word.properties,
        };

        continue;
      } // verb

      // ----------------------------------------
      // adverb
      // ----------------------------------------

      // if (this_word.type === "adverb") {
      // adverbcount++;
      // this_turn.verified_sentence_structure += "adverb ";
      // this_turn.verified_sentence['phrase'+count.phrase] = {
      //   adverb: adverb
      // }

      //   continue;
      // } // adverb

      // ----------------------------------------
      // direction
      // ----------------------------------------

      // if (this_word.type === "direction") {

      //   continue;
      // } // direction

      // ----------------------------------------
      // preposition
      // ----------------------------------------
      // @TODO any special handling for preposition is direction?

      if (this_word.type === "preposition") {
        if (last_word && last_word.type === "preposition") {
          // too many prepositions
          this.game.debug(
            `F1030 | verifySentence.js | parser found two or more prepositions`
          );
          msg += this.getUnparsedMessage(this_turn.input);
          this.game.print(msg);
          return false;
        }
        count.prep++;
        count.phrase++; // a preposition starts a new phrase
        this_turn.verified_sentence_structure += "preposition ";
        this_turn.verified_sentence["phrase" + count.phrase] = {
          preposition: this_word.word,
          preposition_properties: this_word.properties,
        };

        continue;
      } // preposition

      // ----------------------------------------
      // string
      // ----------------------------------------

      // if(this_word.type==="string") {
      //   continue;
      // } // string

      // ----------------------------------------
      // noun
      // ----------------------------------------

      // noun and itself ----------------------------------------
      // @TODO we have no other handling for itself yet

      if (this_word.type === "noun" && this_word.word === "itself") {
        if (!this_turn.verified_sentence["phrase" + count.phrase]?.noun) {
          this.game.debug(`F1862 | verifySentence.js | itself has no referent`);
          msg += this.getUnparsedMessage(this_turn.input);
          this.game.print(msg);
          return false;
        }
        if (last_word && last_word.type !== "preposition") {
          count.phrase++; // a new noun starts a new phrase
        }
        this_turn.verified_sentence_structure += "noun ";
        this_turn.verified_sentence["phrase" + count.phrase].noun = {
          noun: this_turn.verified_sentence["phrase" + (count.phrase - 1)].noun,
          noun_properties: this_word.properties,
        };
        // if there is a prior phrase...
        if (
          this_turn.verified_sentence["phrase" + (count.phrase - 1)].parsedNoun
        ) {
          // copy that phrase's noun to this phrase
          this_turn.verified_sentence["phrase" + count.phrase].parsedNoun =
            new adventurejs.ParsedNoun().set(
              this_turn.verified_sentence["phrase" + (count.phrase - 1)]
                .parsedNoun
            );
        }

        continue;
      } // itself

      // spatial_direction aka up/down/in/out ----------------------------------------

      if (
        this_word.type === "noun" &&
        this_word.properties.spatial_direction &&
        last_word &&
        last_word.type === "verb" &&
        next_word &&
        next_word.type === "noun"
      ) {
        // player has likely said something like "climb up tree"
        // we typically parse directions as nouns but in cases like this
        // we want to treat the direction as a preposition

        this.game.debug(
          ` | verifySentence.js | ${this_word.word} appears to be a spatial direction / preposition for ${next_word.word}`
        );

        count.phrase++;
        this_turn.verified_sentence["phrase" + count.phrase] = {}; // add new phrase
        this_turn.verified_sentence["phrase" + count.phrase].preposition =
          this_word.word;

        continue;
      } // this_word.properties.spatial_direction

      // noun and this_word.adjective ----------------------------------------

      if (
        this_word.type === "noun" &&
        this_word.adjective &&
        next_word &&
        next_word.type === "noun"
      ) {
        // word is both noun and adjective
        // if next word is noun then it's likely that
        // this is an adjective for the next word
        // if we treat the two nouns as one phrase,
        // do they have an unambiguous match?

        // first try getting an asset
        let found_asset_id;
        let found_asset = this.game.getAsset(
          this_word.word + " " + next_word.word
        );
        if (found_asset) found_asset_id = found_asset.id;

        // otherwise parseNoun which does a wider search
        if (!found_asset) {
          let parse_both = this.parseNoun(
            this_word.word + " " + next_word.word
          );
          found_asset_id = parse_both.matches.unambiguous;
          found_asset = this.game.getAsset(found_asset_id);
        }

        if (!found_asset_id) {
          this.game.debug(
            `F1219 | verifySentence.js | ${this_word.word} appears to be an adjective for ${next_word.word} and no ${this_word.word} ${next_word.word} was found`
          );
          msg += `$(We) don't know of any ${this_word.word} ${next_word.word}. `;
          this.game.print(msg, this_turn.output_class);
          return false;
        }

        // we found a noun that matches both words

        this.game.debug(
          `F1863 | verifySentence.js | ${found_asset_id} was found to match ${this_word.word} + ${next_word.word} `
        );

        this_word.asset = found_asset;
        this_word.word = found_asset_id;

        position++; // advance past next word

        // allow to go on to default noun handling
      } // noun and adjective

      // noun default ----------------------------------------

      if (this_word.type === "noun") {
        if (last_word && last_word.type !== "preposition") {
          count.phrase++;
          this_turn.verified_sentence["phrase" + count.phrase] = {}; // add new phrase
        }
        // save the noun
        this_turn.verified_sentence["phrase" + count.phrase].noun =
          this_word.word;
        // carry forward properties from parseSentence
        this_turn.verified_sentence["phrase" + count.phrase].noun_properties =
          this_word.properties;
        // save any exclusions
        if (this_word.exclusion) {
          this_turn.verified_sentence["phrase" + count.phrase].exclusion =
            this_word.exclusion;
        }
        this_turn.verified_sentence_structure += "noun ";
        count.noun++;

        continue;
      } // noun

      // ----------------------------------------
      // adjective
      // ----------------------------------------

      if (this_word.type === "adjective") {
        // Adjectives are tricky because nouns can have them defined in
        // their names, like "red door", and nouns can also have specific
        // adjectives assigned to them, so that "red" and "door" will find
        // the "door" asset with adjective "red". If player entered an
        // adjective that resolved to a noun, that noun will have been recorded
        // and probably our sentence structure will have two nouns in it,
        // something that we won't catch here, so we'll have to check again
        // in handleSentence.js.

        // We only end up here if an
        // adjective has been typed that is not attached to a noun. For
        // instance, no noun in the game uses "violet" but we understand that
        // "violet" is an adjective and if a user inputs "x violet door"
        // we can say "you don't see any violet door".

        if (next_word && next_word.type === "noun") {
          this.game.debug(
            `F1218 | verifySentence.js | ${this_word.word} appears to be an adjective for ${next_word.word} and no ${this_word.word} ${next_word.word} was found`
          );
          msg += `$(We) don't know of any ${this_word.word} ${next_word.word}. `;
          this.game.print(msg, this_turn.output_class);
          return false;
        } // adjective + noun

        continue;
      } // adjective

      // ----------------------------------------
      // unknown
      // ----------------------------------------

      if (this_word.type === "unknown") {
        // @TODO: soft prompt for "oops"
        this_turn.unknown_word = this_word.word;
        this.game.debug(
          `F1200 | verifySentence.js | ${this_word.word} is unknown`
        );
        msg += `The word <em class='unparsed'>${this_word.word}</em> wasn't recognized. `;
        this.game.print(msg, this_turn.output_class);
        return false;
      } // unknown
    }

    // trim the sentence structure
    this_turn.verified_sentence_structure =
      this_turn.verified_sentence_structure.trim();

    return true;
  }; // verifySentence
})();