もちバトル 規則0.7：仮想対戦環境の作り方・更新・改善の引き継ぎ
作成日：2026-11-15
対象モデル：0.7-r2（規則0.7・改訂2）

この文書のパスは、断りがなければ mochi-battle-simulator-r07-r2/ 内からの相対パスです。
このセットは既納品の改訂2に説明を添えたもので、エンジンとBotの動作は変更していません。

【次のLLMへ、そのまま渡せる依頼文】

あなたは、同梱のもちバトル規則0.7オフライン再現モデルの保守担当です。
まずこの文書、README.md、model-notes.md、OPP-12検証報告.mdを読み、
validation/engine.mjs、simulate.mjs、validation/calibrate.mjsの役割を把握してください。
変更前に node validation/run-checks.mjs を実行し、現状の検証結果を保存してください。
その後、今回追加された依頼内容・ルール資料・リプレイを使って必要な変更を行ってください。
物理モデルの修正は記録された手札と両者の提出による連続再生で確かめ、
Botの強化は別に、自律対戦・新しいシード・複数相手・両席で比較してください。
実測、資料にある仕様、暫定の推測を区別し、同じ試合への丸暗記で合わせないでください。
未確認の射程や計算予算を検証済みと書かず、不一致を消す目的で観測値を上書きしないでください。
作業はオフラインで完結させ、本番アカウントへの登録・対戦・設定変更は行わないでください。
納品は、変更コード、根拠、再実行可能な検査、実行結果、残る不確実性を含むZIPにしてください。
新しいダウンロード用ファイル名にアンダースコア「_」は使わないでください。


1. 何を作っているのか

ここでいう「仮想環境」は、もちバトルの戦闘をNode.jsで再現する独自シミュレーターです。
Ubuntuなどの仮想マシン、Pythonのvenv、ゲームの公式サーバー本体ではありません。
LLMを各ティックに呼び出す仕組みでもありません。対戦Botは普通のJavaScript関数です。

目的は次の2つです。
・記録された実戦を再生し、ゲームの処理を正しく再現できているかを調べる。
・同じ再現モデルの上で複数のBotや構成を対戦させ、改良候補を比較する。

この2つは別の検証です。実戦18件を再現できたことは、相手の思考コードや
サーバーの乱数器を再現できたことでも、新しいBotが実戦で強い証明でもありません。

現在の対象は、横6列×縦3行、同時行動、42ティック周期のセレクト、最大600ティックの規則0.7です。
600ティック時点では残HPの割合を比較します。HPの絶対値だけでは判定しません。
パーツを組む5×5の「もち盤」と、6×3の戦闘盤は別物です。
本モデルは焼き込み等を反映済みのstatsを受け取り、もち盤からstatsを算出する機能は持ちません。


2. 起動に必要なものと、最初に動かすコマンド

必要なのはNode.js 24です。今回の動作確認は24.22.1で実行しました。
JavaScriptの標準機能とNode.jsの標準モジュールだけを使います。
npm install、追加パッケージ、Python環境、Docker、GPU、トークンは不要です。
ZIPを展開してから、ターミナルで次を1行ずつ実行します。

cd mochi-battle-simulator-r07-r2
node --version
node validation/run-checks.mjs

成功時は最後までOKが表示され、終了コード0になります。
run-checksは元14戦、追加4戦、旧版との差分、Bot形式、CLI、新規12試合をまとめて実行します。
結果はresults/へ書かれます。実行により生成済みの結果ファイルは更新されます。

まず自律対戦を2試合試す場合：
node simulate.mjs --games 1 --swap --out results/first-run.json

既定では両者とも同梱Bot v3.6b、元キットのD6、B1相当のstats、雷属性です。
--swapはBotと構成を一緒に席0/1へ入れ替えて同じシードをもう一度実行します。
これはミラーの動作確認です。勝率から本番の強さは評価できません。

操作する前にCLIの指定を確認する場合：
node simulate.mjs --help

simulateのオプションは「--out results/a.json」のように値を次の引数に書きます。
calibrateの出力指定は「--out=results/a.json」です。この書式の違いに注意してください。

通常のコマンドはWindowsのPowerShellでも実行できます。以下の例はシェルのglob展開に依存しません。
作業ディレクトリを間違えると、検査スクリプトがreference/やresults/を見つけられません。


3. 先に読むファイルと担当範囲

・README.md
  起動、CLI、設定、結果JSON、未確認部分の概要。
・model-notes.md
  物理モデルの根拠、仕様の優先順、暫定挙動、既存の検証範囲。
