C++实现迷宫的生成与解决

数据结构实验课要求解决一个迷宫问题,这里给定长宽用prime算法随机生成了一个迷宫并从指定起点与终点打印出了迷宫的解决方案,此处用到了栈数据结构,这里的jmc::Stack是我自己写的栈,这里就不放了,可以换成一切具有常规意义的empty、pop、push接口的栈ADT,或者直接使用std::stack就行,注意头文件的#include"Stack"也改一下(2022年4月10日21:34:47我已经改了,可以直接拿来用)。

2022年6月5日02:28:44更新:因为一些原因,我基于EGE图形化了迷宫的生成与解决,并放到了github上,欢迎点个star= =。

Maze.h:

#pragma once
#include<iostream>
#include<vector>
#include<map>
#include<set>
#include<random>
#include<string>
#include<stack>
#include<functional>
#include<algorithm>
#include<cassert>
namespace jmc {
	using blockpic = std::vector<std::string>;
	const blockpic block{
			"▉","  ","※"
	};

	class locat {
	public:
		using rowType = size_t;
		using calType = size_t;

		locat(rowType r = 0, calType c = 0)
			:loc(r, c) {}

		rowType x(void)const { return loc.first; }	//返回一维坐标	
		rowType x(const rowType& r) { loc.first = r; return loc.first; }//修改并返回一维坐标		
		calType y(void)const { return loc.second; }		//返回二维坐标
		calType y(const calType& c) { loc.second = c; return loc.second; }//修改并返回二维坐标
		locat up(void)const { return { loc.first - 1, loc.second }; }
		locat down(void)const { return { loc.first + 1, loc.second }; }
		locat left(void)const { return { loc.first, loc.second - 1 }; }
		locat right(void)const { return { loc.first, loc.second + 1 }; }
		locat& operator()(const rowType& r, const calType& c) {
			x(r), y(c);
			return *this;
		}
		locat& operator()(const locat& oth) {
			return (*this)(oth.x(), oth.y());
		}
		bool operator<(const locat& oth)const {
			return x() == oth.x() ? y() < oth.y() : x() < oth.x();
		}
		bool operator==(const locat& oth)const {
			return x() == oth.x() && y() == oth.y();
		}
		friend std::ostream& operator<<(std::ostream& o, const locat& l)
		{
			o << '(' << l.x() << ',' << l.y() << ')';
			return o;
		}
	private:
		std::pair<rowType, calType> loc;
	};

	

	class Maze
	{
	public:
		using rowType = locat::rowType;
		using calType = locat::calType;
		using locats = std::vector<locat>;

		enum blockType {
			wall,
			road,
			way
		}; 

		Maze(const locat& l) :xyMsg(l), Map(l.x(), mazeLine(l.y(), wall)) {}
		Maze(rowType row, calType cal);		// 随机生成一个迷宫,采用Prim算法

		std::function<locat(const locat& l)> operat[4]{
			[](const locat& l) {return l.up(); },
			[](const locat& l) {return l.down(); },
			[](const locat& l) {return l.left(); },
			[](const locat& l) {return l.right(); },
		};

		auto& operator()(const rowType& r,const calType& c) {
			return Map[r][c];
		}
		auto& operator()(const locat& p) {
			return (*this)(p.x(), p.y());
		}

		rowType  row(void) { return xyMsg.x(); }	// 返回迷宫的行数
		calType cal(void) { return xyMsg.y(); }	// 返回迷宫的列数
		locat& start(void) { return _start; }
		locat& end(void) { return _end; }
		void show(const blockpic pic = block);	// 打印迷宫

	private:
		using mazeLine = std::vector<blockType>;		// 单行迷宫
		using mazeMap = std::vector<mazeLine>;			// 迷宫图

		locats findWall(const locat& p);	//返回一个路周围的墙
		locats findRoad(const locat& p);	//返回一个墙周围的路

		locat xyMsg;
		mazeMap Map;
		locat _start, _end;
	};

