spec/models/main_phase_spec.rb
テストを書く。
マニュアルの Diagram5、German A Ber–Pru と German A Kie–Ber の移動失敗の連鎖処理を実装する。

describe MainPhase do
describe "#resolve_orders" do
let(:phase) { MainPhase.create }
let(:resolved_orders) { phase.resolve_orders }
shared_context "Diagram5 玉突き衝突", diagram: 5 do
let(:army_pru) { FactoryGirl.create(:army, :r, :pru, phase: phase) }
let(:army_ber) { FactoryGirl.create(:army, :g, :ber, phase: phase) }
let(:army_kie) { FactoryGirl.create(:army, :g, :kie, phase: phase) }
let(:prov_pru) { army_pru.province }
let(:prov_ber) { army_ber.province }
let!(:hold_pru) { FactoryGirl.create(:hold_order, unit: army_pru) }
let!(:move_ber_pru) { FactoryGirl.create(:move_order, unit: army_ber, destination: prov_pru) }
let!(:move_kie_ber) { FactoryGirl.create(:move_order, unit: army_kie, destination: prov_ber) }
end
context "Diagram5", diagram: 5 do
subject { resolved_orders }
example { expect(subject.find(move_ber_pru).status).to eq OrderStatus::FAILURE }
example { expect(subject.find(move_kie_ber).status).to eq OrderStatus::FAILURE }
end
end
end
MainPhase#resolve_pileup メソッド
メソッド名が適当になってきた。
MainPhase#resolve_pileup の仕様
- 移動先が塞がってたら移動失敗。
- 移動先に未解決の移動命令があったら処理保留。
- 玉突き衝突解決のためにループ処理。
まず MoveOrder#hold? に手を入れる。
class MoveOrder < Order
def hold?
return true if failure?
return true if standoff?
false
end
end
MainPhase#resolve_move もちょっと修正。
MainPhase#resolve_move の仕様
- 移動先に維持命令を受けた軍があったら処理保留。
- 移動先が競合する移動命令があったら処理保留。
- 移動先に未解決の移動命令があったら処理保留。 ←New!
- 残りは移動成功。
現時点では未処理命令に交換移動セット*1や循環移動セット*2があると、resolve_pileup で無限ループに突入してしまうので注意。
交換移動は Diagram6、循環移動は Diagram7 で実装する。
class MainPhase < Phase
def resolve_orders
setup
resolve_move
resolve_standoff
resolve_pileup
cleanup
orders(true)
end
def resolve_move
moves = @orders.select{|o| o.move? && o.unexecuted?}
moves.collect{|m| m.dst}.uniq.each do |dst|
hold = @orders.select{|o| o.hold? && o.province == dst}[0]
next if hold
move = @orders.select{|o| o.move? && o.src == dst}[0]
next if move
move, *conflicts = moves.select{|m| m.dst == dst}
next if conflicts.size != 0
move.success!
end
end
def resolve_pileup
loop do
moves = @orders.select{|o| o.move?}
break if moves.size == 0
moves.collect{|m| m.dst}.uniq.each do |dst|
hold = @orders.select{|o| o.hold? && o.province == dst}[0]
next unless hold
moves.select{|m| m.dst == dst}.each do |m1|
m1.failure!
end
end
end
end
end
ついでに Order#failure! も実装。
class Order < ActiveRecord::Base
def failure!
self.status = OrderStatus::FAILURE
end
end
メソッド名は Order#fail! の方が良さげかもしれない。後で考える。