・OPP-12検証報告.md
  改訂2で修正した3種類の順序問題と、フォレストバインド5回の分析。
・reference/rules/rules.json
  規則0.7の数値スナップショット。52カード、15パーツ、6ステージ、8レシピ。
  カードの威力、属性、コード、レシピ、基本stats、床、ステージ等。
・reference/README.md
  元の制作キットの依頼と、0.7での変更内容。
・reference/docs/API.md / SERVER-BOT.md
  共通の入出力・戦闘・Bot仕様。ただし文書自体は0.6時点です。
  新規則の実記録と衝突する箇所を、古い文書だけに合わせて戻さないでください。

実装：
・validation/engine.mjs
  戦闘の本体。Arena、legal、compileQueue、cardsFromRules、botRunner、play等。
・simulate.mjs
  構成の読み込み、BotのVM実行、試合プロセス、先後交代、トレース、結果集計。
・validation/calibrate.mjs
  実戦の手札・提出による連続/局所再生と、状態・イベント・サマリーの比較。

データ・設定：
・reference/replays/：元14試合のreplay/resultペア。
・reference/opp12/：追加4試合のreplay/resultペアと依頼README。
・reference/config/：元キットのアカウント、D6、B1等のスナップショット。
・configs/current-mirror.json：自律対戦の既定構成。
・configs/vs-OPP-01.json：OPP-01の公開構成を使うプリセット。
・reference/bot/bot-v36b-current.js：同梱Bot v3.6b。エンジンとは別の方策。
・reference/logs/：対戦要約。完全なリプレイの代わりにはなりません。

検査：
・validation/regressions.mjs：44の仕様境界・モデル整合テスト。
・validation/check-current-bot.mjs：同梱v3.6bの形式・合法性・入力不変性。
・validation/check-simulator.mjs：CLI、先後交代、再現性、トレース、異常終了等9項目。
・validation/check-opp12.mjs：旧r1の不一致を再現し、r2の4戦/全18戦一致を確認。
・validation/analyse-inputs.mjs：元14戦と要約ログの版別・機能別監査。
・validation/analyse-opp12.mjs：追加4戦とフォレストバインドの実測抽出。
・validation/verify-delivery.mjs：モデルのハッシュと結果の整合、固定の受け入れ件数を確認。
・validation/run-checks.mjs：検査全体の実行順を管理。
・validation/archive/engine-r1.mjs：改訂前0.7を凍結した比較用ソース。
・validation/archive/engine-r06.mjs：0.6の保存用。0.7の通常対戦には使いません。

同梱されていないもの：
・公式サーバー本体、相手の非公開Botコード、本番の計算量メーター。
・会話中に貼られたBot v3.7/v3.8、OPP-15の記録、別途作られた相手方策のモデル。
OPP-01プリセットも相手の「構成」を持つだけで、相手の思考は含みません。
--bot1を省略すれば、相手の思考もv3.6bです。


4. このモデルをどう作ったか：再構築する場合の順序

公式ソースをそのまま動かしたのではなく、仕様・実数値・完全リプレイから戦闘を再構成しました。
別のLLMが一から作り直す場合も、次の順序なら原因を追いやすくなります。

1) データの版を固定する。
   ruleset、数値、資料の版、各replay/result、Bot、構成を保存し、ハッシュを取る。
   0.6の要約と0.7の完全記録、構成が変わった時期の成績を無条件に混ぜない。
2) 状態を定義する。
   フェーズ、tick、陣地、床、両者のHP/位置/stats、手札、デッキ、キュー、予備動作、効果を持つ。
3) セレクトを実装する。
   手札の番号の検査、コード制約、錬成、強化の付加、キュー作成、次回への戻し方を独立させる。
4) 1ティックの遷移を実装する。
   時間経過、既存の攻撃の解決、被ダメ/回復、状態異常、移動、新しい命令を順番に処理する。
5) 効果を足す。
   爆弾、波、連射、床、陣取り、シェル、クローク、非常食を、イベントと統計付きで実装する。
6) 公開viewを生成する。
   手札/キューは自分のものだけを出し、相手の非公開情報や内部用の識別子は隠す。
7) 実記録を流す再生器を作る。
   当時の手札と両者の提出を与え、初期状態から最後まで計算して、全フレームで照合する。
8) 最初にずれるフレームを直す。
   途中のHPを観測値に戻して連続再現を装わない。後続の多数のずれは最初の1件の波及かもしれない。
9) 観測が足りない技を分離する。
   推定形状をprovisionalとして明示し、通常評価へ黙って混ぜない。
