实验三 基于A*算法的迷宫游戏
软件实习项目三 —— 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*算法的迷宫游戏相关推荐
- 实验三 基于A*算法的迷宫游戏开发
实验要求: 1.迷宫随机生成 2.玩家走迷宫,留下足迹: 3.系统用A*算法寻路,输出路径 解决问题: 1.如何显示迷宫的图形界面: 2.如何生成随机的迷宫: 3.怎样移动游戏中走迷宫的"玩 ...
- 【项目三 基于A*算法的迷宫游戏开发】
一. 实验要求 1.迷宫随机生成 2.玩家走迷宫,留下足迹 3.系统用A*算法寻路,输出路径 二.前期准备 解决迷宫问题要用到两个算法,深度优先遍历(DFS)生成迷宫,A*算法寻路.那么首先要对这两种 ...
- 基于A*算法的迷宫游戏
文章目录 前言 一.项目要求 二.A*算法 三.项目实现 1.框架结构 2.数据结构 3.主要功能函数 总结 前言 本项目以PyCharm为开发平台,使用python编程语言,基于A*算法随机生成一个 ...
- 实验三、prim算法生成迷宫,A*算法解迷宫(实验准备)
目录 实验要求: 算法简介: prim算法: A*算法: 实验要求: 该项目的主要要求是:首先生成一个迷宫,要求随机生成.而生成迷宫有深度优先算法.prim算法.递归分割算法等.老师说建议使用prim ...
- 基于深度优先算法和A*算法的迷宫游戏开发(Java实现)
先上图 文章目录 一.实验内容 二.深度优先算法生成迷宫 三.A*算法走迷宫 四.结果测试 五.源代码 六.参考文献 一.实验内容 1.要求: 1)迷宫随机生成 2)系统用A*算法寻路,输出路径 3) ...
- 算法实验三 【电子老鼠闯迷宫】分支限界
算法实验三 [电子老鼠闯迷宫]分支限界 1042.电子老鼠闯迷宫 时限:1000ms 内存限制:10000K 总时限:3000ms 描述 有一只电子老鼠被困在如下图所示的迷宫中.这是一个12*12单元 ...
- 20172328 蓝墨云实验——三种查找算法练习
20172328 蓝墨云实验--三种查找算法练习 课程:<软件结构与数据结构> 班级: 1723 姓名: 李馨雨 学号:20172328 实验教师:王志强老师 实验日期:2018年10月1 ...
- TIT 计算机图形学 实验三 使用重心坐标算法绘制颜色渐变的正六面体
TIT 计算机图形学 实验三 使用重心坐标算法绘制颜色渐变的正六面体 前言 参考视频计算机图形学全套算法讲解和C++编码实现(共23讲配套源码),计算机图形学案例视频讲解以及主页相关算法.孔老师是我的 ...
- 河北工业大学数据挖掘实验三 应用 Apriori 算法挖掘频繁项集
河北工业大学数据挖掘实验三 应用 Apriori 算法挖掘频繁项集 一.实验目的 二.实验原理 1.Apriori 算法 2.提高频繁项集逐层产生的效率 三.实验内容和步骤 1.实验内容 2.实验步骤 ...
最新文章
- windows环境下安装python的mysqldb模块
- javaweb---简易邮件发送
- arcgis adf数据_使用ADF列表视图的主从数据
- 基于多源文档片段的神经网络排序模型(Neural Ranking Models with Multiple Document Fields)
- Atitit. 真正的全中国文字attilax易语言的特点以及范例
- golang orm对比
- 免费下载 客道巴巴文档 教程
- Microsoft Network Monitor的select network栏空白
- .lnk 文件恢复默认打开方式
- centos安装python及导入cv2出现的问题及踩坑记录
- 这些AI开源项目可以让你创作出卢浮宫级别的艺术品!
- 苹果审核Metadata Rejected解决
- 抖音通过什么方式变现,抖音变现方式分别有什么
- 阿里云——云迁移中心
- 华硕AC86U路由器最佳设置(解决5G信号断流和米家设备掉线的问题)
- C++ 实现一个复数类
- Aspose.Cells - 在任何平台上操作Excel电子表格
- 使用git checkout和git clean来还原/清除文件
- 想要安装ZBrush!却不知道对电脑有什么特别的要求?
- 基于stm32的蓝牙开关