diff options
Diffstat (limited to 'lib/colonial_twilight')
-rw-r--r-- | lib/colonial_twilight/turn.rb | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/lib/colonial_twilight/turn.rb b/lib/colonial_twilight/turn.rb new file mode 100644 index 0000000..34690e7 --- /dev/null +++ b/lib/colonial_twilight/turn.rb @@ -0,0 +1,239 @@ +#! /usr/bin/env ruby +# frozen_string_literal: true + +module ColonialTwilight + OPERATIONS = { + pass: 'Pass', + rally: 'Rally', + march: 'March', + attack: 'Attack', + terror: 'Terror' + }.freeze + + SPECIAL_ACTIVITIES = { + extort: 'Extort', + subvert: 'Subvert', + ambush: 'Ambush', + oas: 'OAS' + }.freeze + + class Action + attr_reader :type, :space, :cost, :steps, :to_agitate_in + attr_accessor :resources + + def initialize(type, space, cost, to_agitate_in: nil) + @type = type + @space = space + @cost = cost + @steps = [] + @resources = 0 + @to_agitate_in = to_agitate_in + end + + def sanitize! + map = { src_control: :src, dst_control: :dst } + control = _collect_indexes(map) + control.each do |k, v| + v.pop + v.each do |i| + step = steps[i] + step.delete(step[:src] == k ? :src_control : :dst_control) + end + end + end + + def _collect_indexes(map) + hash = {} + steps.each_with_index do |step, i| + map.each do |k, v| + if step.key?(k) + hash[step[v]] ||= [] + hash[step[v]] << i + end + end + end + hash + end + + def name + return 'Agitate' if @type == :agitate + + operation? ? OPERATIONS[@type] : SPECIAL_ACTIVITIES[@type] + end + + def operation? + OPERATIONS.keys.include? @type + end + + def special_activity? + SPECIAL_ACTIVITIES.keys.include? @type + end + + def transfer_steps(steps) + steps.each do |k, v| + transfer_from(k, :fln_underground, v) + end + self + end + + def pass + @steps << { kind: :pass } + self + end + + def activate(num = 1) + @steps << { kind: :activate, src: @space, num: num } if num.positive? + self + end + + def transfer_to(dst, what, num = 1, flip: false) + @steps << { kind: :transfer, src: @space, dst: dst, what: what, num: num, flip: flip } if num.positive? + self + end + + def transfer_from(src, what, num = 1, flip: false) + @steps << { kind: :transfer, src: src, dst: @space, what: what, num: num, flip: flip } if num.positive? + self + end + + def shift(num) + @steps << { kind: :shift, dst: @space, num: num } unless num.zero? + self + end + + def extort + @steps << { kind: :extort, src: @space, what: :fln_underground, flip: true } + self + end + + def terror + @steps << { kind: :set, src: @space, what: :terror, terror: 1 } + @steps << { kind: :set, src: @space, what: :alignment, alignment: :neutral } + self + end + + def agitate(terror, oppose) + @steps << { kind: :agitate, src: @space, terror: terror, shift: oppose } + self + end + + def inspect + "action #{@type} in '#{@space}' cost: #{@cost} #{_to_agitate} : #{_steps}" + end + + def _to_agitate + @to_agitate_in.nil? ? '' : " - to agitate in #{@to_agitate_in}" + end + + def _steps + @steps.inject('') { |r, s| r + "\n #{s.inspect}" } + end + end + + class Turn + attr_reader :actions, :operation, :special_activity + + def initialize + reset(false) + end + + def reset(limited_op_only) + @operation = nil + @special_activity = nil + @actions = [] + @limited_op_only = limited_op_only + end + + def operation_done? + !@operation.nil? + end + + def special_activity_done? + !@special_activity.nil? + end + + def may_special_activity?(special_activity) + @special_activity.nil? || @special_activity == special_activity + end + + def operation_spaces + @actions.select(&:operation?).size + end + alias selected_spaces operation_spaces + + def special_activity_spaces + @actions.select(&:special_activity?).size + end + + def operation_selected?(space) + !@actions.select(&:operation?).find { |a| a.space == space }.nil? + end + + def special_activity_selected?(space) + !@actions.select(&:special_activity?).find { |a| a.space == space }.nil? + end + + def cost + @actions.inject(0) { |s, a| s + a.cost } + end + + def operation_cost + @actions.select(&:operation?).inject(0) { |s, a| s + a.cost } + end + + def special_activity_cost + @actions.select(&:special_activity?).inject(0) { |s, a| s + a.cost } + end + + def pass(cost) + operation_in(:pass, nil, -cost).pass + end + + def agitate_in(space, terror, oppose) + raise "illegal Agitate in #{@operation}" if @operation != :rally + raise "not already selected : #{space.name}" unless operation_selected?(space) + + add Action.new(:agitate, space, terror + oppose).agitate(terror, oppose) + end + + def operation_in(operation, space, cost, to_agitate_in: nil) + raise "unknown operation : #{operation}" unless OPERATIONS.keys.include? operation + + unless @operation.nil? + raise "illegal #{operation} in #{@operation}" if @operation != operation + raise "illegal #{operation} in limited operation #{@operation}" if @limited_op_only + end + raise "already selected : #{space.name}" if operation_selected?(space) + + @operation = operation + add Action.new(operation, space, cost, to_agitate_in: to_agitate_in) + end + + def special_activity_in(special_activity, space, cost, to_agitate_in: nil) + raise "unknown special activity : #{special_activity}" unless SPECIAL_ACTIVITIES.keys.include? special_activity + raise "illegal #{special_activity} in #{@special_activity}" if !@special_activity.nil? && @special_activity != special_activity + raise "illegal #{special_activity} in limited operation #{@operation}" if @limited_op_only + raise "already selected : #{space.name}" if special_activity_selected?(space) + + @special_activity = special_activity + @operation = :attack if special_activity == :ambush + add Action.new(special_activity, space, cost, to_agitate_in: to_agitate_in) + end + + def add(action) + @actions << action + action + end + + def inspect + "Operation : #{@operation} - in #{selected_spaces} spaces => #{operation_cost} Resources\ + \nSpecial Activity : #{@special_activity} - in #{special_activity_spaces}\ + spaces => #{special_activity_cost} Resources\ + \nactions : #{_actions_to_s}" + end + + def _actions_to_s + @actions.inject('') { |s, a| s + "\n - #{a.inspect}" } + end + end +end |