10) 最後にBot実行器とCLIを載せる。
    ゲームの物理と、行動を選ぶ方策を別に保ち、タイムアウトや失敗を勝敗とは別計上する。

既存実装を改善する場合、一から再構築する必要はありません。engine.mjsの該当部分だけを直し、
この同じ流れで記録・根拠・検査を更新してください。


5. 状態と公開APIの読み方

座標は0起点。x=0..5、y=0..2。seatは0/1、facingは通常、席0が+1、席1が-1です。
columns[x]はその列の所有者です。floors[y][x]とは別です。

Arenaが持つ主な状態：
・phase：select / battle / ended。
・tick：戦闘ティック。selectを提出しても増えない。
・step：セレクトを含む処理回数。tickと同じ数ではない。
・selectNo：何回目のセレクトか。handsとの対応に使う。
・players：HP、位置、属性、stats、手札、pool、queue、pending、各種の期限。
・effects：爆弾、進行波、連射の継続効果。
・lastEvents：直前の1遷移で起きたイベントの配列。順序も検証対象。
・stats：与被ダメ、命中、防御、回復、カード使用数、移動等の集計。
・holeUntil / magmaDue / immune：床や状態異常の非公開のタイマー等。

主な関数：
・new Arena(rules, setup, seed)：初期状態と最初の手札を作る。
・game.select([picks0, picks1])：両者のセレクトを処理する。
・game.advance([action0, action1])：両者の提出で1戦闘ティックを進める。
・game.view(you)：指定席から見た公開viewを返す。
・game.finish()：結果と統計を返す。
・game.interrupt(reason)：外部中断。判定負けや引き分けにしない。

注意：Arenaへ直接渡すsetup.playersには、実体のdeck配列が必要です。
configs/にあるdeckFileの解決や30枚デッキの検査はsimulate.mjsのloadSetupが担当します。
configs/のJSONをそのままArenaへ渡すと、deckFileが未解決のままになる場合があります。

Botの形式：
init(info)は任意。select(view)は手札番号の配列、act(view)は次の形式を返します。
{move:"none", attack:"shot"}
moveはnone/up/down/left/right、attackはnone/shot/cardです。
サーバー提出用BotにNode.jsのimport/require/fetch/タイマー/ファイルアクセスを入れません。
シミュレーター本体がNode.jsのfsやvmを使うこととは別の制約です。

公開pendingは主にactionとatです。非公開キューの中身や攻撃に付く全内部情報が
相手Botから見えるわけではありません。評価中にエンジン内部の相手情報を方策へ渡さないでください。

連射のeffectsの例（実記録）：
{kind:"triple", owner:1, left:2, card:"thunder-vulcan", damage:15, perTick:2}
このモデルのleftは残り発射ティック数、perTickは1ティック内の弾数です。
left=2, perTick=2なら、この後は2ティックに計4発です。
leftを「残り弾数」と読んでperTickで割ると、残り時間の予測を誤ります。
内部のborn、statusDone等はviewに出さない管理用情報です。


6. 特に間違えやすい、ティックと同時処理の順序

advanceの入力view.tick=rなら、まず内部のtickはT=r+1になります。
そのTの既存攻撃を解決してから、今回提出された移動と、新しい攻撃命令を処理します。
「今から移動するから、このTに当たる予定の弾を避けられる」とは限りません。

例：tick10のviewを見て通常ショットを提出した場合
・tick11の処理：それ以前の攻撃を解決→今回の移動→ショットを構える。
・そのショットのpending.atは12。tick12の処理で解決する。
予備1のカードなら、同じtick11に命令するとpending.at=13です。
「命令したティック」と「命令を選んだ入力viewのtick」を1つずらさないでください。

現在のadvanceを上から追うと、概ね次の順です。細かい順序はソースと実イベントを優先します。

1) tickとstepを進め、開始時の硬直・無敵と、前の移動から持ち越したマグマ被害を記録。
2) 穴の復旧、状態異常/クロークの時間切れ、硬直の減算、チャージ加算、床の継続効果の準備。
3) pending.at=Tの攻撃/カードを席0→1で解決し、命中候補・回復量・新しい効果等を集める。
   リフレッシュの既存状態解除やクローク有効化は、ここで行う。
4) 既存の爆弾・波・連射を進め、命中候補を加える。
5) 陣取りを解決。取る列に相手がいれば取得せず10ダメージ。相手を押し出さない。
6) 命中候補を並べ、被弾側の席0→1で処理。同じ被弾側では古い効果を先に扱う。
   各被弾側について、攻撃ダメージ→床ダメージ→回復→非常食判定→のけぞり等を処理する。
