MOE3: 玉突き衝突処理のリファクタリング
MoveOrder#resolve_pileup メソッド修正
やっぱり無限ループは怖いのでやっつけ修正。
失敗が確定した MoveOrder
は @orders.select{|o| o.move?}
から外れるので、全ての MoveOrder
の処理が完了するか、処理しきれない分が確定するまでループで回す。
class MainPhase < Phase #(略) # 玉突き衝突 def resolve_pileup last_moves_size = 0 loop do moves = @orders.select{|o| o.move?} break if moves.size == 0 break if moves.size == last_moves_size last_moves_size = moves.size 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
うーん、なんか気に入らない。もっと綺麗に書けるはず。
MainPhase#resolve_piliup メソッド再修正
再帰で書き直してみた。
class MainPhase < Phase #(略) # 玉突き衝突 def resolve_pileup(last_moves_size = 0) moves = @orders.select{|o| o.move?} return if moves.size == 0 return if moves.size == last_moves_size moves.each do |move| hold = @orders.select{|o| o.hold? && o.province == move.dst}[0] move.failure! if hold end resolve_pileup(moves.size) end #(略) end
一般的に、ループの方が再帰よりもリソースや速度面で有利と言われている。
それでも最大 34 回程度のループなら誤差の範囲だろうし、ソースの読みやすさで言えばやはり再帰の方が上なのでこれで良しとする。