さぁ!検索しよう!

数回に分けて公開している、HTML5+JavaScriptによるテトリスのソースコードを解読するシリーズの3です。
今回解読する関数はclearLines()関数です。

参考にした記事

【JavaScript】200行で作るテトリスのレシピ【HTML5】 – コードレシピ

※自分なりの解釈ですので、正確ではないかもしれません。

https://github.com/ottati/canvas-tetris/blob/master/js/tetris.js

clearLines()関数とは

clearLines()は、一行が揃っているか調べ、揃っていたらそれらを消すというものであり、操作ブロックが着地した瞬間に随時実行されます。

// 一行が揃っているか調べ、揃っていたらそれらを消す
function clearLines() {
  for ( var y = ROWS - 1; y >= 0; --y ) {
    var rowFilled = true;
    // 一行が揃っているか調べる
    for ( var x = 0; x < COLS; ++x ) {
      if ( board[ y ][ x ] == 0 ) {
        rowFilled = false;
        break;
      }
    }
    // もし一行揃っていたら, サウンドを鳴らしてそれらを消す。
    if ( rowFilled ) {
      document.getElementById( 'clearsound' ).play();  // 消滅サウンドを鳴らす
      // その上にあったブロックを一つずつ落としていく
      for ( var yy = y; yy > 0; --yy ) {
        for ( var x = 0; x < COLS; ++x ) {
          board[ yy ][ x ] = board[ yy - 1 ][ x ];
        }
      }
      ++y;  // 一行落としたのでチェック処理を一つ下へ送る
    }
  }
}

関数内でやっていること

clearLines()関数で行われる処理を二つに分けると以下のようになります。

  1. 一行が揃っているか調べる処理
  2. 揃った行を消すために、揃った行より上にある行をそれぞれ一段下に落とす処理

1. 一行が揃っているか調べる処理

盤面は横10マス×縦20マスであることから、多次元配列で示すと横0~9、縦0~19となります。
なので、for(var y=ROWS-1;y>=0;--y){...}とすることで、盤面の一番下(19番目の行)から上へと調べることができます。

for ( var y = ROWS - 1; y >= 0; --y ) {
    var rowFilled = true;
    for ( var x = 0; x < COLS; ++x ) {
      if ( board[ y ][ x ] == 0 ) {
        rowFilled = false;
        break;
      }
    }

var rowFilled = true;は、盤面のマスに1であれば(ブロックが入っていれば)true、0でれば(何も入っていなければ)falseを表します。つまり、初期状態はtrueとしているので、初めは全てのマスにブロックが入っているものであると期待しています。

for(var x=0;x<COLS;++x){...}で、1行のマスを左から順に一つずつチェックしていき、その最中で0に遭遇したらその行は揃っていないとみなされ、その時点でrowFilledfalseに切り替わります。更に、breakによってbreak直近のfor文の処理を終わらせ、その行のチェックを止めて一つ上の行のチェックに移ります。y--yによって19→18にカウントダウンします。

2. 揃った行を消すために、揃った行より上にある行をそれぞれ一段下に落とす処理

1.の処理でbreakされることなく1行全てのマスが1であれば、揃った行とみなしif(rowFilled{...}へ進み、揃った行を消すために、揃った行より上にある行をそれぞれ一段下に落とす処理を行ないます。

 if ( rowFilled ) {
      document.getElementById( 'clearsound' ).play();
      for ( var yy = y; yy > 0; --yy ) {
        for ( var x = 0; x < COLS; ++x ) {
          board[ yy ][ x ] = board[ yy - 1 ][ x ];
        }
      }
      ++y;
    }
  }

この揃った行が消えるまでの過程を例に表すと以下の通りです。

揃った行が消えるまでの過程

一番下から5行積まれているときに、のブロックを縦の状態で穴に挿入して4行が一気に消えるパターンを例に挙げると、以下のようにして揃った行が消去されていきます。

1. 棒のブロックが挿入される前の盤面

棒のブロックが挿入される前の盤面は以下の通りです。

/*
board[
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [1,1,1,0,1,1,1,1,1,1],
    [1,1,1,0,1,1,1,1,1,1],
    [1,1,1,0,1,1,1,1,1,1],
    [1,1,1,0,1,1,1,1,1,1],
    [1,1,1,0,1,1,1,1,1,1]
]
*/
2. 棒が挿入された後の盤面

棒が挿入された後の盤面は以下の通りです。

/*
board[
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [1,1,1,0,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1]
]
*/

この時点でyyには行が揃った時のyが代入されるので、yyは19となります。

3. 揃った行を消すために、それより上にある行をそれぞれ一段下へ落とします。

揃った行(19番目の行)を消すために、それより上にある行(board[yy-1][x])をそれぞれ一段下へ落とします。これによって19番目の行は押しつぶされた形になりますね。

for (var yy = y; yy > 0; --yy) {
    for (var x = 0; x < COLS; ++x) {
        board[yy][x] = board[yy - 1][x];
    }
}
    /*
    19番目の行が押しつぶされる処理
    一つ上の行(18番目の行(board[ 19 - 1 ][ 0 ]~board[ 19 - 1 ][ 9 ]))を19番目の行(board[ 19 ][ 0 ]~board[ 19 ][ 9 ])に書き換える(一段したへ落とす)。
    18番目の行が押しつぶされる処理
    一つ上の行(17番目の行(board[ 18 - 1 ][ 0 ]~board[ 18 - 1 ][ 9 ]))を18番目の行(board[ 18 ][ 0 ]~board[ 18 ][ 9 ])に書き換える(一段したへ落とす)。
    .
    .
    .
    20回目の行が押しつぶされる処理
    一つ上の行(0番目の行(board[ 1 - 1 ][ 0 ]~board[ 1 - 1 ][ 9 ]))を1番目の行(board[ 1 ][ 0 ]~board[ 1 ][ 9 ])に書き換える(一段したへ落とす)。
    */

19番目の行が押しつぶされたので、盤面上のブロックは以下のように一つ減って4行となります。

    /*
    board[
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [1,1,1,0,1,1,1,1,1,1],
        [1,1,1,1,1,1,1,1,1,1],
        [1,1,1,1,1,1,1,1,1,1],
        [1,1,1,1,1,1,1,1,1,1]
    ]
    */
4. 18番目の行のチェック処理へ移る前に

19番目の行を消し、その上にあった行もそれぞれ一段下へ落としました。通常次は--yによって一行上の18番目の行のチェック処理に移りますが、先程18番目以降の行を一段下へ落としたため、このまま移ってしまうと今後19番目の行を飛ばして(無視して)18番目の行から上へのチェック処理となってしまいます。
このようなことを防ぐために、--yされる寸前に++yさせます。これによって19であったyが20となり、結果20が--yされて次のチェック処理はyは19となり19番目の行からとなるわけです。

    ++y;
5. 揃った行の消去完了

3.,4.を繰り返すと、4行が全て消えて盤面は以下のようになります。

    /*
    board[
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0],
        [1,1,1,0,1,1,1,1,1,1]
    ]
    */

最後に

このようにして、clearlines()関数は操作ブロックが着地する度に実行され、盤面を下から上へ調べていき、揃った行が決まり次第、その行をそれより上にある行を一段落とす形で消去します。

以上で、【HTML5+JavaScript】テトリスのコード内にある関数を解読3を終わります。