7) 凍結解除、新規状態異常、全体床変更、着弾由来の床変更等を処理する。
8) KOがあれば決着。なければ両者の行動ロックを先に判定し、lockedやcard_lostを出す。
9) 両者の移動を処理。氷の追加移動、ヒビの穴化、マグマ侵入による次ティックの被害も管理。
10) 新規ショット/カード命令を受け付け、pendingとbusyLeftを設定する。
11) 600ティックならHP割合で判定。そうでなく42の倍数なら次のセレクトを開始する。

両者の既存pendingを解決してから被ダメを処理するため、片方が倒れる攻撃を受けても、
そのティックにすでに解決対象だったもう一方の攻撃まで勝手に消してはいけません。
一方、まだ将来に解決予定のpendingは、のけぞりで取り消される場合があります。

busyLeftはこのティックに減らした値だけでなく、開始時に硬直していたかでも命令を止めています。
status.leftやinvincibleLeftは公開時点の残り表示で、内部のuntilと境界が異なります。
通常麻痺15の命中直後にstatus.left=14になる、というような点を実記録で確認してください。

実装上、通常のカード命令はpending.at=T+windup+1、busyLeft=windup+recoveryです。
BEHAVIORは種類・予備・硬直・形状等を持ち、rules.jsonは主に威力・属性等を持ちます。
BotのWR_CARDS/WR35_CARDSの配列と、エンジンのBEHAVIORは配列の順序が違います。
片方の配列をそのままもう片方へコピーしないでください。


7. 物理モデルを変更するときに守る細部

セレクト：
・選んだカードは全部同名、または非ワイルドコードが1種類、が通常の合法条件。
・錬成はレシピの3枚を正確な順序で連続選択し、1枚の仮想カードへ圧縮する。
・強化は直前の攻撃へ付き、連射なら1発ごとに効く。単に次の攻撃全体へ一度足す処理ではない。
・次のセレクトへ戻す未使用キューは先頭1枚だけ。後続を全部デッキへ戻さない。
・各コピーのuidと先頭カードの保持もあるので、カード名だけで管理し直さない。

ダメージ・防御・集計：
・属性一致、弱点、凍結、床、丸めの順序を追う。holyは弾ごとに切り捨て半減する。
・連射の弾には通常カードののけぞり/新規無敵を付けない。既存の無敵やクロークは有効。
・シェルは1発だけ防ぐ。Break Buster/Chargeは対応するショットで貫通するが、クロークは貫通しない。
・スーパーアーマーはのけぞりと将来pendingの取消を防ぐ。麻痺そのものを無効にはしない。
・通常雷カードの麻痺は15、連射由来は5。rules.jsonのparalyze=5だけを全体へ適用しない。
・焼きStatus Guardは初回防御に加え、以後の状態異常時間も短縮する。
・同ティックの非連射攻撃を、先に出した新しい無敵で後続まで防がない。開始時の無敵を使う。
・hitsは防がれた命中も数える。blockedは防いだ量、damageDealtは通った量。
・healedは実際のHP増加量だけではなく、カードの回復量をHP上限前に集計する。
・非常食によるHP復帰をhealedへ混ぜない。KO時の公開HPと最終統計の0下限も分ける。

床：
・Floatは草の回復/聖の半減等の良い効果も含めて床効果を無効化する。
・FloatとHole Walkは別。浮いているだけでは穴/無へ入れない。
・クラック床は離れた元のマスが穴になり、30ティック後に通常床へ戻る。
・Hole Walkは穴への進入を許す能力で、ヒビの穴化を止める能力ではない。
・クラックフィールドは自陣も対象。鋼鉄、既存の穴/無はそのまま。
・氷は条件を満たせば同じ向きに1マスだけ追加で滑る。滑りの連鎖を無限に続けない。
・マグマの20ダメージは、この再現モデルでは侵入の次ティックに適用する。
  古いAPI文書には侵入ティックと書かれているので、記録との整合を優先する。
・同ティックの異なる全体フィールドは相殺。同じ種類同士は実行する。

この章は全組合せを実測済みという宣言ではありません。
たとえば通常バルカン/アクアバルカンの一部の時間設定、+30/重ね強化などには未観測部分があります。
詳細と限界はmodel-notes.mdを確認してください。


8. 実リプレイを追加して再現チェックを回す方法

新しいデータは、まずreference/newdata/など別フォルダーへ置いてください。
元14件のreference/replays/や追加4件のreference/opp12/へ無条件に足すと、
既存の集計・旧版不一致テストの固定件数が変わってしまいます。

最低限ほしいもの：
・m1-replay.json
・m1-result.json
結果ファイル名はreplayの「-replay.json」を「-result.json」に置換したものです。

