2010 07 17

テトリスを作り直すの1

世間では3連休だってのに、俺は日曜も月曜も仕事。

+

半年後にこのコードを見たら作り直したくなるんじゃないか と思ったのが正解。 何となくソースコードを見ているうちに色々気になりだして、ごっそり書き換えたくなった。

まず気になったのが、フィールドを2次元で管理していること。 これはこれで妥当ではあるのだが、1次元配列でもいいんだよな。 配列の添字は、フィールドをZ字に辿る方向で。 テトリミノは、この1次元配列上の位置の集合として表せる。 2次元での位置関係 をそのまま置き換えると、

-1 0 1 2
0 1
10 11
-1 0 1
9
-1 0 1
11
0 1
9 10
-1 0
10 11
-1 0 1
10

オフセットも1次元。 これだと、

と、2次元版よりもシンプルにできそう。 ああ、でも回転が面倒なのか。 2次元なら ( x , y ) を ( y , -x ) とすればいいのだが、1次元だと、えーと… 一旦2次元にして考えればいいのか。

全て整数計算するとして、座標 ( x , y ) は位置に変換すると n = 10 * y + x

逆に、位置 n は、座標形式だと ( n % 10 , n / 10 )

これを回転すると、その座標は ( n / 10 , -( n % 10 ) )

座標を位置に変換する式に適用することで、位置 n0 を回転させた時の位置 n1 を求める式が得られる。 n1 = -10 * ( n0 % 10 ) + n0 / 10

意外に簡単だな。

と思ったが、実際にテトリミノの値をいくつか当てはめてみるとうまくいかない。 負数の位置が駄目。 って、そりゃそうか。 剰余計算とか、切り捨てによる整数化とか、負数を扱っちゃ駄目だよな。 つまり、回転時の変換元から負数を追い出せばいいのか。 テトリミノの位置情報が負数を含まないようにまず平行移動して、移動した先で回転して、また元の位置に戻せばいいのだな。

ということで、回転の中心としている 0 を中心にテトリミノが収まるように 5×5 の正方領域を考える。 中心を 0 とすると、左上は -22 となる。

-22
-1 0 1 2

この 0 を中心に90度回転するのと等価になるように、次の手順を踏む。

  1. 左上の-22が0になるように、右に2、下に2、全体を移動する。

    具体的には、テトリミノの全ての要素(位置)に22を足す。 これで、テトリミノの位置情報から負数が消える。 変換後の位置を n1 とすると、 n1 = n0 + 22

  2. 0 を中心に90度回転する。

    前段で負数が消えているから、先に導出した回転の式がそのまま使える。 回転後の位置を n2 とすると、 n2 = -10 * ( n1 % 10 ) + n1 / 10

  3. 回転後と回転前の本来の中心を合わせるように、下に4移動する。 これで、テトリミノの回転中心で回転したと同じになる。

    各位値に40足してやればいい。 回転後の位置を n3 とすると、 n3 = n2 + 40

  4. 最初の移動を元に戻すために、上に2、左に2、全体を移動する。

    各位値から22引けばいい。 回転後の位置を n4 とすると、 n4 = n3 - 22

遡って置き換えていくと、回転移動の式が得られる。 n4 = ( n0 + 22 ) / 10 - 10 * ( ( n0 + 22 ) % 10 ) + 40 - 22

今更だが、1行は10列としている。 汎用性を持たせるために、1行の列数を W , テトリミノの要素全てを正数化する下駄(つまり回転半径の最大値)を R として整理すると、回転の式は n4 = ( n0 + R ) / W - W * ( ( n0 + R * W + R ) % W ) + R * W

ぱっと見、回転の式とは思えないが、これでちゃんと回転するんだよな。

念のため、この式が成り立つための前提を残しておこう。

気になることのもう一つは、オブジェクトの無駄遣い。 わざわざテトリミノのローカル座標とオフセットとを分けて持っているにも関わらず、オフセットが変わっただけで新しいオブジェクトを作るのはどうなんだ? と。 ということで、こいつ等を纏めずに、それぞれ独立したフィールドの内部情報とする方向で考える。

続きはまた明日。