软件实习项目三 —— Java实现基于A*算法的迷宫游戏

一、实验任务

(1)该题要求随机生成一个迷宫,并求解迷宫;

(2)要求游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘方向键控制,并在行走路径上留下痕迹;系统走迷宫路径要求基于A*算法实现,输出走迷宫的最优路径并显示。

(3)设计交互友好的游戏图形界面。

二、实验准备

(1)具体编程语言:JAVA

(2)确定所用图形界面:Java Swing

(3)算法思想:递归分割法来随机生成迷宫,A*算法来自动寻路

三、设计思路

(1)如何生成迷宫

1.利用Java Swing的相关函数,进行绘画,以砖块为墙,豆豆人为角色,脚印为走过的路的痕迹,空白部分为路来画迷宫。

2.递归分割法的思想:
①开始创建迷宫,使整个空间没有壁,我们称之为“室”。
②在随机位置生成壁将室分割为两个子室,并在壁上随机开孔,使子室联通。
③重复步骤②,直到所有子室全部不可分割(即子室某一个维度等于1)。

(2)控制角色移动

利用Java函数addKeyListener来监听按键⬆️⬇️⬅️➡️的输入,只有当角色周围是路,角色才能继续移动。当角色到达终点时就宣布游戏结束。

(3)基于A*算法的自动寻路

A*算法的主要思想为:
①先判断目标能移动一格的位置,如果只能移动到之前的位置执行④。
②然后再将每一个与终点的距离进行比较。
③移动到距离最近的那一格上,重复①~③。
④返回上一步的位置。

四、功能实现

StartUI类:程序的启动界面,设置界面的布局及按钮,并提供玩家用户选择场地地图大小,以选择游戏难度。