	//给出迷宫问题的解决方案
	class Solute
		:public Maze
	{
	public:
		Solute(const rowType& r, const calType& c)
			:Maze(r, c) {
			rowType tmpR;
			calType tmpC;
			show();

			std::cout << std::endl << std::endl
				<< "请输入起点的行坐标与列坐标:";
			std::cin >> tmpR >> tmpC;
			(*this)(end()(tmpR, tmpC)) = way;
			std::cout << "请输入终点的行坐标与列坐标:";
			std::cin >> tmpR >> tmpC;
			(*this)(start()(tmpR, tmpC)) = way;

			solve(start());
			show();
			std::cout << std::endl << std::endl;
			showWay();
		}

		bool isIn(const locat& p) {
			return p.x() < row() && p.y() < cal();
		}
		bool solve(const locat& p);
		void showWay(void) {
			if (!ans.empty()) {
				std::cout << ans.top();
				ans.pop();
				if (!ans.empty())
					std::cout << " -> ";
				showWay();
			}
		};
	private:
		Maze mark{ locat{row(),cal()} };
		std::stack<locat> ans{};
	};
}

Maze.cpp:

#include "Maze.h"


jmc::Maze::Maze(rowType row, calType cal)
	:xyMsg(2 * row + 1, 2 * cal + 1), Map(2 * row + 1, mazeLine(2 * cal + 1, wall))
{
	// 初始化随机数生成器
	static std::random_device rd;
	static std::default_random_engine e(rd());
	static std::uniform_int_distribution<> d;

	std::map<blockType, std::set<locat>> mark{
		{wall,{}},{road,{}}
	};

	for (rowType i = 1; i < row * 2 + 1; i += 2)
	{
		for (calType j = 1; j < cal * 2 + 1; j += 2)
		{
			(*this)(i,j) = road;
		}
	}

	//随机生成起点,把边框分为四段
	auto i = d(e, decltype(d)::param_type{ 0,3 });
	auto j = i % 2 ?
		d(e, decltype(d)::param_type{ 0,(int)row - 2 }) :
		d(e, decltype(d)::param_type{ 0,(int)cal - 2 });
	switch (i)
	{
	case 0:
		_start(j, 0); break;
	case 1:
		_start(0, j - 1); break;
	case 2:
		_start(row - 1, j); break;
	case 3:
		_start(j + 1, cal - 1); break;
	}
	_start(_start.x() * 2 + 1, _start.y() * 2 + 1);	//将起点对应到实际位置
	locats tmpRoad{ _start };
	locats tmpWall = findWall(_start);
	mark[road].insert(tmpRoad.begin(), tmpRoad.end());
	mark[wall].insert(tmpWall.begin(), tmpWall.end());

	while (!mark[wall].empty())
	{
		auto it = mark[wall].begin();
		std::advance(it,
			d(e, decltype(d)::param_type{ 0,(int)mark[wall].size()-1 }));
		tmpRoad = findRoad(*it);	//随机将一个wall集合中的元素传入findRoad
		auto s1 = mark[road].size();	//插入set前set大小
		bool flag = false;
		for (auto& i : tmpRoad)
		{
			mark[road].insert(i);
			if (s1 != mark[road].size()) {
				s1 = mark[road].size();
				tmpWall = findWall(i);
				mark[wall].insert(tmpWall.begin(), tmpWall.end());
				flag = true;
			}    
		}
		//若size有变化,表示此wall周围的road有未标记的,将此wall置为road
		if (flag) {
			mark[road].insert(*it);
			(*this)(*it) = road;
		}
        mark[wall].erase(it);
	}
	_end(tmpRoad.back());
}

void jmc::Maze::show(const blockpic pic)
{
	size_t m{}, n{};
	for (const auto& i : Map)
	{
 		for (const auto& j : i)
		{
			std::cout << pic[j];
		}
		std::cout << m++ << std::endl;
	}
	for (size_t i = 0; i < cal(); printf("%2d", i++));
}

