はじめに
こんにちは、haruka_apps です。Playload で作品を作りながらわかったことをまとめるシリーズの6本目です。
Playload は、kitao さんが作られた「URL だけでゲームを共有できる」小さなゲームエンジンです。コードは圧縮後 3,000 バイトまで、という制限があります。
私はこの 3,000 バイトにいつも苦しんでいます。あと 50 バイトあれば音が入るのに、という場面が何度もありました。そこで今回は、作品には手を入れず、圧縮のやり方だけで余裕を作れないか を調べました。
これは、成功の記事ではありません。最初の目標(QRコード)には届かず、そのあと見つけた方法も「効く作品には 100 バイト少々効く」くらいです。それでも、どこに壁があるのかは数字ではっきり分かりました。うまくいかなかったことも含めて、道のりをそのまま残します。
- 対象は Playload 0.9.6、私の作品が Arcade に執筆時点で載せていただいている 8 本です。
- Playloadエンジンには一切手を入れていません。すべて、ふつうの Playload の URL として動きます。
- 調査と計測は AI(Claude)と一緒に進めました。Chrome・Safari・スマホでの起動と、QRコードの読み取りは、私が実機で確かめています。
シリーズ目次
| # | タイトル | URL |
|---|---|---|
| 1 | Playloadの画面は「文字のマス」と「動くキャラ」でできている | https://qiita.com/haruka_apps/items/697ad6affba676620ef0 |
| 2 | 絵の描き方の基本 ― 内蔵文字で描く、自作文字は少しずつ書き換える | https://qiita.com/haruka_apps/items/0637e72b1c5b1f73dfd4 |
| 3 | 3KBで3Dを描く ― Cube Maze 3D の描き方 | https://qiita.com/haruka_apps/items/38af177afbb4a4fab397 |
| 4 | 3KBの中に8ビットコンピュータを作った ― PL-8 | https://qiita.com/haruka_apps/items/e14af0ddf02e65bc521f |
| 5 | 迷路を自分で覚えるAI ― td maze. | https://qiita.com/haruka_apps/items/a0e0c1c5c81d943a3b83 |
| 6 | 3KBの壁を圧縮で押してみた ― 8本中7本が小さくなったけれど、すすめはしない話 | https://qiita.com/haruka_apps/items/5b594c6323e05b86c457 |
この記事でわかること
- Playload のゲームを QR コード 1 枚に入れられるのは、どの大きさまでか
- ブラウザの圧縮より小さくできる「予測する圧縮」のしくみ
- それを 3KB の世界に持ち込むと、なぜ簡単には勝てないのか
- 私の 8 本で試した結果と、効く作品・効かない作品の違い
- この方法をおすすめしない理由
先に結論
| 作品 | 今 | 新しい方法 | 差 |
|---|---|---|---|
| SHOGI 9x9 | 2,938 | 2,782 | −156 |
| REVERSI | 2,927 | 2,786 | −141 |
| td maze. | 2,825 | 2,690 | −135 |
| PL-8 CPU | 2,949 | 2,892 | −57 |
| reveal. | 2,946 | 2,903 | −43 |
| Cube Maze 3D | 2,921 | 2,892 | −29 |
| Pseudo 3D Racer | 2,934 | 2,916 | −18 |
| minesweeper ai. | 1,937 | 2,094 | +157 |
(単位はバイト。エディタに表示される Payload と同じ数え方です)
↑ 8 本の結果。青は小さくなった作品、オレンジは大きくなった作品です。
- 3KB ぎりぎりの 7 本は、平均で約 83 バイト小さくなりました。
- ソースコードは 1 文字も変えていません。
- そのかわり、エディタで開いてもコードが読めなくなります。ここが一番大きな代償です。
きっかけは QR コードでした
展示会のチラシに QR コードを刷って、スマホのカメラで読んだらそのままゲームが始まる。そんな配り方ができたら楽しいな、と思ったのが始まりです。
条件は 1 つだけ決めました。スマホの標準カメラで読んで、kitao.dev が直接開くこと。読み取り用のページを自分で用意したり、短縮 URL を使ったりはしません。誰かのサービスが止まったら遊べなくなる、というのを避けたかったからです。
QR コード 1 枚に入る大きさ
QR コードは、いちばん大きいもの(バージョン 40、誤り訂正 L)で 2,953 文字まで入ります。Playload の URL は「書き出しの 35 文字+圧縮データを文字にしたもの」なので、逆算すると、
圧縮後およそ 2,190 バイトまでのゲームなら、QR コード 1 枚に入る
ことになります。3,000 バイトではなく 2,190 バイトなのは、URL にするときにデータが 3 分の 4 倍にふくらむからです。
私の 8 本でこの線の内側にあるのは、minesweeper ai.(1,937 バイト)だけでした。これは実際に QR コードにして、スマホのカメラから起動できることを確かめています。
残りの 7 本は 2,825〜2,949 バイトです。線まで 600 バイト以上あります。
QR に合わせて圧縮を作り変える(届かず)
QR コードには「英数字モード」という詰め方があり、大文字・数字・一部の記号だけなら 1 文字を 5.5 ビットで入れられます(ふつうは 8 ビット)。Playload の URL に使える文字のうち、大文字・数字・ハイフンの 37 種だけで URL を書ければ、同じ QR にもっと多く入るはずです。
そこで、圧縮データを文字にしたとき、なるべくこの 37 種が続くように圧縮のしかたを選ぶ という方法を作りました。圧縮には「同じ中身を表す書き方」がたくさんあるので、その中から QR に都合のよいものを選ぶわけです。
結果はこうでした。
| minesweeper ai. | |
|---|---|
| ふつうの圧縮で QR に使う量 | 2,560 |
| QR に合わせた圧縮 | 2,512(−1.9%) |
動きはしました。この QR コードも、スマホで読めることを確かめています。けれど 1.9% では QR の大きさは変わりません。
机上での限界も計算しました。37 種の文字しか使わない場合、どんなにうまくやっても入るのは 2,767 バイト相当までです。私の 7 本のうち 4 本は、この時点で入らないことが決まります。残りも、現実には舵を切るための損が大きく、届きませんでした。
QR コード 1 枚に入れられるのは minesweeper ai. だけ。 これが最初の結論です。
3,000 バイトの圧縮に戻る
QR はあきらめて、もとの悩みに戻りました。3,000 バイトの中に、もう少し余裕を作れないか、です。
ブラウザの圧縮がやっていること
Playload は、ブラウザに入っている圧縮(DEFLATE)を使います。この圧縮の得意技は「前に出てきた文字の並びを使い回す」ことです。String.fromCharCode が 2 回出てくれば、2 回目は「さっきのあれ」と短く書けます。
逆に言うと、使い回せるのは まったく同じ並び だけです。「for( のあとには let が来やすい」といった、ゆるい傾向は使えません。
予測する圧縮
もっと強い圧縮は、次の 1 ビットを 予測 します。
- 予測する: 「直前の 1 文字」「直前の 2 文字」「2 つ前と 3 つ前」など十数通りの手がかりごとに、「次は 1 になりやすいか」を記録した表を持ちます。
- 混ぜる: 十数個の予測を、重みをつけて 1 つにまとめます。よく当たる手がかりの重みは、読み進めるうちに自動で大きくなります。
- 詰める: 予測が当たったビットはほとんど場所を取らず、外れたビットだけ場所を取る書き方で記録します。
- 戻す: 戻すときは、まったく同じ予測をもう一度やり直しながらデータを読みます。
「次に来るものを当てる」という点では、生成 AI と同じ考え方です。こちらは表が十数枚あるだけの、とても小さなものですが。
この考え方は、JavaScript を小さくする道具として知られる Roadroller(Kang Seonghoon さん作、MIT ライセンス)で使われているものです。今回の方法は、しくみを Roadroller から学び、Playload に合わせてプログラムを書き直したものです。
ただし、戻すためのプログラムも URL に入れる
ここが 3KB の世界のつらいところです。ブラウザは「予測する圧縮」の戻し方を知りません。だから、戻すためのプログラム(復号器)も、ゲームと一緒に URL に入れる 必要があります。
↑ REVERSI の中身。ゲーム本体は 560 バイトほど減りますが、復号器が 420 バイトほど増えます。
本体が 500 バイト減っても、復号器が 500 バイトあれば差し引きゼロです。つまりこれは、復号器をどこまで小さく書けるかの勝負 でした。
勝つまでの道のり
REVERSI(今は 2,927 バイト)で、工夫を 1 つずつ重ねていきました。
↑ REVERSI の変化。最初は今より 159 バイトも大きくなりました。
① 最初の試作(+159)
ちゃんと元のコードに戻りましたが、復号器が 650 バイトほどあり、大負けでした。
② 復号器を短くする+文字合わせ(+13)
復号器を書き直して短くし、さらに「文字合わせ」をしました。これが今回いちばん Playload らしい工夫です。
圧縮したデータは 64 種類の文字で書きます。どの 64 種類を使うかは、実は選べます。一方、ブラウザの圧縮は「よく出る文字ほど短く書く」表を、全体で 1 つだけ作ります。そこで、データに使う文字を、復号器でよく使う文字(数字やかっこなど)に合わせました。すると復号器の文字も「よく出る文字」の仲間入りをして、短く書いてもらえます。これだけで約 80 バイト縮みました。
③ 予測の表を大きくする(−43)
表が小さいと、別々の手がかりが同じ場所にぶつかって予測が濁ります。広げたら、初めて今より小さくなりました。
④ 作品ごとに設定を探す(−109)
どの手がかりを使うか、どれくらい速く学ぶか、を作品ごとに探します。
⑤ 表の引き方を変える(−141)
手がかりの文字を数式で番号にまとめるのをやめ、文字の並びをそのまま見出しにしました。数式がいらなくなり、ぶつかりもなくなりました。
最終的な復号器は約 500 文字です。REVERSI の URL に入っている実物はこうなっています(データの部分は省略)。
// @title REVERSI
for(M=`…ここに圧縮したゲーム本体…`,B=new Uint8Array(4605),W=[],F=[],R={},C={},
K=[0,1,2,21,32,431,642,31,541],O=D=E=A=0;E<4605;B[E++]=P,A=A?P-A&&A:P==290&&P)
for(P=1;P<256;P=P*2+I){ /* 予測して、1 ビット読んで、表を直す */ }
eval(new TextDecoder().decode(B)) // 元のコードに戻して実行
うまくいかなかったこと
勝ち筋より、負けた案のほうがずっと多かったです。
| 試したこと | 結果 |
|---|---|
| QR の英数字モードに合わせた圧縮 | 動いたが −1.9%。QR の大きさは変わらず |
| Roadroller をそのまま使う | コメントや空白を自動で削り、// @title も消える。コードを変えない設定では 8 本とも今より大きくなった |
| 対数を使わない軽い計算に替える | 予測が壊れて、データが 6 倍になった |
| 場面ごとに重みを切り替える | 得は 9 バイト以下。足すコードのほうが大きい |
| Playload の API 名を先に教えておく | 7 バイトしか縮まない |
| よく使う書き方を先に教えておく | 45 バイト縮むが、教える文を入れるのに約 125 バイトかかる |
AI の見積もりも何度か外れました。「100〜350 バイト縮むはず」と出た数字が、よく調べると比べ方が公平でなかった、ということもありました。数字は必ず「同じコードで、同じ数え方で」比べる。当たり前のことですが、身にしみました。
Roadroller と比べる
元になった Roadroller をいちばん強い設定にして比べました。
| REVERSI | |
|---|---|
| 今(ブラウザの圧縮) | 2,927 |
| Roadroller(コードを変えない設定) | 2,963 |
| Roadroller(標準。コメントや空白を削る) | 2,925 |
| 今回の方法(コードを変えない) | 2,786 |
Roadroller が劣っているという話ではありません。Roadroller は 13KB くらいの作品を想定した、何にでも使える道具です。3KB という小ささに合わせて作り直すと、そのぶんだけ得が出た、ということです。
効く作品、効かない作品
8 本の結果をもう一度見ると、きれいに分かれます。
-
よく効いた(−135〜−156): SHOGI 9x9、REVERSI、td maze.
探索や AI など、ロジックが中心の作品です。「次に何が来るか」が当てやすいコードです。 -
少しだけ効いた(−18〜−57): PL-8 CPU、reveal.、Cube Maze 3D、Pseudo 3D Racer
絵や ROM を 16 進の文字列で持っている作品です。こういうデータは、ブラウザの圧縮ですでに限界近くまで詰まっています。 -
逆に大きくなった(+157): minesweeper ai.
もとが 1,937 バイトと小さいので、復号器の 400 バイト超がそのまま重荷になります。
Playload の知識は、圧縮には効きませんでした。 Playload の命令は cls put spr btn btnp play scr pcg の 8 個だけで、どれも 3〜4 文字です。復号器は 1 回見ればすぐ覚えるので、先に教えても 7 バイトしか変わりません。命令が少なくて短いという Playload のよさが、ここでは「工夫の余地がない」理由になっていました。
おすすめの考え方
ここまで書いておいて何ですが、私はこの方法を、ふだんの作品づくりにはおすすめしません。
- コードが読めなくなります。 Playload は、URL を開けばコードが読めて、その場で直せるのが魅力です。この方法を使うと、エディタには暗号のような文字が並ぶだけになります。
- エディタで直せません。 1 文字直すたびに、パソコンで詰め直す必要があります。
- 得は 100 バイトほどです。 その 100 バイトは、たいていコードの書き方を見直せば作れます。そのほうが、作品もよくなります。
3,000 バイトという制限は、工夫を楽しむために kitao さんが決めてくださった線だと私は受け取っています。圧縮の裏技でその線を動かすより、線の内側で頭をひねるほうが Playload らしい、というのが今の気持ちです。
なので、この記事は道具の紹介ではなく、「3KB の壁はどこにあるのか」を調べた記録 として読んでいただけるとうれしいです。
つまずきやすいところ
同じことを試す方のために、気をつける点を残しておきます。
- 比べるときは、同じコード・同じ数え方で。 圧縮の道具の中には、黙ってコメントや空白を削るものがあります。それは圧縮の力ではなく、コードを変えた分です。
- 「ソースが短い」と「圧縮後が短い」は別です。 16 進の文字列を生のバイトに詰め直すと、ソースは 2 割短くなるのに、圧縮後は変わらないか、むしろ増えました。
- 小数の計算はブラウザによってずれる心配があります。 復号器は対数と指数の計算を使います。今回は Chrome・Safari・スマホのどれでも起動しましたが、試すなら必ず実機で確かめてください。
-
見えない制御文字が入ります。 データの中に、
\の代わりの見えない文字が入っています。エディタでコピーや編集をすると壊れるかもしれません。配るなら URL のままにします。 - 起動時に、元に戻す計算が走ります。 パソコンで 0.5 秒ほどです。私のスマホでは気になりませんでしたが、古い機種では待つかもしれません。
おわりに
今回わかったことをまとめます。
- QR コード 1 枚に入るのは、圧縮後およそ 2,190 バイトまで。私の 8 本では minesweeper ai. だけでした。
- QR に合わせて圧縮を作り変える方法は動きましたが、効果は 1.9% でした。
- 「予測する圧縮」を 3KB 向けに作り直すと、8 本中 7 本が小さくなりました。ロジック中心の作品で 135〜156 バイトです。
- 勝負を決めるのは、約 500 文字の復号器でした。データに使う文字を復号器に合わせる工夫が、いちばん効きました。
- ただし、コードが読めなくなります。ふだんの作品づくりには、おすすめしません。
届かなかった目標もありましたが、「ここまでは行ける、ここから先は行けない」という地図が手に入りました。次に 3,000 バイトで悩んだときは、裏技ではなく、コードのほうを見直そうと思います。
しくみを学ばせていただいた Roadroller の Kang Seonghoon さん、そして Playload を作ってくださった kitao さんに感謝します。
最後まで読んでいただき、ありがとうございました。


