再起関数の実装について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
icon01

再起関数の実装について

#1

投稿記事 by icon01 » 13年前

 今、パズルゲームの作成にあたって揃えて消すプログラムに四苦八苦しているところです。そこで助言をいただきたくトピックを立てさせていただきました。
コードが長くて読みにくいかと思いますが、ご容赦ください。

コード:

ヘッダーファイル
EXTERN BYTE DelMin;
EXTERN BYTE DelCtr;
EXTERN BYTE Del[12][12]; // 1なら消える
EXTERN BYTE Chk[12][12]; // 1ならチェック済み
EXTERN int Block[16];	// 画像を入れる変数 
EXTERN int Panel[12][12];	//パネルの大きさ12*12マス
EXTERN int random;//乱数取得変数
EXTERN int Hig,Wid;//縦・横
EXTERN int Panel_Hig,Panel_Wid;

コード:

void Initialize(){	//初期化処理用の関数
    DelMin = 4;
    Panel_Hig = 12;
    Panel_Wid = 12;
	
    for( Hig = 0; Hig < Panel_Hig ; Hig++)
    {
        for( Wid = 0; Wid < Panel_Wid; Wid++)
        {
            random =GetRand(12);
            Panel[Hig][Wid] = random;
        }
    }
}
void stage(){	
    for( Hig = 0; Hig < Panel_Hig ; Hig++)
    {
        for( Wid = 0; Wid < Panel_Wid; Wid++)
        {
            SetDrawBlendMode( DX_BLENDMODE_ALPHA,  192 );
            DrawGraph( Wid*32, Hig*32 , Block[Panel[Hig][Wid]], TRUE );
            SetDrawBlendMode( DX_BLENDMODE_NOBLEND,  0 );
        }
    }
    // 消えたブロックに印(ここの描画の仕方に問題あり?)
    if(Del[Hig][Wid] == 1){
        SetDrawBlendMode( DX_BLENDMODE_ALPHA,  192 );
        DrawGraph( Wid*32, Hig*32 , Block[7], TRUE );
        SetDrawBlendMode( DX_BLENDMODE_NOBLEND,  0 );  
    }	
}
//==============================================================================================
// 消えるブロックの検索
//==============================================================================================
void SearchDelBlock(void){
    for(int i = 0; i < Wid; i++)
    {
        for(int j = 0; j < Hig; j++)
        {
            if(Del[i][j] == 0)
            { // まだ消えていないブロックなら…
                SearchDelBlockR(i, j); // 再帰ルーチンスタート
                if(DelCtr >= DelMin)
                { // 消えるなら…
                    for(int i2 = 0; i2 < Wid; i2++)    //この変数気持ち悪い・・・
                    {
                        for(int j2 = 0; j2 < Hig; j2++) //この変数気持ち悪い・・・
                        {
                            if(Chk[i2][j2] == 1) Del[i2][j2] = 1; // チェックしたブロック→消える
                        }
                    }
                }
                // 次の処理に備えて初期化
                ZeroMemory(Chk, sizeof Chk);
                DelCtr = 0;
            }
        }
    }
}
//==============================================================================================
// 再帰ルーチン
//==============================================================================================
void SearchDelBlockR(int i, int j){

    Chk[i][j] = 1; // チェックしました。
    DelCtr++;
    if(i + 1 < Panel_Wid){ //右
        if(Chk[i + 1][j] == 0 && Block[Panel[i][j]] == Block[Panel[i + 1][j]]) SearchDelBlockR(i + 1, j);
    }
    if(j + 1 < Panel_Hig){ //下
        if(Chk[i][j + 1] == 0 && Block[Panel[i][j]] == Block[Panel[i][j + 1]]) SearchDelBlockR(i, j + 1);
    }
    if(i - 1 >= 0){ //左
        if(Chk[i - 1][j] == 0 && Block[Panel[i][j]] == Block[Panel[i - 1][j]]) SearchDelBlockR(i - 1, j);
    }
    if(j - 1 >= 0){ //上
        if(Chk[i][j - 1] == 0 && Block[Panel[i][j]] == Block[Panel[i][j - 1]]) SearchDelBlockR(i, j - 1);
    }
}
見にくかったらすみません
言うまでもないと思いますがmain.cppでSearchDelBlock();とstage();を呼び出してあります

