MOE3: スタンドオフの解決

spec/models/main_phase_spec.rb

まずテストを書く。

マニュアルの Diagram4、German A Ber–SilRussian A War–Sil のスタンドオフ処理を実装する。

f:id:asagix:20130910224007p:plain:w200

ちなみに Diagram2 は 1 と同じ単純移動(ただし海軍)なので省略。

f:id:asagix:20130910224005p:plain:w200

Diagram3 は海軍にとっての非隣接地域への移動不可に関する事例ということで、MainPhase#resolve_orders の守備範囲外(不可能命令は登録する段階で弾く予定)なので省略。

f:id:asagix:20130910224006p:plain:w200

describe MainPhase do
  describe "#resolve_orders" do
    let(:phase) { MainPhase.create }
    let(:resolved_orders) { phase.resolve_orders }

    #(略)

    shared_context "Diagram4 スタンドオフ", diagram: 4 do
      let(:army_ber) { FactoryGirl.create(:army, :g, :ber, phase: phase) }
      let(:army_war) { FactoryGirl.create(:army, :r, :war, phase: phase) }
      let(:prov_sil) { Province.find_by(code: "sil") }

      let!(:move_ber_sil) { FactoryGirl.create(:move_order, unit: army_ber, destination: prov_sil) }
      let!(:move_war_sil) { FactoryGirl.create(:move_order, unit: army_war, destination: prov_sil) }
    end

    context "Diagram4", diagram: 4 do
      subject { resolved_orders }
      example { expect(subject.find(move_ber_sil).status).to eq OrderStatus::STANDOFF }
      example { expect(subject.find(move_war_sil).status).to eq OrderStatus::STANDOFF }
    end
  end
end

MainPhase#resolve_standoff メソッド

ここでも戦力評価の実装は後回し。

  • 未処理の MoveOrder オブジェクトを全て取得。
  • 移動先一つ一つについて競合する移動命令があるかチェック。
  • 移動先が競合した場合は問答無用でスタンドオフにする。

なんやかんやでこの形。

class MainPhase < Phase
  def resolve_orders
    setup
    resolve_move
    resolve_standoff
    cleanup
    orders(true)
  end

  #(略)

  # スタンドオフ
  def resolve_standoff
    moves = @orders.select{|o| o.move?}
    moves.collect{|m| m.dst}.uniq.each do |dst|
      conflicts = moves.select{|m| m.dst == dst}
      next if conflicts.size == 1

      conflicts.each do |m1|
        m1.standoff!
      end
    end
  end

  #(略)
end

MoveOrder#move? は成否を問わず処理済みなら false を返すようにしてみた。

class MoveOrder < Order
  #(略)

  def move?
    # 処理済の移動命令は移動命令として扱わない
    return true if unexecuted?
    false
  end

  def standoff!
    self.status = OrderStatus::STANDOFF
  end
end