コラム / テトリスぽいもの / 26



次は回転機能をつけたいと思います。
左回転手続きと、右回転手続きを
ピースクラスにつけましょう。
まぁ、テトリスは90度単位でしか回転しないので、
右回転=左回転3回
って事に注目して、
ちょっと楽をします。
では、左回転手続きを定義します。
   TPiece = Class(TObject)
   private
   public
       (略)
       procedure CopySelfToMapBlocks;

       procedure SpinLeft;

       procedure Main;
   end;
特に引数も無いのでこれだけです。
procedure TPiece.SpinLeft;
var buffer:Array[0..3,0..3] of Boolean;
    i,j:Integer;
begin


end;
変数宣言はこれだけです。
まず、Shapeを回転したデータをbufferに入れ、
その後bufferをShapeにまるまるコピーします。

cpy.png

bufferを経由する理由は、
たんに思いついたアルゴリズムがそうだったからです。
Shapeだけでうまく回転する方法が思いつきませんでした。
実装の前に、回転する仕組みを説明します。
4*4の表を順番に番号を振った場合、
図のように回転しなければなりません。

spin.png

番号に注目して、どこがどこに移ってるか見てくださいね。
( 10-15 は A-Fです。
二次元配列ですから、
二重のforループを使うとうまく操作できる、
というのは、ここまでで何度も使った手法ですし、
今回もそうします。
そうすると、さっき振った番号の順番に、
forループの処理が対応すると
うまく回転データをbufferにコピーできますね。
二重のループでは、
for i:=0 to 3 do begin
  for j:=0 to 3 do begin

  end;
end;
とした場合、一番内側に来るときのiとjの値を再確認します。
1ループ目  i:=0 j:=0
2ループ目  i:=0 j:=1
3ループ目  i:=0 j:=2
4ループ目  i:=0 j:=3
5ループ目  i:=1 j:=0
6ループ目  i:=1 j:=1
7ループ目  i:=1 j:=2
8ループ目  i:=1 j:=3
9ループ目  i:=2 j:=0
10ループ目 i:=2 j:=1
11ループ目 i:=2 j:=2
12ループ目 i:=2 j:=3
13ループ目 i:=3 j:=0
14ループ目 i:=3 j:=1
15ループ目 i:=3 j:=2
16ループ目 i:=3 j:=3
というように、
iはj4回ずつ、jは小刻み、 に増えます。
表を参照するときは、
進むのが遅いiを縦座標に、
進むのが早いjを横座標にすることで、

loop.png

こんな風に参照することができました。
ここを少しひねれば、
一つの二重forループの中で、
コピー先bufferの位置は回転した位置、
コピー元shapeの位置は順番に参照
ということができます。
まぁ、回転する前と、した後の矢印はこうなりますね。

yaji.png

矢印そのものは細かく変わるj
矢印の順番は遅く変わるiを対応させればよいのですから、
回転するときは、
jをy座標の動き、下から上へのカウント
iをx座標の動き、左から右へのカウント
にすればよいことになります。

kauto.png

iとx座標のペアは見てのとおり簡単です。
しかし、jとy座標のペアは困ったことになりました。
jは当然、0,1,2,3とカウントされるのですが、
矢印は下から上なので、
3,2,1,0 とカウントしないといけないのです。
0,1,2,3 を 3,2,1,0 にする方法・・・
それは、カウンタをjにするのではなくて、
3-j    にすればいいのです!
(って、中学生でもわかる話だけどさwww
さて、コピー先bufferの座標は、
buffer[3-j,i]
にすればよいことがわかりました。
コピー元Shapeは回転しないので、もちろん
shape[i,j]
です。
んじゃぁいよいよ実装しましょう。
procedure TPiece.SpinLeft;
var buffer:Array[0..3,0..3] of Boolean;
   i,j:Integer;
begin
   for i:=0 to 3 do begin
       for j:=0 to 3 do begin
           buffer[3-j,i]:=shape[i,j];
       end;
   end;
   for i:=0 to 3 do begin
       for j:=0 to 3 do begin
           shape[i,j]:=buffer[i,j];
       end;
   end;
end;
上半分はさっき説明した、
回転先座標に回転元座標のデータを入れています。
下半分はそれをshapeにそのままコピーして戻しています。
これでshapeが回転するはずです。
次回は右回転を作ります。