★関数(再帰呼び出し6)★


「ぷよぷよ」の続きです。

3.削除するべき「ぷよ」を調べる

ここからが核心部分です。

どこで「ぷよ」がつながっているのか分かりませんから、フィールドを1つ1つチェックしていかなければなりません。

この関数は、内部でさらにいくつかの関数に分けて作ります。

少しずつ説明しながら完成させていきましょう。

関数名 CheckDeletePuyo
戻り値 なし
引 数 フィールド、削除フラグ
機 能 削除するべき「ぷよ」を調べる
void CheckDeletePuyo(const int field[ROW][COL], int deleteFlag[ROW][COL])
{
    int i;
    int j;

    for (i = 0; i < ROW; i++) {

        for (j = 0; j < COL; j++) {

        }
    }
}

まだ中身はほとんどありません。

フィールドの中身を全て調べるため、2重ループを準備しました。

しかし、中にはチェックしなくても良いマスもあるはずです。

例えば、

  「ぷよ」が無いマス

  すでに削除予定のマス

 すでにチェック済みのマス

などです。

「ぷよ」が無いマスは意味が分かると思いますが、すでに削除予定のマスとは何でしょうか。

上の方に書いたシチュエーションは、4つ以上つながっている「ぷよ」は1組です。

□□□□□□
□□□□□□
□□□□□□
□□□□□□
□□□□□□
□□□□□□
□□□□□□
□赤青□□□
□赤青赤青□
□青青黄青□

しかし、1度に落ちてくる「ぷよ」は2つですから、2組同時に消えることもあるはずです。

□□□□□□
□□青黄□□
□□↓↓□□
□□□□□□
□□□□□□
□□□□□□
□□□□□□
□赤□□□□
□赤青黄黄□
□青青黄青□

このまま落とすと「青」と「黄」が消えます。

□□□□□□
□□□□□□
□□□□□□
□□□□□□
□□□□□□
□□□□□□
□□□□□□
□赤青黄□□
□赤青黄黄□
□青青黄青□

この場合、フィールドを確認する順番から考えて、「青」の方を先にチェックします。

「青」が4つ並んでいることが分かれば、その時点での削除フラグは↓のようになっているはずです。

 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 1 0 0 0
 0 0 1 0 0 0
 0 1 1 0 0 0

この時に、削除フラグが1になっている個所はチェック不要になります。

以上を踏まえて関数に追加します。

void CheckDeletePuyo(const int field[ROW][COL], int deleteFlag[ROW][COL])
{
    int i;
    int j;

    for (i = 0; i < ROW; i++) {

        for (j = 0; j < COL; j++) {

            if (field[i][j] != NONE && deleteFlag[i][j] == 0) {

            }
        }
    }
}

次に考えることは、つながっている「ぷよ」をチェックすることです。

これは、前回の塗りつぶしと同じ考えでチェックすれば出来ます。

しかし、最終的に削除するのは「4つ以上つながっている「ぷよ」」だけです。

チェック中は、何個つながっているか分かりません。

2つとか3つの時もあるでしょう。

もし、削除フラグの配列を直接使って調べるとなった場合、つながりが4つ未満の「ぷよ」は除外しなければなりません。

↓のシチュエーションの場合、最初にチェックされるのは左上の「赤」です。

□□□□□□
□□□□□□
□□□□□□
□□□□□□
□□□□□□
□□□□□□
□□□□□□
□赤青黄□□
□赤青黄青□
□青青黄青□

これをチェックした時の削除フラグの内容は、

 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 1 0 0 0 0
 0 1 0 0 0 0
 0 0 0 0 0 0

となると思います。

2つしかつながっていませんので、削除フラグを戻さなければなりません。

配列全体を0でクリアすれば良いように見えますが、次のような場合はどうすれば良いでしょうか?

↓は、すでに「青」が削除対象になった状態です。

 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 1 0 0 0
 0 0 1 0 0 0
 0 1 1 0 0 0

この状態で、次の「黄」をチェックすると、削除フラグは↓のようになると考えられます。

 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 1 1 0 0
 0 0 1 1 0 0
 0 1 1 1 0 0

「黄」の「ぷよ」は3つしかつながっていませんので、削除対象ではありません。

しかし、この削除フラグの状態から「黄」の場所だけ「0」に戻すのはかなり面倒です。


もっと簡単な方法が無いか考えます。

削除フラグに格納する数字を「0」「1」だけでなく、種類を増やしてみます。

 0 → 未チェック
 1 → チェック中
 2 → チェック済み
 3 → 削除

最初は0で初期化しておき、チェック中の「ぷよ」には1を格納していきます。

一旦チェックが終わったら、1の数を数え、4以上であれば1を3に書き換え削除確定とします。

4未満であれば、1を2に書き換えチェック済みにします。

※チェック済みの個所は再度調べる必要が無くなりますよね。

とりあえず、流れを書くと、

□□□□□□
□□□□□□
□□□□□□
□□□□□□
□□□□□□
□□□□□□
□□□□□□
□赤青黄□□
□赤青黄青□
□青青黄青□

この状態からチェックを始めます。

最初の「赤」のチェック中は、↓のようになります。

 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 1 0 0 0 0
 0 1 0 0 0 0
 0 0 0 0 0 0

チェック後に1の数を数えると2つですから、削除対象ではありません。

チェック済みに分類しますので、1(CHECKING)を2(CHECKED)に変えます。

 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 2 0 0 0 0
 0 2 0 0 0 0
 0 0 0 0 0 0

次に1つ右の「青」をチェックします。

チェック後は↓のようになります。

 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 2 1 0 0 0
 0 2 1 0 0 0
 0 1 1 0 0 0

1の個数は4つですから、1(CHEKING)を3(DELETE)に変えて削除対象にします。

 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 2 3 0 0 0
 0 2 3 0 0 0
 0 3 3 0 0 0

これを続けていくと、最後は↓のようになります。

 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 0 0 0 0 0
 0 2 3 2 0 0
 0 2 3 2 2 0
 0 3 3 2 2 0

3になっている個所の「ぷよ」を消せば良いことが分かります。


まずは、列挙体を追加しましょう。

enum {
    NO_CHECK,
    CHECKING,
    CHECKED,
    DELETE,
};

次に、これまで書いたプログラムの変更箇所を書いておきます。

int main(void)
{
    int field[ROW][COL] = {
        { 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0 },
        { 0, 1, 2, 0, 0, 0 },
        { 0, 1, 2, 1, 2, 0 },
        { 0, 2, 2, 3, 2, 0 },
    };

    int deleteFlag[ROW][COL] = { NO_CHECK };

    CheckDeletePuyo(field, deleteFlag);

    ShowField(field);

    return 0;
}

main関数にはCheckDeletePuyo関数の呼び出しも追加しておきました。

void CheckDeletePuyo(const int field[ROW][COL], int deleteFlag[ROW][COL])
{
    int i;
    int j;

    for (i = 0; i < ROW; i++) {

        for (j = 0; j < COL; j++) {

            if (field[i][j] != NONE && deleteFlag[i][j] == NO_CHECK) {

            }
        }
    }
}

次回がラストです。


次へ

戻る

目次へ