实验要求
1.要求随机生成一个迷宫,并求解迷宫
2.要求游戏支持玩家走迷官,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘方向键控制,并在行走路径上留下痕迹;系统走迷宫珞径要求基于A*算法实现、输出走迷宫都最优路径显示。
设计过程分析
- 开始界面
这里是用Java中的JFrame添加了背景和按钮,如何设置按钮监听器即可进入下一个界面。主要代码:
设计背景:
//不采用任何布局方式
ct=this.getContentPane();
this.setLayout(null);
//设置背景
bgp=new GamePanel((new ImageIcon("src/images/bj3.png")).getImage());
bgp.setBounds(0,0,600,500);
ct.add(bgp);
创建按钮:
//创建按钮
jb=new JButton("开始游戏");
jb.setBounds(220,180,100,80);
jb.setForeground(Color.BLACK);
jb.setBackground(Color.lightGray);
jb.setBorder(null);
ct.add(jb);
jb.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
close();
new GameStart();
}
});
2.进入游戏界面,这里我设计了四个按钮选择迷宫模式有:简单、中等、困难和自定义模式,自定义输入行数和列数即可。如图:
private JButton button1 = new JButton("简单");
private JButton button2 = new JButton("中等");
private JButton button3 = new JButton("困难");
private JButton button4 = new JButton("自定义");
//将选择按钮包含在选择面板上
JPanel choose = new JPanel();
choose.setLayout(new GridLayout(2, 2));
choose.add(button1);
choose.add(button2);
choose.add(button3);
choose.add(button4);
button1.setBackground(Color.CYAN);
button2.setBackground(Color.ORANGE);
button3.setBackground(Color.red);
button4.setBackground(Color.PINK);
设计监听器进入游戏界面:
//设计监听器
button1.addActionListener(this);
button2.addActionListener(this);
button3.addActionListener(this);
button4.addActionListener(this);
3.游戏界面设置菜单。
菜单有游戏,编辑和帮助。游戏里设置了开始游戏,返回和退出选项。点击开始游戏玩家即可自己操纵鼠标移动小鸟,返回可返回上一个界面重新选择游戏难度,退出即退出游戏。编辑设有编辑当前迷宫和随机生成迷宫,玩家选择编辑当前迷宫即可在迷宫中把墙打通,随机生成迷宫可生成新的迷宫。帮助里设置了选择最优路径,系统自动生成最优路径,玩家不需要操纵。代码如下:
// 菜单项
private JMenuItem m_start = new JMenuItem("开始游戏");
private JMenuItem m_return = new JMenuItem("返回菜单");
private JMenuItem m_exit = new JMenuItem("退出游戏");
private JMenuItem m_selfconfig = new JMenuItem("编辑当前迷宫");
private JMenuItem m_randommake = new JMenuItem("随机生成迷宫");
private JMenuItem m_sortpath = new JMenuItem("显示最短路径");
//菜单
JMenu game = new JMenu("游戏");
JMenu edit = new JMenu("编辑");
JMenu tip = new JMenu("帮助");
game.add(m_start);
game.add(m_return);
game.add(m_exit);
edit.add(m_selfconfig);
edit.add(m_randommake);
tip.add(m_sortpath);
//菜单栏
JMenuBar menu = new JMenuBar();
menu.add(game);
menu.add(edit);
menu.add(tip);
设置菜单监听器,点击按钮实现相应的功能。
3.初始化迷宫组件,生成迷宫。迷宫设置为小鸟找食物。
//初始化迷宫组件,并生成随机路径
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++) {
tp[i][j] = new Maze();
}
Operations.creatMaze(); //深度优先遍历生成至少有一条随机通道的迷宫
//迷宫地图
JPanel mazePane = new JPanel();
mazePane.setLayout(new GridLayout(m, n, 0, 0));
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
mazePane.add(tp[i][j]);
}
}
4.生成迷宫,从某一点开始深度优先遍历与它x 、y坐标相差均为偶数的点构成的图,并打通每一步需要通过的墙。算法思想: 将起点作为当前迷宫单元并标记为已访问,当还存在未标记的迷宫单元,进行循环 。
1).如果当前迷宫单元有未被访问过的的相邻的迷宫单元
(1).随机选择一个未访问的相邻迷宫单元
(2).将当前迷宫单元入栈
(3).移除当前迷宫单元与相邻迷宫单元的墙
(4).标记相邻迷宫单元并用它作为当前迷宫单元
2).如果当前迷宫单元不存在未访问的相邻迷宫单元,并且栈不空
(1).栈顶的迷宫单元出栈
(2).令其成为当前迷宫单元
这个算法叫做“深度优先”,简单来说,就是从起点开始走,寻找它的上下左右4个邻居,然后随机一个走,到走不通的时候就返回上一步继续走,直到全部单元都走完。
部分代码如下:
//深度优先遍历生成至少有一条随机通道的迷宫
public static void creatMaze() {
m = map.m;
n = map.n;
//遍历前初始化工作
isBeVisit = new boolean[m * n];
for (int i = 0; i < m * n; i++) isBeVisit[i] = false; //是否已被访问
//迷宫初始化
for (int i = 0; i < m; i++) { //防止发生两边上全为墙的情况
map.tp[i][0].change(Math.random() * 3 > 1 ? 0 : 1);
map.tp[i][n - 1].change(Math.random() * 3 > 1 ? 0 : 1);
}
for (int i = 0; i < n; i++) {
map.tp[0][i].change(Math.random() * 3 > 1 ? 0 : 1);
map.tp[m - 1][i].change(Math.random() * 3 > 1 ? 0 : 1);
}
for (int i = 1; i < m - 1; i++)
for (int j = 1; j < n - 1; j++)
map.tp[i][j].change(0); //内部的位置初始化全为墙
m_startx = (int) (Math.random() * m / 2);
m_starty = (int) (Math.random() * n / 2); //随机生成小鸟位置
//从小鸟位置开始深度优先遍历与它x 、y坐标相差均为偶数的点构成的图
DFS(m_startx * n + m_starty);
//这一步在 tp[m-2][n-2]与小鸟位置x 、y坐标相差均为偶数时非常重要,保证有到达食物的路径
if (Math.random() * 2 > 1)
map.tp[m - 2][n - 1].change(1);
else
map.tp[m - 1][n - 2].change(1); //两者只要有一个为路即可,故随机取其一
//小鸟和食物的位置作另作处理
map.tp[m_startx][m_starty].change(2); //小鸟
map.tp[m - 1][n - 1].change(3); //食物
changeable_key = false; //键盘不可控制小鸟移动
m_currex = m_startx;
m_currey = m_starty; //开始新游戏前小鸟当前位置与开始位置相等
restart = false;
}
5.最优路径选择,部分代码:
//利用已生成的路径深度图,找到从小鸟和食物之间的最短路径
int[] path = new int[m * n]; //用于存放最短路径的数组
int currePoint = m_startx * n + m_starty; //当前访问点,初始值为小鸟位置
int depth = depthGraph[currePoint]; //小鸟位置的路径深度值
int step = depth - 1; //当前要查找的路径深度
while (step > 0) {
currex = currePoint / n;
currey = currePoint % n;
if (currey + 1 < n && depthGraph[currePoint + 1] == step) {
currePoint += 1;
} else if (currex + 1 < m && depthGraph[currePoint + n] == step) {
currePoint += n;
} else if (currey - 1 >= 0 && depthGraph[currePoint - 1] == step) {
currePoint -= 1;
} else if (currex - 1 >= 0 && depthGraph[currePoint - n] == step) {
currePoint -= n;
}
path[step--] = currePoint;
}
int s; //临时存放位置
for (int i = 1; i < depth; i++) {
s = path[i];
map.tp[s / n][s % n].change(2); //显示最短路径
}
restart = true; //可开始新游戏
}
6.迷宫生成完成,如图所示:
开始游戏界面:
系统推荐最优路径界面
玩家走迷宫显示路径界面:
7.2021/12/16
个人小结:在做这个项目的过程中,首先为了美观想把按钮设成透明,在参考了好多网上的资料也没成功就放弃了。其次在生成迷宫的过程中,刚开始在设置和生成随机数时出现了不少问题,比如随机数会一直生成等,在多次测试后,总算生成成功了。然后在实现最优路径的选择时,也把我难到了,在参考老师PPT上的A*算法的那个网站才写出来了。最后,这个迷宫也有许多不足之处,比如功能不够完善,还没有显示小鸟走过的路径,我本来考虑加一个定时器,每走一步刷新界面,保留上一个页面,可能是我设置的方式不对吧,它还是没有显示,我打算在考虑一下别的思路。然后在添加一些别的功能,比如设置时间限制和背景音乐等。
2021/12/17
经过更改和调试,玩家走迷宫的走过的路径也显示了。如上图所示。