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 回程度のループなら誤差の範囲だろうし、ソースの読みやすさで言えばやはり再帰の方が上なのでこれで良しとする。