既存のreplayには次の情報があります。
・ruleset：適用ルール。
・record.setup.mochi：当時の両者のstats、属性、deck、first等。
・record.steps：kind=selectのpicks、kind=tickのactions。両者分が必要。
・hands：selectNoごとの両者の手札。
・views：公開戦闘状態の時系列。
・events：各遷移のイベント配列。
・resultと、別resultファイルのsummary。
・中断例ではinterruption等、打ち切り理由に対応する情報も確認する。

基本の対応はviews[i]を入力にrecord.steps[i]を処理し、views[i+1]とevents[i+1]を比較します。
selectも1フレームなので、viewsの配列番号をそのままtickとみなさないでください。
各selectNoのhandsはdrawProviderで残存デッキから取り出します。
記録のカードが残っていなければエラーです。足りないカードを捏造して続けてはいけません。

新データ1戦の連続再生：
node validation/calibrate.mjs reference/newdata/m1-replay.json --out=results/new-continuous.json

暫定技を含む試合を、その暫定モデルとの差分まで調べる場合：
node validation/calibrate.mjs reference/newdata/m1-replay.json --allow-provisional --out=results/new-continuous.json

局所再生で原因を分ける場合：
node validation/calibrate.mjs reference/newdata/m1-replay.json --step --allow-provisional --out=results/new-step.json

複数試合は、replayのパスを空白で並べて渡します。引用符を使えばパスに空白があっても扱えます。
calibrateは出力先の親フォルダーを自動作成しません。まず存在するresults/を出力先にしてください。
simulateの--trace出力はサーバーreplayとは別形式で、この再生器へそのまま渡せません。

引数を省略したcalibrateは元14戦だけを対象にします。全18戦は次で再生成できます。
node validation/check-opp12.mjs
node validation/analyse-opp12.mjs

calibrateの終了コード0は一致、1は比較の不一致です。
読み込み失敗や予備動作の解決時の暫定停止もエラー終了になるので、数字だけでなくエラー内容を読みます。
旧モデルの不一致を再現する検査では、終了コード1を期待値として扱います。
これは正常モデルの試合失敗を許してよい、という意味ではありません。


9. 不一致を直す手順

1) 変更前のengineと入力と結果を保存し、ハッシュとルール版を記録する。
2) まず連続再生を実行し、最初のmismatchesを探す。
3) そのframeIndex/stepIndex/tick、直前view、両者の提出、予備動作、効果を並べる。
4) 同じ入力で--stepを実行し、局所の誤りか、それ以前の誤りの波及かを分ける。
5) 値の差と順序だけの差を分け、原因候補を最少の処理まで絞る。
6) 仕様に基づく汎用的な変更を行い、必要な境界ケースをregressionsへ足す。
7) 連続再生を初期状態からやり直し、新データと既存18戦の両方を確認する。
8) 直した根拠と未確認条件をメモへ残す。自律対戦の動作確認も行う。

結果JSONで見る欄：
・matched / comparisons：状態の項目ごとの一致数。
・exactTicks / ticks：その戦闘ティックの比較対象が全部一致した数。
・eventMatched / eventSteps：イベントの内容と配列順まで一致したフレーム数。
・eventOrderOnly：イベントは同じだが順序だけ違うフレーム数。
・records[].summaryErrors：決着と最終サマリーの差。
・mismatches[]：全不一致フレームのtick、frameIndex、kind、stateDiffs、期待/予測イベント等。
・examples[]：表示用に件数を絞った例。これだけで全不一致だと判断しない。

stateDiffsのexpectedは実記録、predictedはモデルです。
比較する状態項目はcalibrateのfields配列と盤面等の比較ループで指定されています。
両席のprivateな手札やdeckCountを含むplayerViewの全キーを、直接実測照合しているわけではありません。
手札はhandsからの供給と残存デッキの検査で扱います。新しい公開フィールドを追加する場合は、
viewの実装だけでなく比較器も更新し、比較しなかった項目を検証済みと数えないでください。
eventKindsが空でも、eventMismatch=trueかつorderOnly=trueならイベント順は違っています。
eventsを種類別に数えるだけ、またはソートして等しいかを見るだけで全一致と判定しないでください。
連続再生でHPが以後ずっと違っても、原因は最初の麻痺1回の誤処理という場合があります。
局所再生のサマリー一致だけで採用せず、連続再生の一致を採用条件にします。

