计算机软件技术实习——迷宫算法核心

文章目录

  • 计算机软件技术实习——迷宫算法核心
    • 前言
    • 一、迷宫构成的要素
    • 二、生成迷宫和寻路算法的实现
    • 1. 迷宫的生成
    • 2.基于数组生成迷宫。
    • 3.自己控制走迷宫。
    • 4.A*算法找出最短路径。
    • 总结

前言

最近都是在忙碌软件实习了,先写计算器,在干贪吃蛇,接着还有迷宫。先说一下迷宫这个项目的相关要求吧。
1.迷宫游戏是非常经典的游戏,在该题中要求随机生成一个迷宫,并求解迷宫;
2.要求游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘方向键控制,并在行走路径上留下痕迹;
3.系统走迷宫路径要求基于A*算法实现,输出走迷宫的最优路径并显示。
设计交互友好的游戏图形界面。

一、迷宫构成的要素

其实迷宫相较于贪吃蛇构成要素确实少了很多,基本就是墙和路,还有走迷宫的人,起点和终点,大概就没了。
但是比贪吃蛇多了一个很重要的东西,那就是算法,我们要把哪一点设置为路,哪一点设置为墙都是需要用算法来解决的问题。有很多方法来生成这样的路吧。比如广度优先BFS,深度优先DFS,随机prim算法都可以生成墙和路。
如果想生成的非常自然,美观的话,我们可以用prim算法来做。但请在学习prim算法的时候一定选对,我当时本来就准备用prim算法的,结果学到最后发现自己学习的好像并不是什么prim算法,反而和深度优先算法有点相似却又不完全是,所以到现在为止我生成迷宫的算法还是比较奇怪的。
我们首先要做的事情是迷宫确定大小,很简单输入一个长和宽,然后用二维数组赋值的方法,两个循环直接赋值。边界的值,起点的值都是自己去决定的,反正都是双重循环进行遍历赋值罢了,起点的坐标可以自己决定。
具体的广度优先遍历其实大概就是一条路走到黑,先找一条通路知道再也到达不了下一个地方,再返回起点,然后继续执行,知道所有的点都被访问过。而深度优先就是先访问周围所有可以达到的节点,然后再次判断这些点可以访问的所有节点,直到访问完成所有的节点。相当于是一层一层遍历的。
有一点值得注意的是我们所需要的是迷宫其实最基本的东西是数组,判断一个数的值来决定是路还是墙。所以只需要生成具体的图形的话,我们所需要的操作,寻路,生成只是在最基础的数组上改变数值罢了。

二、生成迷宫和寻路算法的实现

1. 迷宫的生成

我学的迷宫生成的算法核心也算是深度优先遍历吧。我们可以假设刚刚开始整个地图都是墙,然后假定一个矿工,他要挖穿一条路从起点到终点。就先要确定一个点为起点,然后开始挖,这时候如果我们定义终点的话很可能玩出来的路就只有一条,很可能不是迷宫,而是直接一条现成的路了。所以我推荐的方式是设置一个墙队列,必须判断所有的墙都试过了,那个我们假定的矿工才能休息,才能结束他辛辛苦苦挖路的工作。关于这个墙对列的选择,因为我们是在不知道墙总共有多大,所以要定义一个未知的空间来存放墙对列的横纵坐标。这时候就会想到一个可以视为容器的函数。vector函数,具体教程的链接是这个:https://www.runoob.com/w3cnote/cpp-vector-container-analysis.html。反正大概就是一个可存储任意类型的大的容器,还可以系统分配动态空间,你有多少数据,他就会给你分配多大的空间。
有了这个函数我们就可以用来存放墙对列了,墙对列的值为非空,就会一直判断,一直挖下去。
具体判断准则和挖路的方式是什么呢?我所想到的就是,刚开始全为墙,然后随机取一点先设置为路,接着看这个点上下左右四个方向有没有其他墙存在。判定方法如果这个点上下左右四个方向有小于或者等于一个方向上为路的话,我们就把这个点设置为路,反之,依旧为墙,接着把上下左右四个方向上的所有点放入墙对列中。然后继续取下一个墙对列中的点,继续执行判断,直到最后墙对列为空,我们就停止循环。生成在地图边界上的话是最最需要注意的,我们不可能直接把最外围的墙挖穿,那也就不符合迷宫的基本设定了,所以要想一个好办法,可以在最为外的墙四周全部设置为路,就一定可以防止最外围的墙被挖串了。
可能会有一个疑惑,比如判断下一个墙队列中的点,这个点之前有个方向上判断依旧为墙的点还是会被放入,进行第二次判断。这个问题其实是没必要担心,之前判断为路,就根本进不去墙对列,如果之前判断为墙的话,无论剩下的点怎么变,它都一定是墙了,只能墙变成路,却不能路变成墙,这个过程是不可逆的。
终点是没必要初始化的,一般来说起点是固定的,一般位于地图的左上角,而终点一般位于地图的右下角。终点生成的方式可以用循环尝试靠左下角的墙体,让第一个出现左边有一个为路的话,就可以直接把这个墙体挖了设置为终点。

