ZAPAnet総合情報局 > ZAPAブログ2.0 > わずか565バイトテトリスのプログラミング解説

わずか565バイトテトリスのプログラミング解説

2007年11月01日 プログラミングTIPS
往年の名作「スーパーマリオブラザーズ」、あの濃い内容でわずか40キロバイト」に載っていたわずか565バイトのテトリス。文字数にして551文字。79文字*7行のプログラミングで、テトリスが動きます。

以下のソースコードをメモ帳に貼り付けて、htmlで保存すればテトリスが動きます。
<body onKeyDown=K=event.keyCode><script>X=[Z=[B=A=12]];h=e=K=t=P=0;function Y()
{C=[d=K-38];c=0;for(i=4;i--*K;K-13?c+=!Z[h+p+d]:c-=!Z[h+(C[i]=p*A-Math.round(p/
A)*145)])p=B[i];!t|c+4?c-4?0:h+=d:B=C;for(f=K=i=0;i<4;f+=Z[A+p])X[p=h+B[i++]]=1
if(e=!e){if(f|B){for(l=228;i--;)Z[h+B[i]]=k=1;for(B=[[-7,-20,6,17,-9,3,6][t=++t
%7]-4,0,1,t-6?-A:-1];l--;h=5)if(l%A)l-=l%A*!Z[l];else for(P+=k++,j=l+=A;--j>A;)
Z[j]=Z[j-A]}h+=A}for(i=S="";i<240;X[i]=Z[i]|=++i%A<2|i>228)i%A?0:S+="<br>",S+=X
[i]?"■":"_";document.body.innerHTML=S+P;Z[5]||setTimeout(Y,99-P)}Y()</script>
ソースコードには様々なテクニックが施されているのがわかります。
(そのぶん、非常に読みにくいコードになっていますが)

→テトリスの動作サンプルはこちら
操作方法は、Enterキーで回転、カーソルキー(左右)で移動です。
Windows IE6で動作確認しました。指定のキー以外を押すと変な動作をしてしまいます。


この驚異的なソースコードができあがるまでの軌跡はこちらにありました。
七行プログラミング part2
565バイトテトリスの元となっているソースコードの解説も載っています。
(コメントが入っているので、わかりやすいですね)
<body onKeyDown=K=event.keyCode><pre><script>function Y(){ 

  Z[11]=P; // 得点を表示バッファに書き込み
  E=B[t]; // 現在落下中のブロック
  f=0; // 移動・回転決定用フラグ
  if(K) // キーが押されているか
    if(K!=32){ // 横移動
      d=K-37?1:-1 // d:x方向の差分
      for(i=0;i<4;i++) // 横移動判定
        f+=Z[h+E[i]+d]==S; // 移動先が空白かどうか
      f?0:h+=d; // すべて空白なので移動決定
    }else{ // 回転
      C=[]; // 回転先の座標保持用
      for(i=0;i<4;i++){ // 回転判定
        p=E[i]; // ブロックの各位置
        v=Math.round(p/12); // 回転先の x 座標
        w=p-v*12; // 回転先の y 座標
        C[i]=w*12-v; // 回転先の座標計算
        if(Z[h+C[i]]==S)f=1; // 回転先が空白かどうか
      }
      t*!f?E=B[t]=C:0; // すべて空白なので回転決定
    }
  K=0; // キー入力キャンセル
  for(f=i=0;i<4;i++){ // 落下判定
    f+=Z[12+(p=h+E[i])]==S; // 落下先が空白かどうか
    Z[240+p]=S // ブロック表示のために表示バッファへコピー
  }
  if(f){ // 落下できない
    for(i=0;i<4;i++)Z[h+E[i]]=S; // ブロック停止
    t=++t%7; // 次のブロック決定(現在順送り)
    h=17 // 位置初期化
  }else h+=12; // 一段落下
  for(k=1,i=19;i--;){ // ラインがそろったか判定
    for(j=11;--j&&Z[i*12+j]==S;); // そろったラインを検索
    if(!j){ // そろった
      P+=k++; // 得点 1ライン 1点, ..., テトリス 10点 になる
      for(j=++i*12;j>2*12;)Z[j]=Z[j---12] // 全体を一段下げる
  }}
  for(i=240;i--;){
    D.all(6+i).innerHTML=Z[240+i]; // 表示用バッファを表示
    Z[240+i]=Z[i] // 表示バッファのクリア
  }
  Z[5]!=S?setTimeout(Y,99):0; // 入り口にブロックがあったら終了
}
// ブロックの作成
// 中心からの差分で配置する。棒を除くすべてのブロックはL字にブロックがあり、
B=[[-11],[-24],[2],[13],[-13],[-1],[2,-1]]; // それ以外の1個だけを別にする
for(i=0;i<7;i++)B[i].push(0,1,-12); // ブロック共通部分追加
D=document;
// 位置は、縦方向1マスは 12 単位となる 座標(x,y) なら h=x+y*12
h=17; // ブロックの中心位置初期化
Z=[]; // バッファ [0-239]:固定したブロック用 [240-479]:表示用バッファ
for(K=t=P=i=0;i<240;){
  D.write(i%12?"":"\n","<b></b>"); // HTML表示領域の描画
  Z[240+i]=Z[i]=++i%12<2||i>228?S="□":" "; // 床と壁の設定、番兵にもなる
}
Y()
</script>

