世界を動かす技術を、日本語で。

このゲームは、Windows、Linux、そしてブラウザで動作する13 KiBの単一ファイルです。

概要

Justine Tunneycosmopolitan libc プロジェクトに触発され、 GUI非対応バイナリ肥大化 の課題を踏まえて、 16KiB未満のマルチプラットフォームゲーム 制作に挑戦。 SnakeゲームWindows、Linux、ブラウザ で単一ソースから動作するよう実装。 各プラットフォームごとの工夫バイナリ圧縮技術 を駆使し、 最終ファイルサイズ13,312バイト を実現。 ゲーム内容や操作方法レベル進行実装技術詳細 を解説。 ファイル構造や起動メカニズム も詳述。

マルチプラットフォーム対応Snakeゲーム制作記

  • cosmopolitan libc はCソースを 複数OS対応の単一バイナリ にできるツールキット
  • GUI未対応バイナリ肥大 の課題を背景に、 独自アプローチ で小型ゲーム制作に挑戦
  • 目標16KiB未満SnakeゲームWindows、Linux、ブラウザ で動作させること
  • 単一ソースファイル から全環境向けにビルド・実行可能な設計

ゲーム内容・操作

  • クラシックなSnakeゲーム ルールを全プラットフォーム共通で実装
  • 操作方法矢印キー または WASDキー で移動、 ESC で終了(対応プラットフォームのみ)、 R でリセット、 P で一時停止、 スペース で開始
  • スコア管理 :食べ物1つで 10点15%確率で出現する黄色フルーツ20点
  • フルーツの出現・消滅 :一定間隔で出現し、食べられなければ一定時間で消滅
    • 消滅タイマー はスネークの長さに比例
  • 10個のフルーツを食べると次レベル進行、壁配置がランダム化
    • 迷路生成 :常にスネークの頭からフルーツまでの経路が存在
    • 初期配置 もランダム、進行方向に最低5マスの空きスペースを保証

各プラットフォーム向け実装

  • Windows版 :WinAPIを使い i686 Visual C 向けC言語で実装
    • 圧縮スクリプトデコンプレッサスタブ を付与
    • PEヘッダ の余白を利用し、 シェルスクリプト を埋め込む
    • Windowsのapphelp機構 に依存し、初回起動時にエラー表示される場合あり(再実行で解消)
  • Linux版x86_64 Linux 向けにClangとX11でC言語実装
    • lzma圧縮小型シェルスクリプトドロッパ でバイナリ展開・実行
  • HTML5版JavaScriptHTML5 Canvas で実装
    • ファイル先頭のガーベジデータ を無視し、 CSSで不可視化 して本体レンダリング

ファイル構造と起動メカニズム

  • 3つの実装バイナリを連結 し、各プラットフォームが 自身に対応する部分のみ実行
    • Windows :PEヘッダ認識でWindowsコード実行
    • Linux :シェルスクリプトでバイナリ抽出・展開・実行
    • ブラウザ :HTMLタグまでスキップし、通常のWebページとして解釈
  • 最終ファイルサイズ13,312バイト
  • コード抜粋バイナリレイアウト も詳細に分析

技術的工夫

  • PEファイルシェルスクリプト の共存による ポリグロット設計
  • LZMA圧縮シェルドロッパ でLinuxバイナリの小型化・展開
  • HTMLとCSSの悪用 で不要データの非表示化
  • 全てのバイナリを1ファイルにまとめる ことで、 クロスプラットフォーム性と小型化 を両立

まとめ

  • cosmopolitan libc の課題を踏まえた 独自アプローチ
  • マルチプラットフォーム で動作する 極小ゲーム の実現
  • 圧縮・埋め込み技術バイナリフォーマットの知識 を活かした設計
  • C言語とJavaScript による 3種実装単一ファイル配布 の工夫
  • 技術的チャレンジバイナリ解析 に興味のある開発者向けの事例

Hackerたちの意見

