MOE3: 移動失敗後の維持への移動支援の無効

spec/models/main_phase_spec.rb

マニュアルの Diagram12、同等戦力のスタンドオフで移動に失敗した German A Mun-Sil が支援付きの Austrian A Boh-Mun に撃退される処理を実装する*1

これは、移動支援は移動に失敗したユニットの維持には効果がないことの例示である。

f:id:asagix:20130919100326p:plain:w200

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

    #(略)

    shared_context "Diagram12 移動失敗後の維持には移動支援は適用されない", diagram: 12 do
      let(:army_boh) { FactoryGirl.create(:army, :a, :boh, phase: phase) }
      let(:army_tyr) { FactoryGirl.create(:army, :a, :tyr, phase: phase) }
      let(:army_mun) { FactoryGirl.create(:army, :g, :mun, phase: phase) }
      let(:army_ber) { FactoryGirl.create(:army, :g, :ber, phase: phase) }
      let(:army_war) { FactoryGirl.create(:army, :r, :war, phase: phase) }
      let(:army_pru) { FactoryGirl.create(:army, :r, :pru, phase: phase) }

      let(:prov_mun) { army_mun.province }
      let(:prov_sil) { Province.find_by(code: "sil") }

      let!(:move_boh_mun) { FactoryGirl.create(:move_order, unit: army_boh, destination: prov_mun) }
      let!(:supp_tyr_boh) { FactoryGirl.create(:support_order, unit: army_tyr, target: move_boh_mun) }
      let!(:move_mun_sil) { FactoryGirl.create(:move_order, unit: army_mun, destination: prov_sil) }
      let!(:supp_ber_mun) { FactoryGirl.create(:support_order, unit: army_ber, target: move_mun_sil) }
      let!(:move_war_sil) { FactoryGirl.create(:move_order, unit: army_war, destination: prov_sil) }
      let!(:supp_pru_war) { FactoryGirl.create(:support_order, unit: army_pru, target: move_war_sil) }
    end

    context "Diagram12", diagram: 12 do
      subject { resolved_orders }
      example { expect(subject.find(move_boh_mun).status).to eq OrderStatus::SUCCESS}
      example { expect(subject.find(supp_tyr_boh).status).to eq OrderStatus::SATISFIED }
      example { expect(subject.find(move_mun_sil).status).to eq OrderStatus::DISLODGED}
      example { expect(subject.find(supp_ber_mun).status).to eq OrderStatus::SATISFIED }
      example { expect(subject.find(move_war_sil).status).to eq OrderStatus::STANDOFF}
      example { expect(subject.find(supp_pru_war).status).to eq OrderStatus::SATISFIED }
    end
  end
end

実行すると、German A Mun-Sil が STANDOFF で Austrian A Boh-Mun が FAILURE。

さて。失敗した移動命令に支援が効いているのか、失敗した移動命令への攻撃処理が抜けているのか。

MainPhase#resolve_attacks メソッド

スタンドオフを処理する MainPhase#resolve_conflicts の次に実行されるのがこちら。

ざっと見た感じ、処理そのものに問題はなさそうだが。

  # 維持ユニットへの攻撃
  def resolve_attacks
    moves = @orders.select{|o| o.move?}
    moves.each do |move|
      hold = @orders.select{|o| o.hold? && o.province == move.dst}[0]
      unless hold
        unexecuted_move = @orders.select{|o| o.move? && o.src == move.dst}[0]
        move.success! unless unexecuted_move
        next
      end

      move_supports = @orders.select{|o| o.support? && o.target?(move)}.size
      hold_supports = @orders.select{|o| o.support? && o.target?(hold)}.size

      if move_supports > hold_supports
        move.success!
        hold.dislodged!
      else
        move.failure!
        hold.success! if hold.unexecuted?
      end
    end
  end

これはあれだ、SupportOrder#target? で、支援対象の移動命令が失敗済みなら false を返すようにすれば良いはずだ。きっと。

SupportOrder#target? メソッド

before。

  def target?(order)
    return false if self.target.unit != order.unit
    return false if self.target.dst != order.dst
    true
  end

after。

  def target?(order)
    return false if self.target.unit != order.unit
    return false if self.target.dst != order.dst
    return false if self.target.move? != order.move?
    true
  end

MoveOrder#move? は UNEXECUTED の時だけ true を返すことを思い出して欲しい。

一方で支援命令が保有する標本命令のステータスは UNEXECUTED のまま。

つまり標本命令が MoveOrder なら move? は常に true を返すから、これで move?false を返す失敗済みの移動命令は支援対象として認識されなくなるわけだ。

……うーむ、直観的ではないな。

とにかくテスト

実行。オールグリーン。完了。

MoveOrder#move?SupportOrder#target? の複合技はちょっとトリッキーなので、どこかで見直ししないと未来の自分が理解できなくなりそうな不安がある。注意。

*1:ユニット数が多くて全部書くのめんどい。