数据结构课设,图形化界面迷宫寻路问题(附源码)

目录

题目要求

效果

main.cpp

Draw.cpp

void init(vector>& matrix);

bool inputMap(vector>& matrix);

void setRandMatrix(vector>& matrix, int m, int n);

Search.cpp

vector dfs(vector>& matrix);

vector bfs(vector>& matrix);

bool DrawRedLine(vector dir);

 工程文件


题目要求

大二上学期学的数据结构,期末老师让做课设,每人分配一题,我分到了下面这题,还算有趣

我开始想做纯图形界面的,用c#做,但是老师要求用c++,鉴于qt那么难用,还是控制台吧。。

图形库用的是easyx,使用参考了这篇文章:C/C++图形库EasyX快速上手指南【1】

easyx可以在官网下载:https://easyx.cn/

下载完直接安装,集成环境我用的vs,一键就安装好了

源码需要的话可以联系我


效果

 控制台界面

 导入地图(AUST,yeyeye!!!!!)

随机生成地图,可以自定义行数和列数

 

DFS(深度优先搜索)

 BFS(广度优先搜索)


以下为代码讲解

main.cpp

#include <iostream>
#include <string>
#include<easyx.h>
#include<vector>
#include"Draw.h"//生成matrix和画图的函数在这个文件里面
#include"Search.h"//搜索的函数在这个文件里面
using namespace std;
int main()
{
	for (;;) {
		vector<vector<bool>> matrix;//声明迷宫的二维矩阵,类型为bool类型,true为通路,false为墙
		/*这个matrix很重要嗷,生成、画图、寻路的函数都是关于这个矩阵的*/
		welcome();//欢迎语
		init(matrix);//生成矩阵的函数,能实现读取文件生成和随机生成
		int c;
	/*下面这段没封装起来,主要是我嫌麻烦,没弄了*/
	to://用了goto,我发现在做选项的时候用goto真是好用
		cout << "DFS请按1,BFS请按2" << endl;
		cin >> c;
		bool flag;
	/*dfs和bfs返回的都是一个int型数组,DrawRedLine读这个数组然后把路径画在界面上*/
		if (c == 1)
			flag = DrawRedLine(dfs(matrix));
		else if (c == 2)
			flag = DrawRedLine(bfs(matrix));
		else {
			cout << "输入错误!请重新输入" << endl;
			goto to;
		}
		if (flag)//标志位,寻路搜索完成之后会返回true和false,读取flag判断是否存在路径
			cout << "路径生成完毕" << endl;
		else
			cout << "不存在路径" << endl;
		if (getchar() == 'q') {
			closegraph();
			break;
		}
	}
}

Draw.cpp

void init(vector<vector<bool>>& matrix);

void init(vector<vector<bool>>& matrix) {
	int input;
to1:
	cout << "|导入地图请按1|随机生成地图请按2|" << endl;
	cin >> input;
	if (input == 1) {
		if (!inputMap(matrix)) goto to1;//读取文件生成迷宫
	}
	else if (input == 2) {
		cout << "请输入列数m=";
		cin >> m;
		if (m > 60) goto to2;
		cout << "请输入行数n=";
		cin >> n;
		if (n > 60) goto to2;
		setRandMatrix(matrix, m, n);//随机生成迷宫矩阵
	}
	else {
	to2:
		cout << "输入错误,请重新输入" << endl;
		goto to1;
	}

	
	//这时候matrix已经赋值好力
    //m和n均为全局变量,也已被赋值
	

	wide = 620 / m < 440 / n ? 620 / m : 440 / n;//计算小方块宽度
	/*
	* wide是一个全局变量,因为在之后的生成路径图像中还需要用到
	为了因为行列设置过多导致生成的界面过大或过小
	进行了一些限制,小方块宽度=min(最大宽度/行数和列数)
	*/


	initgraph(wide * m, wide * n, 1);//生成窗口,
	/*
	界面长宽等于行数、列数x小方块宽度
	最后的1是生成模式,这样控制台和界面才会共存
	*/


	setBlock(BLUE);//设定方块初始化
	/*
	* 简单封装了一下,可以生成任意颜色的方块
	void setBlock(COLORREF color) {
	setfillcolor(color);
	setlinestyle(0, 2);//设置线框模式和宽度
	setlinecolor(WHITE);//白色线框
	}
	*/


	cout << "地图生成完毕!" << endl;


	//读matrix,绘制到界面
	for (int i = 0; i < n; i++)
		for (int j = 0; j < m; j++) {
			setfillcolor(matrix[i][j] ? BLUE : YELLOW);//true对应蓝方块,false对应黄方块
			if ((i == 0 && j == 0) || (i == n - 1 && j == m - 2)) setfillcolor(GREEN);//起点终点对应绿方块
			fillrectangle(j * wide, i * wide, wide + j * wide, wide + i * wide);//把(j,i)画在界面上
			/*
			* void fillrectangle (int left, int top, int right, int bottom);
			4个参数分别是左上x,y,右下x,y
			*/
		}
}

