Skip to main content

第5回

ゲームを完成させよう

IsClear()で完成状態を判定して、作ってきた関数をゲームループに組み込んでみよう!


1.クリアしたか調べよう

完成状態では、InitBoard()で入れたときと同じように、どの場所でも

正しい値 = 配列の添字 + 1

になっています。

1か所でも違う場所があれば、まだクリアしていません。

穴埋め

int IsClear(Board* board)
{
    for (int i = 0; i < BOARD_WIDTH * BOARD_HEIGHT; i++)
    {
        if (__________________________________________)
        {
            return 0;
        }
    }

    return 1;
}

模範解答

模範解答を見る
int IsClear(Board* board)
{
    for (int i = 0; i < BOARD_WIDTH * BOARD_HEIGHT; i++)
    {
        if (board->tile[i] != i + 1)
        {
            return 0;
        }
    }

    return 1;
}

2.関数が増えてきたので、プロトタイプ宣言を使おう!

C言語では、基本的に上から順番にプログラムを読んでいきます。

例えば、先に関数を作っておけば、

void Test(void)
{
}

int main(void)
{
    Test();
}

main()を読む時点で、コンパイラはすでにTest()を知っているので呼び出せます。

でも、順番を逆にすると、

int main(void)
{
    Test();
}

void Test(void)
{
}

main()を読んでいる時点では、まだTest()という関数を知りません。

そこで使うのがプロトタイプ宣言です。

void Test(void);

と先に書いておけば、その時点でコンパイラは、

Test()という関数がある

と分かります。

すると、関数本体を後ろに書いても呼び出せるようになります。

void Test(void);    // プロトタイプ宣言

int main(void)
{
    Test();
}

void Test(void)
{
}

つまり、

  • 関数は、書いた順番に呼べるようになる
  • その時点で知らない関数は呼べない
  • プロトタイプ宣言しておけば、その時点で知っていることになる
  • だから、関数の定義はあとに書いてもよくなる

ということです。

スライドパズルでも、ここまでに作った関数を先に宣言しておこう!

void InitBoard(Board* board);
void PrintBoard(Board* board);
Position SearchTile(Board* board, int number);
int CanMoveTile(Board* board, int number);
void MoveTile(Board* board, int number);
int IsClear(Board* board);

これで、main()を先に書いて、そのあとに各関数の本体を書けます。

今までmain()より上に書いていたInitBoard()やPrintBoard()などの関数本体は、main()の下へ移動しておこう!

ソースコード全体は、

#define
↓
#include
↓
構造体
↓
プロトタイプ宣言
↓
main()
↓
各関数の定義

という順番で整理できます。


3.ゲーム全体の流れを作ろう

ここまでに作った関数を並べると、

InitBoard()       盤面を初期化
PrintBoard()      盤面を表示
MoveTile()        タイルを移動
IsClear()         クリア判定

があります。

それでは、この流れをゲームループにしてみよう!

盤面を初期化する
    ↓
盤面を表示する
    ↓
動かすタイルを入力する
    ↓
タイルを動かす
    ↓
クリアしたか調べる
    ↓
まだなら繰り返す

4.main()を完成させよう

numberには、scanf()で入力した「動かしたいタイル番号」を入れます。

どの関数をどこで呼び出せばよいか考えて、main()を完成させよう!

int main(void)
{
    Board board;

    ________________________________;

    while (1)
    {
        int number;

        ________________________________;

        printf("動かすタイル番号 > ");
        scanf("%d", &number);

        ________________________________;

        if (______________________________)
        {
            break;
        }
    }

    ________________________________;

    printf("CLEAR!\n");

    return 0;
}

模範解答

模範解答を見る
int main(void)
{
    Board board;

    InitBoard(&board);

    while (1)
    {
        int number;

        PrintBoard(&board);

        printf("動かすタイル番号 > ");
        scanf("%d", &number);

        MoveTile(&board, number);

        if (IsClear(&board))
        {
            break;
        }
    }

    PrintBoard(&board);

    printf("CLEAR!\n");

    return 0;
}

これでゲームループは完成しました。

ただし、今のままではInitBoard()した直後から完成状態です。

最後に、ゲーム開始時の盤面を崩して問題を作ります。