技術詳細版 / ultracode × Fable 5

AIに塊魂を作らせたら、
実在の東京を呑み込む
ゲームになった

マルチエージェントで設計→実装→レビュー→公開まで。
注目した技術要素を全部出します(削る・飛ばすのは自由)
🟢 この資料もAIと作成技術詳細・全量
これだけのプロンプトで動いた — goal機能
/goal
fableの性能を見るためにブラウザ動作する3Dゲームを作りたい
塊魂のようにオブジェクトを取り込んで大きくなると
シームレスにもう一段階大きい視界と大きさに変えて
取り込むスケールが大きくなっても重くならない仕組みに
ultracodeで実行してデプロイまで完了させて
全作業の思考・意思決定・実装方法をログに残して
cloudflare pages で public 公開して
完成するまで頑張って。
トークン上限に行きそうになったら止めて報告して
この1プロンプト(goal機能)から、設計→実装→レビュー→公開まで自走。各バージョンの実プロンプトは右の記事に全文掲載 →

A. 完成物と前提

何を・なぜ作ったか
完成物
  • ブラウザで動く塊魂風3Dゲーム
  • 秋葉原 2cm → 東京 → スカイツリー → 宇宙
  • スタック: Three.js r177 + Vite 6 のみ
  • 純粋静的・サーバーレス(Cloudflare Pages)
  • 外部アセットは東京地図284KBとドナック20KBのみ。残りは全部コード生成
fable-katamari.pages.dev
ending
エンディング:夜の地球に東京の光
何を検証したか(要件)
  • ブラウザ3Dの塊魂(取り込んで成長)
  • 成長で「一段階大きい視界」へシームレス移行
  • スケールが5桁変わっても重くならない
  • ultracode(マルチエージェント)で実行
  • 全作業の思考・意思決定をログに残す
  • 公開 — そしてオーナーの追加注文に5バージョン応える
= Fable 5 が「一貫した品質で複雑なソフトを育て続けられるか」の実験
のべ約150体
のエージェント / 約1,816万(18M)トークン / 5バージョン / 実働約19時間
数字はすべて作業ログの実測値

B. ultracode

AIが自分を多数エージェントに分裂させて開発する。今日の主役
3つの型
型1 設計コンペ
N案を独立に出す→審査AIが採点・統合(judge panel)
型2 並列実装
契約を凍結→5ストリーム並列→統合者が配線
型3 敵対的レビュー ★
指摘→別AIが反論・論破→確定/棄却
道具は Workflow ツール:pipeline / parallel の合成、resumeFromRunId で中断再開
型1 — judge panel(独立提案→審査)
  • 🟦 設計A:パフォーマンス最優先
  • 🟩 設計B:ゲーム体験最優先
  • 🟨 設計C:シンプルさ最優先
  • 🟪 審査:採点→最良案ベースに長所を移植
各案は構造化スキーマを強制
schema = {
  scaleSystem, physics, rendering,
  spawning, fileLayout,
  interfaces, risks
}
// 3案を互いに不可視で並列生成
// → 🟪 が採点し統合
🟪 協議ログ(実際の採否)
提案主張の核心採否
🟦性能(9点)シム単位帯[0.5,2.5] + 1F相似変換 S=0.2、SoA/零アロケーションベース採用
🟩体験(8.5)シームレス法則:tierIndexは見た目だけ、ゲーム量は半径の連続関数移植
🟨簡潔(7.5)最少パーツ・デバッグ容易性部分採用
クラスト焼き込み(重い)却下→埋没カリング
3案一致物理エンジン不使用・自作アーケード物理独立に全員一致
互いに知らない3体が、物理の結論で一致したのが象徴的
型2 — 契約凍結 + 並列 + 統合
  • Phase 0 で全モジュール契約(イベント/型/定数/骨格スタブ)を凍結
  • 各ストリームは他人のファイルを触らない=5本を真に並列実装
  • 統合者だけが main.js で配線
// v4: main.js に「形だけ一致する no-opスタブ」を置き
// 置換用の構築式と引数順をコメントで埋め込む
const osmSpawner = NOOP; // ← new OsmSpawner(store, hashes, pools, bus, scaleMgr, world)
// → 統合は「構築式の差し替え」だけ。呼び出し側の書換ゼロ
型3 — 敵対的レビュー ★ 今日いちばんの推し
for finding in review_findings:
    skeptic = spawn_independent_agent()
    verdict = skeptic.try_to_refute(finding)
        # コードを読み、到達可能性・数値根拠・仕様適合を検査
    if verdict == REFUTED:
        reject(finding)           # 偽陽性として棄却
    else:
        confirm(finding); fixer.queue(finding)
