lib/gamefic/world/playbook.rb in gamefic-2.1.1 vs lib/gamefic/world/playbook.rb in gamefic-2.2.0

- old
+ new

@@ -1,5 +1,7 @@ +require 'set' + module Gamefic module World # A collection of rules for performing commands. # class Playbook @@ -11,15 +13,18 @@ # An array of defined validators. # # @return [Array<Proc>] attr_reader :validators - def initialize commands: {}, syntaxes: [], validators: [], disambiguator: nil + # @param commands [Hash] + # @param syntaxes [Array<Syntax>, Set<Syntax>] + # @param validators [Array] + def initialize commands: {}, syntaxes: [], validators: [] @commands = commands - @syntaxes = syntaxes + @syntax_set = syntaxes.to_set + sort_syntaxes @validators = validators - @disambiguator = disambiguator end # An array of available actions. # # @return [Array<Gamefic::Action>] @@ -32,29 +37,10 @@ # @return [Array<Symbol>] def verbs @commands.keys end - # Get the action for handling ambiguous entity references. - # - def disambiguator - @disambiguator ||= Action.subclass(nil, Query::Base.new) do |actor, entities| - definites = [] - entities.each do |entity| - definites.push entity.definitely - end - actor.tell "I don't know which you mean: #{definites.join_or}." - end - end - - # Set the action for handling ambiguous entity references. - # - def disambiguate &block - @disambiguator = Action.subclass(nil, Query::Base.new, meta: true, &block) - @disambiguator - end - # Add a block that determines whether an action can be executed. # def validate &block @validators.push block end @@ -136,52 +122,29 @@ syn = Syntax.new(input, translation) add_syntax syn syn end - # Get an array of actions, derived from the specified command, that the - # actor can potentially execute. - # The command can either be a single string (e.g., "examine book") or a - # list of tokens (e.g., :examine, @book). + # Get a Dispatcher to select actions that can potentially be executed + # from the specified command string. # - # @return [Array<Gamefic::Action>] - def dispatch(actor, *command) - result = [] - result.concat dispatch_from_params(actor, command[0], command[1..-1]) if command.length > 1 - result.concat dispatch_from_string(actor, command.join(' ')) if result.empty? - result - end - - # Get an array of actions, derived from the specified command, that the - # actor can potentially execute. - # The command should be a plain-text string, e.g., "examine the book." - # - # @return [Array<Gamefic::Action>] - def dispatch_from_string actor, text - result = [] + # @param actor [Actor] + # @param text [String] + # @return [Dispatcher] + def dispatch(actor, text) commands = Syntax.tokenize(text, actor.syntaxes) - commands.each do |c| - actions_for(c.verb).each do |a| - next if a.hidden? - o = a.attempt(actor, c.arguments) - result.unshift o unless o.nil? - end - end - sort_and_reduce_actions result + actions = commands.flat_map { |cmd| actions_for(cmd.verb).reject(&:hidden?) } + Dispatcher.new(actor, commands, sort_and_reduce_actions(actions)) end # Get an array of actions, derived from the specified verb and params, # that the actor can potentially execute. # # @return [Array<Gamefic::Action>] def dispatch_from_params actor, verb, params - result = [] available = actions_for(verb) - available.each do |a| - result.unshift a.new(actor, params) if a.valid?(actor, params) - end - sort_and_reduce_actions result + Dispatcher.new(actor, [Command.new(verb, params)], sort_and_reduce_actions(available)) end # Duplicate the playbook. # This method will duplicate the commands hash and the syntax array so # the new playbook can be modified without affecting the original. @@ -222,24 +185,25 @@ add_syntax Syntax.new(user_friendly.strip, "#{action.verb} #{args.join(' ')}") unless action.verb.to_s.start_with?('_') end def add_syntax syntax raise "No actions exist for \"#{syntax.verb}\"" if @commands[syntax.verb].nil? + sort_syntaxes if @syntax_set.add?(syntax) + end - @syntaxes.unshift syntax - @syntaxes.uniq!(&:signature) - @syntaxes.sort! do |a, b| + def sort_and_reduce_actions arr + arr.sort_by.with_index { |a, i| [a.rank, i] }.reverse.uniq + end + + def sort_syntaxes + @syntaxes = @syntax_set.sort do |a, b| if a.token_count == b.token_count - # For syntaxes of the same length, length of action takes precedence + # For syntaxes of the same length, sort first word b.first_word <=> a.first_word else b.token_count <=> a.token_count end end - end - - def sort_and_reduce_actions arr - arr.sort_by.with_index { |a, i| [a.rank, -i]}.reverse.uniq(&:class) end end end end