一、实验内容:

1)迷宫游戏是非常经典的游戏,在该题中要求随机生成一个迷宫,并求解迷宫;

2) 要求查找并理解迷宫生成的算法,并尝试用两种不同的算法来生成随机的迷宫。

  1. 要求迷宫游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘方向键控制,并在行走路径上留下痕迹;系统提示迷宫路径要求基于A*算法实现,输出玩家当前位置到迷宫出口的最优路径。设计交互友好的游戏图形界面。

二、深度优先算法生成迷宫

1、整体思路

1)利用深度遍历的思想。访问到一个节点时,搜索这个节点没有被访问过的相邻节点,选择一个继续做同样的操作,直到没有邻节点为止再回溯到上一个访问的节点,并选择另外的邻节点。

2)这种方案生成的迷宫会有一条明显的主路,这条主路特别长,贯穿大部分区域的路线,同时,迷宫的路线一般比较扭曲。这种采用深度优先算法(递归回溯算法)生成的迷宫称之为“主路扭曲型”迷宫。

三、深度优先搜索实现寻路功能

选定格子作为根节点,从它开始随机地深度搜索前进,开出一条路来,直到无路可走了,退回一步,换另一条路,再走到无路可走,回退一步,换另一条。如此循环往复,直到完全无路可走。

选择一个格子根节点,将它压进栈里。然后在栈不为空的时候执行以下循环:

取出一个格子,将它的INTREE标志设置为1,然后将它的所有不在树中的邻居格子压进栈里(顺序随机),并且让这些邻居格子的father指向该格子。

二、代码分析

一、子方格对象类的创建:

我们需要把大的窗口分割为n×n个子方块,对方块类创建的的方格对象进行操作,使得各子方块具有更多的方法和属性,就要用子方块类创建子方块对象,并设计相关的方法,获取和修改各子方块对象的属性。

1、网格类grid的创建以及其构造函数的,坐标变量,标志位,以及父节点对象的初始设置;

class Lattice {static final int INTREE = 1;static final int NOTINTREE = 0;private final int x; // 格子的位置,在第几行private final int y; // 第几列private int flag = NOTINTREE; // flag,标识格子是否已加入树中private Lattice father = null; // 格子的父亲节点public Lattice(int xx, int yy) {x = xx;y = yy;}

2、比如基本的坐标获取方法:

 public int getX() {return x;}public int getY() {return y;}

3、标志位的获取和修改方法;

  public int getFlag() {return flag;}  public void setFlag(int f) {flag = f;}

4、父节点对象的获取和修改;