レビューの指摘をそのまま信じない。別AIがわざと反証を試みる
敵対的レビューの実例(v1)
🟥 指摘:「?r= デバッグ起動でフレーム1の大量吸収が起きる」
🟧 反証:「症状は実在。だがスポーン内容は tierIndexと_scaleExp依存で、ballRadiusSim非依存。提案修正は同じ結果=無効。むしろ prewarm 閾値を踏む分わずかに悪化」 → 棄却
v1:指摘の36%を偽陽性として棄却(9/25)。確定21件中16件を修正
中断からの再開 — resumeFromRunId
  • セッショントークン上限が v3で1回・v4で3回発生
  • 回復後 resumeFromRunId でキャッシュ温存して再開
  • 例:v3レビューはフィクサー/バリデータのみ再実行(設計・敵対検証は再利用)
  • 毎回 二重実行ゼロで完走
長時間・大規模ワークフローが途中で死んでも回復できる

C. シームレスな無限スケール

塊魂の真骨頂を、技術でどう繋ぐか
課題
2cm 〜 数百m / 月 を、
5桁のスケール差で60fps、しかも継ぎ目なく
  • 素朴にやると:座標が巨大化して float32 精度が崩壊(ジッタ)
  • ティア境界で見た目がポップ/ヒッチする
  • オブジェクト数が増えて重くなる
核心 — 相似変換リスケール
// シムは常にボール半径 [0.5, 2.5] の狭帯で回す
on TIER_UP:
  for e in all_sim_entities:   // 球・物体・原点・粒子…
     e.position  *= S          // S = 0.2
     e.radiusSim *= S
  worldScale /= S             // 累積スケールを逆向きに
  // 実寸 trueRadius は double で別管理 → この操作で不変
1フレームで世界全体を一様縮小。シムの数値は常に「ちょうど良い大きさ」
なぜ継ぎ目が見えないか
  • カメラ位置・注視点・フォグは radiusSim の純関数
  • 全状態を一律 0.2倍 = 相似変換 → 描画結果がピクセル同一
  • 実測:KeyR強制リスケールでワールド描画はバイト同一(差分はHUD時計の407pxのみ)
  • v5地表の再計算でも 1 ulp(1.7e-16)しかズレない
実寸は double 管理なので、月でも惑星でも float32 精度が保たれる
対の原則 — シームレス法則
吸収判定・カメラ・フォグ・速度は、すべて半径の連続関数
ティア番号は見た目(スポーン内容・パレット・演出)だけを駆動する。
  • ゲーム量がティア境界で不連続にならない → ポップ/ヒッチが構造的に消える
  • v5「謎の溝」修正も:地表を 実半径0.6→3mでフェード(連続)→ リスケール不変

D. 重くしない設計

自作アーケード物理(依存ゼロ)
  • 動体はボール1個だけという割り切り(コア ~314行)
  • 衝突候補はティア帯別の空間ハッシュ(フラット型付き配列)でマイクロ秒
  • 取り込んだ物はボール子階層に取り付け時1回だけ行列書込、埋没で自動間引き
  • ゼロ・パー・フレーム・アロケーション(モジュールレベルのスクラッチ)
