随机迷宫生成与寻路算法(1)深度优先搜索

迷宫生成算法(1)深度优先搜索

接下来一段时间,想要研究下随机迷宫生成算法,打算在有空可时候偶尔更新一下这方面的学习过程。随机迷宫的生成算法有很多种,比如递归回溯,递归分割,随机Prime等等。今天是第一次尝试随机迷宫生成,就先试一下用递归的方法通过深度优先搜索来生成随机迷宫。

首先我们来明确一下基本观念,迷宫可以通过一个二维数组来表示,二维数组中的元素就表示存在于迷宫中的位置,他们可能是可以行走的路,也有可能是不能进入的障碍物或者围栏。我们只要通过两种不同的字符就可以标记障碍物和通道,比如我们使用false来表示一个位置是障碍物,而使用true来表示位置可以通行。在下面的例子中我们就沿用这个规定,整个迷宫可以使用一个二维的布尔型数组来表示。

接下来我们确定一下对迷宫的看法,我们认为一个迷宫只能有唯一解,也就是说从起点到终点不会有两条不一样的路线。而遍历迷宫的过程可以被看成是一个拆墙的过程,如果拆了一个墙会导致两个已经被标记为通道的方块连接,那么拆这面墙就是不合法的,这个条件是递归回溯过程中最重要的判定条件。

最后来看一下递归回溯算法的过程:

  1. 将初始位置设置为当前位置(入栈),然后将它标记为通路
  2. 从上,下,左,右四个方向寻找当前方块的相邻方块,判断找到的相邻方块周围是否有其他通路,如果有则继续寻找其他相邻方块,如果没有则选择该方块为当前方块(入栈)
  3. 标记当前方块为通路,然后重复进行上一个过程
  4. 如果当前方块的相邻方块都不满足条件,则恢复上一个方块为当前方块(出栈),并继续执行过程2

以上就是深度优先遍历生成随机迷宫的基本步骤,接下来我们看代码实现:

定义一个迷宫类,它的私有数据成员保存着记录迷宫状态的二维数组,查找方向,迷宫尺寸,入口点等信息:

class Maze
{
public:
    //构造函数,通过传入的参数创建并创建并初始化二维数组
	Maze(int row, int column);
	ostream& print();
	//设置迷宫入口的内联函数,注意这个入口并不是遍历起点,而是在边界上挖出的一个洞
	void setEntry(int x,int y)
	{

		if (isValidEntry(x,y)) {
			mazePtr[x - 1][y - 1] = true;
			if (x == 1) {
				startX = 2;
				startY = y;
			}
			if (x == row) {
				startX = row-1;
				startY = y;
			}
			if (y == 1) {
				startX = x;
				startY = 2;
			}
			if (y == column) {
				startX = 2;
				startY = column-1;
			}
			//cout << mazePtr[x - 1][y - 1];
		}
	}
    //创建迷宫
	bool createMaze();
private:
	bool isInRange(int x, int y);
	bool isValidEntry(int x, int y);
    //递归回溯的算法的核心实现
	bool dig(int x, int y);
	int startX;
	int startY;
	int row;
	int column;
	vector<pair<int,int>> direction = { {1,0},{0,1},{-1,0},{0,-1} };
	unique_ptr<bool*[]> mazePtr;
};

然后我们在类外完成对构造函数的定义,他接受行列两个参数来动态创建一个布尔型二维数组,并与此同时初始化为false,允许通过初始化列表来为动态分配的内存初始化是C++11引入的新特性,这里为了方便管理堆内存,采用了智能指针类型unique_ptr来管理动态分配的内存,这种智能指针类型也是C++11新增的:

Maze::Maze(int row, int column):row(row),column(column)
{
	mazePtr.reset(new bool*[row]);
	for (int i = 0; i < row; i++) {
		mazePtr[i] = new bool[column]{false};
	}
}

定义成员函数createMaze,这个函数用来执行迷宫生成算法,通过调用另一个私有成员dig来完成:

bool Maze::createMaze()
{
	int x = startX;
	int y = startY;
	if (!dig(x, y)) {
		return false;
	}
	return true;
}

然后就是实现dig函数,正如他的名字所显示的那样,该函数所做的就是从一个起点开始挖掘障碍物,只要条件允许(存在不邻接多个路径的新方块)他总是尽可能的去多挖掘新的方块:

bool Maze::dig(int x, int y)
{
	if (!isInRange(x, y)) {
		return false;
	}
	mazePtr[x - 1][y - 1] = true;
	//随机改变方向
	//random_shuffle(direction.begin(),direction.end());
	for (int i = 0; i < 4; i++) {
		int tmpX = x + 2 * direction[i].first;
		int tmpY = y + 2 * direction[i].second;
		if (!isInRange(tmpX, tmpY)) {
			continue;
		}
		if (mazePtr[tmpX-1][tmpY-1] == false) {
			mazePtr[x+direction[i].first-1][y+direction[i].second-1] = true;
			//print() << endl;
			if (!dig(tmpX, tmpY)) {
				mazePtr[x + direction[i].first - 1][y + direction[i].second - 1] = false;
				continue;
			}
		}
	}
	return true;
}