其中包含两个函数,一个是读文件给matrix赋值,一个是随机生成

bool inputMap(vector<vector<bool>>& matrix);

bool inputMap(vector<vector<bool>>& matrix) {

	string str;
	ifstream ifile("map.txt", ios::in);
	if (!ifile) {
		cout << "找不到文件" << endl;
		return false;
	}
	while (getline(ifile, str)) {
		vector<bool> temp;
		for (int i = 0; i < str.size(); i++) {
			temp.push_back(str[i] == '1');
		}
		matrix.push_back(temp);
	}
	m = matrix[0].size();
	n = matrix.size();
	cout << m << n << endl;
	ifile.close();
	return true;
}
/*
读map.txt中的字符串,没什么好说的
记得#include<fstream>
*/

void setRandMatrix(vector<vector<bool>>& matrix, int m, int n);

void setRandMatrix(vector<vector<bool>>& matrix, int m, int n) {//随机生成迷宫
	default_random_engine e;
	bernoulli_distribution u(0.3);//修改这个可以增加墙壁生成的概率
	e.seed(time(0));//生成随机数
	/*
	每次调用u(e)30%生成true,70%生成false
	记得#include <random>
	还有#include <ctime>
	*/
	

	//matrix中逐个赋值
	for (int i = 0; i < n; i++) {
		vector<bool> temp;
		for (int j = 0; j < m; j++) {
			if ((i == 0 && j == 0) || (i == n - 1 && j == m - 2)) temp.push_back(0);
			else temp.push_back(u(e));
			//这里值得注意,入口和出口直接赋值为0
		}
		matrix.push_back(temp);
	}
}

此外还有一个DrawRedLine函数,负责画路径,这个因为和搜索比较紧密,在Search.cpp中讲解和展示

这样,界面和地图就生成好了,接下来就是搜索路径和画出路径

Search.cpp

这个文件中有两个函数,输入都是matrix的二维数组,输出都是int型的一维度数组,数组里存的是出口到入口的方向

为什么是出口到入口,和算法有关,我用了动态规划(dp),在看代码之前,先讲一下思路吧:

首先不管什么搜索,布尔型二维数组matrix先会被转换成int型的二维数组dp用于存放方向

dp初始化

void dpInit(vector<vector<bool>>& matrix, vector<vector<int>>& dp) {
	for (int i = 0; i < matrix.size(); i++) {
		vector<int> temp;
		for (int j = 0; j < matrix[0].size(); j++)
			temp.push_back(matrix[i][j] ? 1 : 0);
		dp.push_back(temp);
	}
}

我是这样宏定义的

#define UP 2
#define RIGHT 3
#define DOWN 4
#define LEFT 5
//还有就是0是路1是墙

然后这两个算法会在dp每一行里填入方向,如果有通路的话终点的格子一定会被填入方向,然后根据这个方向倒推(是下面就往上找,左边就往右找),找到的下一格也是这样,最后就能找到起点。

简单画了个图

建一个数组,每找一个格子就存进去,最后返回这个数组。

根据dp数组生成road数组