void initMG(int a, int b) {//初始化地图的函数,a为横坐标,b为纵坐标。 system("cls");for (int i = 0; i < a; i++) {for (int j = 0; j < b; j++) {MG[i][j] = 1;}}for (int i = 0; i < a; i++) {MG[i][0] = 4;MG[i][b - 1] = 4;//第一列和最后一列都赋值为0. }for (int i = 0; i < b; i++) {MG[0][i] = 4;MG[a - 1][i] = 4;//第一行和最后一行都初始化为0。0代表为路,这样设定可以防止外围的墙生成不全。 }vector<int> X;//设置一个容器为X用来存放墙队列横坐标。 vector<int> Y; //设置另一个容器为Y用来存放墙队列的纵坐标。X.push_back(2);//设置初始值 (注意X和Y是从0开始的)。 Y.push_back(2); //初始取出墙队列中的值应该对应坐标为(3,3)的位置。 while (X.size()){//判断X中的墙队列还有没有内容,有的话继续执行,墙对列为空的话退出。int r = rand() % X.size();//生成一个随机的数字对X的大小进行取余运算,结果一定小于X的大小,可以实现随机取出墙队列中的一点。int x = X[r];//把取出来的墙的横坐标赋值给x。 int y = Y[r];//同理纵坐标赋值给y。 int count = 0;//判断上下左右为路的个数 for (int i = x - 1; i < x + 2; i++) {//找出横坐标相邻的点。 for (int j = y - 1; j < y + 2; j++) {//找出纵坐标相邻的点。 if (abs(x - i) + abs(y - j) == 1 && (MG[i][j] == 0 || MG[i][j] == 4)) {//判断上下左右四个方向的值是不是为0,每有一个值为0,count就加一。 ++count;}}}if (count <= 1) {//判断一个墙体上下左右为路的个数小于1的话,就插入新的墙。 MG[x][y] = 0;//设置为路。 for (int i = x - 1; i < x + 2; i++) {//在墙队列中插入新的墙for (int j = y - 1; j < y + 2; j++) {if (abs(x - i) + abs(y - j) == 1 && MG[i][j] == 1) {//找出上下左右四个方向上为墙的单元,插入到墙体队列。 X.push_back(i);Y.push_back(j);}}}}X.erase(X.begin() + r);//删除当前墙的横坐标 Y.erase(Y.begin() + r);//删除当前墙的纵坐标。 }MG[2][1] = 2;//设置迷宫地图的起点。for (int i = a - 3; i >= 0; i--) {if (MG[i][b - 3] == 0) {//找出最靠近墙的那一行,靠下的位置第一个为路的点,将它右边的墙设置为终点。 MG[i][b - 2] = 3;break;}}
}

2.基于数组生成迷宫。

用上述生成墙体的方法其实主要思想是改变数组中的值,下面就得开始打印墙体了。相对于链表和贪吃蛇的定位方法打印,迷宫确实简单地多。直接根据数组的输出就好了。

