MOE3: 行軍解決処理のリファクタリング

行軍解決終了時の未処理命令の一律成功処理

そのうちなんとかすると言ったな。あれは嘘だ。

やはり行軍解決処理が終了する時点で未処理命令が残っているのは好ましくない。

気になるのでさっさと片付けてしまおう。

エラー状況の確認

まず MainPhase#cleanup の一律成功処理を外してテストを実行してみる。

  def cleanup
    @orders.each do |order|
      #order.success! if order.unexecuted? # 暫定コメントアウト
      order.save!
    end
  end

すると当然、French F Gol-Tyn が UNEXECUTED のままなので、前回最後に追加したテストがエラーになる。

MainPhase#resolve_attack メソッドの修正

処理の順番的に resolve_conflicts の次に来るのが resolve_attack である。

現状、移動先に維持命令がない場合はその移動命令の成否判定をスキップする仕様だが、これを変更する。移動先に維持命令がなければその移動は成功でいいじゃないか。

というわけで Before。

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

      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

そして after。

  # 維持ユニットへの攻撃
  def resolve_attack
    moves = @orders.select{|o| o.move?}
    moves.each do |move|
      hold = @orders.select{|o| o.hold? && o.province == move.dst}[0]
      unless hold
        move.success!
        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

おや、Diagram6 のテストがエラーになってしまった。

交換移動の 2 命令が SUCCESS になっている。

MainPhase#resolve_attack メソッドの追加修正

成功確定条件が「移動先に維持命令がない」だけだと、resolve_rotation の前に交換移動命令がそれぞれ SUCCESS で確定してしまうのが問題。

条件に「移動先に未解決の移動命令がない」も追加する。

  # 維持ユニットへの攻撃
  def resolve_attack
    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

テスト。はい OK。

ちょっと泥臭い書き方になったので気が向いたら綺麗にしておこう。

こっそり修正

MainPhase#resolve_moveMainPhase#resolve_attack のメソッド名を、それぞれ resolve_movesresolve_attacks に修正。

主に気分の問題。

MainPhase#resolve_holds メソッド

孤独な維持命令の成功確定処理も忘れないうちに書くだけ書いてしまおう。実行タイミングは apply_supports の次でいいか。

本当はテストを先に書くべきなのだが時には勢いも大切だよね。

  def resolve_orders
    setup
    apply_supports
    resolve_holds
    resolve_moves
    resolve_conflicts
    resolve_attacks
    resolve_pileup
    resolve_rotation
    cleanup
    orders(true)
  end

  #(略)

  # 干渉を受けない維持
  def resolve_holds
    holds = @orders.select{|o| o.hold?}
    holds.each do |hold|
      moves = @orders.select{|o| o.move? && o.dst == hold.province}
      next if moves.size != 0
      next unless hold.unexecuted?
      hold.success!
    end
  end

hold?true を返すすべての未処理命令を SUCCESS にしてしまうので、輸送成否判定を実装する時は resolve_holds の前に入れる必要があることだけ覚えておこう。

そしてテスト。もちろん問題なし。

後始末

お終いに MainPhase#cleanup を綺麗にして完了。お疲れ。

  def cleanup
    @orders.each do |order|
      order.save!
    end
  end