剩下的就是一些提供辅助功能的成员函数了,比如判断指定方块是否超出迷宫边界,计算入口是否合法,通过入口寻找遍历起点,打印迷宫状态等等,他们的实现如下:

bool Maze::isInRange(int x, int y)
{
	if (x <= 0 or x >= row) {
		return false;
	}
	if (y <= 0 or y >= column) {
		return false;
	}
	return true;
}

bool Maze::isValidEntry(int x, int y)
{
	if ((x == 1 and y == 1) ||
		(x == 1 and y == column) ||
		(x == row and y == 1) ||
		(x == row and y == column)) {
		return false;
	}
	if (x == 1 or x == row or y == 1 or y == column) {
		return true;
	}
	return false;
}

ostream& Maze::print()
{
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < column; j++) {
			cout << mazePtr[i][j] << " ";
		}
		cout << endl;
	}
	return cout;
}

调用这段代码可以得到下图这种迷宫:

随机迷宫生成与寻路算法(1)深度优先搜索_第1张图片

显然这样生成的迷宫不具备足够的随机性,接着在上面的递归回溯中添加一些小的改变,让每次遍历方向随机选择,这样就可以得到下面这样的代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;

class Maze
{
public:
	Maze(int row, int column);
	ostream& print();

	void setEntry(int x,int y)
	{

		if (isValidEntry(x,y)) {
			mazePtr[x - 1][y - 1] = true;
			if (x == 1) {
				startX = 2;
				startY = y;
			}
			if (x == row) {
				startX = row-1;
				startY = y;
			}
			if (y == 1) {
				startX = x;
				startY = 2;
			}
			if (y == column) {
				startX = 2;
				startY = column-1;
			}
			//cout << mazePtr[x - 1][y - 1];
		}
	}
	bool createMaze();
private:
	bool isInRange(int x, int y);
	bool isValidEntry(int x, int y);
	bool dig(int x, int y);
	int startX;
	int startY;
	int row;
	int column;
	vector<pair<int,int>> direction = { {1,0},{0,1},{-1,0},{0,-1} };
	unique_ptr<bool*[]> mazePtr;
};

bool Maze::isInRange(int x, int y)
{
	if (x <= 0 or x >= row) {
		return false;
	}
	if (y <= 0 or y >= column) {
		return false;
	}
	return true;
}

bool Maze::dig(int x, int y)
{
	if (!isInRange(x, y)) {
		return false;
	}
	mazePtr[x - 1][y - 1] = true;
    
	//每次遍历四个方向之前,对方向进行一次随机洗牌
	random_shuffle(direction.begin(),direction.end());
    
	for (int i = 0; i < 4; i++) {
		int tmpX = x + 2 * direction[i].first;
		int tmpY = y + 2 * direction[i].second;
		if (!isInRange(tmpX, tmpY)) {
			continue;
		}
		if (mazePtr[tmpX-1][tmpY-1] == false) {
			mazePtr[x+direction[i].first-1][y+direction[i].second-1] = true;
			//print() << endl;
			if (!dig(tmpX, tmpY)) {
				mazePtr[x + direction[i].first - 1][y + direction[i].second - 1] = false;
				continue;
			}
		}
	}
	return true;
}
bool Maze::createMaze()
{
	int x = startX;
	int y = startY;
	if (!dig(x, y)) {
		return false;
	}
	return true;
}

bool Maze::isValidEntry(int x, int y)
{
	if ((x == 1 and y == 1) ||
		(x == 1 and y == column) ||
		(x == row and y == 1) ||
		(x == row and y == column)) {
		return false;
	}
	if (x == 1 or x == row or y == 1 or y == column) {
		return true;
	}
	return false;
}

ostream& Maze::print()
{
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < column; j++) {
			cout << mazePtr[i][j] << " ";
		}
		cout << endl;
	}
	return cout;
}

Maze::Maze(int row, int column):row(row),column(column)
{
	mazePtr.reset(new bool*[row]);
	for (int i = 0; i < row; i++) {
		mazePtr[i] = new bool[column]{false};
	}
}

int main(int argc, char*argv[])
{
	Maze maze(15,15);
	maze.setEntry(1, 2);
	//cout << "设置入口后:" << endl;
	//maze.print();
	maze.createMaze();
	maze.print();
	system("pause");
	return 0;
}

这次结果如下,随机性增强了很多:

随机迷宫生成与寻路算法(1)深度优先搜索_第2张图片

这次就先做着写,接下来在慢慢补充改进。

你可能感兴趣的:(C++)