さぁ!検索しよう!

HTML5+JavaScriptによるテトリスのコード内にあるnewShape関数を解読します

メモとして残しておきます。

参考にした記事

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

newShape関数

newShape関数は、「shapesからランダムにブロックのパターンを出力し、盤面の一番上へセットする」というものです。

var current; // 今操作しているブロックの形
var currentX, currentY; // 今操作しているブロックの位置

// 操作するブロックのパターン
var shapes = [
    [ 1, 1, 1, 1 ],
    [ 1, 1, 1, 0,
      1 ],
    [ 1, 1, 1, 0,
      0, 0, 1 ],
    [ 1, 1, 0, 0,
      1, 1 ],
    [ 1, 1, 0, 0,
      0, 1, 1 ],
    [ 0, 1, 1, 0,
      1, 1 ],
    [ 0, 1, 0, 0,
      1, 1, 1 ]
];

// ブロックの色
var colors = [
    'cyan', 'orange', 'blue', 'yellow', 'red', 'green', 'purple'
];

// shapesからランダムにブロックのパターンを出力し、盤面の一番上へセットする
function newShape() {
  var id = Math.floor( Math.random() * shapes.length );  // ランダムにインデックスを出す
  var shape = shapes[ id ];
  // パターンを操作ブロックへセットする
  current = [];
  for ( var y = 0; y < 4; ++y ) {
    current[ y ] = [];
    for ( var x = 0; x < 4; ++x ) {
      var i = 4 * y + x;
      if ( typeof shape[ i ] != 'undefined' && shape[ i ] ) {
        current[ y ][ x ] = id + 1;
      }
      else {
        current[ y ][ x ] = 0;
      }
    }
  }
  // ブロックを盤面の上のほうにセットする
  currentX = 5;
  currentY = 0;
}

http://coderecipe.jp/recipe/iHjJBJx9Si/

処理内容

newShape関数内で行われていることは以下の通りです。

ブロック情報をランダムに取得

var id = Math.floor( Math.random() * shapes.length );

Math.randomメソッドは、0以上1未満のランダムな値が返ります。更に、Math.randomメソッドで取得した値をMath.floorメソッドで囲むことで、小数点以下が切り捨てられたランダムな値となります。
また、最大値を設定するには、Math.randomメソッドに最大値にしたい値 + 1を掛けます。

これらのことから、Math.floor( Math.random() * shapes.length )は、Math.randomメソッドにshapes.length(7)を掛けることで、0~6のランダムな値を取得しています。よって、idにはshapes内の0~6番目のうちのどれかのブロック情報が代入されます。

ブロックを取得

var shape = shapes[ id ];

先程取得したブロック情報idを基にshapesからブロックを取り出します。

ブロックを盤面の一番上にセット

例としてidに6が代入されると、以下のようにしてブロックが盤面の一番上にセットされます。

1. 6番目の

shapes内の6番目にある配列内のブロックをshapeに代入しています。

var shape = shapes[ id ];//shape → [ 0, 1, 0, 0,1, 1, 1 ]

for文を2重にすることで2次元配列を作ることが出来ます。

for文の処理は、コンソールで確認すると一度に結果が表示されるので一度で処理が完了しているように思えますが、そうではありません。
実際には、1行目→2行目→3行目…のように順番に処理しているのです。なので1行目の処理が終わるまで2行目の処理は行われません。

今回登場するfor文は4つの配列要素が入る配列が4行ある(4×4)2次元配列に対して処理します。なので、16回の処理が行われます。