public class StartUI extends JFrame implements ActionListener {private JButton button1 = new JButton("24 X 24");private JButton button2 = new JButton("35 X 35");private JButton button3 = new JButton("46 X 46");private JButton button4 = new JButton("?");StartUI() {//将选择按钮包含在选择面板上JPanel choose = new JPanel();choose.setLayout(new GridLayout(2, 2));choose.add(button1);choose.add(button2);choose.add(button3);choose.add(button4);//注册侦听器button1.addActionListener(this);button2.addActionListener(this);button3.addActionListener(this);button4.addActionListener(this);//提示信息JPanel message = new JPanel() {   //匿名内部类protected void paintComponent(Graphics g) {setSize(200, 300);g.drawString("请选择地图大小", 100, 35);}};//主界面布局setLayout(new BorderLayout(120, 40));add(choose, BorderLayout.CENTER);add(message, BorderLayout.NORTH);add(new JPanel(), BorderLayout.EAST);add(new JPanel(), BorderLayout.WEST);add(new JPanel(), BorderLayout.SOUTH);//基本设置setTitle("豆豆人找朋友");setSize(800, 600);setLocationRelativeTo(null);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setVisible(true);setResizable(false);}

Map类:生成地图界面,地图框架的设计,包括菜单栏、剩余时间显示等,并为各组件、按钮、键盘、事件注册监听器,再初始化地图组件,随机生成迷宫。创建一个时间控制的线程和进行控制时间的方法。

public class Map extends JFrame implements ActionListener, KeyListener, Runnable {static int m, n;static Paint[][] tp = null;        //显示动画,同一包内类均可访问//时间限制static Thread timeThread;                //时间控制线程static int timelimit, remaintime;static JPanel timePanel = new JPanel() {     //剩余时间显示面板public void paintComponent(Graphics g) {super.paintComponent(g);String rt;if (timelimit == 0) {rt = "无敌版";setForeground(Color.GREEN);    //绿色表示无时间限制} else {rt = remaintime / 3600 + " : " + (remaintime - (remaintime / 3600) * 3600) / 60 + " : " + remaintime % 60;if (remaintime > 10)setForeground(Color.BLUE);      //剩余时间充足时为蓝色elsesetForeground(Color.RED);      //剩余时间很少时为红色}g.drawString("剩余时间:  " + rt, 220, 16);}};// 菜单项private JMenuItem m_start = new JMenuItem("开始新游戏(S)");private JMenuItem m_time = new JMenuItem("游戏时间限制(L)");private JMenuItem m_return = new JMenuItem("返回主界面(R)");private JMenuItem m_exit = new JMenuItem("退出游戏(Q)");private JMenuItem m_selfconfig = new JMenuItem("编辑当前地图(E)");private JMenuItem m_randommake = new JMenuItem("随机生成地图(Z)");private JMenuItem m_sortpath = new JMenuItem("显示最短路径(T)");Map(int x, int y) {m = x;n = y;tp = new Paint[m][n];timelimit = remaintime = 0;                //初始化时,时间为0,代表没有时间限制timeThread = new Thread(this);timeThread.start();timeThread.checkAccess();//初始化地图组件,并生成随机路径for (int i = 0; i < m; i++)for (int j = 0; j < n; j++) {tp[i][j] = new Paint();}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]);}}

Paint类:显示墙、路、玩家角色、终点、路径的面板。创建标志flag并为其赋值,不同的值画出的图形类别不同,使Operations类根据其需求转换flag的值,以画出墙、路和路径。

public class Paint extends JPanel implements MouseListener {private boolean changeable_click;//标志 0:墙 1:路 2:豆豆人 3:小黄豆 4:脚印private int flag;private Image wall = new ImageIcon("/Users/xiaoying/Desktop/IMG_8697.jpg").getImage();        //墙private Image road = new ImageIcon("/Users/xiaoying/Desktop/IMG_8689.jpg").getImage();        //路private Image role = new ImageIcon("/Users/xiaoying/Desktop/IMG_8665.PNG").getImage();        //豆豆人private Image bean = new ImageIcon("/Users/xiaoying/Desktop/IMG_8663.PNG").getImage();        //小黄豆private Image step = new ImageIcon("/Users/xiaoying/Desktop/IMG_8701.jpg").getImage();        //脚印Paint(int f) {flag = f;changeable_click = false;    //初始化时不能通过鼠标点击改变 flag 的值addMouseListener(this);}Paint() {this(0);}//重写paintComponent方法,画图public void paintComponent(Graphics g) {super.paintComponent(g);if (flag == 0)g.drawImage(wall, 0, 0, getWidth(), getHeight(), this);else if (flag == 1)g.drawImage(road, 0, 0, getWidth(), getHeight(), this);else if (flag == 2)g.drawImage(role, 0, 0, getWidth(), getHeight(), this);else if (flag == 3)g.drawImage(bean, 0, 0, getWidth(), getHeight(), this);elseg.drawImage(step, 0, 0, getWidth(), getHeight(), this);}

Operations类:自定义随机生成地图的数据,深度算法遍历DFS生成至少有一条随机路径的迷宫地图,使用A*算法搜寻并显示最短路径。设置开始游戏时时间的控制,玩家角色的移动,走过路径的生成,也可以提示用户玩家最佳路径。

public class Operations{static int m, n;   //用于拷贝map.m和map.n的值static int m_currex, m_currey;   //豆豆人当前位置static int m_startx, m_starty;   //豆豆人开始位置static boolean changeable_key = true;     //可用键盘控制豆豆人移动static boolean restart = false;private static boolean[] isBeVisit = null;//生成随机地图专用数据//迷宫地图最短路径深度图算法public static void findPath() {Map.timeThread.checkAccess();   ;    //时间控制线程休眠changeable_key = true;   //可用键盘控制豆豆人setEditable(false);       //不可编辑m = Map.m;n = Map.n;int max = m * n;                 //任意一点到小黄豆的最短路径长度不会超出m*n。int[] depthGraph = new int[m * n];     //路径深度图//路径深度图初始化depthGraph[m * n - 1] = 0;                //小黄豆到自己的距离自然是0for (int i = 0; i < m * n - 1; i++) {if (Map.tp[i / n][i % n].isWall())depthGraph[i] = -1;           //墙表示为-1,表示无通路elsedepthGraph[i] = max;           //未确定距离时已max表示}boolean flag = true;             //循环过程中是否有某点的路径深度被修改int currex, currey;               //记录当前访问点的坐标int aroundmin;                   //周围可行方向的最小路径深度 + 1//动态更新路径深度图直至其达到稳态(即最后一次循环过程中不再有路径深度被修改)while (flag) {flag = false;for (int s = m * n - 1; s >= 0; s--) {if (depthGraph[s] != -1) {aroundmin = depthGraph[s];currex = s / n;currey = s % n;if (currey + 1 < n && depthGraph[s + 1] != -1 && depthGraph[s + 1] + 1 < aroundmin)aroundmin = depthGraph[s + 1] + 1;if (currex + 1 < m && depthGraph[s + n] != -1 && depthGraph[s + n] + 1 < aroundmin)aroundmin = depthGraph[s + n] + 1;if (currey - 1 >= 0 && depthGraph[s - 1] != -1 && depthGraph[s - 1] + 1 < aroundmin)aroundmin = depthGraph[s - 1] + 1;if (currex - 1 >= 0 && depthGraph[s - n] != -1 && depthGraph[s - n] + 1 < aroundmin)aroundmin = depthGraph[s - n] + 1;if (aroundmin < depthGraph[s]) {depthGraph[s] = aroundmin;flag = true;}}}}//利用已生成的路径深度图,找到从豆豆人到小黄豆之间的最短路径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;                //可开始新游戏}//深度优先遍历生成至少有一条随机通道的迷宫地图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);elseMap.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 = true;  //键盘可控制豆豆人移动m_currex = m_startx;m_currey = m_starty;  //开始新游戏前豆豆人当前位置与开始位置相等restart = false;}//从S点开始深度优先遍历与它x 、y坐标相差均为偶数的点构成的图,并打通每一步需要通过的墙public static void DFS(int s) {Map.tp[s / n][s % n].change(1);isBeVisit[s] = true;

五、成果展示

迷宫游戏开始界面

迷宫游戏界面+角色走过的路径显示

提供显示最佳路径

迷宫通关界面

源代码如下:

package Maze;
//程序开始界面import java.awt.*;
import java.awt.event.*;import javax.swing.*;@SuppressWarnings("serial")
public class StartUI extends JFrame implements ActionListener {private JButton button1 = new JButton("24 X 24");private JButton button2 = new JButton("35 X 35");private JButton button3 = new JButton("46 X 46");private JButton button4 = new JButton("?");StartUI() {//将选择按钮包含在选择面板上JPanel choose = new JPanel();choose.setLayout(new GridLayout(2, 2));choose.add(button1);choose.add(button2);choose.add(button3);choose.add(button4);//注册侦听器button1.addActionListener(this);button2.addActionListener(this);button3.addActionListener(this);button4.addActionListener(this);//提示信息JPanel message = new JPanel() {   //匿名内部类protected void paintComponent(Graphics g) {setSize(200, 300);g.drawString("请选择地图大小", 100, 35);}};//主界面布局setLayout(new BorderLayout(120, 40));add(choose, BorderLayout.CENTER);add(message, BorderLayout.NORTH);add(new JPanel(), BorderLayout.EAST);add(new JPanel(), BorderLayout.WEST);add(new JPanel(), BorderLayout.SOUTH);//基本设置setTitle("豆豆人找朋友");setSize(800, 600);setLocationRelativeTo(null);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setVisible(true);setResizable(false);}public static void main(String[] args) {new StartUI();}//按钮事件处理public void actionPerformed(ActionEvent e) {if (e.getSource() == button1) {dispose();new Map(18, 18);} else if (e.getSource() == button2) {dispose();new Map(25, 25);} else if (e.getSource() == button3) {dispose();new Map(40, 40);} else {getData();}}public void getData() {int m = 0, n = 0;String crowString;try {crowString = JOptionPane.showInputDialog("请输入自定义的行数(>5)");m = Integer.parseInt(crowString);crowString = JOptionPane.showInputDialog("请输入自定义的列数(>5)");n = Integer.parseInt(crowString);if (m <= 5 || n <= 5) throw new Exception();else {dispose();  //setVisible(false);new Map(m, n);}} catch (Exception e) {JOptionPane.showMessageDialog(null, "由于用户取消或输入不符合要求等原因,未正常创建地图。","未创建地图!", JOptionPane.ERROR_MESSAGE);}}}```java
package Maze;//地图界面import java.awt.*;
import java.awt.event.*;import javax.swing.*;@SuppressWarnings("serial")
public class Map extends JFrame implements ActionListener, KeyListener, Runnable {static int m, n;static Paint[][] tp = null;        //显示动画,同一包内类均可访问//时间限制static Thread timeThread;                //时间控制线程static int timelimit, remaintime;static JPanel timePanel = new JPanel() {     //剩余时间显示面板public void paintComponent(Graphics g) {super.paintComponent(g);String rt;if (timelimit == 0) {rt = "无敌版";setForeground(Color.GREEN);    //绿色表示无时间限制} else {rt = remaintime / 3600 + " : " + (remaintime - (remaintime / 3600) * 3600) / 60 + " : " + remaintime % 60;if (remaintime > 10)setForeground(Color.BLUE);      //剩余时间充足时为蓝色elsesetForeground(Color.RED);      //剩余时间很少时为红色}g.drawString("剩余时间:  " + rt, 220, 16);}};// 菜单项private JMenuItem m_start = new JMenuItem("开始新游戏(S)");private JMenuItem m_time = new JMenuItem("游戏时间限制(L)");private JMenuItem m_return = new JMenuItem("返回主界面(R)");private JMenuItem m_exit = new JMenuItem("退出游戏(Q)");private JMenuItem m_selfconfig = new JMenuItem("编辑当前地图(E)");private JMenuItem m_randommake = new JMenuItem("随机生成地图(Z)");private JMenuItem m_sortpath = new JMenuItem("显示最短路径(T)");Map(int x, int y) {m = x;n = y;tp = new Paint[m][n];timelimit = remaintime = 0;                //初始化时,时间为0,代表没有时间限制timeThread = new Thread(this);timeThread.start();timeThread.checkAccess();//菜单JMenu game = new JMenu("游戏");JMenu edit = new JMenu("编辑");JMenu tip = new JMenu("提示");game.add(m_start);game.add(m_time);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);//初始化地图组件,并生成随机路径for (int i = 0; i < m; i++)for (int j = 0; j < n; j++) {tp[i][j] = new Paint();}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]);}}//菜单和时间显示放在同一面板上JPanel northPanel = new JPanel();northPanel.setLayout(new GridLayout(1, 1));northPanel.add(menu);northPanel.add(timePanel);timePanel.setBackground(new Color(245, 240, 245));menu.setBackground(new Color(245, 240, 245));//添加到框架setLayout(new BorderLayout());add(northPanel, BorderLayout.NORTH);add(mazePane, BorderLayout.CENTER);add(new JPanel(), BorderLayout.SOUTH);//注册监听器m_start.addActionListener(this);m_time.addActionListener(this);m_return.addActionListener(this);m_exit.addActionListener(this);m_selfconfig.addActionListener(this);m_randommake.addActionListener(this);m_sortpath.addActionListener(this);addKeyListener(this);//基本设置setTitle("豆豆人找朋友");setSize(850, 650);setLocationRelativeTo(null);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setVisible(true);setResizable(false);}Map() {this(25, 25);}//重写run方法,进行时间控制public void run() {if (timelimit > 0) {while (true) {try {Thread.sleep(1000);if (remaintime > 0)remaintime--;timePanel.repaint();if (timelimit > 0 && remaintime == 0) {if (Operations.m_currex != m - 1 || Operations.m_currey != n - 1) {Object[] options = {"新游戏", "重来一次"};int response = JOptionPane.showOptionDialog(this,"  很遗憾,你没有在规定时间内完成任务\n请选择开始新的游戏,或重玩此游戏","游戏超时!", JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]);if (response == 0) {Operations.restart = true;Operations.start();} else {remaintime = timelimit;tp[Operations.m_currex][Operations.m_currey].change(1);Operations.m_currex = Operations.m_startx;Operations.m_currey = Operations.m_starty;tp[Operations.m_currex][Operations.m_currey].change(2);}}}} catch (Exception e) {}}}}//菜单事件处理public void actionPerformed(ActionEvent e) {if (e.getSource() == m_start) {Operations.start();} else if (e.getSource() == m_return) {dispose(); //关闭当前窗口new StartUI();} else if (e.getSource() == m_exit) {System.exit(0);} else if (e.getSource() == m_selfconfig) {Operations.selfconfig();} else if (e.getSource() == m_randommake) {Operations.randommake();} else if (e.getSource() == m_sortpath) {Operations.findPath();} else if (e.getSource() == m_time) {Operations.setTime();}}//键盘事件处理public void keyTyped(KeyEvent e) {}public void keyReleased(KeyEvent e) {}public void keyPressed(KeyEvent e) {switch (e.getKeyCode()) {case KeyEvent.VK_DOWN:Operations.down();break;case KeyEvent.VK_UP:Operations.up();break;case KeyEvent.VK_LEFT:Operations.left();break;case KeyEvent.VK_RIGHT:Operations.right();break;case KeyEvent.VK_S:Operations.start();break;case KeyEvent.VK_Q:System.exit(0);break;case KeyEvent.VK_R:dispose();new StartUI();break;case KeyEvent.VK_E:Operations.selfconfig();break;case KeyEvent.VK_Z:Operations.randommake();break;case KeyEvent.VK_T:Operations.findPath();break;case KeyEvent.VK_L:Operations.setTime();break;}}}```java
package Maze;import sun.lwawt.macosx.CPrinterGraphics;import javax.swing.*;
import java.awt.*;
import java.awt.image.ImageObserver;
import java.util.ArrayList;public class Operations{static int m, n;   //用于拷贝map.m和map.n的值static int m_currex, m_currey;   //豆豆人当前位置static int m_startx, m_starty;   //豆豆人开始位置static boolean changeable_key = true;     //可用键盘控制豆豆人移动static boolean restart = false;private static boolean[] isBeVisit = null;//生成随机地图专用数据//迷宫地图最短路径深度图算法public static void findPath() {Map.timeThread.checkAccess();   ;    //时间控制线程休眠changeable_key = true;   //可用键盘控制豆豆人setEditable(false);       //不可编辑m = Map.m;n = Map.n;int max = m * n;                 //任意一点到小黄豆的最短路径长度不会超出m*n。int[] depthGraph = new int[m * n];     //路径深度图//路径深度图初始化depthGraph[m * n - 1] = 0;                //小黄豆到自己的距离自然是0for (int i = 0; i < m * n - 1; i++) {if (Map.tp[i / n][i % n].isWall())depthGraph[i] = -1;           //墙表示为-1,表示无通路elsedepthGraph[i] = max;           //未确定距离时已max表示}boolean flag = true;             //循环过程中是否有某点的路径深度被修改int currex, currey;               //记录当前访问点的坐标int aroundmin;                   //周围可行方向的最小路径深度 + 1//动态更新路径深度图直至其达到稳态(即最后一次循环过程中不再有路径深度被修改)while (flag) {flag = false;for (int s = m * n - 1; s >= 0; s--) {if (depthGraph[s] != -1) {aroundmin = depthGraph[s];currex = s / n;currey = s % n;if (currey + 1 < n && depthGraph[s + 1] != -1 && depthGraph[s + 1] + 1 < aroundmin)aroundmin = depthGraph[s + 1] + 1;if (currex + 1 < m && depthGraph[s + n] != -1 && depthGraph[s + n] + 1 < aroundmin)aroundmin = depthGraph[s + n] + 1;if (currey - 1 >= 0 && depthGraph[s - 1] != -1 && depthGraph[s - 1] + 1 < aroundmin)aroundmin = depthGraph[s - 1] + 1;if (currex - 1 >= 0 && depthGraph[s - n] != -1 && depthGraph[s - n] + 1 < aroundmin)aroundmin = depthGraph[s - n] + 1;if (aroundmin < depthGraph[s]) {depthGraph[s] = aroundmin;flag = true;}}}}//利用已生成的路径深度图,找到从豆豆人到小黄豆之间的最短路径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;                //可开始新游戏}//深度优先遍历生成至少有一条随机通道的迷宫地图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);elseMap.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 = true;  //键盘可控制豆豆人移动m_currex = m_startx;m_currey = m_starty;  //开始新游戏前豆豆人当前位置与开始位置相等restart = false;}//从S点开始深度优先遍历与它x 、y坐标相差均为偶数的点构成的图,并打通每一步需要通过的墙public static void DFS(int s) {Map.tp[s / n][s % n].change(1);isBeVisit[s] = true;//用于以随机顺序存储方向   右0下1左2上3int[] direction = new int[4];boolean[] isStored = new boolean[4];//方向是否已被存储for (int i = 0; i < 4; i++) isStored[i] = false;//当前点对应的实际坐标int currex = s / n, currey = s % n;//按随机顺序存储方向int rand, length = 0;//随机数 用于产生随机顺序 ,length表示已存储方向的个数while (length < 4) {rand = (int) (Math.random() * 4);    //0~3if (!isStored[rand]) {direction[length++] = rand;//修改为true,防止重复存储isStored[rand] = true;}}for (int i = 0; i < 4; i++) {switch (direction[i]) {case 0:if (currey + 2 < n) {                             //右if (!isBeVisit[s + 2]) {//打通[currex][currey]与[currex][currey+2]之间的墙,下同Map.tp[currex][currey + 1].change(1);DFS(s + 2);}}break;case 1:if (currex + 2 < m) {                              //下if (!isBeVisit[s + 2 * n]) {Map.tp[currex + 1][currey].change(1);DFS(s + 2 * n);}}break;case 2:                                               //左if (currey - 2 >= 0) {if (!isBeVisit[s - 2]) {Map.tp[currex][currey - 1].change(1);DFS(s - 2);}}break;case 3:                                            //上if (currex - 2 >= 0) {if (!isBeVisit[s - 2 * n]) {Map.tp[currex - 1][currey].change(1);DFS(s - 2 * n);}}break;}}}//开始游戏public static void start() {if (restart) creatMaze();Map.remaintime = Map.timelimit;Map.timeThread.checkAccess();changeable_key = true;    //使用键盘控制豆豆人setEditable(false);       //不可编辑}//设置时间public static void setTime() {int time;String timeStr;try {timeStr = JOptionPane.showInputDialog("请输入最大时间限制(单位为秒):\n提示:输入0代表无时间限制)");time = Integer.parseInt(timeStr);if (time < 0) throw new Exception();Map.timelimit = time;   //设置完时间后重新开始游戏Object[] options = {"新游戏", "当前游戏"};int response = JOptionPane.showOptionDialog(null, "请选择是否开始新游戏还是重新玩当前游戏","游戏时间设置成功", JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]);if (response == 0) {restart = true;start();} else if (response == 1) {Map.tp[m_currex][m_currey].change(1);m_currex = m_startx;m_currey = m_starty;Map.tp[m_currex][m_currey].change(2);restart = false;start();}} catch (Exception e) {JOptionPane.showMessageDialog(null, "由于用户取消或输入不符合要求等原因,游戏时间限定设置失败。","未更改游戏时间限制", JOptionPane.ERROR_MESSAGE);Map.timeThread.checkAccess(); //返回调用前状态}}//豆豆人的移动//只有往右走和往下走才有可能到小黄豆,因此只检测这两种情况是否成功找到小黄豆public static void down() {if (!changeable_key) return;if (m_currex == m - 1 && m_currey == n - 1) return;if (m_currex + 1 == m - 1 && m_currey == n - 1) {Map.tp[m_currex][m_currey].change(1);m_currex++;restart = true;int anwser = JOptionPane.showConfirmDialog(null, "恭喜你帮助豆豆人找到小黄豆,是否开始新的游戏。","成功找到小黄豆!", JOptionPane.YES_NO_CANCEL_OPTION);if (anwser == JOptionPane.YES_OPTION) start();} else if (m_currex + 1 < m && !Map.tp[m_currex + 1][m_currey].isWall()) {Map.tp[m_currex][m_currey].change(4);Map.tp[++m_currex][m_currey].change(2);}}public static void up() {if (!changeable_key) return;if (m_currex == m - 1 && m_currey == n - 1) return;if (m_currex - 1 >= 0 && !Map.tp[m_currex - 1][m_currey].isWall()) {Map.tp[m_currex][m_currey].change(4);Map.tp[--m_currex][m_currey].change(2);}}public static void left() {if (!changeable_key) return;if (m_currex == m - 1 && m_currey == n - 1) return;if (m_currey - 1 >= 0 && !Map.tp[m_currex][m_currey - 1].isWall()) {Map.tp[m_currex][m_currey].change(4);Map.tp[m_currex][--m_currey].change(2);}}public static void right() {if (!changeable_key) return;if (m_currex == m - 1 && m_currey == n - 1) return;if (m_currex == m - 1 && m_currey + 1 == n - 1) {Map.tp[m_currex][m_currey].change(1);m_currey++;restart = true;int anwser = JOptionPane.showConfirmDialog(null, "恭喜你帮助豆豆人找到小黄豆,是否开始新的游戏。","成功找到小黄豆!", JOptionPane.YES_NO_CANCEL_OPTION);if (anwser == JOptionPane.YES_OPTION) start();} else if (m_currey + 1 >= 0 && !Map.tp[m_currex][m_currey + 1].isWall()) {Map.tp[m_currex][m_currey].change(4);Map.tp[m_currex][++m_currey].change(2);}}//设置是否能编辑public static void setEditable(boolean e) {for (int i = 0; i < m; i++)for (int j = 0; j < n; j++)Map.tp[i][j].setChangeable_click(e);//即使在编辑模式下豆豆人和小黄豆也不能修改Map.tp[m - 1][n - 1].setChangeable_click(false);Map.tp[m_startx][m_starty].setChangeable_click(false);}//自定义地图public static void selfconfig() {Map.timeThread.checkAccess();    //时间控制线程休眠changeable_key = true;      //可用键盘移动豆豆人setEditable(true);           //可以使墙变路、路变墙m_startx = m_currex;m_startx = m_currex;restart = false;               //保证开始游戏时使用的是编辑得到的地图Map.timeThread.checkAccess();   //时间控制回到调用函数前状态}//随机迷宫@SuppressWarnings("deprecation")public static void randommake() {Map.timeThread.checkAccess();    //时间控制线程休眠creatMaze();changeable_key = false;setEditable(true);restart = false;        //保证开始游戏时使用的是编辑得到的地图Map.timeThread.checkAccess();   //时间控制回到调用函数前状态}
}```java
package Maze;//显示墙、路、豆豆人和小黄豆的面板import java.awt.*;
import java.awt.event.*;
import javax.swing.*;@SuppressWarnings("serial")
public class Paint extends JPanel implements MouseListener {private boolean changeable_click;//标志 0:墙 1:路 2:豆豆人 3:小黄豆 4:脚印private int flag;private Image wall = new ImageIcon("/Users/xiaoying/Desktop/IMG_8697.jpg").getImage();        //墙private Image road = new ImageIcon("/Users/xiaoying/Desktop/IMG_8689.jpg").getImage();        //路private Image role = new ImageIcon("/Users/xiaoying/Desktop/IMG_8665.PNG").getImage();        //豆豆人private Image bean = new ImageIcon("/Users/xiaoying/Desktop/IMG_8663.PNG").getImage();        //小黄豆private Image step = new ImageIcon("/Users/xiaoying/Desktop/IMG_8701.jpg").getImage();        //脚印Paint(int f) {flag = f;changeable_click = false;    //初始化时不能通过鼠标点击改变 flag 的值addMouseListener(this);}Paint() {this(0);}//重写paintComponent方法,画图public void paintComponent(Graphics g) {super.paintComponent(g);if (flag == 0)g.drawImage(wall, 0, 0, getWidth(), getHeight(), this);else if (flag == 1)g.drawImage(road, 0, 0, getWidth(), getHeight(), this);else if (flag == 2)g.drawImage(role, 0, 0, getWidth(), getHeight(), this);else if (flag == 3)g.drawImage(bean, 0, 0, getWidth(), getHeight(), this);elseg.drawImage(step, 0, 0, getWidth(), getHeight(), this);}//访问器public int getFlag() {return flag;}//是否为墙public boolean isWall() {return flag == 0;}//是否可通过点击实现墙路互变public boolean isChangeable() {return changeable_click;}//设置为是否能墙路互变public void setChangeable_click(boolean c) {changeable_click = c;}//修改标志并重画面板public void change(int f) {flag = f;repaint();}//鼠标事件处理public void mouseEntered(MouseEvent e) {}public void mouseExited(MouseEvent e) {}public void mousePressed(MouseEvent e) {}public void mouseReleased(MouseEvent e) {}public void mouseClicked(MouseEvent e) {if (!changeable_click) return;if (flag == 0) {flag = 1;repaint();} else {flag = 0;repaint();}}}


实验三 基于A*算法的迷宫游戏相关推荐

  1. 实验三 基于A*算法的迷宫游戏开发

    实验要求: 1.迷宫随机生成 2.玩家走迷宫,留下足迹: 3.系统用A*算法寻路,输出路径 解决问题: 1.如何显示迷宫的图形界面: 2.如何生成随机的迷宫: 3.怎样移动游戏中走迷宫的"玩 ...

  2. 【项目三 基于A*算法的迷宫游戏开发】

    一. 实验要求 1.迷宫随机生成 2.玩家走迷宫,留下足迹 3.系统用A*算法寻路,输出路径 二.前期准备 解决迷宫问题要用到两个算法,深度优先遍历(DFS)生成迷宫,A*算法寻路.那么首先要对这两种 ...

  3. 基于A*算法的迷宫游戏

    文章目录 前言 一.项目要求 二.A*算法 三.项目实现 1.框架结构 2.数据结构 3.主要功能函数 总结 前言 本项目以PyCharm为开发平台,使用python编程语言,基于A*算法随机生成一个 ...

  4. 实验三、prim算法生成迷宫,A*算法解迷宫(实验准备)

    目录 实验要求: 算法简介: prim算法: A*算法: 实验要求: 该项目的主要要求是:首先生成一个迷宫,要求随机生成.而生成迷宫有深度优先算法.prim算法.递归分割算法等.老师说建议使用prim ...

  5. 基于深度优先算法和A*算法的迷宫游戏开发(Java实现)

    先上图 文章目录 一.实验内容 二.深度优先算法生成迷宫 三.A*算法走迷宫 四.结果测试 五.源代码 六.参考文献 一.实验内容 1.要求: 1)迷宫随机生成 2)系统用A*算法寻路,输出路径 3) ...

  6. 算法实验三 【电子老鼠闯迷宫】分支限界

    算法实验三 [电子老鼠闯迷宫]分支限界 1042.电子老鼠闯迷宫 时限:1000ms 内存限制:10000K 总时限:3000ms 描述 有一只电子老鼠被困在如下图所示的迷宫中.这是一个12*12单元 ...

  7. 20172328 蓝墨云实验——三种查找算法练习

    20172328 蓝墨云实验--三种查找算法练习 课程:<软件结构与数据结构> 班级: 1723 姓名: 李馨雨 学号:20172328 实验教师:王志强老师 实验日期:2018年10月1 ...

  8. TIT 计算机图形学 实验三 使用重心坐标算法绘制颜色渐变的正六面体

    TIT 计算机图形学 实验三 使用重心坐标算法绘制颜色渐变的正六面体 前言 参考视频计算机图形学全套算法讲解和C++编码实现(共23讲配套源码),计算机图形学案例视频讲解以及主页相关算法.孔老师是我的 ...

  9. 河北工业大学数据挖掘实验三 应用 Apriori 算法挖掘频繁项集

    河北工业大学数据挖掘实验三 应用 Apriori 算法挖掘频繁项集 一.实验目的 二.实验原理 1.Apriori 算法 2.提高频繁项集逐层产生的效率 三.实验内容和步骤 1.实验内容 2.实验步骤 ...

最新文章

  1. windows环境下安装python的mysqldb模块
  2. javaweb---简易邮件发送
  3. arcgis adf数据_使用ADF列表视图的主从数据
  4. 基于多源文档片段的神经网络排序模型(Neural Ranking Models with Multiple Document Fields)
  5. Atitit. 真正的全中国文字attilax易语言的特点以及范例
  6. golang orm对比
  7. 免费下载 客道巴巴文档 教程
  8. Microsoft Network Monitor的select network栏空白
  9. .lnk 文件恢复默认打开方式
  10. centos安装python及导入cv2出现的问题及踩坑记录
  11. 这些AI开源项目可以让你创作出卢浮宫级别的艺术品!
  12. 苹果审核Metadata Rejected解决
  13. 抖音通过什么方式变现,抖音变现方式分别有什么
  14. 阿里云——云迁移中心
  15. 华硕AC86U路由器最佳设置(解决5G信号断流和米家设备掉线的问题)
  16. C++ 实现一个复数类
  17. Aspose.Cells - 在任何平台上操作Excel电子表格
  18. 使用git checkout和git clean来还原/清除文件
  19. 想要安装ZBrush!却不知道对电脑有什么特别的要求?
  20. 基于stm32的蓝牙开关

热门文章

  1. 女人四十学计算机有用吗,四十岁的女人,开始学做美容工作,有前途吗
  2. OBS Studio 27.0.1版本编译
  3. Spring Boot进阶:原理、实战与面试题分析
  4. 杂记-----------
  5. JS - 将tree(树形)数据结构格式改为一维数组对象格式(扁平化)
  6. C#理论 —— 文件操作、委托和事件
  7. 服务器被攻击 常见的服务器攻击方式有哪些
  8. joda-time 使用详解
  9. 古诗词PPT模板推荐
  10. [App] FTP 命令全集