jmc::Maze::locats jmc::Maze::findWall(const locat& p)
{
	locats ret;
	locat tmp;
	if (p.x() != 1) {
		tmp = p.up();
		if ((*this)(tmp) == wall)
			ret.push_back(tmp);
	}
	if (p.y() != 1) {
		tmp = p.left();
		if ((*this)(tmp) == wall)
			ret.push_back(tmp);
	}
	if (p.x() != row() - 2) {
		tmp = p.down();
		if ((*this)(tmp) == wall)
			ret.push_back(tmp);
	}
	if (p.y() != cal() - 2) {
		tmp = p.right();
		if ((*this)(tmp) == wall)
			ret.push_back(tmp);
	}
	return ret;
}

jmc::Maze::locats jmc::Maze::findRoad(const locat& p)
{
	assert(p.x() != 0 && p.x() != row() && p.y() != 0 && p.y() != cal());

	locats ret;
	locat tmp;

	for (auto& i : operat)
	{
		tmp = i(p);
		if ((*this)(tmp) == road)
			ret.push_back(tmp);
	}

	return ret;
}

bool jmc::Solute::solve(const locat& p)
{
	if (p == end()) return true;
	mark(p) = road;
	(*this)(p) = way;
	ans.push(p);

	for (auto& i : operat)
	{
		auto tmp = i(p);
		if (isIn(tmp) && (*this)(tmp) != wall
			&& mark(tmp) != road && solve(tmp)) {
			return true;
		}
	}
	
	ans.pop();
	mark(p) = wall;
	(*this)(p) = road;
	return false;
}

主函数文件(测试用):

#include"Maze.h"
int main(int argc, char* argv[])
{
	jmc::Solute s(30, 30);
	return 0;
}

运行截图:

随机生成迷宫并指定起点与终点
输出解决路径:
在这里插入图片描述
当然这里也可以写成展示每一步走的动画的样子,加个延时与清屏就可以了这里就不演示了。

欢迎指正与询问

  • 2
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
生成迷宫的一种常见方法是利用深度优先搜索算法(DFS)和回溯法。以下是一个简单的示例代码,生成一个大小为5x5的迷宫: ```c #include <stdio.h> #include <stdlib.h> #include <time.h> #define ROW 5 #define COL 5 int maze[ROW][COL]; void init_maze() { int i, j; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { maze[i][j] = 1; } } } void print_maze() { int i, j; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { printf("%d ", maze[i][j]); } printf("\n"); } } void generate_maze() { srand(time(NULL)); int x = rand() % ROW; int y = rand() % COL; maze[x][y] = 0; while (1) { int direction = rand() % 4; switch (direction) { case 0: // up if (x - 1 < 0 || maze[x - 1][y] == 0) { continue; } maze[x - 1][y] = 0; x--; break; case 1: // down if (x + 1 >= ROW || maze[x + 1][y] == 0) { continue; } maze[x + 1][y] = 0; x++; break; case 2: // left if (y - 1 < 0 || maze[x][y - 1] == 0) { continue; } maze[x][y - 1] = 0; y--; break; case 3: // right if (y + 1 >= COL || maze[x][y + 1] == 0) { continue; } maze[x][y + 1] = 0; y++; break; } if (x == 0 && y == 0) { break; } } } int main() { init_maze(); generate_maze(); print_maze(); return 0; } ``` 该程序首先初始化迷宫,将所有单元格的值设为1,表示未访问。然后,随机选择一个起始位置,将其值设为0,表示已访问。接下来,程序随机选择一个方向(上、下、左、右),并检查该方向上的单元格是否已被访问。如果该方向上的单元格未被访问,则将其值设为0,表示已访问,并移动到该单元格。如果该方向上的单元格已被访问,则程序选择另一个方向。当程序回到起始位置时,生成迷宫完成。最后,程序将生成迷宫输出到控制台。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值