void showing(int a, int b)
{system("cls");for (int i = 0; i < a; i++) {for (int j = 0; j < b; j++) {if (MG[i][j] == 1)printf("■");if (MG[i][j] == 0)printf("  ");if (MG[i][j] == 3)printf("终");if (MG[i][j] == 4)printf("  ");if (MG[i][j] == 2)printf("◆");}printf("\n");}
}

3.自己控制走迷宫。

主要还是按键的一般思想吧,相较于贪吃蛇的移动,只有一些判断条件的改变。上下左右对应的ASCII码值和贪吃蛇一样的都是72,80,75,77。还是运用到了getch函数和kbhit函数。
判断条件的话,我们可以想一下,如果你要向右走,那么意味着右边是不能有墙的,同理其余三个方向也是。如果向右走的话,那么在数组之中其实就是行数不变,列数加一。所以在判断了向右边走是可以的之后,就可以把原来所在数组位置的值赋为路的值,然后横坐标不变,纵坐标加一的数组位置的值,赋值为人的值,然后再次打印出来就好了。具体的话我们还需要确定现在人在数组之中的位置,直到这个的话我们才可以继续判断移动是否可行。还有一个是否达到终点的判断,就是现在人所在的这个纵横坐标是否等于终点的纵横坐标。
代码如下:

void move(int a, int b)
{int c=0, d=0;while (c!=zx||d!=zy){nowxy(a, b);c = nowx;d = nowy;/*接受键盘输入的上下左右,并以此改变方向*/if (_kbhit())//kbhit()检测是否有按键按下,有按下返回键值, {ch = _getch();switch (ch){case 72://迷宫中人向上行走。if (MG[c - 1][d] == 0|| MG[c - 1][d] == 3){MG[c][d] = 0;MG[c - 1][d] = 2;}showing(a, b);break;case 80://迷宫中人向下行走。if (MG[c + 1][d] == 0|| MG[c + 1][d] == 3){MG[c][d] = 0;MG[c + 1][d] = 2;}showing(a, b);break;case 75://迷宫中人向左行走。if (MG[c][d-1] == 0||MG[c][d - 1] == 3){MG[c][d] = 0;MG[c][d - 1] = 2;}showing(a, b);break;case 77://迷宫中人向右行走。if (MG[c][d + 1] == 0||MG[c][d + 1] == 3){MG[c][d] = 0;MG[c][d + 1] = 2;}showing(a, b);break;case 'r' :initMG(a, b);break;          case 'R':initMG(a, b);break;}}}

上面的nowxy是判断现在所处的位置,具体代码在下面:

void nowxy(int a, int b)
{for(int i=0;i<a;i++)for(int j=0;j<b;j++)if (MG[i][j] == 2){nowx = i;nowy = j;}
}

4.A*算法找出最短路径。

生成了迷宫,也可以实现自己走迷宫了,我们还需要判断一下迷宫的最短路径,也就是寻路的算法。寻路算法其实是多种多样的,但其中最为快速的就是A算法了,它的用时和所占内存运行时间都是最优选择,在理论上是时间最优的,但是也有缺点:它的空间增长是指数级别的。所以我们着重讲解一下A算法的具体实现。
A算法的核心思想是F值来实现最短优先,这个F值是由两个值为指标相加构成的。
F=G+H就是A
算法的核心,那么这几个字母都代表着什么意思呢?

  • G = 从起点A,沿着产生的路径,移动到网格上指定方格的移动耗费。

  • H = 从网格上那个方格移动到终点B的预估移动耗费。
    对于G值的计算方法,我们所想到的就是先赋值一个0,然后从起点开始,每一次进行移动的操作就会增加1.
    对于H值计算的方法,我们一般采用曼哈顿距离,简单来说,就是现在你所处的这个点和终点的横纵坐标差的绝对值的和。
    有了这些还是不够,我们要怎么去比较呢?这个时候就需要用到优先队列的知识了,并且重载运算符,来让优先队列执行判断。
    思路具体就是这样的,那些必要的算法基础还是要靠学习算法和优先队列相关知识才行的。

void A_start(int x0, int y0, int x1, int y1, int a, int b)//四个参数分别为起点和终点的横坐标,纵坐标 {// 初始化起点 Node node(x0, y0);node.G = 0;node.H = Hdistance(x0, y0, x1, y1);node.F = node.G + node.H;valF[x0][y0] = node.F;que.push(node); // 起点加入open表while (!que.empty()){Node node_top = que.top();que.pop();visit[node_top.x][node_top.y] = true; // 访问该点,加入closed表 if (node_top.x == x1 && node_top.y == y1) // 到达终点 break;// 遍历node_top周围的4个位置 for (int i = 0; i < 4; i++){Node node_next(node_top.x + dir[i][0], node_top.y + dir[i][1]); // 创建一个node_top周围的节点 if (NodeIsLegal(node_next.x, node_next.y, a, b) && !visit[node_next.x][node_next.y]) // 该节点坐标合法,且未加入close表{// 计算从起点并经过node_top节点到达该节点所花费的代价 node_next.G = node_top.G + dir[i][0], 2 + dir[i][1], 2;// 计算该节点到终点的曼哈顿距离node_next.H = Hdistance(node_next.x, node_next.y, x1, y1);// 从起点经过node_top和该节点到达终点的估计代价node_next.F = node_next.G + node_next.H;// 说明找到了更优的路径,则进行更新 node_next.F < valF[node_next.x][node_next.y];// 说明该节点还未加入open表中,则加入  valF[node_next.x][node_next.y] == 0; if (node_next.F < valF[node_next.x][node_next.y] || valF[node_next.x][node_next.y] == 0){// 保存该节点的父节点 path[node_next.x][node_next.y][0] = node_top.x;//根据path三维数组来确定最短优先路径在二维数组的具体位置。path[node_next.x][node_next.y][1] = node_top.y;valF[node_next.x][node_next.y] = node_next.F; // 修改该节点对应的valF值 que.push(node_next); // 加入open表}}}}}
int Hdistance(int x, int y, int x1, int y1)
{return (abs(x - x1) + abs(y - y1));
}
bool NodeIsLegal(int x, int y, int m, int n)
{if (x < 0 || x >= m || y < 0 || y >= n) return false; // 判断边界 if (MG[x][y] == 1) return false; // 判断障碍物 return true;
}

总结

核心的思想和算法就讲解这么多了,具体实现的话还是要有交互界面,或者初始化函数的。关键的地图生成和最短路径的查找最需要的还是算法知识,这些都是计算机最为基础的东西,努力学习算法吧,原与君共勉!

计算机软件技术实习——迷宫算法核心相关推荐

  1. 计算机软件技术实习02

    计算机软件技术实习 第二周学习记录 实验1--支持算术表达式求解的计算器 前言 上周说到,我选择用MFC来完成本次实验.那么,现在我来总结一下本周的学习成果. 退格功能的实现 由于我设置的文本框是通过 ...

  2. 计算机软件技术实习预习日志

    计算机软件技术实习预习日志 目录 计算机软件技术实习预习日志 文章目录 实验项目 一.实验要求 二.开发工具 三.实验原理 1.后缀表达式计算法 前缀表达式 中缀表达式 后缀表达式 中缀表达式转换为后 ...

  3. 计算机软件技术实习 项目一 简单计算器的实现(实验准备)1-(1)

    目录 一.需求分析 二.重难点 三.编程语言 四.开发工具/平台 一.需求分析 1. 能通过设计的按钮控件输入并实现算术表达式,表达式在文本框中显示,运算结果输出显示: 2.保存和浏览历史运算记录: ...

  4. 计算机软件技术实习01

    计算机软件技术实习 第一周学习记录 实验1--支持算术表达式求解的计算器 前言 这是我第一次真正接触到计算机软件技术的实战内容,说实话我觉得这次的实习任务对我来说非常具有挑战性,有很多知识都是我目前还 ...

  5. 计算机软件技术实习项目二(一) 贪吃蛇项目准备

    计算机软件技术实习项目二(一) 贪吃蛇项目准备 文章目录 计算机软件技术实习项目二(一) 贪吃蛇项目准备 一.贪吃蛇介绍 二.开发工具 三.实现原理 1.消息与消息队列 2.消息映射 3.设备环境 4 ...

  6. 22-23学年计算机软件技术实习1——计算器

    目录 学习目标 1.1准备工作 1.1.1搭建QT开发环境 1.2用户界面UI设计 1.2.1显示模块 1.2.2按键模块 1.3功能实现 1.3.1输入输出反馈 1.3.2退格与清除功能 1.3.3 ...

  7. 计算机软件技术实习实验一

    实验内容: (1) 学习图形界面的设计,创建基于对话框的应用程序,添加按钮.编辑框等控件: (2) 能通过设计的按钮控件输入并实现简单算术运算,要求表达式在编辑框中显示, 能将运算结果,输出在编辑框内 ...

  8. 计算机软件技术实习 迷宫游戏(一)

    主要内容 1.迷宫游戏是非常经典的游戏,在该题中要求随机生成一个迷宫,并求解迷宫. 2.要求查找并理解迷宫生成的算法,并尝试用两种不同的算法来生成随机的迷宫. 3.要求迷宫游戏支持玩家走迷宫,和系统走 ...

  9. 计算机软件技术实习:计算器(1)

    1.1 所用知识点 Java Swing 界面编程 计算器逻辑运算实现 1.2 基本思路 (1)设置计算器显示窗口 (2)数学运算基本法则:先乘除后加减 (a)正确处理输入字符串判断后进行一一计算 ( ...

  10. 计算机软件技术研究,计算机软件技术研究论文.docx

    计算机软件技术研究论文 < 高职软件技术专业分段式技能大赛人才培养思考 > [摘要]将分段式技能大赛作为集中式技能大赛的补充拓展和优化组合,既对专业教学大有裨益,又可有效促进"教 ...

最新文章

  1. PyTorch 版 EfficientDet 比官方 TF 实现快 25 倍?这个 GitHub 项目数天狂揽千星
  2. Remote Direct Memory Access (RDMA)
  3. 直播 | DPDK中国技术峰会2017
  4. 重新理解@Resource注解
  5. vmwaretools安装
  6. 传统服务器性能不足 解决方案,云平台和传统服务器的优劣
  7. 《PostgreSQL 指南:内幕探索》之基础备份与时间点恢复(下)
  8. LED流水灯程序——小白的单片机笔记
  9. VBA:单元格的引用
  10. sql语句中----删除表数据的三兄弟
  11. oracle autovue是什么软件,AutoVue
  12. 【ACCV2022】论文阅读笔记Lightweight Alpha Matting Network Using Distillation-Based Channel Pruning
  13. HDU - 4489 The King’s Ups and Downs (排列组合+dp)
  14. 你会换掉Postman吗?我正在用HTTP Client...
  15. 从细胞发现到DNA分子结构的发现,人类经历了三百年
  16. 【压缩感知合集5】压缩感知简介和数学模型分析
  17. C++ --取出网址中域名和协议名
  18. svm对未知数据的分类_【干货分享】支持向量机学习「下」利用SVM对歌曲进行分类...
  19. html个人网页步骤,个人网页制作的流程和步骤
  20. 城市连动纯js代码DEMO

热门文章

  1. 极域教室管理软件全屏广播窗口化,解除网络限制,阻止老师监控进程,阻止黑屏安静,强制杀死极域进程等
  2. 虚拟机安装程序没有找到安装在此计算机上的硬盘驱动器,安装VMware提示无效驱动器:E:\ 解决方法...
  3. 随机森林回归简单示例
  4. jenkins中配置git下载代码
  5. 12个常见idea快捷键
  6. 三代测序组装工具Canu学习笔记
  7. TA入门笔记(十五)
  8. 微波雷达感应技术,在民用领域的发展应用,低功耗雷达传感模块
  9. 浅谈分子动力学(MD)模拟及其势文件
  10. 软件测试场景 例子,软件测试基础(六)用例设计方法之场景法