自分の場合: - ブラウザ: .htmlに名前を変えたら動いた - Linux: "./snake.com: line 20: lzma: command not found"。xzパッケージをインストールしたら動いた(XWaylandは有効にしてたからX11は動いたけど、厳しいWaylandセッションだと必要かも)。 - Windows: .comでも.exeに名前を変えても「アプリケーションが正しく開始できませんでした (0xc0000005)。OKをクリックしてアプリケーションを閉じてください。」って出る。これをどうやって動かすか分からないけど、AV関連ではないことは確か(このサンドボックスVMではそれを削除してる)。編集: 今は3つとも動くようになった。Windowsでは、以前にいくつかのアプリをテストするために全プログラムでDEPを有効にしてたから、それをオフにしたら起動できた。

投稿に書いてあるよ。

$ chmod +x snake.com $ ./snake.com だけで実行すると、Monoを使おうとして「アセンブリ './snake.com' を開けません: ファイルに有効なCILイメージが含まれていません。」ってなる。でも、Bashで明示的に実行すると動く: $ bash snake.com かなり便利だけど、どのLinuxでもすぐには動かないね、少なくとも:p Debian 13で試してる。

Windows 11では動くよ。

Polyglotの面白いところは、誰もそれをもっと早くやらなかったことだね。10年か20年前には実現可能だったはず。

そういえば、最初のポリグロットファイルがいつ公開されたのか気になるな。ずっと前からあったと思ってたけど。EICAR.COMがCOM/plaintextポリグロットとして思い浮かぶね。

クロスプラットフォームではないけど、Windows用のkkriegerゲームを思い出す。96kのFPSゲームで、当時は見た目がすごく印象的だった。 https://web.archive.org/web/20100304155706/http://www.thepro...

あのリンクから: 現代のPCでもまだ動く!ダウンロードして起動できた。

メトロイドプライム4をプレイしてるときにこれを思い出した。これと同じアイデアで、どんな現代のゲームが作れるんだろう?

関連する話だけど、エミュレーターでオリジナルのゼルダゲームのファイルサイズが小さいのを見ると、どれだけ少ないテキストやコード、情報でこんなに素晴らしい体験ができたのか、どれほどの影響があったのか、そして自分にどれだけの時間の魅了をもたらしたのかに驚かされる。 https://en.wikipedia.org/wiki/The_Legend_of_Zelda_(video_gam...

ゼルダの伝説1は128kBだったよ、圧縮なしでね。続編はその倍だね。

僕が一番好きだったトリックは、すべてのダンジョンや洞窟が一つの長方形のマップの一部だったこと。デザイナーたちは特定のデザインを彫り込んで、他のレベルは残りのマップ画面に合わせて作られてたから、スペースにぴったり収まってたんだ。洞窟は単画面の隙間を埋めるために追加されてた。 https://ian-albert.com/games/legend_of_zelda_maps/

確かに、魔法だね。コモドール64についてはどう?64KBの中に巨大なマップがあるゲーム(Eindeloos, Radarsoft, 1985)があったよ。最近、誰かがそのマップ(500画面)を抽出したんだけど、PNGだけで800KBもあるんだ。ストーリーを見て、ズームインしてマップの中の小さなハートを探してみて! https://adayinthelifeof.nl/2025/03/07/endless.html

https://js13kgames.com/

一つのファイルでどこでも動かせるアプリケーションのアイデアが大好きなんだ。サーバーレスプラットフォームでこれを実現しようと頑張ってるよ。複雑なデータ駆動型アプリをたった一つの.htmlファイルと、主に宣言的なHTMLマークアップだけで作れるんだ(リモートサーバーから読み込まれるウェブコンポーネントのおかげでね)。現代のブラウザ機能を使えば、バンドルシステムは必要ない。これを取り除くと、全く新しい世界が広がるよ。file://プロトコルを使って.htmlファイルを読み込む能力は、強力で、しばしば見落とされがちな機能なんだ。実際には、HTMLファイルをダブルクリックするだけで、ブラウザでアプリがすぐに動くってことだね。

