使用opengl和c++实现迷宫地图的路径搜索和显示(基于DFS)
写在前面,本人计算机专业渣渣,这是我的第一篇博客,代码写的不好,仅作我的学习记录吧。
最近学校编程实验课的一道作业,要求完成迷宫的路径搜索,用c++完成。本渣渣花了几天的时间加上在网上参考大神们的源码,终于基本上完成任务了,现在贴出来做个记录(ps.刚刚基本了解了一下markdown的语法,所以排版请见谅啦)
迷宫的路径搜索算法请示就是一个图的深度优先搜索。本人大二,正在学习数据结构,也是刚刚才接触DFS,虽然基本上了解了DFS的含义,但是第一次编程实现还是有一定困难的,刚开始的话,因为自己对递归调用不熟悉嘛,所以手动用循环实现基本功能(循环实现stack功能,应该是吧),但是只能算完成了50%吧,只能实现寻找一条通路,不能找到全部通路。尴尬!这里就不贴啦(代码已经被我改的乱七八糟,不忍直视了!)
后来查了资料使用递归方法实现了多条路径的搜索
主要问题在于如何判断在找到终点后回退回去重新寻找的过程
我的代码如下:
//这是stack.h头文件,自己实现的模拟栈操作(自己造轮子哦,但是很惨)
//一开始总是无法实现功能,反复查找问题,发现查找路径主体应该没有问题,检查了一下
//自己写的stack,结果有基础数据写错了,差个1这样的,这个应该很常见吧,翻车了,正
//是个悲伤地故事
#include<iostream>
using namespace std;
struct node
{
int x;
int y;
int d;//表示下个节点的方向
bool operator== (node t)
{
if (this->x == t.x&&this->y == t.y)
return true;
else
return false;
}
node(int x, int y)
{
this->x = x;
this->y = y;
}
void set_node(int x, int y)
{
this->x = x;
this->y = y;
}
node() = default;
};
// struct post
// {
// node sites;
// int num; //路径的第几个节点
// };
class stack
{
private:
node* head;
int top;
int size;
public:
stack(int s)
{
size = s;
head = new node[s];
top = -1;
}
stack(const stack &other) //拷贝构造函数
{
top = other.top;
size = other.size;
head = new node[size];
for (int i = 0; i <= top; i++)
{
head[i] = other.head[i];
}
}
bool isempty()
{
if (top == -1)
return true;
else
return false;
}
bool isfull()
{
if (top == size - 1)
return true;
else
return false;
}
void push_back(node e)
{
if (!isfull())
{
head[++top] = e;
}
else
{
int temsize = 2 * size;
node* tem = new node[temsize];
for (int i = 1; i < size / 2; i++)
{
tem[i] = head[i];
}
delete head;
head = tem;
size = temsize;
head[++top] = e;
}
}
bool pop_back(node& e)
{
if (!isempty())
{
e = head[top--];
return true;
}
else
{
return false;
}
}
void clear()
{
top = -1;
}
void des()
{
delete head;
}
node operator[](int i)
{
return head[i];
}
int getUsedsize()
{
return top + 1;
}
};
下面是查找路径的代码
//find_map.h头文件
#include "stack.h"
#include<fstream>
#include<vector>
#include<string>
#include<sstream>
class find_map
{
private:
vector<vector<char>> map;
// char map[10][10] = {
// { '1', '1', '1', '1', '1', '1', '1', '1', '1', '1'},
// { '1', '0', '1', '1', '0', '0', '0', '1', '1', '1'},
// { '1', '0', '0', '0', '0', '1', '0', '1', '1', '1'},
// { '1', '1', '1', '1', '1', '1', '0', '1', '1', '1'},
// { '1', '1', '1', '1', '0', '0', '0', '1', '1', '1'},
// { '1', '1', '1', '1', '0', '0', '1', '1', '1', '1'},
// { '1', '1', '1', '0', '0', '1', '1', '1', '1', '1'},
// { '1', '1', '1', '0', '1', '1', '1', '1', '1', '1'},
// { '1', '1', '1', '0', '0', '1', '1', '1', '1', '1'},
// { '1', '1', '1', '1', '1', '1', '1', '1', '1', '1'}
// };
//迷宫数据,开始测试用的,完成后可以从文件读取数据(1为墙,0位空)
int rows = 10;
int columns = 10;
node start;
node end;
vector<stack> sta;//存储路径
public:
find_map()
{
rows = 0;
columns = 0;
cout << "请输入要读取的文件路径:" << endl;
string filepath;
cin >> filepath;
ifstream readmap(filepath);
if (readmap)
{
string aLine;
while (getline(readmap, aLine))
{
istringstream Line(aLine);
char tem;
vector<char> temline;
while (Line >> tem)
{
temline.push_back(tem);
}
map.push_back(temline);
}
rows = map.size() - 2;
columns = map[0].size();
start.set_node(map[rows][0] - 48, map[rows][1] - 48); //char to int
end.set_node(map[rows + 1][0] - 48, map[rows + 1][1] - 48);
}
else
{
cout << "打开文件失败!";
exit(-1);
}
}
void pf()
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
cout << map[i][j] << ' ';
}
cout << endl;
}
// cout << (int)map[rows][0]-48 << endl;
// cout << rows << endl;
// cout << columns << endl;
// cout << start.x << ' ' << start.y << endl;
// cout << end.x << ' ' << end.y << endl;
}
bool isgo(node nownode)
{
if (map[nownode.x][nownode.y] == '0')
return true;
else
return false;
}
void recovrey()
{
for (auto &i : map)
{
for (auto &j : i)
{
if (j == '@')
j = '0';
}
}
}
void setpass(node tem) //表示可以通过
{
map[tem.x][tem.y] = '*';
}
node nextnode(node tem, int i) //1,2,3,4表示要走的方向
{
node next;
next.x = tem.x;
next.y = tem.y;
switch (i)
{
case 1:next.y = next.y + 1; break; //向右走
case 2:next.x = next.x + 1; break; //向下走
case 3:next.y = next.y - 1; break; //向左走
case 4:next.x = next.x - 1; break; //向上走
}
return next;
}
void setNotPass(node tem) //表示走过但无法通过
{
map[tem.x][tem.y] = '@';
}
void setRecover(node tem) //表示走过但无法通过
{
map[tem.x][tem.y] = '0';
}
//核心代码 寻找路径
bool find_path()
{
bool statu = false;
stack path(rows*columns);
node nownode; //现在位置
int temnum = 1; //节点在路径的位置
int nextfoot = 1; //下一步方向
nownode = start;
nownode.d = nextfoot;
do
{
if (isgo(nownode))
{
setpass(nownode); //留下足迹
if (nownode.x == end.x&&nownode.y == end.y) //查找成功
{
nownode.d = 4;
path.push_back(nownode);
statu = true;
sta.push_back(path);//压入栈中
temnum++;
// if (temnum == 3)
// {
// return true;
// }
}
else
{
nextfoot = 1;
nownode.d = nextfoot;
path.push_back(nownode);
nownode = nextnode(nownode, nextfoot); //寻找下一个步
//nownode.d = nextfoot;
//nextfoot++;
}
}
else //当前位置不通
{
if (!path.isempty())
{
path.pop_back(nownode);
nextfoot = nownode.d;
//cout<<'(' << nownode.x <<','<< nownode.y<<')' << endl;
map[nownode.x][nownode.y] = '0';
while (nownode.d == 4 && !path.isempty())
{
//setNotPass(e.sites);
//setRecover(e.sites);
path.pop_back(nownode);
nextfoot = nownode.d;
map[nownode.x][nownode.y] = '0';
}
if (nownode.d < 4)
{
nextfoot++; //转换方向
//nownode = nextnode(nownode, nextfoot);
nownode.d = nextfoot;
path.push_back(nownode);
setpass(nownode);
nownode = nextnode(nownode, nextfoot);
}
}
}
} while (!path.isempty());
//path.des(); //销毁栈
return statu;
}
//存储迷宫数据到文件
void storage_map()
{
ofstream out("结果.txt");
for (auto i : map)
{
for (auto j : i)
out << j << ' ';
out << endl;
}
int num = 1;
for (auto i : sta)
{
out << "第" << num << "条路径为:" << endl;
out << "start" << "->";
for (int j = 0; j < i.getUsedsize(); j++)
{
out << "(" << i[j].x << ',' << i[j].y << ')' << "->";
}
out << "end" << endl;
num++;
}
}
void seeout()
{
for (auto i : map)
{
for (auto j : i)
cout << j << ' ';
cout << endl;
}
int num = 1;
cout << sta.size()<<endl;
for (int i = 0; i < sta.size(); i++)
{
cout << "第" << num << "条路径为:" << endl;
cout << "start" << "->";
for (int j = 0; j < sta[i].getUsedsize(); j++)
{
cout << "(" << sta[i][j].x << ',' << sta[i][j].y << ')' << "->";
}
cout << "end" << endl;
num++;
}
}
};
主函数(没什么功能,哈哈~~~)
int main()
{
find_map t;
cout << "读入地图数据:" << endl;
t.pf();
cout << endl;
vector<stack> a; //储存路径
if (t.find_path())
{
cout << "找到路径:" << endl;
t.pf();
//t.storage_map();
t.seeout();
}
else
cout << "未找到路径!";
}
赶着去上课,OpenGL图形界面的实现晚上回来再贴喽(悲伤脸)
好啦,上课回来啦
接着写
其实迷宫的图形化实现也是作业的要求啦,不过是选做啦,正巧,选修了计算机图形学,学了点OpenGL的语法,所以就想用OpenGL实现以下迷宫的二维图形化,其实也挺简单的,就是调用OpenGL的简单画线和画四边形的代码组织一下实现的,代码有点渣渣,高手请指教。本来想
使用上面写好的find_map头文件实现的,但是发现OpenGL绘图要调用类的成员函数有点麻烦
,好吧,我放弃了(就是这么懒,好气哦)
OpenGL绘图实现
#include "stack.h"
#include<fstream>
#include<vector>
#include<string>
#include<sstream>
#include<algorithm>
#include <gl/glut.h>
#include <gl/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
//设置地图大小
#define SCREEN_WIDTH 20
#define SCREEN_HEIGHT 20
vector<vector<char>> map; //存储迷宫
int rows = 10;
int columns = 10;
node start; //数组中的开始位置
node mend; //数组中的结束位置
vector<stack> sta;//存储路径
bool ans = false;
//OpenGL 数据
float startx , starty ; //OpenGL的开始坐标
float endx , endy ; //OpenGL的结束坐标
//定义函数
void find_map()
{
rows = 0;
columns = 0;
cout << "请输入要读取的文件路径:" << endl;
string filepath;
cin >> filepath;
int num = 0;
ifstream readmap(filepath);
if (readmap)
{
string aLine;
while (num < 20&&getline(readmap, aLine))
{
num++;
istringstream Line(aLine);
char tem;
vector<char> temline;
while (Line >> tem)
{
temline.push_back(tem);
}
map.push_back(temline);
}
rows = map.size();
columns = map[0].size();
int temx, temy;
readmap >> temx;
readmap >> temy;
/* cout << temx << ' ' << temy << endl;*/
start.set_node(temx, temy); //char to int
readmap >> temx;
readmap >> temy;
mend.set_node(temx, temy);
startx = start.y + 0.5;
starty = SCREEN_WIDTH - start.x;
endx = mend.y + 0.5;
//cout << "endx==" << endx << endl;
endy = SCREEN_WIDTH - float(mend.x);
//cout << "endy==" << endy << endl;
}
else
{
cout << "打开文件失败!";
exit(-1);
}
}
void pf()
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
cout << map[i][j] << ' ';
}
cout << endl;
}
// cout << (int)map[rows][0]-48 << endl;
// cout << rows << endl;
// cout << columns << endl;
// cout << start.x << ' ' << start.y << endl;
// cout << end.x << ' ' << end.y << endl;
}
bool isgo(node nownode)
{
if (map[nownode.x][nownode.y] == '0')
return true;
else
return false;
}
void recovrey()
{
for (auto &i : map)
{
for (auto &j : i)
{
if (j == '@')
j = '0';
}
}
}
void setpass(node tem) //表示可以通过
{
map[tem.x][tem.y] = '*';
}
node nextnode(node tem, int i) //1,2,3,4表示要走的方向
{
node next;
next.x = tem.x;
next.y = tem.y;
switch (i)
{
case 1:next.y = next.y + 1; break; //向右走
case 2:next.x = next.x + 1; break; //向下走
case 3:next.y = next.y - 1; break; //向左走
case 4:next.x = next.x - 1; break; //向上走
}
return next;
}
void setNotPass(node tem) //表示走过但无法通过
{
map[tem.x][tem.y] = '@';
}
void setRecover(node tem) //表示走过但无法通过
{
map[tem.x][tem.y] = '0';
}
//核心代码 寻找路径
bool find_path()
{
bool statu = false;
stack path(rows*columns);
node nownode; //现在位置
int temnum = 1; //节点在路径的位置
int nextfoot = 1; //下一步方向
nownode = start;
nownode.d = nextfoot;
do
{
if (isgo(nownode))
{
setpass(nownode); //留下足迹
if (nownode.x == mend.x&&nownode.y == mend.y) //查找成功
{
nownode.d = 4;
path.push_back(nownode);
statu = true;
sta.push_back(path);//压入栈中
temnum++;
// if (temnum == 3)
// {
// return true;
// }
}
else
{
nextfoot = 1;
nownode.d = nextfoot;
path.push_back(nownode);
nownode = nextnode(nownode, nextfoot); //寻找下一个步
//nownode.d = nextfoot;
//nextfoot++;
}
}
else //当前位置不通
{
if (!path.isempty())
{
path.pop_back(nownode);
nextfoot = nownode.d;
//cout<<'(' << nownode.x <<','<< nownode.y<<')' << endl;
map[nownode.x][nownode.y] = '0';
while (nownode.d == 4 && !path.isempty())
{
//setNotPass(e.sites);
//setRecover(e.sites);
path.pop_back(nownode);
nextfoot = nownode.d;
map[nownode.x][nownode.y] = '0';
}
if (nownode.d < 4)
{
nextfoot++; //转换方向
//nownode = nextnode(nownode, nextfoot);
nownode.d = nextfoot;
path.push_back(nownode);
setpass(nownode);
nownode = nextnode(nownode, nextfoot);
}
}
}
} while (!path.isempty());
//path.des(); //销毁栈
return statu;
}
//存储迷宫数据到文件
void storage_map()
{
ofstream out("结果.txt");
for (auto i : map)
{
for (auto j : i)
out << j << ' ';
out << endl;
}
int num = 1;
for (auto i : sta)
{
out << "第" << num << "条路径为:" << endl;
out << "start" << "->";
for (int j = 0; j < i.getUsedsize(); j++)
{
out << "(" << i[j].x << ',' << i[j].y << ')' << "->";
}
out << "end" << endl;
num++;
}
}
void seeout()
{
for (auto i : map)
{
for (auto j : i)
cout << j << ' ';
cout << endl;
}
int num = 1;
sort(sta.begin(), sta.end());
//cout << sta.size();
for (int i = 0; i < sta.size(); i++)
{
cout << "第" << num << "条路径为:" << endl;
cout << "start" << "->";
for (int j = 0; j < sta[i].getUsedsize(); j++)
{
cout << "(" << sta[i][j].x << ',' << sta[i][j].y << ')' << "->";
}
cout << "end" << endl;
num++;
}
}
//openGL 部分
void gldisplay()
{
glShadeModel(GL_FLAT);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.32, 0.86, 0.67);
glMatrixMode(GL_MODELVIEW); //设置当前操作的矩阵为“模型视图矩阵”
glLoadIdentity(); //把当前矩阵设置为单位矩阵
glBegin(GL_QUADS);
float x = 0, y = SCREEN_HEIGHT;
for (int i = 0; i < SCREEN_WIDTH; ++i)
{
for (int j = 0; j < SCREEN_HEIGHT; ++j)
{
if (map[i][j] == '1')
{
glVertex2f(x, y);
glVertex2f(x, y - 1);
glVertex2f(x + 1, y - 1);
glVertex2f(x + 1, y);
x++;
if (j == SCREEN_HEIGHT - 1)
{
y--;
x = 0;
}
}
else
{
x++;
if (j == SCREEN_HEIGHT - 1)
{
y--;
x = 0;
}
}
}
}
glEnd();
glFlush();
//绘制起点
glShadeModel(GL_FLAT);
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_TRIANGLES);
glVertex2f(startx, starty);
glVertex2f(startx - 0.5, starty - 1);
glVertex2f(startx + 0.5, starty - 1);
glEnd();
glFlush();
//绘制终点
glShadeModel(GL_FLAT);
glColor3ub(255u, 255u, 0u);
glBegin(GL_TRIANGLES);
glVertex2f(endx, endy);
glVertex2f(endx - 0.5, endy - 1);
glVertex2f(endx + 0.5, endy - 1);
glEnd();
glFlush();
if (ans == true)
{
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_LINE_STRIP);
for (int j = 0; j < sta[0].getUsedsize() - 1; j++)
{
/*cout << "(" << sta[0][j].x << ',' << sta[0][j].y << ')' << "->";*/
glVertex2f(sta[0][j].y + 0.5, SCREEN_WIDTH - sta[0][j].x - 0.5);
glVertex2f(sta[0][j + 1].y + 0.5, SCREEN_WIDTH - sta[0][j + 1].x - 0.5);
}
glEnd();
glFlush();
}
}
void glspecial(int key, int x, int y)
{
switch (key)
{
case GLUT_KEY_LEFT:
if (map[20 - int(starty)][int(startx) - 1] == '0')
startx -= 1; //向左移动
glutPostRedisplay();
break;
case GLUT_KEY_RIGHT:
if (map[20 - int(starty)][int(startx) + 1] == '0')
startx += 1; //向右移动
glutPostRedisplay();
break;
case GLUT_KEY_UP:
if (map[20 - (int(starty) + 1)][int(startx)] == '0')
starty += 1; //向上移动
glutPostRedisplay();
break;
case GLUT_KEY_DOWN:
if (map[20 - (int(starty) - 1)][int(startx)] == '0')
starty -= 1; //向下移动
glutPostRedisplay();
break;
}
}
void glcommon(unsigned char key , int x, int y)
{
if (key == 'y')
{
ans = true;
glutPostRedisplay();
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_LINE_STRIP);
for (int j = 0; j < sta[0].getUsedsize() - 1; j++)
{
/*cout << "(" << sta[0][j].x << ',' << sta[0][j].y << ')' << "->";*/
glVertex2f(sta[0][j].y + 0.5, SCREEN_WIDTH - sta[0][j].x - 0.5);
glVertex2f(sta[0][j + 1].y + 0.5, SCREEN_WIDTH - sta[0][j + 1].x - 0.5);
}
glEnd();
glFlush();
}
}
// void glstart()
// {
// glColor3f(1.0, 0.0, 0.0);
// glMatrixMode(GL_MODELVIEW); //设置当前操作的矩阵为“模型视图矩阵”
// glLoadIdentity(); //把当前矩阵设置为单位矩阵
// glBegin(GL_TRIANGLES);
// glColor3f(1.0, 0.0, 0.0);
// glVertex2f(1.5, 9);
// glVertex2f(1, 8);
// glVertex2f(2, 8);
// glEnd();
// glFlush();
// }
void glmyReshape(int w, int h) {
if (!h) return;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h) {
glOrtho(-20.0, 20.0, -20.0*(GLfloat)h / (GLfloat)w, 20.0*(GLfloat)h / (GLfloat)w, -20.0, 20.0);
}
else {
glOrtho(-20.0*(GLfloat)w / (GLfloat)h, 20.0*(GLfloat)w / (GLfloat)h, -20.0, 20.0, -20.0, 20.0);
}
}
int main(int argc, char** argv)
{
find_map();
cout << "读入地图数据:" << endl;
pf();
cout << endl;
vector<stack> a; //储存路径
if (find_path())
{
cout << "找到路径:" << endl;
pf();
//t.storage_map();
seeout();
cout << "输入y以显示最佳路径!" << endl;
}
else
cout << "未找到路径!";
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
glutInitWindowSize(1000, 800);
glutInitWindowPosition(0, 0);
glutCreateWindow("一个迷宫");
glutDisplayFunc(gldisplay);
glutReshapeFunc(&glmyReshape);
glutSpecialFunc(glspecial);
glutKeyboardFunc(glcommon);
glutMainLoop();
return 0;
}
迷宫数据存储在二维数组里,为了简单起见,我把迷宫画在窗口的第一象限。