物理エンジンを入れない=バンドルも軽い(v1で gzip 154KB)
描画 — InstancedMesh プール + ドローコール台帳
  • アーキタイプ×ティアの InstancedMesh プール
  • ドローコール上限を台帳で管理(v1=55 → v2=60 → v3以降=72
  • レビュー確定例:生インスタンスが0になった瞬間に mesh.count を畳む(ティアを跨いで蓄積していた)
  • v4 EXTRAは BatchedMesh(1メッシュで複数ジオメトリ)で +draw を抑制
実測:街中 50〜58 / 72、フィナーレ最悪 70 / 72

E. リアル東京(v4)

OpenStreetMap の実データをボクセル化
なぜ Google Maps でなく OpenStreetMap か
  • Google Maps Platform ToS は派生データセット化・再配布を禁止
  • OpenStreetMap(ODbL)採用。帰属表示で合法に「実在の東京」
  • リリースゲートに ODbL 遵守を組込:アプリ内クレジット / ライセンスリンク / 抽出日時 / 派生DB公開(data/osm-raw + scripts/osm)
real tokyo
OSM由来の実在ビルで埋まった東京
ビルド時パイプライン(決定論的・再実行でバイト同一)
fetch  Overpass(UA必須/スロット監視/429リトライ/再開可能)
build  dedupe(type,id) → 外環ステッチ → 投影 →
       カバレッジ重心クリップ → 除外ゾーン → OBB → 高さ →
       ボクセル量子化 → 長屋マージ(14,546) →
       クリアランスベイク(13,367 inset/14,319 drop) →
       バンド分け → KEEP_K間引き → FKT4 v1 バイナリ
verify 予算/カウント±20%/重複0/POLY u16/バイト同一/
       ナビゲビリティ/ランドマーク実距離 → predeployゲート
成果と勘所

284KB に圧縮

  • 実在ビル 14,563棟(水平1:5/高さ1:2.5)
  • 道路 12,980 / 河川・公園 1,478
  • core 105KB + outer 181KB(上限1,536KB)

ナビゲビリティ・ベイク

  • 1:5だと実街路がボールより狭い
  • 道路回廊に食い込む建物を内側に押す/落とす
  • 到達率 r=1で100% / r=3で96.7% をゲートに
ランタイム & リスケール法則の適用
  • OsmWorld:シャード fetch + デコード、ティア2デッドラインで手続き生成にフォールバック(ワンウェイ)
  • OsmSpawner:共有ObjectStoreに FLAG_OSMアドミッション制御で生存数4096を保証
  • BatchedMesh プール2本(容量2048/1024、boot時に容量充足アサート)
  • 地表Groupの変換は scale=1/worldScale, pos=-shift純関数=実座標タイルでもリスケール不変

F. バグ物語

AIが作ったものを、人間が遊ぶと
① 銅像→月 のピボット(v2)
  • 当初「民家→街の銅像ゴール」で設計
  • 批評AIが15件の重大問題検出(屋内カメラ破綻、ドアのソフトロック…)
  • オーナーが実プレイ→「ゴールは月に変更、家の中案は取り下げ」
  • 判断:最もリスクの高い地形/壁/段差を丸ごとキャンセル。旧設計の流用部はJSONにサルベージして再投入
② 成長暴走の構造的修正(v3)
  • キュレート配置がティア帯ウィンドウ外で吸収判定に不可視→ 東京タワーをすり抜け
  • 修正:DYNAMIC RE-BANDING — 昇格時に生存配置の tierOfclamp(naturalBand, t-1, t+1) で再スタンプ
  • 既存64個/フレームのラウンドロビン内で償却、約370配置すべてに効く
③ 開幕が真っ暗(v4→v5)— 一番の珍事
原因:実在のJR総武線の高架が、実座標でちょうどスタート地点の真上を通り、線路リボンが2cmボールの「天井」になっていた
  • 地表の浮かせ量がフォグ下限で半径の90%/道路Yオフセットが半径の3倍も重なる
  • 修正:浮き/オフセットを半径10%上限でクランプ+パイプラインで店周辺のリボンをクリップ+verifyに再発ゲート
リアルデータゲームならではのバグ
④ 批評AIが自走して検算した(v4設計)
  • 批評エージェントが設計の数字を信じず自分でOverpassカウントクエリを再実行
  • 見積もりが実測の1.5倍ズレ(建物58,155棟)と発覚 → 全予算を実測で再導出、ペーシング 2.2x→2.8x
  • 実装でもランドマークは手打ちIDでなく名前/タグクエリで解決+実距離アサート
役割を与えると、検証手段を自律的に選ぶ

G. 進化と学び

テスト・検証駆動 — Fableの自走力の源泉 ★
  • エージェントの約71% / トークンの約58%検証・レビューに投下
  • 敵対的検証で71件中31件(44%)を偽陽性として棄却(v1 9/30・v2 6/19・v3 16/22)
  • ヘッドレス約3,000アサート(最大塊v4 OSM 2,376)+ 実機検証25パス
  • KeyR pixel-identity を1 ulpまで / osm:verify ALL GREEN ゲート(到達率≥95%・バイト同一・予算240KB)
  • ビルド緑でも壊れる回帰を自己捕捉:ACCEL_K終端速度デッドキャップ / 開幕不可視 / 成長暴走 / 批評AIのOverpass再実測
「たくさん作れる」より前に「たくさん検証して自分の出力を疑える」。これが精度と自走の背骨
v1 → v5 早見表
テーマ主な追加規模
v1無限スケール土台相似変換/シームレス法則/自作物理48体/377万
v2月ゴールBGM/背景/スコア/銅像→月ピボット38体/382万
v3箱庭東京スマホ最適化/コレクション/ドナック実況40体/607万
v4リアル東京OSM 14,563棟/モデル品質/3回中断再開~16体/352万
v5ポリッシュ開幕誘導/スタックチャン/地球エンディング6体/98万
AIに任せて見えたこと
  • AIの指摘は 別のAIに論破させると精度が上がる(偽陽性36%棄却)
  • エージェントは役割を与えると自走して検算(Overpassを自分で叩く)
  • 契約凍結が並列を可能にする — 統合は構築式の差し替えだけ
  • 中断は resumeFromRunId で回復可能
  • そして最後は 人間が遊んでバグを見つけた(自動検証はエラー0で素通り)
まとめ

AIと人が組むと、ここまで作れる

judge panel・契約凍結並列・敵対的検証 + 相似変換リスケール + OSM 284KB。
すべて実コードと実測値に裏打ちされている。
遊ぶ:fable-katamari.pages.dev
ソース(MIT):github.com/aieo-product/fableDemoGame
ありがとうございました 🌏
記事 全画面モード — ← / → で解除してスライドに戻る(F / Esc も可)