■ ■ ■ ■ ■ ■ ■ ■ ■
S □ □ □ □ □ □ □ ■
■ □ ■ ■ ■ ■ ■ □ ■
■ □ ■ □ ■ □ □ □ ■
■ □ □ □ ■ ■ ■ □ ■
■ □ ■ □ ■ □ ■ □ ■
■ □ ■ □ ■ □ ■ □ ■
■ □ ■ □ □ □ ■ □ G
■ ■ ■ ■ ■ ■ ■ ■ ■
この図では黒塗り四角(■)が壁,白塗り四角(□)が通路,Sはスタート地点,Gがゴール地点を意味しています.
MazeMap.h
#ifndef MAZEMAP_H
#define MAZEMAP_H
/**
* @class MazeMap
* @breif 迷路情報保持用クラス
*/
class MazeMap {
public:
/** 「壁」の表示 */
static const char wall = 'O';
/** 「通路」の表示 */
static const char passage = ' ';
/** 「スタート地点」の表示 */
static const char start = 'S';
/** 「ゴール地点」の表示 */
static const char goal = 'G';
/**
* コンストラクタ
* @param w 迷路幅の基底
* @param h 迷路高の基底
* @attention 実際の迷路幅は2w+1,迷路高は2h+1
*/
MazeMap(int w, int h);
/**
* デストラクタ
*/
virtual ~MazeMap();
/**
* 迷路幅用ゲッタ
* @return 迷路幅
*/
int getWidth();
/**
* 迷路高用ゲッタ
* @return 迷路高
*/
int getHeight();
/**
* 指定座標が迷路の範囲内に存在するかをチェック
* @param x x座標
* @param y y座標
* @retval true 迷路の範囲内
* @retval false 迷路の範囲外
*/
bool isInside(int x, int y);
/**
* 指定座標が壁かどうかをチェック
* @param x x座標
* @param y y座標
* @retval true 壁
* @retval false 壁でない
* @attention 指定座標が迷路範囲外であればfalseを返す
*/
bool isWall(int x, int y);
/**
* 指定座標のマップ情報のセッタ
* @param x x座標
* @param y y座標
* @param c 要素
* @retval true 指定座標が迷路の範囲内
* @retval false 指定座標が迷路の範囲外
* @attention 要素を指定しない場合,通路とする
*/
bool set(int x, int y, char c = passage);
/**
* 指定座標のマップ情報のゲッタ
* @param x x座標
* @param y y座標
* @return マップ情報
* @attention 迷路範囲外の座標を指定した場合,'\0'を返す
*/
char get(int x, int y);
/**
* マップ情報の初期化
* @attention 全ての要素を「壁」にする
*/
void clear();
private:
/** 迷路幅 */
int width;
/** 迷路高 */
int height;
/** 迷路情報 */
char **data;
};
#endif /* MAZEMAP_H */
|
MazeMap.cpp
#include "MazeMap.h"
MazeMap::MazeMap(int w, int h) {
width = 2 * w + 1;
height = 2 * h + 1;
data = new char *[width];
for (int i = 0; i < width; ++i) {
data[i] = new char[height];
}
}
MazeMap::~MazeMap() {
for (int i = 0; i < width; ++i) {
delete[] data[i];
}
delete[] data;
}
int MazeMap::getWidth() {
/* ここを実装してください */
}
int MazeMap::getHeight() {
/* ここを実装してください */
}
bool MazeMap::isInside(int x, int y) {
/* ここを実装してください */
}
bool MazeMap::isWall(int x, int y) {
/* ここを実装してください */
}
bool MazeMap::set(int x, int y, char c) {
/* ここを実装してください */
}
char MazeMap::get(int x, int y) {
/* ここを実装してください */
}
void MazeMap::clear() {
/* ここを実装してください */
}
|
main.cpp
#include <iostream>
#include "MazeMap.h"
/**
* 迷路の表示
* @param mazeMap mazeMapインスタンスの参照
*/
void printMap(MazeMap &mazeMap) {
for (int y = 0; y < mazeMap.getHeight(); ++y) {
for (int x = 0; x < mazeMap.getWidth(); ++x) {
std::cout << mazeMap.get(x, y); // get()を使って表示
}
std::cout << std::endl;
}
std::cout << std::endl;
}
/**
* 指定座標を通路に設定
* @param mazeMap MazeMapインスタンスの参照
* @param x x座標
* @param y y座標
*/
void testSet(MazeMap &mazeMap, int x, int y) {
std::cout << "set(" << x << ", " << y << ") = " << (mazeMap.set(x, y) ? "success" : "failure") << std::endl;
}
/**
* 指定座標が迷路範囲内かどうかをチェックし,結果を出力
* @param mazeMap mazeMapインスタンスの参照
* @param x x座標
* @param y y座標
*/
void testIsInside(MazeMap &mazeMap, int x, int y) {
std::cout << "(" << x << ", " << y << ") = " << (mazeMap.isInside(x, y) ? "inside" : "outside") << std::endl;
}
/**
* 指定座標が壁かどうかをチェックし,結果を出力
* @param mazeMap mazeMapインスタンスの参照
* @param x x座標
* @param y y座標
*/
void testIsWall(MazeMap &mazeMap, int x, int y) {
std::cout << "(" << x << ", " << y << ") " << (mazeMap.isWall(x, y) ? "= " : "!= ") << "wall" << std::endl;
}
int main() {
MazeMap mazeMap(2, 1); // サイズをいろいろ変えてみる
// マップのサイズを表示
// (マップのサイズはコンストラクタに渡した値を 2倍して1足した値になる)
std::cout << "width=" << mazeMap.getWidth() << std::endl;
std::cout << "height=" << mazeMap.getHeight() << std::endl;
// 迷路データクリア (全て壁 'O'になる)
mazeMap.clear();
// set()の動作チェック
std::cout << "##### Test for set #####" << std::endl;
testSet(mazeMap, 0, 1); // successになるはず
testSet(mazeMap, 1, 1); // successになるはず
testSet(mazeMap, 2, 1); // successになるはず
testSet(mazeMap, 2, 2); // successになるはず
testSet(mazeMap, 6, 4); // failureになるはず
testSet(mazeMap, -1, 0); // failureになるはず
// 中身を表示 (意図した通りにセットされているかをチェック)
std::cout << "##### Print Map #####" << std::endl;
printMap(mazeMap);
// isInside()の動作チェック
std::cout << "##### Test for isInside #####" << std::endl;
testIsInside(mazeMap, 0, 0); // inside になるはず
testIsInside(mazeMap, mazeMap.getWidth() - 1, 0); // inside になるはず
testIsInside(mazeMap, 0, mazeMap.getHeight() - 1); // inside になるはず
testIsInside(mazeMap, -1, -1); // outsideになるはず
testIsInside(mazeMap, mazeMap.getWidth() + 1, 0); // outsideになるはず
testIsInside(mazeMap, 0, mazeMap.getHeight() + 1); // outsideになるはず
testIsInside(mazeMap, mazeMap.getWidth(), 0); // outsideになるはず
testIsInside(mazeMap, 0, mazeMap.getHeight()); // outsideになるはず
// isWall()の動作チェック
std::cout << "##### Test for isWall #####" << std::endl;
testIsWall(mazeMap, 0, 0); // wall になるはず
testIsWall(mazeMap, 1, 0); // wall になるはず
testIsWall(mazeMap, -1, -1); // not wall になるはず (範囲外は壁ではない)
testIsWall(mazeMap, 1, 1); // not wall になるはず
testIsWall(mazeMap, 2, 1); // not wall になるはず
std::cout << std::endl;
// 再び迷路データクリア (全て壁 'O'になる)
mazeMap.clear();
// 中身を表示 (きちんとクリアされているかをチェック)
std::cout << "##### Print Map #####" << std::endl;
printMap(mazeMap);
return 0;
}
出力例
width=5 height=3 ##### Test for set ##### set(0, 1) = success set(1, 1) = success set(2, 1) = success set(2, 2) = success set(6, 4) = failure set(-1, 0) = failure ##### Print Map ##### OOOOO OO OO OO ##### Test for isInside ##### (0, 0) = inside (4, 0) = inside (0, 2) = inside (-1, -1) = outside (6, 0) = outside (0, 4) = outside (5, 0) = outside (0, 3) = outside ##### Test for isWall ##### (0, 0) = wall (1, 0) = wall (-1, -1) != wall (1, 1) != wall (2, 1) != wall ##### Print Map ##### OOOOO OOOOO OOOOO
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
奇数座標からランダムに1つ選択します.
下図では,左上を原点(0,0)として右に3,下に3の座標(3,3)が選択されたものとします.
(「○」は現在の注目点を意味します)
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ○ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
次に穴を掘り進める方向をランダムに選択し,その方向に2マス先のマスを調べて,まだ「通路」でなければ「通路」にして2マス分を通路にします.
以下の例では現在の注目点から下に「通路」を延ばそうして,下に2マス先を調べて,まだ「通路」になっていなかったので,下に2マス分延ばした状態です.
(同時に注目点も移動させます)
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ □ ■ ■ ■ ■ ■
■ ■ ■ □ ■ ■ ■ ■ ■
■ ■ ■ ○ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
移動先のマスで先ほどと同様に,次に掘り進める方向をランダムに選択し,選択した方向の2マス先を調べ,「通路」でない場合にはそちらの方向に「通路」を延ばしていくという操作を繰り返します.
もし,ランダムに選択した方向の2マス先が「通路」であった場合は,「通路」を延ばす作業はここで一旦終了します.
以下の図において,左に「通路」を延ばそうとした場合,左に2マス先は既に「通路」になっているので,これ以上「通路」を延ばせません.
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ □ ■ ■ ■ ■ ■
■ ■ ■ □ ■ ■ ■ ■ ■
■ ■ ■ □ ■ ○ ■ ■ ■
■ ■ ■ □ ■ □ ■ ■ ■
■ ■ ■ □ □ □ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
「通路」を延ばせなくなった場合は,新たに奇数座標をランダムに選択し,そのマスを起点として「通路」を延ばす作業を行います.
以上の作業を,「通路」を延ばせるマスが無くなるまで繰り返します.
以下の図では,どの奇数座標をとっても,通路を延ばすことができないので終了となります.
■ ■ ■ ■ ■ ■ ■ ■ ■
■ □ □ □ □ □ □ □ ■
■ □ ■ ■ ■ ■ ■ □ ■
■ □ ■ □ ■ □ □ □ ■
■ □ ■ □ ■ ■ ■ □ ■
■ □ ■ □ ■ □ ■ □ ■
■ □ ■ □ ■ □ ■ □ ■
■ □ □ □ □ □ ■ □ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ □ ■ ■ ■ ■ ■
■ ■ ■ □ ■ ■ ■ ■ ■
■ ■ ■ □ ■ ○ ■ ■ ■
■ ■ ■ □ ■ □ ■ ■ ■
■ ■ ■ □ □ □ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■
(注目点○では下と左には「通路」を延ばせませんが,右と上にはまだ「通路」を延ばせます.)
MazeMakerSimple.h
#ifndef MAZEMAKERSIMPLE_H
#define MAZEMAKERSIMPLE_H
#include <cstdlib>
#include "MazeMap.h"
class MazeMakerSimple {
public:
/**
* コンストラクタ
* @param mazeMap MazeMapインスタンス
*/
MazeMakerSimple(MazeMap *mazeMap);
/**
* 迷路生成
*/
void generate();
private:
/** MazeMapインスタンス */
MazeMap *mazeMap;
/**
* 座標(x,y)において dで指定された方向をチェック
* @param x x座標
* @param y y座標
* @param d 方向を示すインデックス
*/
void checkDirection(int x, int y, int d);
/**
* 座標(x,y)において全方向をチェック
* @param x x座標
* @param y y座標
*/
void checkPoint(int x, int y);
};
#endif /* MAZEMAKERSIMPLE_H */
|
MazeMakerSimple.cpp
#include "MazeMakerSimple.h"
MazeMakerSimple::MazeMakerSimple(MazeMap *mazeMap) {
this->mazeMap = mazeMap;
}
void MazeMakerSimple::generate() {
// 迷路マップ情報をクリア
mazeMap->clear();
// ランダムに迷路作成開始地点を選択(ただし,奇数座標)
int x = rand() % (mazeMap->getWidth() / 2) * 2 + 1;
int y = rand() % (mazeMap->getHeight() / 2) * 2 + 1;
// ランダムに選んだ (x,y)の座標から迷路の作成開始
checkPoint(x, y);
// 最後に左上にスタート地点、右下にゴール地点を置いて終了
mazeMap->set(0, 1, mazeMap->start);
mazeMap->set(mazeMap->getWidth() - 1, mazeMap->getHeight() - 2, mazeMap->goal);
}
void MazeMakerSimple::checkDirection(int x, int y, int d) {
// 4つの方向を示す構造体
static const struct pos {
int x, y;
} steps[] = {
{0, +1}, // 下
{+1, 0}, // 右
{0, -1}, // 上
{-1, 0} // 左
};
// 引数dの値により,steps[]で示される方向の1つを決定
int dx = steps[d & 3].x;
int dy = steps[d & 3].y;
if (mazeMap->isWall(x + 2 * dx, y + 2 * dy)) { // 指定された方向の2マス先が壁であれば
mazeMap->set(x + dx, y + dy); // その方向の1マス先を通路にセット
mazeMap->set(x + 2 * dx, y + 2 * dy); // その方向の2マス先を通路にセット
// 注目点を2マス先に移して,チェックを継続
checkPoint(x + 2 * dx, y + 2 * dy);
}
}
void MazeMakerSimple::checkPoint(int x, int y) {
if (!mazeMap->isInside(x, y)) { // 迷路の範囲外なら
return;
}
// 指定座標を通路にセット
mazeMap->set(x, y);
// 探索する方向を乱数で設定
int d = rand();
// 再探索の順番を設定(時計回り or 反時計回り)
int dd = (rand() & 1) * 2 - 1; // -1 or +1
// 4方向に通路が延ばせるか順番にチェック
for (int i = 0; i < 4; ++i) {
checkDirection(x, y, d);
d += dd;
}
}
|