第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.これで基本ゲーム完成!
ここまでで、
盤面を作る
盤面を崩す
表示する
入力する
動かす
クリアを調べる
繰り返す
という一連のゲーム処理が完成しました。
基本ゲームが完成したら、自分で機能を追加してみよう!
No Comments