プログラミング
56,000行のDOOMを、私が作った言語で
56,000 lines of DOOM, in a language I made up (betlang.dev)
要約
開発者は、独自のプログラミング言語「bet」を開発し、その言語でDOOMの56,000行を書き直しました。このプロジェクトは、スラングのようなキーワードと堅牢なコンパイラを組み合わせた実験であり、AIコーディングエージェントの能力をテストする目的もありました。メモリ管理にはアリーナ方式を採用し、ゲームのフレームレートに影響を与えないように設計されています。
全文翻訳
なぜ私がbet、つまり私が作った言語で56,000行のDOOMをビルドしたのか。
betは、表面上はスラングのキーワードを持ち、内部には真面目なコンパイラを持つ、小さくリアルなプログラミング言語です。LLVMを通じてネイティブコードにコンパイルされ、メモリを自己管理し、セルフホストし、DOOMを実行します。私はこれを自己完結型の実験として構築し、完了させました。
私は、何か売れるものを作ろうとする休憩中にbetを構築しました。明確なエッジを持つ、実際に完成させられるプロジェクトが欲しかったのです。そして、コンパイラを構築することは長い間私のリストにありました。ジョーク言語がその口実を与えてくれました。グループチャットのように読めるキーワードを、実際に機能しなければならないコンパイラで包みました。最初に一つのルールを設けました:リサーチ禁止。誰かがこれを以前に構築したかどうかは調べませんでした。なぜなら、一行も書く前にインターネットがアイデアを潰してしまうのを恐れたからです。結果的に、Geoffrey Huntleyがすでにそうしていました。そして、私はそれを後になるまで知りませんでした。
構文レベルでのみ機能する言語は、実際には単なる衣装です。そのため、私はそれに一つの実際の仕事を与えました。ゲームはオブジェクトを絶えず割り当て、解放します。手動のブックキーピングとガベージコレクションの両方で、節約できないフレームが失われます。betはこれをアリーナで処理します:スコープに割り当て、フレームで使用し、スコープ全体を一度に解放します。このパターン自体は新しくありません。Zigは、一度の呼び出しですべてを解放するアリーナアロケータを出荷しており、Odinはゲームコードのために同じアリーナとテンポラリアロケータのアイデアを組み込んでいます。私はbetに独自のキーワードを持つファーストクラスの機能としてそれを持たせたいと思いました。これにより、言語は既存のもののスラングリスキンではなく、独自の設計として読めるようになります。
これはジョークです。それでも、実際に何かを成し遂げさせたいと思いました。私がそれを構築したもう一つの理由は、実験を行うためでした。私は、実装に一切手を出さなかった場合、AIコーディングエージェントが困難で明確に定義されたプロジェクトでどこまで進めるかを見たかったのです。私の仕事はアーキテクトでした:問題と受け入れ基準を設定し、それらを維持しました。コードレビューは一切行いませんでした。変更は、テストに合格し、受け入れ基準を満たし、コーパスをクリアした場合にのみ出荷されました。それが唯一のゲートでした。
それが機能することの証明は、DOOMの完全なポートです。レンダラー、ゲームロジック、WADローディング、オーディオのid Softwareの元のCソースコードはbetで書き直されました。56,000行以上に及び、実際のシェアウェアゲームをウィンドウで実行するネイティブバイナリにコンパイルされました。それを起動すると、DOOMはアトラクトモードで自動的に再生されます。これらのクリップは、ゲームの元の記録された入力であり、シミュレーションを通じて再生されます。固定小数点演算がそれを実際のテストにしています:わずか一歩のずれで、プレイヤーは壁にぶつかり始めます。betは、参照Cビルドと同期して、フレームごとに、入力をロックステップで再生するため、シミュレーション全体がビット・トゥ・ビットで同一になります。
ショートカットなしでそこまで到達できたことが、トイ言語と実際のソフトウェアを運べる言語を分けるものであり、私がコードレビューを一度も行わなかったビルドで最も信頼している結果です。このようにして、コンパイラ、セルフホスティング、ドキュメント、そしてこのサイトまで、エンドツーエンドで完了しました。そして、そこで終わりです。機能リクエストが来ればレビューしますが、ロードマップもバージョン2も予定されていません。betはジョークであり、実験であり、このようなビルドが実際に何を取るのかを学ぶ方法であり、それらすべてを成し遂げました。もし私について一つ言いたいことがあるとすれば、それは私が始めたことを最後までやり遂げるということです。
私が真剣に取り組んだ部分
メモリモデルが実際の作業です。
手動のfree()ブックキーピングとGCの一時停止は、どちらもフレームバジェットを破壊します。betでは、フレームにスコープされたアリーナであるcribに割り当て、evictでO(1)で全体を解放します。オブジェクトごとの追跡はなく、フレームの途中で停止することはありません。
frame.
bet
crib wave // このフレームのアリーナ
squad e in spawn(wave) {
e.tick()
}
evict wave // 全て解放、O(1)
それが実験です。表面上はジョーク、その下には一つの難しい問題を正しく解決し、テスト以外には何もゲートとしないビルド。ネイティブコードにコンパイルされ、セルフホストされ、DOOMを実行します。製品になることは決して意図されておらず、そうなる必要もありません。完了しました。
どのように構築されたか
実際にかかった時間。
コミット追跡されたタイムログからの実際の数値。アイドル時間を除外したアクティブな作業時間をカウントしています。その除外には一つのギャップがあります:3つのエージェントが使用制限で一時停止した間、時計は動き続けていたため、porting-gamesの行は約12時間高く表示されています。それらを差し引くと、実際の合計は約22時間になります。
porting games
doom, oregon trail, pong
18h
self-hosting the compiler
bet, compiling bet
4h
security review
cwage issues #30–#48
3h
infra, tooling & site
ci, docker, freeze, docs
3h
frontend
lexer · parser · grammar
2h
language & collections
keywords, types, containers
2h
LLVM backend
the actual hard part
1h
arena memory
crib · evict · the point
29m
total
≈ 34h tracked
· most of it on porting games
先行事例 · 正当な評価
betがすでに構築されるまで、cursedの存在を知りませんでした。Geoffrey Huntleyが同じアイデアに先に到達しました:スラングキーワード言語、LLVM経由でコンパイル、AIの指示によって書かれた、私がbetを構築したのと同じ方法です。彼はそれをうまくやり遂げ、私より先に到達しました。その功績は彼にあります。↗ghuntley.com/cursed
オリジナルの記事
𝕏@GeoffreyHuntley
始めた人をフォローしてください