Skip to main content

第6回

パズルの問題を作ろう

ゲーム開始時に盤面を崩して、遊べる問題を作ってみよう!


1.数字をランダムに並べるだけではダメ

例えば、乱数を使って1~16を適当に並べれば、ランダムな盤面は作れそうです。

しかし、スライドパズルにはどう動かしても完成できない並びがあります。

そのため、数字を完全にランダムに並べる方法は使いません。


2.完成状態から崩せばよい

完成状態から始めて、

動かせるタイルを1枚動かす
↓
また動かせるタイルを1枚動かす
↓
これを何回も繰り返す

という方法で盤面を崩します。

この方法なら、実際に正しいルールで動かした結果なので、元の完成状態へ戻すことができます。
この処理を実行する関数ShuffleBoard()を作っていこう!


3.乱数を使う準備

乱数を使うために、ソースファイルの先頭へstdlib.hとtime.hを追加しよう!

#define _CRT_SECURE_NO_WARNINGS

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

さらに、main()の最初で乱数を初期化します。

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

time(0)で現在時刻を数値として取得し、その値をrand()の初期値に使います。

(unsigned int)は、srand()へ渡す型に合わせるための書き方です。ここでは「乱数を毎回変えるために必要」と考えておけば大丈夫です。


4.乱数でタイル番号を選ぼう

1~15のタイル番号をランダムに選ぶには、次のように書けます。

int number = rand() % (BLANK_TILE - 1) + 1;

BLANK_TILEは16なので、BLANK_TILE - 1は数字タイルの数15になります。

ただし、ランダムに選んだタイルが必ず動かせるとは限りません。

そこで、作っておいた、

CanMoveTile()

で確認し、動かせるときだけ、

MoveTile()

で動かします。


5.ShuffleBoard()を作ろう

盤面を崩す処理を何回試すか、定数にしておこう!

#define SHUFFLE_COUNT 100

この定数は、ほかの#defineと同じ場所へ追加します。

さらに、ShuffleBoard()もmain()より後ろに定義できるよう、プロトタイプ宣言へ追加しよう!

void ShuffleBoard(Board* board);

100回動かしても、偶然また完成状態へ戻る可能性があります。

そこで、シャッフルが終わったあともIsClear()が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()
MoveTile()

を使ってみよう!

模範解答

模範解答を見る
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);
            }
        }
    }
}

6.main()に追加しよう

main()では、最初に乱数を初期化してから盤面を作ります。

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

InitBoard(&board);
ShuffleBoard(&board);

これで、完成状態を作ったあとに盤面を崩してゲームを始められます。


7.これで基本ゲーム完成!

ここまでで、

盤面を作る
盤面を崩す
表示する
入力する
動かす
クリアを調べる
繰り返す

という一連のゲーム処理が完成しました。

基本ゲームが完成したら、自分で機能を追加してみよう!