1回目の処理

  1. current内に[]を1個追加しています。この時点でyには0が入っています。
    for ( var y = 0; y < 4; ++y ) {
        current[ y ] = [];
    /*
    current[
        []
    ]
    */
    
  2. 2つ目のfor文の処理によって、iにはyが0、xが0なので、4*0+0で0が代入されます。4を掛けている訳は、4回のループ中で16個の配列を生成するためです。4回という少ない処理で多くの配列を生成することができるのです。
    for ( var x = 0; x < 4; ++x ) {
            var i = 4 * y + x;//i = 4 * 0 + 0
    
  3. if(typeof shape[i] != 'undefined' && shape[i]){}は、shape配列内のi番目に配列要素が存在(typeof shape[i] != 'undefined')且つ(&&)shape配列内のi番目の配列要素が0でない場合(shape[i])という条件となります。
    配列をundefinedと比較することで、その配列内に要素が存在するかどうか確認できます。また、条件式において0は結果としてfalseが返ります。なので、0より大きい数値だけが通るのです。trueとなるのです。
    上記のことから、0番目の処理対象となるshape[0]は、配列要素である0は存在しますが、二つ目の条件である0以外の場合には一致しないため、falseが返ります。なので、elseの処理に進みます。

    /*
    shape[0] → shape[(0),1,0,0,1,1,1] → false
    */
    
  4. current[y][x] = 0は、current配列内の[y]行目の[x]番目の位置に0が代入されるという意味です。ですので、current[0][0](current配列内の[0]行目の[0]番目)に0が代入されます。
    /*
    current[
        [(0),]
    ]
    */
    

2回目の処理

ここに操作ブロックの現在処理している場所がわかる画像

2回目の処理はyは0のままで、xが1に増えます。それ以外は1回目の処理と同様です。

  1. xは1に増えたので、yは0、xは1で、4*0+1となりiには1が代入されます。
    /*
    for ( var x = 0; x < 4; ++x ) {
            var i = 4 * y + x;//i = 4 * 0 + 1
    */
    
  2. shape[1]には配列要素が存在し、0ではないためtrueが返り、if以下の処理に進みます。
    /*
    shape[1] → shape[0,(1),0,0,1,1,1] → true
    */
    
  3. current[y][x] = id + 1は、current配列内の[y]行目の[x]番目の位置にid + 1が代入されるという意味です。id+1としている訳は、idが0であった場合に空のマスとならないことを防ぐためです。current配列内において0は空のマスを表すので、current配列内の[0]行目の[1]番目の位置に7が代入されます。
    /*
    current[
        [0,(7)]
    ]
    */
    

3回目の処理

3回目の処理はyは0のまま、xは2に増えます。

  1. yは0、xは2で、4*0+2となりiには2が代入されます。
    /*
    for ( var x = 0; x < 4; ++x ) {
            var i = 4 * y + x;//i = 4 * 0 + 2
    */
    
  2. shape[2]には配列要素が存在しますが、0であるためfalseが返りelse以下の処理に進みます。
    /*
    shape[2] → shape[0,1,(0),0,1,1,1] → false
    */
    
  3. current配列内の[0]行目の[2]番目の位置に0が代入されます。
    /*
    current[
        [0,7,(0)]
    ]
    */
    

4回目の処理

4回目の処理はyは0のまま、xは3に増えます。この処理が終了してようやく1行目の4マスに対する処理が完了します。

  1. yは0、xは3で、4*0+3となりiには3が代入されます。
    /*
    for ( var x = 0; x < 4; ++x ) {
            var i = 4 * y + x;//i = 4 * 0 + 3
    */
    
  2. shape[3]には配列要素が存在しますが、0であるためfalseが返り、else以下の処理に進みます。
    /*
    shape[3] → shape[0,1,0,(0),1,1,1] → false
    */
    
  3. current配列内の[0]行目の[3]番目の位置に0が代入されます。
    /*
    current[
        [0,7,0,(0)]
    ]
    */
    

2行目以降の処理もこれまでと同様に行なわれ、16回目の処理が終了すると、4×4の操作ブロックが完成します。
完成した操作ブロックは盤面上部の中央の位置にセットされます。

currentX = 5;
currentY = 0;

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

参考文献

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

[JavaScript] null とか undefined とか 0 とか 空文字(”) とか false とかの判定について | phiary

JavaScriptでランダムの数(乱数)を作る方法

Math.random() – JavaScript | MDN