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



前回告知したバグですが、
実は、途中まで作ってるときは、
自分で気付いておらず、
記事を書きながら実行したら例外発生イヤーンと
なったわけでして、
そのバグって言うのは、
「ブロックの初期位置が0未満だと落ちてこない&例外が出る」
ってものです。
これができないと、画面のてっぺんからピースが出てくる動きが
表現できませんよね。
えー、原因ですが、
TMapBlocks.isBlock 関数の挙動が悪かったようです。
再掲してみます。
function    TMapBlocks.isBlock(x,y:integer):Boolean;
begin
   if (x<0)or(9<x)or(y<0)or(19<y) then begin
       Result:=True;//フィールドの外は埋まっている
       Exit;//終了
   end;
   //範囲内に収まっていたら
   Result:=Blocks[y,x];//そのまま返す
end;
えー、画面のてっぺん、つまり、y<0の条件ですが、
これのせいで、「画面のてっぺんはブロックで埋まっている」
という概念になっています。
で、ピースのメインルーチンでは、
isHitという関数を使って、
「もし下に一歩下りたときブロックに重なってしまうか」
という判定をしていました。
もし、ピースの初期位置が0<yなら、
一歩降りても、自分の長さが2以上なので、
一歩降りる→埋まる

bagu.png

となり、ピースは、埋まったときの自殺処理、すなわち、
「フィールドに自分をコピーして自殺」
をしようとします。
さて、ここで例外が発生します。
「自分をコピーする処理の内部では、範囲判定をしていませんでした。」
すると、
「画面の外、つまり、y座標が負の数の座標にフラグを立てようとする」
となります。

tatanai.png

配列の要素を参照するとき、
定義していない番号を参照しようとすると、
「アドレス○○でアドレス○○に対する書き込み違反」
って感じのエラーが出ます。
という、バグなのでした。
直すのは簡単です。
isBlockの概念を

akeru.png

こうするだけです。
function    TMapBlocks.isBlock(x,y:integer):Boolean;
begin
   if (x<0)or(9<x)or(y<0)or(19<y) then begin
       Result:=True;//フィールドの外は埋まっている
       Exit;//終了
   end;
   //範囲内に収まっていたら
   Result:=Blocks[y,x];//そのまま返す
end;
↓変更↓
function    TMapBlocks.isBlock(x,y:integer):Boolean;
begin
   if (x<0)or(9<x)or(19<y) then begin
       Result:=True;//フィールドの外は埋まっている
       Exit;//終了
   end;
   //範囲内に収まっていたら
   Result:=Blocks[y,x];//そのまま返す
end;
if条件の(y<0)を取るだけです。(繋いでるorも一個取ってね
それじゃぁ、初期化文を直しましょう。
FormのonCreateです。
procedure TForm1.FormCreate(Sender: TObject);
begin
    (略)
   Piece.y:=0;
    (略)
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
    (略)
   Piece.y:=-3;
    (略)
end;
これで実行してみてください。
アッー! まだバグってた!!!!
デバッグによるバグはよくあります・・・・
isBlockの直し方を間違えてました。
function    TMapBlocks.isBlock(x,y:integer):Boolean;
begin
   if (x<0)or(9<x)or(19<y) then begin
       Result:=True;//フィールドの外は埋まっている
       Exit;//終了
   end;
   //範囲内に収まっていたら
   Result:=Blocks[y,x];//そのまま返す
end;
ifはスルーしますが、
Result:=Blocks[y,x];//そのまま返す
で、範囲外参照になっちゃいますwwww
さらに編集
function    TMapBlocks.isBlock(x,y:integer):Boolean;
begin
   if (x<0)or(9<x)or(19<y) then begin
       Result:=True;//フィールドの 左 右 下 はうまっている
       Exit;//終了
   end;
   if y<0 then begin
       Result:=False;//フィールドの 上 なら、埋まっていない
       Exit;
   end;
   //フィールドの 内側 なら 
   Result:=Blocks[y,x];//そのまま返す
end;
   if y<0 then begin
       Result:=False;//フィールドの 上 なら、埋まっていない
       Exit;
   end;
を追加しました。
yが0未満、つまり、フィールドより上の範囲をみたら、
強制でフラグオフ(偽(False))にして、Exitします。
なんかもうゴリゴリです。ウホウホ。
今度こそ実行してOKです。
落ちてきた落ちてきた。