プログラミング
テトリスをすべて見るための私の探求
My quest to see all of Tetris (antithesis.com)
要約
筆者は、同社が「テトリスをクリアした」と宣言したものの、ティーンエイジャーゲーマーがゲームの限界を超えたことを知り、自身の探求を開始しました。目標は、レベル255を一周してゲームをリセットさせる「リバース」を達成することでしたが、レベル160で停滞していました。最終的には、同社のユニークなファジング技術と、ゲームの内部構造を解析するアプローチを用いて、この目標を達成しました。
全文翻訳
2026年の初め、私は冷や汗をかいていました。数年前に、Antithesisは「テトリスをクリアした」と宣言したのです。私たちは、クリアしたゲームの名前を会議室につける伝統があり、2021年以来、オフィスには「テトリス」という名前の部屋があります。しかし、私の勝利宣言は、事態の進展によって色褪せてしまいました。後述するように、ティーンエイジャーゲーマーは、私たちの製品が到達できなかったゲームの領域まで、人間のパフォーマンスを拡張する方法を学んでいたのです。私は、ティーンエイジャーに追いつくために、自由時間を使うことをサイドクエストとしました。目標は「リバース」でした。つまり、テトリスの全レベルをクリアし、レベルカウンターが255から0にロールオーバーするまでプレイすることです。これが起こると、ゲームはレベル0のスピードとカラーリングに戻り、再びレベルを登り始めます。もしテトリスのすべてを見ることができれば、確かにそれをクリアしたと主張できるはずです。残念ながら、私たちの探求はレベル160で停滞していました。私は新しい部署に異動するところで、それが起こるとテトリスに取り組む時間が、おそらく二度となくなるだろうと知っていました。しかし、私はそこにいました、停滞していました。しかし、話が先走っています。この話には、最も苛立たしい「停滞」状況の解決にもかかわらず、ハッピーエンドがあることを最初にお伝えしておきます。物語は最初から始めるべきです。もし、私たちが最終的に何をしたのか、そして途中で見つけた奇妙なグリッチだけを見たいのであれば、「リバース!」に直接ジャンプできます。
なぜテトリスなのか?
Antithesisの初期の頃、新しいエンジニアは通過儀礼を受けていました。それは、象徴的な1980年代の任天堂エンターテイメントシステム(NES)のゲームをプレイし、できればクリアすることによって、私たちのシステムを学ぶことでした。時間が経つにつれて、私たちはマリオ、ゼルダ、メトロイドを含む多くのクラシックゲームに挑戦してきました。私が2019年に参加したとき、私はアーカノイドを選びました。なぜなら、それは私の2番目に好きなNESゲームだったからです。私のお気に入りのゲーム、テトリスはすでに試みられていました。1
1 今日、「アーカノイド」という名前の会議室があることを嬉しく思います。しかし、それはまた別の話です。
私は90年代に、ヨーロッパの顧客訪問からの帰りのフライト中に、テトリスに半ば中毒になりました。当時の革新的な機内エンターテイメントシステムには、座席でプレイできるバージョンがありました。私は、飛行中にブロックを回転させ、配置する方法を理解するために、空間認識能力を使うことを楽しみました。完了したラインがスコアとともに消えていくのを見ることは、即座に達成感を与えてくれました。もちろん、テトリスはあなたを長く快適な状態にさせてはくれません。ちょうど調子に乗ってきたところで、ブロックはより速く落ちてきます。もしあなたが、新しいブロックで空きスペースを覆ってしまうようなミスを犯すと、あなたは防御を強いられ、今すぐクラッシュアウトしないために、将来の健康を犠牲にしなければならなくなります。終わりというものは存在しません。あなたはただ負けるまでプレイし続けます。誰も無傷では済みません。(これはすべて、何かの比喩かもしれません。)
以前テトリスをクリアしようとしたとき、私たちは進歩しましたが、ゲームのかなり先まで進むことはできませんでした。しかし、テトリスをクリアするという考えは私の中に残り、(非常に)長期間にわたるサイドプロジェクトとなりました。しかし、「クリアする」とは一体何を意味するのでしょうか?通常、Antithesisでは、ゲームを「クリアする」とは、勝利の結論にゲームをプレイする一連のユーザー入力を発見することを意味します。テトリスには勝利の状態がないため、何がゲームをクリアしたと見なされるかを正確に定義することは、少し曖昧です。このことについての私たちの理解は、(ティーンエイジャーゲーマーの助けを借りて)進むにつれて進化しました。
Antithesisはどのようにゲームをプレイするか
Antithesisはテトリスを通常とは異なる方法でプレイします。まず、その仕組みを説明します。私たちは、ゼロから完璧なユーザー入力を作成する、典型的なツールアシストスピードランのようなことはしません。また、ゲームをプレイする方法を知っていて、画面を見て反応するロボットを作成するのでもありません。代わりに、既知の開始ゲーム状態に対してランダムな入力の比較的小さなトライアルを実行し、各トライアルがゲームに与える影響を評価し、目標に関して興味深いと見なすゲーム状況を記憶します。私たちは、NESゲームで起こることは、それが受け取るユーザー入力によって完全に決定されるという事実を利用します。これにより、ゲームのスナップショットを取ることをケチることができます。ゲームに送信したすべての入力を保存している限り、状態の履歴の中で最も近いスナップショットから開始し、私たちが望む正確なゲームモーメントに到達するために必要な入力を供給することで、任意のゲーム状態を再作成できます。2
2 私たちの決定論的ハイパーバイザーを使用すると、任意のプログラムで同じことができます!そこに至るためにシステムに注入したランダム性を再生することで、テスト実行の任意の瞬間を正確に再作成できます。実行中、これにより、テストしているプログラムの興味深い場所に復帰できます。実行後、バグ状況の再現を起動できるため、顧客はデータをキャプチャし、What-if分析を実行できます。
テスト実行の典型的なセットアップでは、1つのAntithesisファザーが32のエミュレートされたNESセッションに、ファジングしているゲームをロードして供給します。私たちは各セッションをfceuxエミュレータでホストし、fceuxのLua統合を使用して、エミュレータをヘッドレスモードで実行するハーネス(harness)スクリプトを作成します。3
3 私たちは、MD5ハッシュが0x5b0e571558c8c796937b96af469561c6のROMから、オリジナルの変更されていないNTSCバージョンのテトリスを使用します。エミュレータには、fceuxバージョン「2.2.3 debug」を使用します。この投稿の例を作成するために、ファザー入力からfceuxムービーを生成し、fceuxバージョン「2.7.0-interim git」でムービーを再生します。
ファザー内には、「タクティック(tactic)」と呼ばれるコンポーネントがあり、テストしているゲームの入力を生成します。次に、「ストラテジー(strategy)」コンポーネントが、入力の結果を観察し、次に何をすべきかをファザーに指示します。(これについては、以前のGradiusをクリアした投稿を参照してください。)
これがどのように連携するかを見るには、1フレームにズームインすると役立ちます。NESゲームは、1秒間に60回の画面更新レートでサイクルします。各サイクルには、次のアクションが含まれます。
私たちのハーネスは、入力パイプからファザーからの入力バイトを受け取ります。ハーネスは入力バイトを読み取り、待機中のエミュレータに送信します。エミュレータは入力バイトを処理し、ゲームメモリを変更し、新しいゲーム画面を生成してから、ハーネスに戻ります。ハーネスはエミュレータを調査し、特定のゲームメモリ位置の値と、オプションで新しいゲーム画面をキャプチャします。結果は、ファザーに非同期で処理するために戻る出力パイプに配置されます。ハーネスは次の入力を処理するためにループバックします。
ファザー(非同期)
ハーネス
エミュレータ
保存/ロードゲーム
保存/ロード
タクティック
入力バイトを生成
ストラテジー
結果を観察
入力パイプ
バイト
結果
出力パイプ
入力バイトごと:
1. バイトを読み取る
2. エミュレータに送信する
5. エミュレータメモリを読み取る
6. 結果をファザーに送信する
3. 1フレーム実行する
• 入力を受け入れる
• ゲームメモリを更新する
• ゲーム画面を生成する
4. ハーネスに戻る
× 32
並列
コアループ。ファザーはこのループに対して非同期です。入力のバッチを事前に生成し、ハーネスからの戻りを受信するにつれて観察します。
その仕組みが整ったら、ゲームをクリアすることは2つのことに集約されます。
適切なタクティック(入力生成アルゴリズム)を見つける
ゲームをクリアするようにファザーを導く適切なストラテジーを見つける。
終わりがないゲームであるテトリスについて、私たちの最初の考えは、それをクリアすることは、最高の人間ができることと同じくらいうまくやることだろうということでした。私たちが考慮に入れなかったのは、他のすべての人間でした。
ラウンド1:学習
私の同僚であるアレックス・プシェニチュキンは、すでにファザーにテトリスをプレイさせようとしていました。彼は、エミュレータメモリを読み取ることで、現在のブロックレイアウトを取得するための基礎を理解していました。彼はこれを使用して、彼が見つけたテトリスをプレイすることに関するいくつかの研究に基づいて、最初の「良さ」の尺度を計算しました。このバージョンは、入力を作成したタクティックが基本的にランダムな入力を生成していたため、プレイに長い時間がかかりました。4
私たちの実行結果をムービーに変換して見たとき、ブロックが落ちるのを見るのは、穏やかなそよ風に捕らえられた雪の結晶を見るようでした。それらは画面をゆっくりとさまよい、行ったり来たりしながら揺れ動き、最終的にランダムな場所に落ち着きました。
4 例外は、pを押すボタンでした。