恥ずかしながら、何が良くて何がいけないのか自分でもわからない状態なのです。此処には書いていませんが、Spaceキーを押すと、任意の場所の左右のブロックを入れ替える関数の実相には成功したのですが。それで4つ以上並べてもうんともすんとも言いません。(もしかしたらそっちに問題があるのかもしれませんが)

どなたか回答お願いいたします。

box
記事: 2002
登録日時: 15年前

Re: 再起関数の実装について

#2

投稿記事 by box » 13年前

まずは、ゲームプログラミングの要素を取っ払って、
再帰関数の書き方や動き方の基礎を押さえる方がいいのではないか、
と思います。
題材は、陳腐化しているかもしれませんが、階乗とかフィボナッチ数列とかハノイの塔とか、
まあいくらでもころがっていると思います。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

box
記事: 2002
登録日時: 15年前

Re: 再起関数の実装について

#3

投稿記事 by box » 13年前

ぱっと見ですけど、再帰関数が無限ループに陥っているように見えなくもない気がするような感じがしないでもないです。
再帰呼び出しから抜け出す条件が、何か必要ではないのかなぁ、と思ったりしなくもないです。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

icon01

Re: 再起関数の実装について

#4

投稿記事 by icon01 » 13年前

boxさん返信ありがとうございます。
ループを抜ける条件ですが、

コード:

if(i + 1 < Panel_Wid)
{ //右
    if(Chk[i + 1][j] == 0 && Block[Panel[i][j]] == Block[Panel[i + 1][j]]) SearchDelBlockR(i + 1, j);
}
if(j + 1 < Panel_Hig)
{ //下
    if(Chk[i][j + 1] == 0 && Block[Panel[i][j]] == Block[Panel[i][j + 1]]) SearchDelBlockR(i, j + 1);
}
if(i - 1 >= 0)
{ //左
    if(Chk[i - 1][j] == 0 && Block[Panel[i][j]] == Block[Panel[i - 1][j]]) SearchDelBlockR(i - 1, j);
}
if(j - 1 >= 0)
{ //上
    if(Chk[i][j - 1] == 0 && Block[Panel[i][j]] == Block[Panel[i][j - 1]]) SearchDelBlockR(i, j - 1);
}
では不十分なのでしょうか?

実行しても特に変わったことはありませんし、(再帰関数を実装する前と同じ挙動という意味)まあ、見えないところでメモリを食い荒らしている可能性は否定できませんが。

sperobo

Re: 再起関数の実装について

#5

投稿記事 by sperobo » 13年前

stage関数の消えたブロックに印をつけるところに問題がありそうですね。
Delを調べて描画する処理がループの外に出ているので、
Del[ Panel_Hig ][ Panel_Wid ]しか処理していない事になるのではないでしょうか。

icon01

Re: 再起関数の実装について

#6

投稿記事 by icon01 » 13年前

sperobo さん返信ありがとうございます。

そんなところに落とし穴があったとは!とりあえず自分の意図した挙動ではありませんが、ブロックが4つ隣接すればブロックの種類が変わるという当初の目的が達せられたので解決とさせていただきます。
一応コードを

コード:

for( Hig = 0; Hig < Panel_Hig ; Hig++)
{
    for( Wid = 0; Wid < Panel_Wid; Wid++)
    {
        SetDrawBlendMode( DX_BLENDMODE_ALPHA,  192 );
        DrawGraph( Wid*32, Hig*32 , Block[Panel[Hig][Wid]], TRUE );
        SetDrawBlendMode( DX_BLENDMODE_NOBLEND,  0 );
        // 消えたブロックに印
        if(Del[Hig][Wid] == 1){
            SetDrawBlendMode( DX_BLENDMODE_ALPHA,  192 );
            DrawGraph( Wid*32, Hig*32 , Block[7], TRUE );
            SetDrawBlendMode( DX_BLENDMODE_NOBLEND,  0 );
        }
    }
}

閉鎖

“C言語何でも質問掲示板” へ戻る