初期化やfor文の条件式で文字数を削ったり、処理自体を改良したりしています。
圧縮前と圧縮後のコードを読み比べると、どのようにプログラムを改良したのか勉強になります。


ぷよぷよ版

ついでに、テトリスと並ぶ落ちゲー代表作「ぷよぷよ」のソースコードも載せておきます。(2chのみんなでぷよぷよするぞ!阪神優勝25連鎖より)
<body onKeyDown=K=event.keyCode-38><script>function T(){h=100,B=Math.floor(Math
.random(l=8,p=-1)*16+4)}function Y(){for(S="",i=104;i--;i%8||(S+="<br>"))n=N[i]
,S+=n?~n?"<a style=color:#"+(249*n)+">●</a>":"■":"_";document.body.innerHTML
=S;N=[K-50?h-=M[h+l-K]|M[h-K]?0:K:M[h+p]?0:(x=p,p=-l,l=x)];for(K=0;++i<113;N[i]
=M[i]);N[h]=B>>2;N[h+l]=B%4+1;if(!(++e%10))if(h-=8,M[h]+M[h+l])for(M=N,T(f=0);f
=!f;){for(i=112;i--;g=C[i]=0);for(;g=!g;)for(i=0;i<103;i++)if(n=M[i+8],!M[i]&&n
)M[i]=n,M[i+8]=g=0;for(;i--;){n=c=0;for(E=[i];M[i]>0&n>=c;c++)for(j=4;j--;t>102
|C[t]|M[i]-M[t]||(C[t]=E[++n]=t))x=p,p=-l,l=x,t=E[c]+p;if(n>3)for(;n;)f=M[E[n--
]]=0}}M[100]||setTimeout(Y,49)}for(M=N=[C=[i=113]];--i;M[i-1]=i%8>1&i>7?0:-1);Y
(T(e=K=0));</script>

→動作サンプルはこちら
操作方法は、xキーで回転、カーソルキー(左右下)で移動です
Windows IE6で動作確認しました。指定のキー以外を押すと変な動作をしてしまいます。

大容量が当たり前になった時代とは言え、職人的なプログラミングテクニックはいつの時代でも感動ものです。
わずか40キロバイトのマリオも素晴らしいですが、わずか565バイト(0.565キロバイト)のテトリスも素晴らしいですね。(しかも色とか付いていないのに、プレイするとついついはまっちゃいます)


追記

続・わずか565バイトテトリスのプログラミング解説
あの565バイトテトリスが、ついに500バイトを切った!
わずか96キロバイトの超絶FPSゲーム
わずか681バイトで動くぷよぷよ