   public grid getFather() {                 //获取方格对象的父节点;return father;}public void setFather(grid f) {           //修改方格对象的父节点;father = f;}

二、迷宫Maze类的设计;

(1)、类的声明以及基本变量设置;

1、创建迷宫类继承 JPanel类;

2、关于迷宫窗口的长宽、方格数目,边距宽度

3、将整个窗口定义位一个网格类二维矩阵;设置相关变量;

class Maze extends JPanel {//private static final long serialVersionUID = -8300339045454852626L;private final int NUM;//num是数组和地图的边长,private final int width;//width 每个格子的宽度和高度;private final int padding;//地图面板的大小;private final Lattice[][] maze;//lattic类的数组maze,用于保存每个格子;private int ballX, ballY;//起始位置坐标;private boolean drawPath = false; // flag,标识是否画出路径

2、迷宫类的构造函数:

建立二重循环,对二维矩阵的迷宫内部按照方格分别定义n×n个网格对象;

后续调用创建迷宫和按键监视器得操作得方法。

 Maze(int m, int wi, int p) {NUM = m;width = wi;padding = p;maze = new Lattice[NUM][NUM];for (int i = 0; i <= NUM - 1; i++)for (int j = 0; j <= NUM - 1; j++)maze[i][j] = new Lattice(i, j);createMaze();setKeyListener();this.setFocusable(true);}

3、迷宫得默认构造方法:

1、对每个子方格对象的构造,通过循环,设置每个子方格对象的父节点位null,标志位为非入树的空状态;

2、设置初始的位置坐标(myX,myY);

3、将路径标识位也置为false状态;

4、设置控件获取焦点;

 // 初始化游戏,重开一局时使用private void init() {for (int i = 0; i <= NUM - 1; i++)for (int j = 0; j <= NUM - 1; j++) {maze[i][j].setFather(null);maze[i][j].setFlag(Lattice.NOTINTREE);}ballX = 0;ballY = 0;drawPath = false;createMaze();// setKeyListener();this.setFocusable(true);repaint();}

5、根据子网格对象的坐标获取和直接由对象获取坐标的信息的四个方法:

(由于私有变量只能通过对象调用函数访问)

四、源代码

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Random;
import java.util.Stack;
import javax.swing.*;
class Lattice {static final int INTREE = 1;static final int NOTINTREE = 0;private final int x; // 格子的位置,在第几行private final int y; // 第几列private int flag = NOTINTREE; // flag,标识格子是否已加入树中private Lattice father = null; // 格子的父亲节点public Lattice(int xx, int yy) {x = xx;y = yy;}public int getX() {return x;}public int getY() {return y;}public int getFlag() {return flag;}public Lattice getFather() {return father;}public void setFather(Lattice f) {father = f;}public void setFlag(int f) {flag = f;}public String toString() {return "(" + x + "," + y + ")\n";}
}
class Maze extends JPanel {//private static final long serialVersionUID = -8300339045454852626L;private final int NUM;//num是数组和地图的边长,private final int width;//width 每个格子的宽度和高度;private final int padding;//地图面板的大小;private final Lattice[][] maze;//lattic类的数组maze,用于保存每个格子;private int ballX, ballY;//起始位置坐标;private boolean drawPath = false; // flag,标识是否画出路径Maze(int m, int wi, int p) {NUM = m;width = wi;padding = p;maze = new Lattice[NUM][NUM];for (int i = 0; i <= NUM - 1; i++)for (int j = 0; j <= NUM - 1; j++)maze[i][j] = new Lattice(i, j);createMaze();setKeyListener();this.setFocusable(true);}// 初始化游戏,重开一局时使用private void init() {for (int i = 0; i <= NUM - 1; i++)for (int j = 0; j <= NUM - 1; j++) {maze[i][j].setFather(null);maze[i][j].setFlag(Lattice.NOTINTREE);}ballX = 0;ballY = 0;drawPath = false;createMaze();// setKeyListener();this.setFocusable(true);repaint();}// 由格子的行数,得到格子中心点的像素X坐标public int getCenterX(int x) {return padding + x * width + width / 2;}// 由格子的列数,得到格子中心点的像素Y坐标public int getCenterY(int y) {return padding + y * width + width / 2;}public int getCenterX(Lattice p) {return padding + p.getY() * width + width / 2;}public int getCenterY(Lattice p) {return padding + p.getX() * width + width / 2;}// 检查是否到达最后一个格子,若是则走出了迷宫,重开一局游戏private void checkIsWin() {if (ballX == NUM - 1 && ballY == NUM - 1) {JOptionPane.showMessageDialog(null, "你赢了!", "迷宫小游戏",JOptionPane.PLAIN_MESSAGE);init();}}// 移动小球,c为按键码synchronized private void move(int c) {int tx = ballX, ty = ballY;// System.out.println(c);switch (c) {case KeyEvent.VK_LEFT -> ty--;case KeyEvent.VK_RIGHT -> ty++;case KeyEvent.VK_UP -> tx--;case KeyEvent.VK_DOWN -> tx++;case KeyEvent.VK_SPACE -> drawPath = !drawPath;default -> {}}// 若移动后未出界且格子之间有路径,则进行移动,更新小球位置,否则移动非法if (!isOutOfBorder(tx, ty)&& (maze[tx][ty].getFather() == maze[ballX][ballY]|| maze[ballX][ballY].getFather() == maze[tx][ty])) {ballX = tx;ballY = ty;}}private void setKeyListener() {this.addKeyListener(new KeyAdapter() {public void keyPressed(KeyEvent e) {int c = e.getKeyCode();move(c);repaint();checkIsWin();}});}// 是否出界private boolean isOutOfBorder(Lattice p) {return isOutOfBorder(p.getX(), p.getY());}private boolean isOutOfBorder(int x, int y) {return x > NUM - 1 || y > NUM - 1 || x < 0 || y < 0;}// 获取格子的邻居格子private Lattice[] getNeis(Lattice p) {final int[] adds = {-1, 0, 1, 0, -1};if (isOutOfBorder(p)) {return null;}Lattice[] ps = new Lattice[4]; // 四个邻居格子,顺序为上右下左,出界的邻居为nullint xt;int yt;for (int i = 0; i <= 3; i++) {xt = p.getX() + adds[i];yt = p.getY() + adds[i + 1];if (isOutOfBorder(xt, yt))continue;ps[i] = maze[xt][yt];}return ps;}// 构建随机树,创建迷宫private void createMaze() {// 随机选一个格子作为树的根Random random = new Random();int rx = NUM-1;int ry =  NUM-1;// 深度优先遍历Stack<Lattice> s = new Stack<>();Lattice p = maze[rx][ry];Lattice[] neis;s.push(p);//根节点压入栈中;while (!s.isEmpty()) {//如果栈不是空;p = s.pop();//推出栈顶;p.setFlag(Lattice.INTREE);//标志在栈内。neis = getNeis(p);//遍历周围的格子;放进数组;int ran = Math.abs(random.nextInt());//随机生成一个数;for (int a = 0; a <= 3; a++) {//生成树;ran++;ran %= 4;//ran的值永远是0,1,2,3。assert neis != null;if (neis[ran] == null || neis[ran].getFlag() == Lattice.INTREE)//节点附近的节点有出界或者在树内;continue;s.push(neis[ran]);//否则压入栈;neis[ran].setFather(p);//记录父节点;}}}// 抹掉两个格子之间的边private void clearFence(int i, int j, int fx, int fy, Graphics g) {int sx = padding + ((Math.max(j, fy)) * width),sy = padding + ((Math.max(i, fx)) * width),dx = (i == fx ? sx : sx + width),dy = (i == fx ? sy + width : sy);if (sx != dx) {sx++;dx--;} else {sy++;dy--;}g.drawLine(sx, sy, dx, dy);}protected void paintComponent(Graphics g) {//画网格super.paintComponent(g);g.setColor(Color.black);for (int i = 0; i <= NUM; i++) {//画横线g.drawLine(padding + i * width, padding, padding + i * width,padding + NUM * width);}for (int j = 0; j <= NUM; j++) {//画竖线g.drawLine(padding, padding + j * width, padding + NUM * width,padding + j * width);}g.setColor(this.getBackground());//用背景色覆盖for (int i = NUM - 1; i >= 0; i--) {for (int j = NUM - 1; j >= 0; j--) {Lattice f = maze[i][j].getFather();if (f != null) {int fx = f.getX(), fy = f.getY();clearFence(i, j, fx, fy,g);}}}g.drawLine(padding, padding + 1, padding, padding + width - 1);int last = padding + NUM * width;g.drawLine(last, last - 1, last, last - width + 1);g.setColor(Color.orange);g.fillOval(getCenterX(ballY) - width / 3, getCenterY(ballX) - width / 3,width / 2, width / 2);if (drawPath) drawPath(g);}private void drawPath(Graphics g) {if (drawPath)g.setColor(Color.orange);Lattice p = maze[0][0];//入口格子为对象while (p.getFather() != null) {g.drawLine(getCenterX(p), getCenterY(p), getCenterX(p.getFather()),getCenterY(p.getFather()));p = p.getFather();}g.fillOval(getCenterX(p) - width / 3, getCenterY(p) - width / 3,width / 2, width / 2);//设置末尾圆点位置;p = maze[NUM - 1][NUM - 1];//出口格子为对象while (p.getFather() != null) {if (p.getFlag() == 3)break;g.drawLine(getCenterX(p), getCenterY(p), getCenterX(p.getFather()),getCenterY(p.getFather()));//画出父子节点之间的通路;p = p.getFather();}}
}
class Cal extends JFrame implements ActionListener{public static void main(String[] args) {final int  LX1 = 800, LY1 = 300;JPanel pan = new JPanel();//创建容器;pan.setBounds(20,40,240,280);JLabel pan4=new JLabel("迷宫小游戏");pan4.setHorizontalAlignment(SwingConstants.CENTER);//JButton pan1=new JButton("简单模式");//按钮显示;JButton pan2=new JButton("开始游戏");//JButton pan3=new JButton("困难模式");GridLayout grid2 = new GridLayout(2, 1);pan.setLayout(grid2);//容器分割为四行,用于存放按钮。pan.add(pan4);//pan.add(pan1);//按钮加入容器中。pan.add(pan2);//pan.add(pan3);Cal frame1 = new Cal();//创建Cal类的对象frame1.frame1.setTitle("迷宫小游戏");//设置框架标题。frame1.add(pan);//pan容器加入框架;frame1.setLayout(null);//设置容器与框架边界有间隔。frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//默认关闭;frame1.setSize(300, 400 );frame1.setLocation(LX1, LY1);//frame1在屏幕的显示位置;frame1.setVisible(true);//框架可视化;frame1.setResizable(false);//大小不可更改;//pan1.addActionListener(frame1);pan2.addActionListener(frame1);//按钮加入侦听;//pan3.addActionListener(frame1);}@Overridepublic void actionPerformed(ActionEvent e) {/*if(e.getActionCommand().equals("简单模式")) {final int n = 20, width = 600, padding = 30, LX = 700, LY = 100;JPanel p = new Maze(n, (width - padding - padding) / n, padding);JFrame frame = new JFrame("迷宫小游戏 简单模式");frame.getContentPane().add(p);frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);frame.setSize(width + padding, width + padding + padding);frame.setLocation(LX, LY);frame.setVisible(true);frame.setResizable(false);}*/if(e.getActionCommand().equals("开始游戏")) {final int n = 30, width = 700, padding = 25, LX = 700, LY = 100;//设定大小;JPanel p = new Maze(n, (width - padding - padding) / n, padding);//生成迷宫;JFrame frame = new JFrame("迷宫小游戏");frame.getContentPane().add(p);//迷宫容器加入frame.frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);frame.setSize(width + padding, width + padding + padding);//设置frame的大小。frame.setLocation(LX, LY);//屏幕上的显示位置;frame.setVisible(true);//可视化;frame.setResizable(false);}/*if(e.getActionCommand().equals("困难模式")) {final int n = 50, width = 800, padding = 20, LX = 400, LY = 50;JPanel p = new Maze(n, (width - padding - padding) / n, padding);JFrame frame = new JFrame("迷宫小游戏 困难模式");frame.getContentPane().add(p);frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);frame.setSize(width + padding, width + padding + padding);frame.setLocation(LX, LY);frame.setVisible(true);frame.setResizable(false);}*/}
}

基于Java的迷宫小游戏相关推荐

  1. 基于java的俄罗斯方块小游戏设计(含源文件)

    欢迎添加微信互相交流学习哦! 项目源码:https://gitee.com/oklongmm/biye 题 目          小游戏开发 摘    要     俄罗斯方块是我们最常见的游戏之一,该 ...

  2. 连连看游戏的设计与实现——基于JAVA语言的小游戏

    说明:本篇博客主要讲述练练看游戏的设计与实现.前半部分为分析与类和属性的说明,后半部分为程序的实现与程序代码.第一次写小游戏,仍存在许多问题,也借鉴了CSDN前辈的代码想法,如有不妥,还望多批评指正. ...

  3. java写华容道_基于java的华容道小游戏

    一.华容道游戏简介 华容道,古老的中国游戏,以其变化多端.百玩不厌的特点与魔方.独立钻石棋一起被国外智力专家并称为"智力游戏界的三个不可思议".它与七巧板.九连环等中国传统益智玩具 ...

  4. 基于java的华容道小游戏

    一.华容道游戏简介 华容道,古老的中国游戏,以其变化多端.百玩不厌的特点与魔方.独立钻石棋一起被国外智力专家并称为"智力游戏界的三个不可思议".它与七巧板.九连环等中国传统益智玩具 ...

  5. java游戏界面制作_软件设计之基于Java的连连看小游戏(二)——游戏基础界面的制作及事件的添加...

    上次完成到游戏首页的制作,今天完成了游戏基础界面的制作以及事件的简单添加.由于功能尚未完全实现,因此游戏界面的菜单列表只是简单地添加了一下,其余菜单列表以及倒计时等在后续的制作中逐一完善. 1.首先在 ...

  6. 基于Java坦克大战小游戏设计 (2)

    接下来继续完善element下物体类,以及添加有关整体游戏进程.配置.绘画相关的类. game包:GameRunThread线程用来开始并处理游戏运行中出现的各种事件,继承thread类. publi ...

  7. 基于Java坦克大战小游戏设计(1)

    还是采用上学期学习到的设计模式,抽点时间写下这个游戏,游戏逻辑部分先写简单一点的,主要想加深对java设计.模块化等方面的理解. 首先先写Element包下各个元素的类:包括子弹.坦克.树木.河流.家 ...

  8. 基于HTML5的WebGL实现的2D3D迷宫小游戏

    为了实现一个基于HTML5的场景小游戏,我采用了HT for Web来实现,短短200行代码,我就能实现用"第一人称"来操作前进后退上下左右,并且实现了碰撞检测. 先来看下实现的效 ...

  9. java毕业设计——基于java+J2ME的堡垒游戏设计与实现(毕业论文+程序源码)——堡垒游戏

    基于java+J2ME的堡垒游戏设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+J2ME的堡垒游戏设计与实现,文章末尾附有本毕业设计的论文和源码下载地址哦. 文章目录: 基于ja ...

最新文章

  1. 不区分大小写比较Java_java-如何使字符串比较不区分大小写?
  2. 系统接口502异常_基于SpringBoot2.0的后台权限管理系统
  3. Vigenère密码
  4. xshell6保持连接_使用Xshell6+Xftp6连接Linux云服务器(图文教程)
  5. SAP不同产品的UI开发策略概述
  6. 【暴力】MSWORLD
  7. 查看layui的版本号
  8. scala unix时间戳_下载,在Linux,Unix,Windows上安装Scala
  9. MQ(队列消息的入门)
  10. Java读写gif格式图片,解决ImageIO读取gif文件只读取第一帧的问题(read and write gif format pictures in Java)
  11. python竖线_python 读取竖线分隔符的文本方法
  12. 聊聊Excel的大批量导入导出
  13. Windows8/Silverlight/WPF/WP7周学习导读(11月12日-11月18日)
  14. 如何选购一台笔记本电脑
  15. Word文档误删怎样恢复?6种实用方法分享给你
  16. F-One融资B轮,企业绩效管理与分析服务再升级!
  17. 闵帆老师《论文写作》课程心得
  18. 实际采用 FleaPHP 的网站
  19. 数据分析师进阶必备6大数学利器
  20. 模拟实现十字路口交通灯管理系统(Java)

热门文章

  1. 用C语言实现最大公因数与最小公倍数
  2. vue返回顶部的组件BackTop
  3. 微信小程序 等待几秒、_微信小程序应用打开很慢怎么办?附解决方案
  4. 灵魂相似的人,总会相逢
  5. Fn键的功能笔记本fn键在哪?
  6. 互动娱乐成互联网新战场 腾讯互娱走向前台 阿里小米忙布局
  7. 看看某网友是如何骂中国男篮的
  8. XML中的CDATA(字符数据)
  9. 每一节网课背后,硬核黑科技大曝光
  10. 【算法】逻辑题算法题语言特性(集合贴1)