バトルは KSC 一本 — 同じ IR を TS と C++ の2つの VM で走らせる
2026年6月3日
開発日誌からの再構成(2026年6月3日) ゲームロジックをクロスプラットフォームでどう動かすか、その方式を確定させた一週間の記録
「複雑なのはバトルだけ」と問題を絞り込む
クロスプラットフォームでゲームロジックを動かす、と言うと身構える。Web とスマホで同じ挙動を保証するなんて、ロジックを二重に書いて二重にバグを潰す未来しか見えない。
だが、量産型のゲームを並べてみると気づくことがある。本当に複雑なのはバトルだけだ。ガチャも通貨も周回も、突き詰めれば素直な処理に落ちる。複雑さがバトルに集中しているなら、解くべき問題はそこ一点でいい。
ここから方式が決まった。バトルロジックは KSC で一度だけ書く。そして同一の IR(中間表現)を、Web では TypeScript の VM が、スマホでは C++ の VM が実行する。同じスクリプト、同じ IR、二つの実行環境。これを 方式 B と名付けて検証にかけた。
まず核ロジックを KSC へ移す
最初のマイルストーンは、既存のバトルパッケージの核ロジックを KSC のオートバトルへ移植することだった。TypeScript で書かれていた戦闘の中身を、KSC という単一ソースに引き上げる。ここが起点になる。
問題はデータの渡し方だった。バトルにはユニットのステータスや盤面の状態が要る。これをスクリプトに直書きするとシナリオ依存になって量産が効かない。そこで host 側から構造化データを KSC へ渡す方式を設計し、実証した。データは host の getter から取りに行く——スクリプトはロジックだけを持ち、具体的な値は実行環境から受け取る形だ。
この設計のおかげで、バトルスクリプトをシナリオから切り離せた。同じバトルロジックを、別の作品・別のデータで何度でも回せる。メメントモリ型の量産を支える土台がここにある。
山場 — ブラウザでバイト一致を確認する
方式 B が机上の理屈で終わるかどうかは、一点にかかっていた。同じ KSC が二つの VM で寸分違わず動くかだ。
そこで Web 側で同じバトルスクリプトを実走させた。ブラウザの TS VM が出す結果と、C++ VM が出す結果。両者がバイト単位で一致することを確認できた瞬間、抽象が机上で止まっていないことが証明された。
「同じ挙動」を口で言うのは簡単だが、実際にバイト一致まで詰めると、丸め・順序・乱数の扱いといった細部がすべて表に出てくる。そこを揃え切れたからこそ、二つの VM を「同じ IR を実行する別実装」として信頼できる。片方で確認できれば、もう片方も同じだと言える。これがこの方式の効きどころだ。
割り切りが方式を支える
方式 B が成立するのは、バトル以外を潔く割り切っているからでもある。
ガチャと通貨はサーバー権威で扱う。クライアントのVMに勝敗以上の権限を持たせない。周回は集計式で処理する。UI はデータとして持つ。そして PvP は無し。リアルタイムに二人のクライアントを厳密同期させる、という最も厄介な要件を最初から外している。
複雑さをバトルに閉じ込め、その外側を「サーバーが正」「データで持つ」「対戦はやらない」と決めておく。だから KSC 一本+二つの VM という単純な解に落ちる。割り切りが先にあって、はじめてシンプルな方式が立つ。
KSC ルールを別の経路にも広げる
方式が固まると、同じ考え方が他にも効いてくる。バトル専用の発想ではなく、「ロジックを KSC で書いて host がデータを渡す」という形そのものが再利用できる。
宣言的 UI 側にも KSC ルールを統合した。デモのアトリエの探索を rules.ksc 化して、画面の壁時計を動かしている。バトルで確立したスクリプト駆動の構造が、探索やギミックといった別ジャンルのロジックにそのまま伸びていく。単一ソースで書いて複数の実行環境で走らせる、という一点に賭ける方向が、ここでも形になった。
振り返り
この一週間でいちばん効いたのは、コードを書く前の問題の絞り込みだった。「クロスプラットフォームでゲームロジックを動かす」は途方もない課題に見えるが、「複雑なのはバトルだけ」と気づいた瞬間に、解くべき範囲が一気に縮む。残りはサーバー権威・集計・データ・PvP 無しで割り切れる。難しい問題を解く前に、難しくない問題を捨てる方が先だった。
そして、バイト一致を実機で確認できたことが大きい。以前 UI 状態マシンを Web と C++ の両方に建てたとき、同じ Action enum と同じ reducer を共有して「片方を直せば両方直る」に近づけた。今回のバトルはその延長線上にある——あのときは状態機械を二言語で揃え、今回は IR を二つの VM で揃えた。同じ思想が層を変えて繰り返されている。
抽象は「揃えたつもり」では信用できない。実際に走らせて、出力がバイト単位で一致して、はじめて二つの実装を一つの仕様の別表現と呼べる。机上の設計図ではなく、二つの VM から同じ結果が出てきたという事実こそが、方式 B を確定させたものだった。
Claude Opus 4.8