项目概述
项目目标和主要内容
迷宫游戏是非常经典的游戏,在该题中要求随机生成一个迷宫并且求解迷宫。综合考察了包括绘制图形界面、布尔型数组的使用以及最小生成树算法和A*或深度优先算法等在内的多方面知识的应用。
项目的主要功能
要求游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘方向键控制,并在行走路径上留下痕迹;系统走迷宫路径要求基于A*算法实现,输出走迷宫的最优路径并显示。
项目设计
项目总体框架
- 用Java语言实现游戏编程,用Prim算法生成迷宫。
- 对于玩家走迷宫方式,选择键盘监听来实现用户通过键盘来控制坐标的移动,并用生成路径填色的方式来描述玩家足迹。
- 对于系统走迷宫方式,用深度优先搜索找到离开迷宫的路线,并用生成路径填色的方法来达到敲击空格键显示迷宫答案或隐藏的效果,在JPanel画迷宫,然后挂载到JFrame。
- Java的GUI图形界面比较容易实现,但因为需要经常更新觉得在迷宫的位置,画面会有卡顿显现。JPanel( )采用双缓冲和流布局的新JPanel,因此用JPanel就可以解决GUI更新问题。
类关系及描述
PMap类:随机Prim算法实现用于生成迷宫
生成随机迷宫方法:public boolean[][] prim(int startX,int startY,int widthLimit,int heightLimit,boolean haveBorder)
EMap类:DSF算法实现用于求解离开迷宫路线
DFS算法实现方法:private void dfs(int x,int y,int c)
离开迷宫实现方法:public ArrayList<Integer> exitmap()
判断边界方法:private boolean ise(int dx,int dy)
PaintMap类:绘制迷宫,实现角色移动更新
画迷宫方法:public void paint(Graphics g)
向上移动方法:public void moveUp()
向下移动方法:public void moveDown()
向左移动方法:public void moveLeft()
向右移动方法:public void moveRight()
空格按下显示路线方法:public void PressSp()
判断边界方法:private boolean IsEdge(int x,int y)
判断胜利方法:private void Win(int x,int y)
Text类:主函数类
键盘监听回调方法:public void keyPressed(KeyEvent key)
关键算法分析
算法 1:随机Prim算法
算法功能
用于实现随机生成迷宫,随机生成的迷宫是指迷宫的起点和终点是固定的,但内部结构是随机的,并且整个迷宫只有一条路径可以做到从起点到达终点。
算法基本思想
- 让迷宫全都是墙。
- 选一个格,作为迷宫的通路,然后把它的邻墙放入列表。
- 当列表里还有墙时,从列表里随机选一个墙:如果它对面的格子不是迷宫的通路,把墙打通,让对面的格子成为迷宫的通路,并把那个格子的邻墙加入列表;如果对面的格子已经是通路了,那就从列表里移除这面墙
算法 2:深度优先算法
- 算法功能
- 用于实现系统走迷宫功能,生成能够从起点到达重点的路径并将路径返回。
- 算法基本思想
- 从一个点向四周开始搜索,选择任意一个方向向下搜索:如果是通路,就继续选择任意一个方向向下搜索;如果遇到墙返回上一父节点,选择其他方向搜索,不断递归,并对已经搜索过的方向进行标注以免重复搜索,同时删除列表中已被证明不可达的路径。
- 直到产生一条能够到达终点的路径,酒从终点不断追溯之前父节点,直到遇到起点,生成路径,加入到列表中
最坏情况需要遍历搜索所有块。最好情况一次就能找到通路
算法 3:A*算法
算法功能
用于实现系统走迷宫功能,生成能够从起点到达重点的路径并将路径返回。
算法基本思想
首先说明一下,A*算法中包含三个重要的参数: G表示从起点到当前顶点n的实际距离;H表示当前顶点n到目标顶点的估算距离(根据所采用的评估函数的不同而变化);F = G + H,代表了该节点的综合预估值,值越小,到达目标的成本就越小,所以访问的时候尽量优先考虑最小的。
下面是路径搜索的具体过程:
首先需要创建两个集合,一个存储待访问的节点(openlist),一个存储已经访问过的节点(closelist)
添加起点openlist列表,并且计算该点的预估值。
查找openlist里预估值最小的节点,作为当前访问的节点,并且从openlist删除该节点。
获取当前节点的邻居节点,计算出他们的预估值 并且添加到openlist列表中。
把当前节点添加到closelist中,代表已经访问过了。
重复以上步骤,直到找到目标节点位置为止。
循环输出最终节点的父节点,就是我们需要的路径了。
A*算法会去尽可能遍历可通行方格,并计算F值,直到找到终点
其他
本程序迷宫使用到了继承、接口、ArrayList等方面的知识,提高了代码的简洁度和使用效率。
继承达到了方便修改、减少代码量的作用,用extends实现,例如public class PMap extends JPanel。
接口解决了类不能多重继承的问题,用implements来定义,public class Test extends JFrame implements ActionListener,KeyListener。
ArrayList是集合的一种实现,实现了接口List,List接口继承了Collection接口。ArrayList有很多常用方法,add,addAll,set,get,remove,size,isEmpty等。
程序运行结果分析
实现了通过Prim算法生成随机的迷宫。
实现了两种走迷宫方式:玩家走迷宫,通过键盘方向键控制,并在行走路径上留下痕迹;系统走迷宫要求基于A*算法实现,输出走迷宫的最优路径并显示。
项目的难点和关键点
其次对于项目的关键点,一是在于使用Prim算法随机生成迷宫,并将生成的一个迷宫通过布尔型数组返回;二是通过深度优先算法为之前随机生成的迷宫求解出一条迷宫通路,并返回路径来进行后续的描绘路径。