★マインスイーパー パネルを一気に開く★


実際のマインスイーパーでは、「周りの爆弾の個数が0」のパネルを開くと自動的に回りのパネルが開くようになっています。

そのパネルの周りに爆弾が無い事は分かっているのですから、いちいちクリックしていくのは面倒な作業ですからね。

周りのパネルを調べる方法は説明しましたので、簡単に出来そうです。

しかし、よく考えてください、「周りのパネルを開いた時に、開いたパネルが0のパネルだったら」どうしますか?

そのパネルを中心に、また周りのパネルを開かなければなりません。

このような時に役立つのが「再帰呼び出し」です。


周りのパネルを開く


では、再帰を使った関数として「OpenAdjacentPanel関数」を作ります。

「Open関数」から呼び出しますので、privateメンバ関数として作成しましょう。

「Mine.h」を開き、プロトタイプ宣言を追加します。

class Mine {
public:

    Mine();

    ~Mine();

    //フィールドの初期化
    void Initialize(const int row, const int col, const int bombMax);

    //フィールドの描画
    void Draw(Engine *pEngine);

    //パネルを開く
    bool Open(const POINT &refPoint);

private:

    const int ADJUST_X; //調整用左座標
    const int ADJUST_Y; //調整用上座標

    const int PANEL_SOUR_X; //パネルの転送元左座標
    const int PANEL_SOUR_Y; //パネルの転送元上座標

    const int HIDE_SOUR_X; //閉じた状態の転送元左座標
    const int HIDE_SOUR_Y; //閉じた状態の転送元上座標

    const int PANEL_WIDTH;  //パネルの幅
    const int PANEL_HEIGHT; //パネルの高さ

    enum {
        NONE,     //爆弾なし
        BOMB = 9, //爆弾
    };

    struct Field {
        int m_number; //周りにある爆弾の数(0〜8)、爆弾は9
        bool m_bOpen; //オープンフラグ
    };

    Field *m_pField; //フィールド

    int m_row; //縦方向のパネル数
    int m_col; //横方向のパネル数

    int m_bombMax; //爆弾の最大数

    RECT m_sour;
    RECT m_dest;

    //フィールドのクリア
    void ClearField();

    //爆弾のセット
    void SetBomb();

    //爆弾の個数のセット
    void SetNumber();

    //周りにある爆弾の数を数える
    int GetAroundBombNum(const int x, const int y);

    //フィールド外か?
    bool IsOutsideField(const POINT &refPoint) const;

    //隣接したパネルを開く(再帰)
    void OpenAdjacentPanel(const int x, const int y);
};

引数は、開いたパネルの座標です。


「Mine.cpp」を開き、「IsOutsideField関数」の下に「OpenAdjacentPanel関数」の本体を追加します。

//隣接したパネルを開く(再帰)
void Mine::OpenAdjacentPanel(const int x, const int y)
{
    int index = y * m_col + x;

    m_pField[index].m_bOpen = true;

    if (m_pField[index].m_number != NONE) {
        return;
    }

    for (int i = -1; i < 2; i++) {
        for (int j = -1; j < 2; j++) {

            int wx = x + j;
            int wy = y + i;

            if (wx >= 0 && wx < m_col && wy >= 0 && wy < m_row) {

                index = wy * m_col + wx;

                if (!m_pField[index].m_bOpen) {

                    OpenAdjacentPanel(wx, wy);
                }
            }
        }
    }
}

引数で受け取った座標のパネルを開きます。

開いたパネルが「0」で無い場合は再帰を止めます。

「0」の場合は周りのパネルを調べていきます。

開いていないパネルがあれば、そのパネルの座標を指定して再帰呼び出しを行います。


では、「Open関数」にコードを追加しましょう。

//パネルを開く
bool Mine::Open(const POINT &refPoint)
{
    if (IsOutsideField(refPoint)) {
        return false;
    }

    int x = (refPoint.x - ADJUST_X) / PANEL_WIDTH;
    int y = (refPoint.y - ADJUST_Y) / PANEL_HEIGHT;

    int index = y * m_col + x;

    if (!m_pField[index].m_bOpen) {

        m_pField[index].m_bOpen = true;

        if (m_pField[index].m_number == BOMB) {

            return true;
        }
        else {

            if (m_pField[index].m_number == NONE) {

                OpenAdjacentPanel(x, y);
            }
        }
    }

    return false;
}

開いたパネルが「0」の場合、再帰用の関数を呼び出します。


実行して確認してみましょう。

<実行結果 クライアント領域のみ>

↑のように、一気にパネルが開けば成功です。


次へ

戻る

目次へ