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