Table of contents
bool inputMap(vector>& matrix);
void setRandMatrix(vector>& matrix, int m, int n);
Question requirements
I learned data structure in the first semester of my sophomore year. At the end of the semester, the teacher asked me to do the course design and assigned each person a question. I was assigned the following question, which is quite interesting.
I started to want to use C# to create a pure graphical interface, but the teacher asked me to use C++. Since Qt is so difficult to use, I decided to use the console. .
The graphics library uses easyx. Please refer to this article for usage: C/C++ Graphics Library EasyX Quick Start Guide [1]
easyx can be downloaded from the official website: https://easyx.cn/
Install directly after downloading. I use vs for the integrated environment and it can be installed with one click.
If you need the source code, please contact me
Effect
Console interface
Import map (AUST, yeyeye!!!!!)
Randomly generate maps, you can customize the number of rows and columns
DFS (depth first search)
BFS (Breadth First Search)
The following is a code explanation
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
*/
}
}
It contains two functions, one is to read the file and assign values to the matrix, and the other is to randomly generate
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);
}
}
In addition, there is a DrawRedLine function, which is responsible for drawing paths. Because this is closely related to search, it is explained and displayed in Search.cpp.
In this way, the interface and map are generated, and the next step is to search for the path and draw the path.
Search.cpp
There are two functions in this file. The inputs are two-dimensional arrays of matrix, and the outputs are one-dimensional arrays of type int. The arrays store the direction from the exit to the entrance.
Why is it from the exit to the entrance? It has something to do with the algorithm. I used dynamic programming (dp). Before looking at the code, let me talk about the idea first:
First, regardless of the search, the Boolean two-dimensional array matrix will first be converted into an int two-dimensional array dp for storing the direction.
dp initialization
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);
}
}
I defined the macro like this
#define UP 2
#define RIGHT 3
#define DOWN 4
#define LEFT 5
//还有就是0是路1是墙
Then these two algorithms will fill in the direction in each row of dp. If there is a path, the end grid will definitely be filled in the direction, and then work backwards based on this direction (if it is below, look up, and on the left, look to the right). The same goes for the next square you find, and you can finally find the starting point.
Just drew a simple picture
Create an array, save it every time you find a grid, and finally return this array.
Generate road array based on dp array
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;
}
}
}
}
Okay, next is the core algorithm. The reason why this is a data structure course is for this:
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;
}
There’s nothing much to say. DFS is really easy to write if you write a lot.
Search up, down, left, and right, and first determine whether the grid is reasonable. If not, jump out of the recursion. If it is reasonable, give the direction of the last recursion to this grid, and then enter the next recursion.
vector<int> bfs(vector<vector<bool>>& matrix);
For convenience, we first define a structure 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;
}
If you don’t know bfs, you can go search first. Here is a brief explanation of the idea.
Create a queue and push to the starting point first
Loop until queue is empty
Is there a road around the front of the team? If so, save these points to the end of the team. If not, push out this point.
draw a picture
Finally, the drawing function
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;
}