C++での迷路表示プログラムについて
Posted: 2020年7月17日(金) 00:25
C++初心者です。
C++にて迷路を作成し、ファイル出力するプログラムを作成しています。
ファイルにデータを書き込む方法がわからず、どうすれば良いのか困っています。
"ここを実装してください"というところを実装すれば動くはずです。
1つのソースファイルで作っていますが複数のファイルに分けてもらっても大丈夫です。
ご助言いただければ幸いです。
ソースコードは以下に示します。(みづらくてすみません。)
C++にて迷路を作成し、ファイル出力するプログラムを作成しています。
ファイルにデータを書き込む方法がわからず、どうすれば良いのか困っています。
"ここを実装してください"というところを実装すれば動くはずです。
1つのソースファイルで作っていますが複数のファイルに分けてもらっても大丈夫です。
ご助言いただければ幸いです。
ソースコードは以下に示します。(みづらくてすみません。)
#include <cstdlib>
#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実装
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() {
return width;
}
int MazeMap::getHeight() {
return height;
}
bool MazeMap::isInside(int x, int y) {
if ((0 <= x) && (x < width) && (0 <= y) && (y < height)) {
//std::cout << "Inside true" << std::endl;
return true;
}
else {
//std::cout << "Inside false" << std::endl;
return false;
}
}
bool MazeMap::isWall(int x, int y) {
if (isInside(x, y) == false) {
return false;
}
if (**data == wall) {
return true;
}
else {
return false;
}
}
bool MazeMap::set(int x, int y, char c) {
if (isInside(x, y) == true) {
//std::cout << "Set true" << std::endl;
data[x][y] = c;
return true;
}
else {
//std::cout << "Set false" << std::endl;
return false;
}
}
char MazeMap::get(int x, int y) {
return data[x][y];
}
void MazeMap::clear() {
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++)
data[i][j] = wall;
}
}
//------MazeMakerClass------
#ifndef MAZEMAKERSIMPLE_H
#define MAZEMAKERSIMPLE_H
#include <cstdlib>
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 */
//MazeMaker実装
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;
}
}
//------MazePrinterClass------
#ifndef MAZEPRINTER_H
#define MAZEPRINTER_H
class MazePrinter {
public:
/**
* コンストラクタ
* @param display Displayインスタンスのポインタ
*/
MazePrinter(Display* display);
/**
* 出力
* @param mazeMap MazeMapインスタンスのポインタ
*/
virtual void output(MazeMap* mazeMap);
private:
/** Displayインスタンスのポインタ*/
Display* display;
};
#endif /* MAZEPRINTER_H */
//MazePrinter実装
MazePrinter::MazePrinter(Display* display) {
this->display = display;
}
void MazePrinter::output(MazeMap* mazeMap) {
display->start(); // 表示前処理
for (int y = 0; y < mazeMap->getHeight(); ++y) {
for (int x = 0; x < mazeMap->getWidth(); ++x) {
display->put(mazeMap->get(x, y)); // 1文字表示処理
}
display->newLine(); // 改行処理
}
display->end(); // 表示後処理
}
//------DisplayClass------
#ifndef DISPLAY_H
#define DISPLAY_H
#include <ostream>
class Display {
public:
/**
* デストラクタ
*/
virtual ~Display();
/**
* 表示前処理(純粋仮想関数)
*/
virtual void start() = 0;
/**
* 表示後処理(純粋仮想関数)
*/
virtual void end() = 0;
/**
* 1文字出力処理 (純粋仮想関数)
* @param c 出力文字
*/
virtual void put(char c) = 0;
/**
* 改行処理(純粋仮想関数)
*/
virtual void newLine() = 0;
protected:
/**
* 出力先の参照(純粋仮想関数)
* @return 出力先の参照
*/
virtual std::ostream& stream() = 0;
};
#endif /* DISPLAY_H*/
//Display実装
Display::~Display() {
}
//------DisplayScreenClass------
#ifndef DISPLAYSCREEN_H
#define DISPLAYSCREEN_H
#include <iostream>
class DisplayScreen : public Display {
public:
virtual void start();
virtual void end();
virtual void put(char c);
virtual void newLine();
protected:
virtual std::ostream& stream();
};
#endif /* DISPLAYSCREEN_H */
//DisplayScreen実装
#ifndef DISPLAYFILE_H
#define DISPLAYFILE_H
#include <fstream>
class DisplayFile : public DisplayScreen {
public:
/**
* コンストラクタ
* @param fileName 出力ファイル名(指定しない場合"output.txt")
*/
DisplayFile(const char* fileName = "output.txt");
virtual ~DisplayFile();
protected:
virtual std::ostream& stream();
private:
std::ofstream ofs;
};
#endif /* DISPLAYFILE_H */
//------DisplayFileWideClass------
#ifndef DISPLAYFILEWIDE_H
#define DISPLAYFILEWIDE_H
class DisplayFileWide : public DisplayFile {
public:
virtual void put(char c);
};
#endif /* DISPLAYFILEWIDE_H */
//DisplayFileWide実装
void DisplayFileWide::put(char c) {
/* ここを実装してください */
}
/**
* 表示方法の選択
* @return Displayインスタンスのポインタ
* @attention Displayインスタンスを生成しているので,不要になった時点でdeleteすること
*/
Display* selectDisplay() {
int outputMode;
Display* display = NULL;
do {
std::cout << "output mode [1-6] ? ";
std::cin >> outputMode;
switch (outputMode) {
case 1: display = new DisplayScreen; // 画面に出力
break;
case 2: display = new DisplayFile; // ファイルに出力
break;
case 5: display = new DisplayFileWide; // ファイルに横長に出力
break;
default:
std::cout << "illegal output mode " << outputMode << std::endl;
break;
}
} while (display == NULL);
return display;
}
int main(int argc, char** argv) {
// 乱数の初期化設定
// 本来は実行する度に変化する時刻情報などを用いたほうが良いが,開発を容易に(実行の度に結果が変わらないように)するために,乱数の初期値を手動で設定し固定する
int seed;
std::cout << "seed ? ";
std::cin >> seed;
srand(seed); // 乱数種の設定
// 迷路幅,迷路高の基底の設定
int w, h;
std::cout << "size (w, h) ? ";
std::cin >> w >> h;
// 迷路マップ情報生成
MazeMap mazeMap(w, h);
// 迷路作成
MazeMakerSimple mazeMaker(&mazeMap);
mazeMaker.generate();
// 迷路出力
Display* display = selectDisplay();
MazePrinter mazePrinter(display);
mazePrinter.output(&mazeMap);
// selectDisplay()内で生成したインスタンスを解放
delete display;
return 0;
}