座標、列の所有者、床、属性、Float、無敵、クローク、シェル、複数命中、回復の同時発生を順に確認します。
ダメージの値が同じでも、floor/status/locked/windup/card_lostの順番が違えば未解決です。
サーバーのイベント配列は単なる見た目ではなく、隠れた処理順を推測する証拠です。


10. 過去の修正例：OPP-12の4戦から何を直したか

改訂前r1で新4戦を連続再生すると、状態684項目、イベント155フレーム、サマリー13項目がずれました。
局所再生を使うと、状態の原因は次の3ティック・5項目まで絞れました。

・m2 tick92：リフレッシュと雷カードが同時解決。
  実測は新しい麻痺が残る。旧版は後処理でその新規麻痺まで消していた。
  解除をカード解決時へ移し、その後の新規状態付与を残した。
・m2 tick335 / 350：HP6→ショット6→草床10回復。
  実測はHP10、非常食あり。旧版はダメージ直後に食料を食べてから回復し、HP410になった。
  同ティックのダメージと回復を処理した後に、非常食が必要かを判定した。
・ほかに132フレームの順序だけの差。
  相手の移動/新しい予備動作より先に、両者のlockedを出すようにした。

これで追加4戦が一致し、元14戦の一致も維持しました。
詳しい全ティック一覧はresults/opp12-mismatch-index.md、完全差分は
results/opp12-baseline-continuous.jsonとopp12-baseline-step.jsonです。
改訂後のmismatchesは空です。

教訓は「何百ティックずれても、場当たり的に何百か所を直さない」です。
最初の状態差を直した後で、独立した別の境界条件が残っていないかを調べます。


11. 暫定技と、provisionalを外すための条件

現在の暫定5技：
giga-cannon / meteor-bomb / tidal-wave / forest-bind / aqua-grenade

通常モードでは、その技の解決時にエラーにします。手札に現れただけでは停止しません。
--allow-provisionalを明示すれば暫定形状で試せますが、正しさが確定した意味ではありません。
実際に使用した暫定技はrows[].assumptions.provisionalCardsUsed等へ記録されます。
summary.provisionalGamesは、暫定技を使った完走試合の数です。

フォレストバインドについて実測済みの部分：
・追加4戦で5回解決。2回は実ダメージ、2回はクローク防御、1回はhitなし。
・全例dy=0。同じ行の前方距離1/2でhit、距離3でhitなし。
・木属性の使用者から雷属性の標的へ、基礎250×一致1.2×弱点2＝600。
・コマンドの2ティック後に解決。コマンド直後busyLeft=3、解決時busyLeft=1。
・防御されなかった2回では、新規statusなし、通常のflinchのみ。
・解決時stunLeft=1、invincibleLeft=3。命中+2ティックで移動とカード使用を再開。

まだ不明な部分：
・別の行への射程。全行、上下1行、同じ行だけ、十字型が今回の5例では区別できない。
・別属性、別防御、別配置の全組合せまで確かめたわけではない。
したがって「前方2列・全行」は暫定のままで、provisionalを維持しています。
名前の「バインド」から追加の長い拘束を勝手に入れてはいけません。

次の有用な記録はdx=1/2、dy=1/2の使用例です。
dy=2は全行かどうか、dx=2かつdy=1は十字型かどうかの区別に役立ちます。
無敵/クロークなしの命中と不発、左右両方向、範囲の境界を優先します。

暫定を外す際は、次の手順を取ります。
1) どの形状/時間/状態/防御を確認したか、実測のfile・tick・eventsを整理。
2) 同じ実測に適合する別仮説がないか、反例となる位置を調べる。
3) 必要な境界サンプル、または権威のある現行仕様で残る曖昧さを解消。
4) 物理モデルと暫定一覧を更新し、旧/新データを連続再生。
5) 文書、集計、受け入れ検査の暫定件数と判定も根拠に基づいて更新。

新しい単体テストが通ることは、推測した形状がサーバーと一致する証拠ではありません。
単体テストはその仮説の実装が一貫していることを確認し、実記録で仮説自体を検証します。
他の4暫定技には、同梱18戦での発動実例がありません。


12. Bot同士を比較する方法：物理モデルの修正とは分ける

候補ソースをcandidate.jsとしてプロジェクト内へ置いた例：
node simulate.mjs --bot0 candidate.js --bot1 reference/bot/bot-v36b-current.js --games 10 --swap --seed 12345 --out results/candidate-vs-base.json

全6ステージ・10シード・両席の120試合：
node simulate.mjs --bot0 candidate.js --bot1 reference/bot/bot-v36b-current.js --games 10 --stage all --swap --seed 12345 --out results/candidate-all-stages.json