void roadPush(vector<vector<int>>& dp, vector<int>& road) {
	int m = dp[0].size(), n = dp.size();
	if (dp[n - 1][m - 2] != 0) {
		int y = n - 1, x = m - 2;
		while (x != 0 || y != 0) {
			road.push_back(dp[y][x]);
			//cout << dp[y][x];
			switch (dp[y][x])
			{
			case UP: y++; break;
			case DOWN:y--; break;
			case RIGHT:x--; break;
			case LEFT:x++; break;
			default: break;
			}
		}
	}
}

好了,接下来就是核心算法了,之所以这是数据结构课设,就是为了这个:

vector<int> dfs(vector<vector<bool>>& matrix);

void recursion(vector<vector<int>>& dp, int dir = 1, int x = 0, int y = 0) {
	if (x == dp[0].size() || y == dp.size() || x < 0 || y < 0 || dp[y][x] != 0) return;
	dp[y][x] = dir;
	recursion(dp, RIGHT, x + 1, y);
	recursion(dp, LEFT, x - 1, y);
	recursion(dp, UP, x, y - 1);
	recursion(dp, DOWN, x, y + 1);
}
vector<int> dfs(vector<vector<bool>>& matrix) {

	vector<int> road;
	vector<vector<int>> dp;//初始化dp矩阵
	dpInit(matrix, dp);
	recursion(dp);
	roadPush(dp, road);
	return road;
}

没什么好说的,dfs写多了真的很好写

上下左右挨个找,先判断这一格是否合理,不合理跳出递归,合理的话把上一次递归的方向给这一格,然后进入下一次递归

vector<int> bfs(vector<vector<bool>>& matrix);

为了方便先定义了一个结构体Point

typedef struct Point {
	int x;
	int y;
	Point(int x, int y) {
		this->x = x;
		this->y = y;
	}

};

vector<int> bfs(vector<vector<bool>>& matrix) {

	vector<int> road;
	int m = matrix[0].size(), n = matrix.size();
	vector<vector<int>> dp;//初始化dp矩阵
	dpInit(matrix, dp);


	queue<Point> points;//建立队列,储存可扩展的点
	points.push(Point(0, 0));
	dp[0][0] = 1;
	while (!points.empty()) {
		int x = points.front().x, y = points.front().y;
		bool dead = true; //标志位,若所有条件不满足则pop掉这个点
		//找右边
		if (x + 1 < m && dp[y][x + 1] == 0) {
			points.push(Point(x + 1, y));
			dp[y][x + 1] = RIGHT;
			dead = false;
		}

		//找左边
		if (x - 1 >= 0 && dp[y][x - 1] == 0) {
			points.push(Point(x - 1, y));
			dp[y][x - 1] = LEFT;
			dead = false;
		}

		//找下面
		if (y + 1 < n && dp[y + 1][x] == 0) {
			points.push(Point(x, y + 1));
			dp[y + 1][x] = DOWN;
			dead = false;
		}

		//找上面
		if (y - 1 >= 0 && dp[y - 1][x] == 0) {
			points.push(Point(x, y - 1));
			dp[y - 1][x] = UP;
			dead = false;
		}
		if (dead) points.pop();


	}
	roadPush(dp, road);
	return road;
}

bfs不会的话可以先去搜搜,这里就简单说一下思路

建立一个队列,先推入起点

循环,直到队列为空

队首的四周是否有路,有的话把这些点存到队尾,没有就推出这个点

画个图

最后是画图函数

bool DrawRedLine(vector<int> dir);

bool DrawRedLine(vector<int> dir) {
	if (dir.empty()) return false;//数组为空说明不存在通路
	setBlock(RED);//设置方块颜色为红
	for (int i = 0, x = m - 2, y = n - 1; i < dir.size() - 1; i++) {
		switch (dir[i])
		{
		case UP: y++; break;
		case DOWN:y--; break;
		case RIGHT:x--; break;
		case LEFT:x++; break;
		default: break;
		}
		fillrectangle(x * wide, y * wide, wide + x * wide, +wide + y * wide);
	}
	return true;

}

 工程文件

https://pan.baidu.com/s/1qwFX9ehL7myxAzdtSWFJ2A

猜你喜欢

转载自blog.csdn.net/qq_38830492/article/details/128279254
今日推荐