■ ■ ■ ■ ■ ■ ■ ■ ■ 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; } } |