\当サイトではリンク広告を利用しています。/
こんにちは!プログラミングとレトロゲームが大好物なみなさん!
今回のテーマは…なんと「7行で動くテトリス」!
「えっ、あのブロックがカシャカシャ落ちてくるテトリスを、たった7行で?」
そう思った方、めちゃくちゃ普通です。でもマジなんです。
この「7行テトリス」は、コード界隈でひっそりと語り継がれてきた“伝説の最小実装”のひとつ。HTMLで書かれたその短すぎるコードは、一見どう動くのかわからないようで、実は究極に無駄がない美しさを持っています。
今回はその7行テトリスの全貌を、初心者にもわかりやすく解説していきます!
コードの構造、動作の仕組み、そしてなぜこんなに短くできるのか——その秘密を一緒に覗いてみましょう♪
記事の先頭にも埋め込みで遊べるようにしていますが、この7行テトリスの元のゲームページはこちらです(新しいタブで開きます)。
「7行テトリス」は、JavaScriptで書かれた超短縮版テトリスです。
その名の通り、たった7行(約300文字)でテトリスのプレイが可能!
この作品は、「コードゴルフ」や「ショートコーディング」と呼ばれる分野で知られる代表的なスクリプトです。
コードゴルフとは:
目的の機能を、できるだけ短く実装することを競うプログラミング遊びのこと。※なぜゴルフにたとえられているのかというと、ゴルフはできるだけ打数を少なくした方が勝ちだからですね。
ゲームとして遊べるだけでなく、プログラミングの圧縮技術の極致としても注目されています。
以下が実際のコードです(コピペで遊べます。遊び方は後述。):
<body id=D onKeyDown=K=event.keyCode-38><script>Z=X=[B=A=12];function Y(){for(C
=[q=c=i=4];f=i--*K;c-=!Z[h+(K+6?p+K:C[i]=p*A-(p/9|0)*145)])p=B[i];for(c?0:K+6?h
+=K:t?B=C:0;i=K=q--;f+=Z[A+p])k=X[p=h+B[q]]=1;if(e=!e)if(h+=A,f|B)for(Z=X,X=[l=
228],B=[[-7,-20,6,h=17,-9,3,3][t=++t%7]-4,0,1,t-6?-A:2];l--;)for(l%A?l-=l%A*!Z[
l]:(P+=k++,c=l+=A);--c>A;)Z[c]=Z[c-A];for(S="";i<240;S+=X[i]|(X[i]=Z[i]|=++i%A<
2|i>228)?i%A?"■":"■<br>":"_");D.innerHTML=S+P;Z[5]||setTimeout(Y,99-P)}Y(h=e=
K=t=P=0)</script> すごく読みにくいですよね。でも、大丈夫です。以下のように段階的に分解していけば、中で何をしているのかがわかってきます。
冒頭の埋め込みでも遊べますが、ほかのやり方としては自分の環境で遊ぶこともできます。
最も基本的な方法は、実際にこのコードをメモ帳などのテキストエディタに貼り付けます。
保存の時、適当な名前をつけ、テキストファイルではなく「すべてのファイル」を選択し「.html」の拡張子をつけてHTMLとして保存します。
それをブラウザで開いて実行すると、テトリスがプレイできます!
ターミナル風の「■」「_」で表示されるので、まるで懐かしいDOSゲームのような雰囲気です。
もう一つの方法としては、codepenなどのHTMLがリアルタイムで編集・表示できるウェブツールを使い、そこにコピペすることです。
すると保存などをしなくてもコピペで動作します。
それではゲームの動く仕組みを段階的に解説していきましょう。
<body id=D onKeyDown=K=event.keyCode-38>
K にキーコードを格納。K=0K=-1K=1K=2Z = X = [B = A = 12];
A = 12:盤面の横幅(12列)X:現在の盤面Z:次の状態を保持する配列(Xと同一参照)B = [[-7,-20,6,h=17,-9,3,3][t=++t%7]-4, 0, 1, t-6?-A:2];
t はブロックの種類(0~6)for(c -= !Z[h+(K+6?p+K:C[i]=p*A-(p/9|0)*145)]) ...
ここは最も難解な部分ですが、要するに「押されたキーに応じて回転・左右移動・下移動を試み、衝突がないなら位置を更新する」という処理。
for(Z=X, X=[l=228], ...; l--;) {
for(l%A ? l -= l%A*!Z[l] : (P+=k++, c=l+=A); --c>A;)
Z[c]=Z[c-A];
}
P はスコア(行数)for(S=""; i<240; S+=X[i]|(X[i]=Z[i]|=++i%A<2|i>228)?i%A?"■":"■<br>":"_");
D.innerHTML=S+P;
X[i]:現在の盤面状態■ または _ を出力<br> を使用Z[5] || setTimeout(Y, 99 - P)
Y() を再呼び出し(加速処理)7行テトリスでは、上ボタンを押しても回転がうまく働きません。
これは以下の理由によります。
7行テトリスでは、ブロックの回転処理が基本的に存在しないか、非常に限定的な方法でしか行われていません。そのため、回転キー(↑キー)を押しても、回転しない/期待通りに動かないのが仕様です。
7行テトリスは、コードゴルフ(=とにかく短く書くことが目的)の作品です。そのため、以下のような「通常のゲーム開発」では重要な機能が削られています:
| 一般のテトリス | 7行テトリスでは |
|---|---|
| ブロック回転 | 実装されていない / 固定形状 |
| 壁蹴り回転 | 実装されていない |
| 回転毎に衝突判定 | 実装されていない |
以下のようなコード片でキー入力が処理されていますが:
for(C=[q=c=i=4];f=i--*K; c-=!Z[...] ) p=B[i];
ここで K = event.keyCode - 38 によって上下左右を扱っているように見えますが、↑キー(keyCode = 38 → K = 0)の処理が事実上無視されています。
そのため、そもそも回転キーを押しても何の効果もないんです。
この7行コードはすごいけど、やっぱり読みづらい。
もし、これを普通のJavaScriptで書いたら、100〜200行くらいにはなります。
例えば、以下のようにモジュールごとに分けると読みやすくなります。
drawField():盤面の描画checkCollision():衝突判定moveBlock(dir):ブロックの移動rotateBlock():回転処理dropBlock():1段下に落とすfixBlock():ブロックを固定clearLines():揃った行の削除このようにして、機能を分離すれば、学習にも再利用にも便利なコードになります。
「7行テトリス」は、プログラミングの奥深さと楽しさを凝縮した芸術作品です。
ぜひ、あなたの目と指で、そのミニマルな世界を体験してみてください!