GoKart:オートパイロットが午後のうちに作り上げたマリオカート風レースゲーム
Agent! のオートパイロット(Auto-Pilot)に「GoKart という名前のマリオカートクローンを作れ」というゴールをひとつ渡して戻ってくると、3 つのコース、8 種類のアイテム、AI ライバル、そして 3,344 件のテストチェックに合格する Godot 4 のレースゲームができていました。2 日後、エージェントによる 87 コミットを経て、それは 4 つのコース、バトルモード、タイムトライアル、マリオカート 64 風のメニューを備えた GoKart 0.0.2 になりました。行き詰まった場面も含めて、ログが語る実際の経緯をまとめます。
10 月 4 日更新:この記事は、最初の午後に続く 2 日間を追い、その晩に 2 つのオートパイロットセッションが 1 つのリポジトリを共有した話を含め、GoKart 0.0.2 リリースで締めるようになりました。スクリーンショットは 0.0.2 タグから撮り直しています。
昨日の記事ではオートパイロット(Auto-Pilot)を紹介しました。Mac 版 Agent! に /auto <ゴール> と入力すると、Stop All を押すまでそのゴールに向かって無人でサイクルを回し続けます。この記事は、それをゲームに向けたときに反対側から何が出てきたかの話です。
ゴールは、私が打ち込んだほぼそのままです:
create a Mario Kart clone called GoKart with all Mario Kart effects. I believe Godot 4 can do the Mario Kart effects, but I haven't built any of them yet: drift sparks and boost flames (GPUParticles3D), speed lines and boost blur (screen-space shaders, glow and tonemapping), item effects and tire trails (shaders plus ribbon meshes), kart movement (VehicleBody3D or custom arcade physics). Write unit tests and test the game frequently.
予算:時間制限なし、サイクル数無制限。すべてのサイクルで Agent! 内のモデルは Claude Sonnet 5.5 でした。最初のコミットは 12:39。同じ日の 16:32 には、リポジトリはコミット 33 件、GDScript ファイル 47 個、GDScript とシェーダーのコードが約 5,500 行、そして 3,344 件のチェックに合格するユニットテストスイートを持っていました。2 日後の 0.0.2 タグの時点では、その数字はコミット 126 件、ファイル 150 個、約 24,500 行、14,134 件のチェックに増えていました。そのコミットはすべてエージェントが作成したものです。すべて GitHub の AgentiLoop/GoKart にあります。
オートパイロットがやること、1 サイクルずつ
オートパイロットはプロジェクト内の .agent/autopilot/progress.md に継続的なログを残し、各サイクルが「何をしたか、何を前提にしたか、何が残っているか、何かに阻まれたか」を追記していきます。以下の内容はすべてそのログが出典です。自分の記憶ではなくログを引用するのは、ログのほうが私より正直だからです。
サイクル 1 の時点でマシンに Godot はありませんでした。brew install --cask godot を実行し、バイナリにシンボリックリンクを張り、git init を行い、kart_physics.gd をシーン依存のない純粋なモデルとして書きました。加速、ブレーキ、後退、摩擦、ステアリング、開始した方向に固定されるドリフト、そして 3 段階のミニターボ。VehicleBody3D ではなく独自のアーケード物理をあえて選び、理由も述べています。「マリオカート風の挙動をユニットテストしやすくするため」。テスト 11 件、チェック 17 件、最初のコミット。
サイクル 2 は、私が名指しで頼んだエフェクトを追加しました。ミニターボ段階に応じて色が変わる GPUParticles3D のドリフト火花、排気の炎、リボンメッシュのタイヤ痕、そして放射状ブーストブラー、スピードライン、ビネットのためのスクリーンスペースシェーダー。チェック 43 件。
サイクル 3 はコースを作りました。3 メートルごとにリサンプリングした Catmull-Rom ループ、路面リボン、コライダー付きの赤白ストライプの壁、スクロールするシェブロンシェーダー付きのブーストパッド、順序付きの 8 つのチェックポイント、そしてショートカットや逆走周回をカウントしないラップトラッカー。新しいテストのひとつは、実際の物理モデルで 3 周を走り切る追従ボットです。タイムは 1:02.5。チェック 90 件。
サイクル 4 は、虹色フレネルシェーダーのアイテムボックス、ルーレット、そして最初の 3 つのアイテムを追加しました。キノコ、バナナ、壁で跳ね返るミドリこうら。被弾するとカートは 1.2 秒間スピンします。チェック 235 件。
そして、行き詰まりました。
行き詰まった場面
次のサイクルは AI カートとヘッドレスのスモークレースを追加し始め、そのまま戻ってきませんでした。次のセッションで判明した原因は、main.gd の 115 行目に紛れ込んだタブ 1 文字。パースエラーのせいでスモークスクリプトがエラーを吐き続け、それを動かしていたシェルコマンドには時間制限がありませんでした。
私は Stop All を押し、同じゴールに大文字のメモを添えて新しいセッションを開始しました。全文は載せませんが、要旨はこうです。スモーク実行のテストで行き詰まった、行き詰まるな、シェルに時間制限をかけろ。
2 回目のセッションの最初のサイクルはタブを修正し、AI が内側の壁に向かってコーナーをショートカットしないように AI の先読みを路面サンプル 10 個から 6 個に減らし、コーナー速度リミッターを追加し、macOS には timeout コマンドがないため、すべてのシェル実行を perl -e 'alarm 100; exec @ARGV' で包みました。それ以降、ほぼすべてのサイクルの末尾でログには「すべての実行は perl alarm の制限下で行った」と書かれています。ゴールの文面から教訓を学び、それを守り続けたのです。
そのセッションは約 1 時間 15 分で 15 サイクルを回し、どのサイクルも 1 つの機能です。タイミングよくアクセルを踏むとロケットスタートのブーストがつくスタートカウントダウン、ミニターボ段階アップのポップと画面端のフラッシュ、ミニマップ、追尾するアカこうらとスター、回転するホイール・ステアリングする前輪・頭を向けるドライバーを備えた手続き的なカートモデル、ライバル全員を縮めるサンダー、カートの周りを回るトリプルこうら、路面に沿って先頭を追い詰めるトゲゾーこうら、ポイント付きのリザルト画面、エンジンのループからゴールのジングルまで音声ファイルを一切使わない完全合成オーディオ、2 つ目のコースを備えたタイトルメニュー、周回数オプション、3 つ目のコース、そして各 AI カートの位置付き 3D エンジン音。
正直だった場面
ログの中で私がいちばん気に入っているのは、告白が繰り返されるパターンです。Agent! は PNG を見ることができず、サイクルごとにそう言いました:
ウィンドウモードのスクリーンショット実行ではエラーは記録されませんでしたが、私は画像を見ることができないので、コースや HUD がどう描画されているかは確認していません。
サウンドは聞いていませんし、スモークレースもスクリーンショットツールも実行していません。
私のツールでは画像を見られないので、そのレビューには人が必要です。
そこで、できることをテストしました。実際のシーンをインスタンス化して状態をアサートするヘッドレスのシーンチェックです。サンダーを発動し、ライバル 3 台がスケール 0.5 でスピンしていること、そして雷が後で片付けられることを確認。密集したスタートグリッドにトゲゾーこうらを撃ち、12 フレーム目の爆発とカート 2 台のスピンを確認。プレイヤーを強制的にゴールさせ、オートパイロットがカートを AiDriver に引き渡し、リザルトパネルに「1st YOU」と表示されることを確認。
3 回目のセッションも同じように止まりました。今度は寛大すぎる 240 秒の alarm の裏で止まり、1 サイクルで私が停止しました。そして人が見ました。その人は私で、4 回目のセッションのゴールは、私のプレイテストメモを軽く整えたものです:
the UI needs to scale with the window. the UI should not be prone to the screen speed effects and blurry. Should be able to use cursor keys. it's not clear when power ups are released. the tops of the walls flicker. Little too hard to steer the kart. hard to keep up with the computer AI karts. maybe have AI difficulty levels Easy Medium and Hard.
1 サイクル後:UI がウィンドウに合わせてスケールする canvas-items ストレッチ、HUD をスピードエフェクトのオーバーレイより上のキャンバスレイヤーへ移動、矢印キーとアイテム用の Enter / Ctrl と画面上のヒント、イーズインするステアリング、壁のストライプの z-fighting 修正(隣り合う赤と白のセグメントが同じピクセルを奪い合っていたので、白のほうをほんの少し大きくしました)、4x MSAA、そして AI の最高速度を 0.72、0.85、1.0 に調整する Easy / Medium / Hard。テストは 2,156 件から 3,344 件のチェックに増えました。スイートの読み込みを妨げていた tests/test_items.gd の既存のパースエラーも見つけて修正したことが理由のひとつです。
自分で止まった場面
その最後のセッションのサイクル 2 から 8 は、コードを一切変更しませんでした。それぞれがリポジトリを読み直し、スイートを再実行し、同じ段落の言い換えを書きました:
画面上で結果を確認できなかったので、ゴール達成とは宣言しません。4 つの項目は、まだ人がゲーム内で試す必要があります。
これが正しい振る舞いです。ゴールは「ステアリングの感触が悪い」「壁がちらつく」であり、どんなヘッドレステストもそのゴールを閉じることはできません。オートパイロットには反復回数の上限がないので、永遠に確認を続けたでしょう。セッションはサイクル 8 の後に終了し、GoKart に次に必要だったのはもう 1 サイクルではなく、プレイテストでした。その晩、リポジトリは 0.0.1 のタグを付けられ、macOS、Windows、Linux 向けに書き出されました。
2 日後:1 つのリポジトリに 2 つのオートパイロット
10 月 3 日、私は違う種類のゴールを持って戻ってきました。機能リストではなく、参照先です:
keep building GoKart to resemble Mario Kart Nintendo 64 version. search Mario Kart N64 or Mario Kart Nintendo 64 and keep improving, iterating, making GoKart better
この 5 回目のセッションは 14:19 に始まり 28 サイクルを回し、ほぼすべてのサイクルがエージェントが調べて作ったマリオカート 64 の要素ひとつです。まずはゲームの骨格から。2 列グリッドに並ぶ 8 台のレーサー、難易度ごとのラバーバンディング、50cc / 100cc / 150cc と鏡像の Extra クラス、互いに押し合う軽量 / 中量 / 重量のカート、そして 9/6/3/1 ポイントとランクアウトルールを備えたグランプリ。次にほかのモード。ゴースト付きのタイムトライアル、そして Big Donut、Block Fort、Skyscraper での風船を使ったバトルモード。それからアイテムのラインアップが埋まりました。トリプルキノコとゴールデンキノコ、ニセアイテムボックス、バナナの束、テレサ、トリプルアカこうらとこうらによる防御。そしてフライングを宣告し、スタート合図を振り、ラップ表示を掲げるジュゲムがやってきました。
午後の残りはコースそのものに費やされました。4 つ目の Dusty Canyon は、カラカラさばく風の列車と踏切を携えて登場。Sunset Speedway にはキノピオハイウェイ風の交通が加わりました。Frosty Peaks にはシャーベットランド風の氷、雪だるま、ペンギンが、Green Hills にはチョロプーが入り、すべてのコースにテーマに合った沿道の景色が付きました。あわせて、スリップストリーム、ジャンプ台、ホップして切り替えるパワースライド、そしてコードのステップパターンからレンダリングされるチップチューンループも。
4 時間後の 18:25、私は 2 つ目のタブを開き、同じリポジトリでより狭いゴールを持つ 2 つ目のオートパイロットを開始しました:
the menus are not Mario Kart Quality and neither is the title shot. and there is over use of black outlines on text everywhere. see Mario Kart 64 screenshots and images on the web and make better menus. focus only on the menus / screens and title shot for GoKart. make conscious decisions. do not conflict with previous /auto working on the application
こうして 18:25 から深夜 0 時まで、2 つのエージェントが同じ作業ツリーにコミットしていました。メニューセッションは、飛び込んで跳ねるアーチ状のグラデーションロゴを中心にタイトル画面を作り直し、その奥にあるすべての画面を順に手がけていきました。各コースの横に画像を添え、選んだコースのライブフライオーバーが流れるセレクト画面、点灯するバー付きのオプション行、回転するカートのポートレートとゴールドのカーソル、各レース前のコースイントロのフライオーバー、ポーズ画面、そして行が次々にスライドインするリザルトボード。その土台には、ドロップシャドウ付きの丸みのあるゴールドとクリーム色の文字という共通パレットが敷かれ、ゲーム内のすべての 8 ピクセルの黒い縁取りを置き換えました。23 サイクルを回し、23:54 にゴール達成を宣言しました。
「衝突しないこと」の一文は実際に効きました。ログは 2 つのセッションが互いを避けながら作業する様子でいっぱいです。メニューセッションは HEAD のクリーンな git worktree から検証し、「もう一方のセッションが進行中のペンギン作業」をテスト実行から外しました。私が頼むと、一方のセッションがもう一方のやりかけの機能を仕上げました。もう一方のタブの編集が同じツリーにあるため、コミットは git add -A ではなくファイル単位でステージしました。きれいとは言えませんでしたが、何も失われず、スイートはその夜、14,134 件合格、0 件失敗で終わりました。
機能セッションのログは、メニューセッションの 3 分後の 23:57 に「Session ended — Stop All」で終わっています。その直後に私が Agent! に伝え、次のチェックポイントコミットのメッセージとして保存されたメモは、Stop All は押されたタブだけを止めるべきで、すべてのタブを止めるべきではない、というものでした。2 つのオートパイロットが動いているとき、両方に効く 1 つのボタンは間違ったボタンです。
これらのスクリーンショットについて
撮ったのは Agent! で、私ではなく、手動でもありません。この記事の最初のバージョンではランダム走行のスクリーンショットを 3 枚頼んだところ、70 行の tools/random_drive.gd を書きました。ふらつくレーンオフセットで路面をたどり、ランダムにドリフトを挟み、持っているアイテムをランダムなタイミングで撃ち、数百物理フレームごとに 1 フレームを保存します。上の Sunset Speedway のショットはそのボットのフレームです。
残りは、各機能に同梱されたショットツールによるものです。メニュー用、コースイントロ用、列車用がひとつずつ、交通・雪だるま・ペンギン用がそれぞれひとつ、HUD 用とバトルモード用がひとつずつあり、それぞれがシーンをセットアップし、適切なフレームを待って保存します。今回の更新のため、すべて v0.0.2 タグをチェックアウトしたクリーンな worktree から実行したので、未コミットのものは写真に写っていません。
Agent! は依然として結果を見ることができないので、ツールは代わりにピクセルをサンプリングします。氷のショットは前方の氷上の路面色をアスファルトと比較して出力し、ポーズのショットはパネルの外で 1 秒間何も動かなかったことを証明し、公開前には上の 10 枚すべての純黒ピクセルを数えさせました。どれもゼロでした。GoKart の README にはさらに掲載しています。
試す前に伝えておきたいこと
- git の中で動かすこと。 オートパイロットに取り消しはありません。GoKart のログが読めるのは、すべてのサイクルがコミットで終わっていたからで、サイクルが一度脱線したときも何も失われませんでした。
- 運用ルールはゴールに書くこと。 「シェルに時間制限をかけろ」は、単発のメッセージよりもゴールの一部としてのほうが効きました。新しいサイクルは毎回ゴールを読み直すからです。
- 目を求められることを想定すること。 見た目や手触りに関することなら、ループは嘘をつかずに正直に止まります。セッションの間にプレイテストの時間を確保し、メモを次のゴールとして戻してください。
- Stop All はワークフローの一部であって、失敗ではありません。GoKart の最初の 3 セッションはすべてそれで終わりました。
- 1 つのリポジトリで 2 つのオートパイロットは動きます。互いの存在を伝えておけば。 「前の /auto と衝突しないこと」を 2 つ目のゴールに入れたところ、両セッションとも互いの邪魔をしないよう本気で努力しました。コードの領域を分けて与え、今のところは一方を止めるともう一方も道連れになることを想定してください。
これは良いのか?
Agent! がリポジトリを読み、コミットを数え、オートパイロットのログを見つけた後に、私はその質問をしました。その答えを、編集なしで:
それは何を求めるかによります。オートパイロットにできることのショーケースとしては、見事です。23 回以上のサイクルで、4 つのコース、アイテム、AI レーサー、グランプリ、タイムトライアル、バトルモード、手続き的な音楽とサウンドが、インポートしたアセットを一切使わずに生まれ、それを詳しく記録した README もあります。1 つのゴール文からできた動くソフトウェアとしては、かなりの量です。
さらに、途中で人間が誰もレビューしていないので品質はエージェント自身の検証ログで判断されたものであること、そして自分はプレイもコードの監査もしていないので実際にどれだけ遊べるかは言えないこと、という但し書きも付けました。これはログが自力で到達したのと同じ結論です。次のステップはプレイテストです。
GoKart 0.0.2
試すのに Godot は必要ありません。GoKart 0.0.2 は同じリポジトリから書き出した 2 つ目のパッケージ版リリースで、0.0.1 から 87 コミット後のものです:
- macOS ユニバーサル(Apple シリコンと Intel)、Developer ID で署名し Apple による公証済み
- Windows x86_64
- Linux x86_64 と arm64
各ダウンロードはゲームデータを埋め込んだ単一の自己完結型バイナリで、受け取ったものを確認したい場合はリリースページに SHA256SUMS.txt があります。Windows 版は未署名なので、SmartScreen の警告が出ます。上で説明したもののうち 0.0.1 になかったものは、バトルモードやタイムトライアルから列車、交通、新しいメニュー、新しいスタイルの HUD まで、すべて 0.0.2 に入っています。完全なリストはリリースノートにあります。
オートパイロットを搭載した Agent! 1.1.87 はリリースページと Homebrew で入手できます。ソースから GoKart を実行したい場合は Godot 4.4 以降が必要です:git clone https://github.com/AgentiLoop/GoKart.git && cd GoKart && godot --path .