残念ながら、多くのブラウザ機能は非HTTPSの環境では使えないんだよね。

あなたのシングルファイルHTMLアプリの開発はどんな感じで進めてるの?公開されてる例とかある?すごく興味深いね。

関連する話だけど、WindowsのEXEファイルはDOSで実行できる(少なくともDOSが存在していた頃、つまりWindows 3.1xや9xの時代ね)。でも大抵の場合、DOSの部分は「このプログラムはMicrosoft Windowsを必要とします。」って表示して終了しちゃう。例外としてregedit.exeがあって、これを使うとDOSでもレジストリの値をインポートできるんだ。(あれ、でもどうやってWindows APIを使わずにそれができるんだろう?)

例外としてregedit.exeがある これってどこかで変わったかもしれないね。君の質問の後半部分、Windows APIを使わずにどうやって変更を加えたのか気になってたんだけど(古いDOS APIを使ってると思ってた)、僕のregedit.exeには「このプログラムはDOSモードでは実行できません。」っていうDOSスタブが入ってるよ。

へぇ、でも、どうやってWindows APIを使わずにそれができるんだろう?何も知らないけど、彼らがWindows APIが使ってるコードを直接インポートするか、実装コードの場所を知ってそれを読み込むか、ライブラリを静的にリンクしてるんじゃないかな!結局、regeditは他の非公式プログラムが守らなきゃいけないクリーンネスルールに従う必要がないからね。多分、レジストリエディティングAPIやフォーマットが変わったら、regeditもそれに合わせて更新されるから!

Linuxの実行ファイルを抽出したら、正しく読み込んで実行できるのに、readelfやobjdumpがそれに対してエラーを出すのに驚いたよ。調査してみたら、動的リンカーの名前がPT_DYNAMICヘッダーエントリの「未使用」フィールドに押し込まれていて、スペースを節約していることが分かったんだ。プログラムヘッダー: タイプ オフセット VirtAddr PhysAddr ファイルサイズ メモリサイズ フラグ アライン INTERP 0x0000000000000088 0x0000000000010088 0x0000000000010088 0x000000000000001c 0x000000000000001c 0x0 [要求されたプログラムインタープリタ: /lib64/ld-linux-x86-64.so.2] DYNAMIC 0x00000000000000e0 0x00000000000100e0 0x6c2f343662696c2f 2つの質問があるんだけど、1. これは手動でやったの?それともこれを行うツールを使ってるの?他にもサイズ削減のトリックがいくつか見えるよ。2. こんなバイナリに対しても動作する実行ファイルを調べるツールって誰か知ってる?

どうやってチョークするの?ここではどっちも問題なく動いてるよ。

ndisasmを使えば読めるし、16進数エディタも使えるよ。こんな形式を壊すツールなんて必要ないし、無駄な保存で価値もない。AVや他のものに問題を引き起こす可能性もあるし。WindowsのDEPについてのコメントも見たけど、正直言って、こんなものには10フィートの棒でも触りたくないわ。もし作成者がみんなに遊んでもらいたいなら、普通のバイナリを提供すればいいのに。こんな難解なゴチャゴチャじゃなくてさ。

こういうメカニズムが「強化」されて、本当の「ユニバーサル」バイナリを作ることができるのかな?Haxeみたいなソースからソースにコンパイルする言語を使って。Haxeなら、一度アプリケーションを書くだけで、win32とlinuxの両方をC++にコンパイルしてターゲットにできるし(その後、各プラットフォーム用のツールでコンパイルする)、さらにHTMLをJavaScriptにコンパイルしてターゲットにできる。記事で説明されてるような同じ連結メカニズムとヘッダーの悪用を使えば、3つのターゲットを一つのファイルにまとめて、すべてのプラットフォームで実行できるようになるんだ!

一番面白いのは、そのものが3つの独立したグラフィックスベースのゲームの実装をつなぎ合わせて、巧妙なマルチプラットフォームローダーに包まれてることなんだ。それでいて、たったの13KiBしか使わないんだから。