Skip to main content

第7回 あとできることって何?

完成!そして自由制作へ

C言語版スライドパズルの基本部分が完成したはず。

最後に全体の構成を確認して、自分だけの機能を追加してみよう!
こうやって次々出来ることやれることを追加していくと、ゲーム作るって、終わりがないよね。


1.今回作った関数

作った関数と役割を確認してみよう。

InitBoard()
PrintBoard()
SearchTile()
CanMoveTile()
MoveTile()
IsClear()
ShuffleBoard()
InitBoard()       盤面を初期化する
PrintBoard()      盤面を表示する
SearchTile()      タイルを探す
CanMoveTile()     タイルが動かせるか調べる
MoveTile()        タイルを動かす
IsClear()         クリアしたか調べる
ShuffleBoard()    問題を作る

1つの大きな処理を書くのではなく、仕事ごとに関数へ分けてゲームを作りました。


2.main()を見るとゲームの流れが分かる

完成したmain()を見て、ゲーム全体の流れを確認してみよう!

int main(void)
{
    Board board;

    srand((unsigned int)time(0));

    InitBoard(&board);
    ShuffleBoard(&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;
}

ここを見ると、

初期化
↓
問題を作る
↓
表示
↓
入力
↓
移動
↓
クリア判定
↓
繰り返す

という流れが分かります。

これが、今回使ったゲームループです。


3.Hit & Blowで学んだことを使えた?

Hit & Blowで学んだ、配列・構造体・関数・ポインタ・条件分岐・繰り返し・乱数・ゲームループを、別のゲームでも組み合わせて使うことができました。

ゲームの種類が変わっても、必要な処理を分けて組み合わせる考え方は同じです。


4.自由制作

自分で追加したい機能を考えて、プログラムを改造してみましょう。

★ 比較的簡単

  • 手数を表示する
  • タイトルを表示する
  • クリア時に手数を表示する
  • 入力した数字が1~15以外なら注意を表示する
  • もう一度遊べるようにする

★★ 少し難しい

  • 難易度を選べるようにする
  • シャッフル回数を難易度ごとに変える
  • タイムを計測する
  • 3×3の盤面を作る
  • 盤面の表示を見やすくする

★★★ 挑戦

  • Undoを作る
  • WASDなどで操作できるようにする
  • 盤面サイズを変更できるようにする
  • 自分で新しいルールを追加する

5.制作課題として提出するとき

基本部分が完成したら、少なくとも1つは自分で機能を追加してみよう!

追加した機能について、

何を追加したか
どういう処理で作ったか
どこが難しかったか

を説明できるようにしておこう!


6.完成コード

最後に、基本版のソースコード全体を確認しておこう!

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define BOARD_WIDTH 4
#define BOARD_HEIGHT 4
#define BLANK_TILE (BOARD_WIDTH * BOARD_HEIGHT)
#define SHUFFLE_COUNT 100

typedef struct
{
    int tile[BOARD_WIDTH * BOARD_HEIGHT];
} Board;

typedef struct
{
    int x;
    int y;
} Position;

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);
void ShuffleBoard(Board* board);

int main(void)
{
    Board board;

    srand((unsigned int)time(0));

    InitBoard(&board);
    ShuffleBoard(&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;
}

void InitBoard(Board* board)
{
    for (int i = 0; i < BOARD_WIDTH * BOARD_HEIGHT; i++)
    {
        board->tile[i] = i + 1;
    }
}

void PrintBoard(Board* board)
{
    for (int y = 0; y < BOARD_HEIGHT; y++)
    {
        for (int x = 0; x < BOARD_WIDTH; x++)
        {
            int index = y * BOARD_WIDTH + x;

            if (board->tile[index] == BLANK_TILE)
            {
                printf("    ");
            }
            else
            {
                printf("%3d ", board->tile[index]);
            }
        }

        printf("\n");
    }
}

Position SearchTile(Board* board, int number)
{
    Position result = { -1, -1 };

    for (int y = 0; y < BOARD_HEIGHT; y++)
    {
        for (int x = 0; x < BOARD_WIDTH; x++)
        {
            int index = y * BOARD_WIDTH + x;

            if (board->tile[index] == number)
            {
                result.x = x;
                result.y = y;
                return result;
            }
        }
    }

    return result;
}

int CanMoveTile(Board* board, int number)
{
    Position blank = SearchTile(board, BLANK_TILE);
    Position tile = SearchTile(board, number);

    int dx = blank.x - tile.x;
    int dy = blank.y - tile.y;

    if (dx * dx + dy * dy == 1)
    {
        return 1;
    }

    return 0;
}

void MoveTile(Board* board, int number)
{
    if (CanMoveTile(board, number) == 0)
    {
        return;
    }

    Position blank = SearchTile(board, BLANK_TILE);
    Position tile = SearchTile(board, number);

    int blankIndex = blank.y * BOARD_WIDTH + blank.x;
    int tileIndex = tile.y * BOARD_WIDTH + tile.x;

    int temp = board->tile[blankIndex];

    board->tile[blankIndex] = board->tile[tileIndex];
    board->tile[tileIndex] = temp;
}

int IsClear(Board* board)
{
    for (int i = 0; i < BOARD_WIDTH * BOARD_HEIGHT; i++)
    {
        if (board->tile[i] != i + 1)
        {
            return 0;
        }
    }

    return 1;
}

void ShuffleBoard(Board* board)
{
    while (IsClear(board))
    {
        for (int i = 0; i < SHUFFLE_COUNT; i++)
        {
            int number = rand() % (BLANK_TILE - 1) + 1;

            if (CanMoveTile(board, number))
            {
                MoveTile(board, number);
            }
        }
    }
}