--gamesはステージ・向きごとのシード数です。
現在の試合seedは、初期seed＋ステージ番号×1009＋試行番号×37をuint32化したもの。
同じ初期seedのgamesを増やすと先頭の試合は重複します。別集計で二重に数えないでください。

単一試合の詳細トレース：
node simulate.mjs --bot0 candidate.js --games 1 --seed 12345 --trace results/candidate-trace.json --out results/candidate-one.json

--traceは合計1試合限定です。--swapや--stage allとは併用できません。
viewsは両者が行動を決めた入力、submittedは両者の提出、eventsは解決、afterは解決後viewです。
traceはモデル上の新規対戦であり、実サーバーの観測とは別物です。

構成を変える場合：
node simulate.mjs --setup configs/vs-OPP-01.json --bot0 candidate.js --games 10 --swap --out results/build-comparison.json

configsのplayers[0/1]は論理的なBot0/1の構成。席0/1の固定ではありません。
stats、attr、deck配列、first、またはdeckFileを指定できます。
deckFileは設定JSONからの相対パス。Botや出力パスは通常、実行ディレクトリから解決されます。
30枚、同名4枚以内、有効コード、先頭カードの存在を検査します。
所持数、コイン、パーツ配置の合法性を調べるビルド検査器は付いていません。

結果の読み方：
・summary.completed / interrupted / errors：完走、中断、実行失敗を別々に確認。
・summary.bots[0/1]：論理的なBotごとの勝敗、残HP割合、与被ダメ等。
・rows[].bot0Seat：Bot0の実際の席。
・rows[].result.winner：席番号。winnerBotは論理的なBot番号。
・rows[].players[0/1]：集計では席を交代してもBot0/1順。
異常終了を負けや引き分けに混ぜて勝率を出さないでください。

比較の進め方：
1) 物理モデルを固定し、候補と基準Botのソースハッシュを保存する。
2) まず形式、セレクトの合法性、入力の非破壊、例外や長い停止がないかを確認する。
3) 同じシード集合と構成で両席を試す。変更は小さく分け、機能のON/OFF差を見る。
4) 開発に使ったシード/相手と、最終確認用の未使用シード/相手を分ける。
5) 勝率だけでなく、残HP割合、与被ダメ、失敗数、暫定技使用、ステージ別の偏りを見る。
6) 特定の相手の再現用方策を使った場合、本物の相手AIではないことを結果へ明記する。

check-current-bot.mjsは同梱v3.6bを固定パスで読む検査です。
candidate.jsを--bot0で指定しても、この検査まで候補へ自動切替にはなりません。
新Botの記録入力検査が必要なら、別の検査として候補パスを受け取るよう拡張してください。
v3.6bの合格15,898入力を、未検査の候補Botの合格数として流用しないでください。


13. ゲームのアップデートへ対応する手順

まず何が変わったかを、カード数値、時間、射程、状態、床、キュー、構成、APIの項目別に整理します。
ルール版が変わった場合、旧版データを新モデルへ無理に通して合わせません。

推奨手順：
1) 旧版一式を残し、新しいrules、仕様、replay/resultを別の入力群として保存。
2) 変更された規則だけを抜き出し、明文化された事実と推定を区別。
3) 数値はrules、時間/形状はBEHAVIOR、処理はArenaの該当関数へ反映。
4) 新規/変更技は、観測前に既知の技へ似せただけなら暫定とする。
5) 新ルールの再生器・設定・init情報・view・合法性検査も対応させる。
6) 旧版の再現結果は旧版として保存し、新版の件数と混ぜない。
7) 新版の新規自律対戦、受け入れ検査、版番号、文書をまとめて更新。

この版は複数ルールを自動切替する作りではありません。
engineのconstructor、simulateのloadSetupとBot init、calibrate、検査、設定には0.7前提があります。
rules.jsonのruleset文字列だけを変えれば新版になる、という構造ではありません。
modelVersionはsimulate.mjsとverify-delivery.mjsにもあり、改訂時に整合させます。

既存検査には意図的な固定値があります。
・元14戦/5,794ティック、OPP-12の4戦/1,959ティック、全18戦/7,753ティック。
・52カード、暫定5技、44境界テスト、9 CLIチェック、Bot入力15,898回、新規12試合。
・旧r1の固定ハッシュと、不一致684項目/155イベントフレーム等。
・analyse-opp12のフォレスト5回と、その当時の暫定維持の判定。
新しい検証群を追加するときは、その群用の集計/検査を作ってからrun-checksへ加えるのが安全です。
歴史的な4戦や旧r1の不一致という事実は、新モデルの都合で書き換えません。
件数や暫定判断を変更する必要がある場合も、変更理由と新しい入力の根拠を示してください。
単にテストを緑にするために期待値を予測値へ置き換えてはいけません。


