MOE3: 移動失敗後の維持への移動支援の無効
spec/models/main_phase_spec.rb
マニュアルの Diagram12、同等戦力のスタンドオフで移動に失敗した German A Mun-Sil
が支援付きの Austrian A Boh-Mun
に撃退される処理を実装する*1。
これは、移動支援は移動に失敗したユニットの維持には効果がないことの例示である。
describe MainPhase do describe "#resolve_orders" do let(:phase) { MainPhase.create } let(:resolved_orders) { phase.resolve_orders } #(略) shared_context "Diagram12 移動失敗後の維持には移動支援は適用されない", diagram: 12 do let(:army_boh) { FactoryGirl.create(:army, :a, :boh, phase: phase) } let(:army_tyr) { FactoryGirl.create(:army, :a, :tyr, phase: phase) } let(:army_mun) { FactoryGirl.create(:army, :g, :mun, phase: phase) } let(:army_ber) { FactoryGirl.create(:army, :g, :ber, phase: phase) } let(:army_war) { FactoryGirl.create(:army, :r, :war, phase: phase) } let(:army_pru) { FactoryGirl.create(:army, :r, :pru, phase: phase) } let(:prov_mun) { army_mun.province } let(:prov_sil) { Province.find_by(code: "sil") } let!(:move_boh_mun) { FactoryGirl.create(:move_order, unit: army_boh, destination: prov_mun) } let!(:supp_tyr_boh) { FactoryGirl.create(:support_order, unit: army_tyr, target: move_boh_mun) } let!(:move_mun_sil) { FactoryGirl.create(:move_order, unit: army_mun, destination: prov_sil) } let!(:supp_ber_mun) { FactoryGirl.create(:support_order, unit: army_ber, target: move_mun_sil) } let!(:move_war_sil) { FactoryGirl.create(:move_order, unit: army_war, destination: prov_sil) } let!(:supp_pru_war) { FactoryGirl.create(:support_order, unit: army_pru, target: move_war_sil) } end context "Diagram12", diagram: 12 do subject { resolved_orders } example { expect(subject.find(move_boh_mun).status).to eq OrderStatus::SUCCESS} example { expect(subject.find(supp_tyr_boh).status).to eq OrderStatus::SATISFIED } example { expect(subject.find(move_mun_sil).status).to eq OrderStatus::DISLODGED} example { expect(subject.find(supp_ber_mun).status).to eq OrderStatus::SATISFIED } example { expect(subject.find(move_war_sil).status).to eq OrderStatus::STANDOFF} example { expect(subject.find(supp_pru_war).status).to eq OrderStatus::SATISFIED } end end end
実行すると、German A Mun-Sil
が STANDOFF で Austrian A Boh-Mun
が FAILURE。
さて。失敗した移動命令に支援が効いているのか、失敗した移動命令への攻撃処理が抜けているのか。
MainPhase#resolve_attacks メソッド
スタンドオフを処理する MainPhase#resolve_conflicts
の次に実行されるのがこちら。
ざっと見た感じ、処理そのものに問題はなさそうだが。
# 維持ユニットへの攻撃 def resolve_attacks 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
これはあれだ、SupportOrder#target?
で、支援対象の移動命令が失敗済みなら false
を返すようにすれば良いはずだ。きっと。
SupportOrder#target? メソッド
before。
def target?(order) return false if self.target.unit != order.unit return false if self.target.dst != order.dst true end
after。
def target?(order) return false if self.target.unit != order.unit return false if self.target.dst != order.dst return false if self.target.move? != order.move? true end
MoveOrder#move?
は UNEXECUTED の時だけ true
を返すことを思い出して欲しい。
一方で支援命令が保有する標本命令のステータスは UNEXECUTED のまま。
つまり標本命令が MoveOrder
なら move?
は常に true
を返すから、これで move?
が false
を返す失敗済みの移動命令は支援対象として認識されなくなるわけだ。
……うーむ、直観的ではないな。
とにかくテスト
実行。オールグリーン。完了。
MoveOrder#move?
と SupportOrder#target?
の複合技はちょっとトリッキーなので、どこかで見直ししないと未来の自分が理解できなくなりそうな不安がある。注意。
*1:ユニット数が多くて全部書くのめんどい。
MOE3: 同等戦力の維持ユニットへの攻撃
spec/models/main_phase_spec.rb
マニュアルの Diagram11、French F Gol-Tyn
、French F Wes S F Gol-Tyn
、Italian F Tyn-Holds
、Italian F Rom S F Tyn-Holds
を実装する。
繰り返しになるが、MOE3 の地名は jDip 準拠なので "Gol" は "Lyo" となる。
それと、現行の MOE2 もそうだが、支援命令は対象が移動かそれ以外かのみを区別する都合により、上の例であれば F Rom S F Tyn-Holds
は実際の画面では F Rom S F Tyn
と表示される予定。
では、テストを書こう。
describe MainPhase do describe "#resolve_orders" do let(:phase) { MainPhase.create } let(:resolved_orders) { phase.resolve_orders } #(略) shared_context "Diagram11 同等戦力の維持ユニットへの攻撃", diagram: 11 do let(:fleet_lyo) { FactoryGirl.create(:fleet, :f, :lyo, phase: phase) } let(:fleet_wes) { FactoryGirl.create(:fleet, :f, :wes, phase: phase) } let(:fleet_tyn) { FactoryGirl.create(:fleet, :i, :tyn, phase: phase) } let(:fleet_rom) { FactoryGirl.create(:fleet, :i, :rom, phase: phase) } let(:prov_tyn) { fleet_tyn.province } 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!(:hold_tyn) { FactoryGirl.create(:hold_order, unit: fleet_tyn) } let!(:supp_rom_tyn) { FactoryGirl.create(:support_order, unit: fleet_rom, target: hold_tyn) } end context "Diagram11", diagram: 11 do subject { resolved_orders } example { expect(subject.find(move_lyo_tyn).status).to eq OrderStatus::FAILURE} example { expect(subject.find(supp_wes_lyo).status).to eq OrderStatus::SATISFIED } example { expect(subject.find(hold_tyn).status).to eq OrderStatus::SUCCESS} example { expect(subject.find(supp_rom_tyn).status).to eq OrderStatus::SATISFIED } end end end
問題なく成功。
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_move
と MainPhase#resolve_attack
のメソッド名を、それぞれ resolve_moves
、resolve_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
Redmine を Nginx + Unicorn で動かす
前回までのあらすじ
Nginx を Apache のリバースプロキシに設定した。
今日の目標
現在 Apache + Passenger で動いている Rails アプリの Redmine を Nginx + Unicorn に切り替えてみよう。
Redmine は /var/www/redmine
に設置されている。
Gem の追加
Redmine の gem に unicorn を追加する。
もしRedmine本体は使用しないgem(例: mongrel, fcgi)もロードしたい場合、 Gemfile.local というファイルをRedmineのディレクトリに作成してください。 Redmineのインストール — Redmine Guide 日本語訳
ということなので Gemfile.local を作成した。
# Gemfile.local gem "unicorn"
インストール。
$ bundle install
Unicorn の設定
Redmine の config ディレクトリに unicorn.rb というファイルを作成した。
worker_processes 1 timeout 60 listen File.expand_path("tmp/sockets/unicorn.sock", ENV['RAILS_ROOT']) pid File.expand_path("tmp/pids/unicorn.pid", ENV['RAILS_ROOT']) stdout_path File.expand_path("log/unicorn.stdout.log", ENV['RAILS_ROOT']) stderr_path File.expand_path("log/unicorn.stderr.log", ENV['RAILS_ROOT']) preload_app true GC.respond_to?(:copy_on_write_friendly=) and GC.copy_on_write_friendly = true before_fork do |server, worker| # この設定はpreload_app trueの場合に必須 defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect! # USR2シグナルで旧プロセスを終了させる old_pid = "#{server.config[:pid]}.oldbin" if old_pid != server.pid begin sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU Process.kill(sig, File.read(old_pid).to_i) rescue Errno::ENOENT, Errno::ESRCH end end end after_fork do |server, worker| # この設定はpreload_app trueの場合に必須 defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection end
とりあえず socket のパス。これ大事。
tmp/sockets/unicorn.sock
pid のパス。これも大事。
tmp/pids/unicorn.pid
Unicorn 起動
え、Unicorn て Apache とか Nginx とかとは別口で起動しておかないといけないの?
後で自動化できないか調べなきゃ。
$ bundle exec unicorn_rails -c config/unicorn.rb -D -E production
コマンドのオプションは -c
で設定ファイル指定、-E
で動作モード指定、-D
がデーモン化。
終了の仕方は力ずく。
$ kill -quit 'cat /var/www/redmine/tmp/pids/unicorn.pid'
Nginx の設定
/etc/nginx/sites-available/ に設定ファイルを作成して /etc/nginx/sites-enabled/ からリンクを張る。
upstream redmine { server unix:/var/www/redmine/tmp/sockets/unicorn.sock; } server { listen 80; server_name redmine.tepidworks.jp; location / { root /var/www/redmine/public; if ( -f $request_filename ) { break; } proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; proxy_pass http://redmine; } }
if ( -f $request_filename ) { break; }
があるので、静的ファイルへのアクセスは Rails を通さないで Nginx が直接返す。*1
Nginx 再起動
$ sudo service nginx restart
無事アクセスできた。
*1:少なくとも、確認した限り静的ファイルへのアクセスでは Rails がログを吐かなかったので Unicorn に処理は渡っていないはず。
Apache のリバースプロキシとして Nginx を導入する
参考
環境
Debian 7.1 Apache 2.2.22 Nginx 1.2.1
Nginx
Rails 関連の情報を漁っていて、前々から「Nginx + Unicorn が良いらしい」と聞いていたので興味はあったのだが、Nginx については「Apache より軽量な HTTP サーバー」程度の認識しかなかった。
ところが今さらながらに Nginx と Apache の共存が可能と知り、「Nginx + Unicorn + Rails」な環境に向けて、ひとまず Apache のリバースプロキシとして Nginx を導入することにした。
実は再挑戦だった
基本的に参考サイトの見よう見まねでの作業となったが、改めてサーバーの環境を確認したところ、Nginx はなぜかインストール済み。
そういえば半年ほど前に入れるだけ入れて良くわからないまま放置していたのだった。
その時に変な風に設定をいじっていたら嫌だなあと思いつつ、面倒くさいので再インストールはしなかったが、結果的に問題はなかったので良しとしておこう。
mod_rpaf
良く分からないのが mod_rpaf だが、どうやら Nginx が受けたリクエストを Apache に渡す時点で、Apache から見ると全部 localhost からのアクセスに見えてしまって実際のアクセス元が分からなくなってしまう問題への対処として必要らしい。
apt で一発。apache のモジュール設定の変更から再起動までやってくれた。素晴らしい。
# aptitude install libapache2-mod-rpaf
Nginx の設定
Nginx がポート 80 で受けたリクエストを 8000 へリダイレクトさせるための設定ファイルを /etc/nginx/sites-available/ に作成して /etc/nginx/sites-enabled/ からリンクを張った。
/etc/nginx/sites-available/default へのリンクは使わないので削除。
このあたりの構成は debian 系ユーザであれば apache の設定と同じなので馴染みやすいと思う。
a2ensite
や a2dissite
に相当するコマンドがあればなお良かったが贅沢は言うまい。
apache の設定
/etc/apache2/sites-available/ にある VirtualHost 別の設定ファイルのポートをすべて 8000 に変更、/etc/apache2/ports.conf の NameVirtualHost を *:8000
に、Listen を localhost:8000
に変更した。
Listen の設定で localhost を指定したのは、外部からのポート 8000 へのアクセスを弾くためだ。
再起動
Nginx と Apache を再起動して、無事にアクセスできることを確認。
めでたしめでたし。
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
だが、美しくない。そのうちなんとかする。
MOE3: 支援適用処理
spec/models/main_phase_spec.rb
再掲。
describe MainPhase do describe "#resolve_orders" do let(:phase) { MainPhase.create } let(:resolved_orders) { phase.resolve_orders } #(略) shared_context "Diagram8 移動支援", diagram: 8 do let(:army_mar) { FactoryGirl.create(:army, :f, :mar, phase: phase) } let(:army_gas) { FactoryGirl.create(:army, :f, :gas, phase: phase) } let(:army_bur) { FactoryGirl.create(:army, :g, :bur, phase: phase) } let(:prov_bur) { army_bur.province } let!(:move_mar_bur) { FactoryGirl.create(:move_order, unit: army_mar, destination: prov_bur) } let!(:supp_gas_mar) { FactoryGirl.create(:support_order, unit: army_gas, target: move_mar_bur) } let!(:hold_bur) { FactoryGirl.create(:hold_order, unit: army_bur) } end context "Diagram8", diagram: 8 do subject { resolved_orders } example { expect(subject.find(move_mar_bur).status).to eq OrderStatus::SUCCESS } example { expect(subject.find(supp_gas_mar).status).to eq OrderStatus::SATISFIED } example { expect(subject.find(hold_bur).status).to eq OrderStatus::DISLODGED } end end end
MainPhase#apply_supports メソッド
前回のテストが通っていないので、新規のテストは不要。
例によってメソッド名に悩むが、わりとどうでもいいことなのでこのまま行く。
対象が存在すれば SATISFIED
、存在しなければ INVALID
にするだけの簡単なお仕事です。
class MainPhase < Phase def resolve_orders setup apply_supports resolve_move resolve_standoff resolve_attack resolve_pileup resolve_rotation cleanup orders(true) end #(略) # 支援適用 def apply_supports supports = @orders.select{|o| o.support?} supports.each do |support| target = @orders.select{|o| support.target?(o)}[0] if target support.satisfied! else support.invalid! end end end #(略) end
省略しているけど Order#satisfied!
と Order#invalid!
は当然実装済み。
これで前回書いたテストは無事通ってめでたしめでたし。
今後の展望
Diagram9 は移動支援の前提条件確認*1なので省略。
Diagram10 から 14 はこれまでの行軍処理に支援と戦力を盛り込んでいく形になるはず。
支援のカットについては Diagram15 までお預け。
*1:支援する軍が支援対象の移動先に移動可能であること。