小刘的周记第十五周之随机迷宫的生成

以二维数组模拟一个矩形迷宫,利用随机广度优先生成不含有回路的迷宫中任意两点的正确路径。

与随机深度优先算法类似,随机广度优先算法也是基于图论的算法,可以用来生成迷宫。该算法与广度优先搜索算法类似,不同之处在于在遍历相邻节点时是随机选择一个节点进行遍历,而不是按照固定顺序进行遍历。同样由于随机选择的特性,该算法生成的迷宫具有随机性和不可预测性,同时保证没有循环路径。

代码:

#include <iostream>
#include <queue>
#include <vector>
using namespace std;
/*定义方向,这段代码定义了几个常量,分别表示四个方向和方向的数量。具体来说,RIGHT等于0,代表向右;DOWN等于1,
代表向下;LEFT等于2,代表向左;UP等于3,代表向上。而WAY_NUM等于4,表示总共有四个方向。这些常量可以在程序中方便地使用,避免出现数字硬编码,增加代码的可读性和可维护性。 */
const int RIGHT = 0;
const int DOWN = 1;
const int LEFT = 2;
const int UP = 3;
const int WAY_NUM = 4;
/*定义行走状态,这段代码定义了两个常量,分别表示“是”和“否”的状态。具体来说,YES等于4,代表“是”;NO等于5,
代表"否"。这些常量可以在程序中方便地使用,避免出现数字硬编码,增加代码的可读性和可维护性。*/ 
const int YES = 4;
const int NO = 5;
/*迷宫
这段代码定义了一个迷宫类Maze,并实现了该类的两个成员函数——initNode和setNodeState。
在Maze的构造函数中,首先根据传入的行和列数开辟_pMaze的空间。其中_pMaze是一个二维数组,存储了迷宫中每个节点的信息,包括节点的横坐标、纵坐标、节点的类型(墙或路)以及节点到周围四个方向的通行状态等。
initNode函数用于初始化迷宫中某一节点的信息。其中x和y表示该节点的横坐标和纵坐标,val表示该节点是墙还是路。根据节点的坐标和类型,初始化该节点到周围四个方向的通行状态。
setNodeState函数用于根据迷宫中每个节点的信息,设置该节点到周围四个方向的通行状态。当该方向可以通行时,将状态设置为YES;否则,将状态设置为NO。通行状态的设置可以根据迷宫中每个节点的类型(1表示墙,0表示路)进行判断。*/
class Maze{
public:
	Maze(int row, int col):_row(row), _col(col)
	{
		_pMaze = new Node*[_row];
		for (int i = 0; i < _row; ++i)
		{
			_pMaze[i] = new Node[_col];
		}
		_pPath.resize(_row * _col);  //开辟空间
	}
	void initNode(int x, int y, int val)
	{
		_pMaze[x][y]._x = x;
		_pMaze[x][y]._y = y;
		_pMaze[x][y]._val = val;
		for (int i = 0; i < WAY_NUM; ++i)
		{
			_pMaze[x][y]._state[i] = NO;
		}
	}
	void setNodeState()
	{
		for (int i = 0; i < _row; ++i)
		{
			for (int j = 0; j < _col; ++j)
			{
				if (_pMaze[i][j]._val == 1)
				{
					continue;
				}

				if (j < _col - 1 && _pMaze[i][j + 1]._val == 0)
				{
					_pMaze[i][j]._state[RIGHT] = YES;
				}

				if (i < _row - 1 && _pMaze[i + 1][j]._val == 0)
				{
					_pMaze[i][j]._state[DOWN] = YES;
				}

				if (j > 0 && _pMaze[i][j - 1]._val == 0)
				{
					_pMaze[i][j]._state[LEFT] = YES;
				}

				if (i > 0 && _pMaze[i - 1][j]._val == 0)
				{
					_pMaze[i][j]._state[UP] = YES;
				}
			}
		}
	}
//广度优先遍历搜索路径
/*这段代码实现了搜索迷宫路径的功能,其中用到了广度优先搜索算法。
首先判断起点(0, 0)是否是墙,如果是墙则无法走通,直接返回。
使用队列(_queue)存储处理过程中的每一个节点,初始时将起点(0, 0)压入队列。当队列不为空时,循环对队列中的每一个节点进行处理。
对于每一个节点,分别向其四个方向进行搜索。找到一个可以通行的方向时,将该方向的通行状态设为NO,并将下一个节点加入队列。
同时,记录路径信息。对于正在处理的节点,如果它的右侧节点可以通过,则记录右侧节点从当前节点移动而来,并将右侧节点加入队列。路径信息保存在_pPath数组中,每个节点对应数组中的一个位置,位置计算方法为:x * 列数 + y。
在每次加入队列的过程中,都进行判断,如果已经找到目标节点,则返回。
对于队列中的每个节点,处理完成后需要将其出队列(_queue.pop())。
最终,如果找到通往终点的路径,则路径信息存储在_pPath中,否则_pPath数组为空。*/
void searchMazePath()
{
	if (_pMaze[0][0]._val == 1)
	{
		return;
	}
	_queue.push(_pMaze[0][0]);
	while (!_queue.empty()){
		Node front = _queue.front();
		int x = front._x;
		int y = front._y;
		// 右方向
		if (_pMaze[x][y]._state[RIGHT] == YES){
			_pMaze[x][y]._state[RIGHT] = NO;
			_pMaze[x][y + 1]._state[LEFT] = NO;
			// 记录下一个结点是从哪个节点走过来的
			_pPath[x*_col + y + 1] = _pMaze[x][y];
			_queue.push(_pMaze[x][y + 1]);
			if (check(_pMaze[x][y + 1]))
				return;
		}
		// 下方向
		if (_pMaze[x][y]._state[DOWN] == YES){
			_pMaze[x][y]._state[DOWN] = NO;
			_pMaze[x + 1][y]._state[UP] = NO;
			_pPath[(x + 1)*_col + y] = _pMaze[x][y];
			_queue.push(_pMaze[x + 1][y]);
			if (check(_pMaze[x + 1][y]))
				return;
		}
		// 左方向
		if (_pMaze[x][y]._state[LEFT] == YES){
			_pMaze[x][y]._state[LEFT] = NO;
			_pMaze[x][y - 1]._state[RIGHT] = NO;
			_pPath[x*_col + y - 1] = _pMaze[x][y];
			_queue.push(_pMaze[x][y - 1]);
			if (check(_pMaze[x][y - 1]))
				return;
		}
		// 上方向
		if (_pMaze[x][y]._state[UP] == YES){
			_pMaze[x][y]._state[UP] = NO;
			_pMaze[x - 1][y]._state[DOWN] = NO;
			_pPath[(x - 1)*_col + y] = _pMaze[x][y];
			_queue.push(_pMaze[x - 1][y]);
			if (check(_pMaze[x - 1][y]))
				return;
		}
		// 出队列
		_queue.pop();
	}
}
/*这段代码实现了展示迷宫路径的功能,主要是根据搜索得到的路径信息(_pPath)回溯查找路径上的节点,并将其标记为*。最后输出标记过的迷宫以展示路径。
如果队列为空,说明不存在一条通往终点的路径,此时输出相应的提示信息。
否则,从右下角的节点开始回溯寻找路径节点。在回溯过程中,先将(x, y)节点标记为*,然后根据_pPath存储的路径信息找到路径上的上一个节点。重复执行该过程,一直回溯到起点(0, 0),标记完整条路径上的节点,最终输出标记过的迷宫。
在输出过程中,如果某个节点被标记,输出一个*;否则,输出该节点的类型值(val)。*/
void showMazePath(){
	if (_queue.empty()){
		cout << "不存在一条迷宫路径!" << endl;
	}
	else{
		// 回溯寻找迷宫路径节点
		int x = _row - 1;
		int y = _col - 1;  //x,y现在处于右下角出口
		for (;;){
			_pMaze[x][y]._val = '*';
			if (x == 0 && y == 0)
				break;
				Node node = _pPath[x*_col + y];  //取出_pPath存的路径的上一结点
				x = node._x;
				y = node._y;
		}
		for (int i = 0; i < _row; ++i){
			for (int j = 0; j < _col; ++j){
				if (_pMaze[i][j]._val == '*'){
					cout << "* ";
				}
				else{
					cout << _pMaze[i][j]._val << " ";
				}
			}
			cout << endl;
		}
	}
}
/*这段代码给出了Maze类的完整定义,包括私有成员变量_pMaze、_row、_col、_queue、_pPath以及私有嵌套结构体Node。
_pMaze是一个指向Node类型的指针,其大小为_row * _col。每个Node表示迷宫中的一个节点,保存该节点的横纵坐标、节点的类型(墙或路)以及节点到周围四个方向的通行状态。_row和_col分别表示迷宫的行数和列数。
_queue是一个STL队列,存储遍历过程中的每一个节点。_pPath是一个STL向量,用于存储广度优先遍历时,节点的行走信息。
私有嵌套结构体Node有4个成员变量:_x、_y、_val和_state。_x和_y表示该节点的横纵坐标,_val表示该节点的类型(墙或路),_state是一个长度为4的整型数组,用于记录该节点到周围四个方向的通行状态。
check函数用于判断给定节点是否为迷宫的右下角出口节点,如果是,返回true,否则返回false。*/
private:
	// 定义迷宫节点路径信息
	struct Node
	{
		int _x;
		int _y;
		int _val; // 节点的值
		int _state[WAY_NUM]; // 记录节点四个方向的状态
	};
	// 检查是否是右下角的迷宫出口节点
	bool check(Node &node)
	{
		return node._x == _row - 1 && node._y == _col - 1;
	}
	Node **_pMaze;
	int _row;
	int _col;
	queue<Node> _queue; // 广度遍历依赖的队列结构
	vector<Node> _pPath; // 记录广度优先遍历时,节点的行走信息
};
/*
这是应用上述迷宫类Maze的主函数。
首先提示用户输入迷宫的行列数,并创建迷宫对象maze。然后提示用户输入迷宫的路径信息,通过调用Maze类的initNode函数对每个节点进行初始化。接着调用Maze类的setNodeState函数,设置所有节点到周围四个方向的通行状态。
使用Maze类的searchMazePath函数搜索迷宫中是否存在通往终点的路径。最后使用Maze类的showMazePath函数输出标记过的迷宫路径。
完整代码如下:*/
int main(){
	cout << "请输入迷宫的行列数(例如:10 10):";
	int row, col, data;
	cin >> row >> col;
	Maze maze(row, col); // 创建迷宫对象
	cout << "请输入迷宫的路径信息(0表示可以走,1表示不能走):" << endl;
	for (int i = 0; i < row; ++i)
	{
		for (int j = 0; j < col; ++j)
		{
			cin >> data;
			// 可以初始化迷宫节点的基本信息
			maze.initNode(i, j, data);
		}
	}
	// 开始设置所有节点的四个方向的状态
	maze.setNodeState();
	// 开始从左上角搜索迷宫的路径信息了
	maze.searchMazePath();
	// 打印迷宫路径搜索的结果
	cout << "路径如下:" << endl;
	maze.showMazePath();
	return 0;
}

运行结果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值