14. 再現度をさらに上げる改善候補（まだ実装していない案）

優先度の高いものから、必要な実データを集めて進めます。
・フォレストバインドの別行サンプルと、未観測4錬成の発生・境界位置・防御例を追加。
・既存状態があるところへリフレッシュと属性攻撃が重なる、回復後も致死となる等の同時処理を収集。
・通常/アクアバルカンの時間、+30と重ね強化、異コード混在錬成の合法性を実測。
・機能ごとの証拠台帳を設け、file/tick/座標/条件/仮説/未検証部分を機械可読に管理。
・検証データの一覧をmanifest化し、歴史的な18戦と新規追加分の集計を分離。
・BEHAVIORの位置依存配列を名前付き項目に整理し、予備/硬直/形状の取り違えを防ぐ。
・simulateのVM実行器とengine側のbotRunner/playの重複を整理し、同じ規約へ寄せる。

最適化は実測してから行います。まず実行時間とメモリのどこが重いかを測り、
変更前後で同じ記録・同じシードの状態、イベント順、統計が変わらないかを確認します。
イベントを省略した高速モードを作る場合も、検証用の完全モードを残してください。
性能のために観測と異なる当たり判定や、弾をまとめたダメージ計算へ黙って置換しないでください。


15. この環境の限界

・本番の1判断20単位/1試合2400単位、16MB上限を計測・強制していません。
・CLIは1判断1秒のVMタイムアウト、1試合既定30秒のプロセスタイムアウトを使います。
  --timeout-msで変更できるのは後者です。単位制予算と同じ意味ではありません。
・例外やタイムアウトはモデルの試合エラーになります。本番の空行動継続や連続失敗停止は未再現です。
・1試合につき席ごとのVMを維持し、init後にselect/actを繰り返します。
  サーバーの復元実行等のライフサイクルを完全に再現したものではありません。
・Nodeのvmは、この用途では信頼するBotを比較する実行器です。敵対的なコードの安全実行を保証しません。
・Math.randomは試合/席でシード化、Date.nowは固定。ゲーム本体のシャッフルも独自の決定的乱数です。
  同じseedでもサーバーと同じ手札になると仮定しません。再生照合は記録された手札を入力します。
・非公開の相手方策、未提供のリプレイ、未観測の全組合せまで再現していません。
・Botのコメントにある形状や比較成績を、そのまま確定したゲーム仕様として採用しません。


16. 現在の合格基準と、更新後の納品

現在のエンジンSHA-256：
2c6395a0804e12bc37213ea711e8544d0e9caff720a16774770b26f47158b34e
同梱Bot v3.6bのSHA-256：
fbcdb9f9aa6d1c6d9f120d62083c7196a419506a3c1469621979ea1aa28e026b

全18戦の連続再生の合格値：
・7,753 / 7,753 戦闘ティック一致。
・381,781 / 381,781 状態比較一致。
・7,968 / 7,968 イベントフレーム一致。配列順を含む。
・702サマリー比較で差分0。
・44境界テスト、9 CLIチェック、v3.6bの15,898入力が合格。
・全6ステージ×両席の新規12試合が完走、errors=0。
これらはresults/delivery-check.json等にあります。完全一致はこの観測範囲での結論です。

物理モデルを更新した場合の納品に含めるもの：
1) 動くコード一式と設定。モデル版とBot版を別々に記載。
2) 入力データの版・ハッシュと、変更前の比較対象。元の観測値は保存。
3) 変更箇所、理由、file/tick/eventsの根拠、未確認条件。
4) 再実行コマンドと、実行した結果JSON。実行していない検査は未実行と明記。
5) 元データと新データの連続再生結果、不一致が残れば全件の索引。
6) 新しい境界テスト、暫定一覧、既知の限界、同じseedでの動作確認結果。
7) Bot強化も行った場合は、物理モデルの照合結果と別にしたA/B比較。

checksums.sha256は配布時点の全ファイルの一覧です。
検査を再実行するとresults/が再生成され、実行場所等によって一部のハッシュが変わる場合があります。
ソースのハッシュ、結果に記録されたengineSha256、検査の成功を合わせて追跡してください。
古い結果JSONのengineSha256を、新しいソースのものへ手編集して整合したように見せてはいけません。

実サーバーへの適用はこの作業と別です。オフライン検査だけで本番登録済みとは報告しません。
今回のセットは、ほかのLLMがこの会話なしで作業を引き継げる出発点として使ってください。

