MOE3: 支援付きスタンドオフの解決
spec/models/main_phase_spec.rb
さくさく行こう。
マニュアルの Diagram10、French F Gol-Tyn
、French F Wes S F Gol-Tyn
、Italian F Nap-Tyn
、Italian F Rom S F Nap-Tyn
の支援付きスタンドオフを実装する。
ちなみに MOE3 の地名は jDip 準拠なので、"Gulf of Lyon" の略称は "Gol" ではなく "Lyo" とする。
describe MainPhase do describe "#resolve_orders" do let(:phase) { MainPhase.create } let(:resolved_orders) { phase.resolve_orders } #(略) shared_context "Diagram10 支援付きスタンドオフ", diagram: 10 do let(:fleet_lyo) { FactoryGirl.create(:fleet, :f, :lyo, phase: phase) } let(:fleet_wes) { FactoryGirl.create(:fleet, :f, :wes, phase: phase) } let(:fleet_nap) { FactoryGirl.create(:fleet, :i, :nap, phase: phase) } let(:fleet_rom) { FactoryGirl.create(:fleet, :i, :rom, phase: phase) } let(:prov_tyn) { Province.find_by(code: "tyn") } let!(:move_lyo_tyn) { FactoryGirl.create(:move_order, unit: fleet_lyo, destination: prov_tyn) } let!(:supp_wes_lyo) { FactoryGirl.create(:support_order, unit: fleet_wes, target: move_lyo_tyn) } let!(:move_nap_tyn) { FactoryGirl.create(:move_order, unit: fleet_nap, destination: prov_tyn) } let!(:supp_rom_nap) { FactoryGirl.create(:support_order, unit: fleet_rom, target: move_nap_tyn) } end context "Diagram10", diagram: 10 do subject { resolved_orders } example { expect(subject.find(move_lyo_tyn).status).to eq OrderStatus::STANDOFF } example { expect(subject.find(supp_wes_lyo).status).to eq OrderStatus::SATISFIED } example { expect(subject.find(move_nap_tyn).status).to eq OrderStatus::STANDOFF } example { expect(subject.find(supp_rom_nap).status).to eq OrderStatus::SATISFIED } end end end
MainPhase#resolve_standoff メソッド
既存のコードに手を入れることなくテストが通ってしまった。
戦力が同等なので当たり前。
# スタンドオフ 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 |move| move.standoff! end end end
spec/models/main_phase_spec.rb
テストを追加する。
Diagram10 を改変し、Italian F Rom S F Nap-Tyn
を外す。
これで French F Gol-Tyn
が SUCCESS
、Italian F Nap-Tyn
が FAILURE
になれば成功。
describe MainPhase do describe "#resolve_orders" do let(:phase) { MainPhase.create } let(:resolved_orders) { phase.resolve_orders } #(略) shared_context "Diagram10.1 戦力差のある同一地域への移動", diagram: 10.1 do let(:fleet_lyo) { FactoryGirl.create(:fleet, :f, :lyo, phase: phase) } let(:fleet_wes) { FactoryGirl.create(:fleet, :f, :wes, phase: phase) } let(:fleet_nap) { FactoryGirl.create(:fleet, :i, :nap, phase: phase) } let(:prov_tyn) { Province.find_by(code: "tyn") } let!(:move_lyo_tyn) { FactoryGirl.create(:move_order, unit: fleet_lyo, destination: prov_tyn) } let!(:supp_wes_lyo) { FactoryGirl.create(:support_order, unit: fleet_wes, target: move_lyo_tyn) } let!(:move_nap_tyn) { FactoryGirl.create(:move_order, unit: fleet_nap, destination: prov_tyn) } end context "Diagram10.1", diagram: 10.1 do subject { resolved_orders } example { expect(subject.find(move_lyo_tyn).status).to eq OrderStatus::SUCCESS } example { expect(subject.find(move_nap_tyn).status).to eq OrderStatus::FAILURE } end end end
そのままテストを実行すると、もちろん NG。
さあ、MainPhase#resolve_standoff
を修正しよう。
メソッド名も変更した方が良さそうだな。resolve_conflicts
でいいか。
resolve_standoff 改め reslove_conflicts メソッド
# 同一地域への移動 def resolve_conflicts 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 # 戦力抽出 supported_moves = {} conflicts.each do |move| supported_moves[move] = @orders.select{|o| o.support? && o.target?(move)}.size end # 最大戦力取得 max_supports = supported_moves.values.sort.last if supported_moves.select{|k,v| v == max_supports}.size != 1 # 最大戦力が同等の移動が複数ある場合は全てスタンドオフ conflicts.each do |move| move.standoff! end # スタンドオフ地域に維持ユニットがいたら維持成功とする hold = @orders.select{|o| o.hold? && o.province == dst}[0] if hold && hold.unexecuted? hold.success! end else # 最大戦力の移動以外は失敗 conflicts.each do |move| next if move == supported_moves.key(max_supports) move.failure! end end end end
移動先に維持ユニットがいたら別途攻撃成否判定が必要になるので、このタイミングでは最大戦力の移動命令を SUCCESS
にできない。
「スタンドオフ地域に維持ユニットがいたら~」は少々気の回し過ぎかもしれないが、どうせいずれは必要になるので入れておく。
MainPhase#cleanup メソッド
このままだと French F Gol-Tyn
が UNEXECUTED
のまま処理が終了してしまうので、MainPhase#cleanup
で未処理命令を一律 SUCCESS
にする処理を入れてお茶を濁す。どこからも干渉されない単品の維持命令などもこれで成功確定できる。
def cleanup @orders.each do |order| order.success! if order.unexecuted? order.save! end end
だが、美しくない。そのうちなんとかする。