Java实现可视化迷宫

代码地址如下:
http://www.demodashi.com/demo/14547.html

需求

使用深度优先算法求解迷宫路径,使用Java实现求解过程的可视化,可单步运行,形象直观。

演示效果

红色格子为迷宫终点,迷宫可放大缩小,为了录屏选择了较小的尺寸,有多种不同难度的迷宫可以加载。

  1. 简单迷宫
  2. 复杂迷宫

项目运行

文件中有两个运行脚本,Windows下直接双击win运行.bat即可,linux和Mac运行sh文件中的命令即可,喜欢用IDE的也可自行创建项目。
运行项目后,点击菜单栏左上角的Map加载迷宫地图, 点击右下角的Run开始解迷宫,Step可单步运行,可通过速度进度条调节速度。

项目结构

Maze
├── classes      # 存放编译生成的class文件
├── lib.jar      # 打包好的gui库
├── map          # 迷宫地图文件
│   ├── EasyMaze.txt
│   ├── FinalMaze01.txt
│   ├── FinalMaze02.txt
│   ├── FinalMaze03.txt
│   ├── FinalMaze04.txt
│   └── FinalMaze05.txt
├── src
│   ├── MazeBug.java
│   └── MazeBugRunner.java
├── linux运行.sh     # 运行脚本
└── win运行.bat     # 运行脚本

原理方法

使用深度优先算法,每个格子下一步都有上下左右4种走法,但是这4种走法并不是都是合法的,比如有些格子有障碍物,有些格式在边界之外,去掉这些剩下的才是合法的走法。

深度优先算法的思想就是:

  1. 找出当前位置A下一步合法的的格子,选择其中一个,往前走一步到达B。
  2. 如果B相邻的有合法格子,重复第1步;如果没有合法的,后退一步回到A,选择A的其他合法格子走;
  3. 重复以上方法,直到找到迷宫终点;

算法优化:上面的方法选择下一步走的方向是随机的,或者按照上下左右的顺序选择。但是很多迷宫都有偏向性,比如如果有右偏向性,那么每次都优先往右走可以更快走出迷宫。所以在实现的时候,记录每个方向走的次数,每往一个方向走一步就加1,如果回退就该方向减1,每次走都优先走次数最多的方向,当迷宫有偏向性时,该方法效率更高。

以项目中的迷宫为例,大部分情况下偏向性所需步数更少。

 普通方法:   534 1175 350 973 1052
 偏向性:       552 761 330 175 420

代码实现

/*
 * 节点:存储方向和该方向所走的次数
 * 往一个方向前进则加1,后退则减1
 */
class Node {
    private int dir;   // 方向,角度值
    private int ct;    // 该方向所走次数

    public Node(int initdir, int initct) {
        dir = initdir;
        ct = initct;
    }

    public int getDir() {
        return dir;
    }

    public int getCt() {
        return ct;
    }

    public void setCt(int deta) {
        ct += deta;
    }
}

// 深度优先算法解迷宫,并且以小甲虫的形式呈现
public class MazeBug extends Bug {
    private Location next;             // 下一步要走的格子
    private Integer stepCount = 0;     // 所走的步数
    private boolean isEnd = false;     // 是否到达迷宫出口
    private boolean hasShown = false;  // 是否显示了结束信息
    private Stack<Location> path = new Stack<>(); // 存储走过的路径
    private ArrayList<Node> arr = new ArrayList<>();

    public MazeBug() {
        setColor(Color.GREEN);
        arr.add(new Node(0, 0));
        arr.add(new Node(90, 0));
        arr.add(new Node(270, 0));
        arr.add(new Node(180, 0));
    }

    // 周期性执行
    public void act() {
        boolean willMove = canMove();   // 是否还能继续移动

        if (isEnd) {  // 是否结束
            if (!hasShown) { // 是否显示结束消息
                String msg = stepCount.toString() + " steps";
                JOptionPane.showMessageDialog(null, msg);
                hasShown = true;
            }
            return;
        } else if (willMove) { // 向前移动一个,步数加1
            move();
            ++stepCount;
        } else { // 不能移动,后退一步,将该方向的计数器减1
            Grid<Actor> grid = getGrid();
            Location loc = this.getLocation();
            Location top = path.pop();
            ++stepCount;
            grid.remove(top);
            this.setDirection(loc.getDirectionToward(top));
            this.moveTo(top);
      // 在走过的死路留下一朵白花
            Flower flower = new Flower(Color.WHITE);
            flower.putSelfInGrid(getGrid(), loc);

            // 方向计数器减1
            int dir = 180 + ((getDirection() / 90) % 2) * 180 - getDirection();
            for (Node node : arr)
                if (node.getDir() == dir) {
                    node.setCt(-1);
                    return;
                }
        }
    }

    // 找出和当前位置相邻的、合法的并且从未走过的格子
    public Location getValid(Location loc) {
        Grid<Actor> gr = getGrid();
        if (gr == null)
            return null;

        // 将每个方向走过的次数从大到小排序,下一步优先选次数多的方向走
        Location adjLocation;
        arr.sort(new Comparator<Node>() {
            @Override
            public int compare(Node a, Node b) {
                return (a.getCt() < b.getCt()) ? 1 : -1;
            }
        });

        for (Node node : arr) {
            adjLocation = this.getLocation().getAdjacentLocation(node.getDir());
            if (gr.isValid(adjLocation)
                    && (gr.get(adjLocation) == null || gr.get(adjLocation).getColor().equals(Color.RED))) {
                node.setCt(1);
                return adjLocation;
            }
        }
        return null;
    }

    // 判断当前位置是否可以继续移动
    public boolean canMove() {
        Grid<Actor> gr = getGrid();
        Actor adj;
        Location loc = this.getValid(this.getLocation());
        if (loc != null) {
            adj = gr.get(loc);
            next = loc;
            isEnd = adj != null && adj.getColor().equals(Color.RED);
            return true;
        }
        return false;
    }

    // 将甲虫的方向转向下一格,往前移动一步,将原来的位置压栈,并放置一朵绿花,表示走过的路径
    public void move() {
        Grid<Actor> gr = getGrid();
        if (gr == null)
            return;
        Location loc = this.getLocation();
        path.push(loc);
        this.setDirection(loc.getDirectionToward(next));
        this.moveTo(next);
        Flower flower = new Flower(this.getColor());
        flower.putSelfInGrid(gr, loc);
    }
}

其他:

跟算法无关的代码,比如GUI方面的都打包成lib.jar了,如果想要自己更改可以自行解压。Java实现可视化迷宫

代码地址如下:
http://www.demodashi.com/demo/14547.html

注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

原文地址:https://www.cnblogs.com/demodashi/p/10474104.html

时间: 2024-10-10 05:25:43

Java实现可视化迷宫的相关文章

java写的迷宫代码

迷宫代码:截图如下:package com.zxl.maze; /* * 抽象类表示选择不同的算法*/ public abstract class AbstractMap { /* * 得到数据*/ public abstract boolean[][] getData(int m,int n); /* * 重置*/ public abstract void reset(int m,int n); } package com.zxl.maze; /* *深度优先,生成迷宫*/ import ja

Java之可视化日历小练习

20150802 看完时间日期类Date和DateFormat以及日历类Calendar和GregorianCalendar类之后做个小日历来练习一下相应的方法和属性,加强一下记忆: 描述:输入指定格式的日期如:2001-12-25,输出当月的日历 思路: 1.先从键盘输入指定格式的字符串(str) 2.将字符串用格式转换类转换成日期( Date date = format.parse(str) ) 3.将日期设置成日历类( calendar.setTime(date) ) 4.获取当月第一天是

超级好用的 Java 数据可视化库:Tablesaw

本文适合刚学习完 Java 语言基础的人群,跟着本文可了解和使用 Tablesaw 项目.示例均在 Windows 操作系统下演示 本文作者:HelloGitHub-秦人 HelloGitHub 推出的<讲解开源项目>系列,今天给大家带来一款基于 Java 语言的数据可视化库开源项目--Tablesaw Tablesaw是一款 Java 的数据可视化库.它主要包括两部分:一部分是数据解析库,另一部分是数据可视化库.数据解析库主要是加载数据,对数据进行操作(转化,过滤,汇总等).数据可视化库就是

【JAVA】可视化计算器

import java.awt.Color; import java.awt.Font; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;   import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptE

java的WindowBuilder可视化插件

一直做在安卓用xml作界面,对于java的控件不熟悉,也不习惯用代码做UI尤其是布局. 找了一下发现可以安装windowbuilder来实现java的可视化编程,但是很多资料里的连接都失效了. 刚自己弄完比较熟悉,总结一下希望能帮到. 我是直接在线安装的windowbuilder 一.找到对应版本的windowbuilder 打开这个链接:http://www.eclipse.org/windowbuilder/download.php 如下图,显示eclipse的版本号和对应的插件链接 ecl

java可视化编程-eclipse安装windowbuilder插件(转载)

原文地址:http://blog.csdn.net/jason0539/article/details/21219043 一直做在安卓用xml作界面,对于java的控件不熟悉,也不习惯用代码做UI尤其是布局. 找了一下发现可以安装windowbuilder来实现java的可视化编程,但是很多资料里的连接都失效了. 刚自己弄完比较熟悉,总结一下希望能帮到. 我是直接在线安装的windowbuilder 一.找到对应版本的windowbuilder 打开这个链接:http://www.eclipse

R语言在数据处理上的禀赋之——可视化技术(一)

本文首发 https://program-dog.blogspot.com R语言在可视化上可谓非常出众,想必这也是为什么R语言在数据处理方面受到追捧的原因之一. 上一节已经大体了解了R语言的基本数据类型,以及优势所在.R的可视化技术同样也是优势大大滴.这也是R的数据类型为可视化立下汗马功劳,为啥这样说呢? Java的可视化技术 我们再拿Java开刀,和做一下对比.希望Java他老爹不要见怪.大家都知道,java做图真心说不上漂亮,为什么又拿java做对比呢?原因之一是我对java比较熟悉一点,

并发编程 || Java线程详解

通用线程模型 在很多研发当中,实际应用是基于一个理论再进行优化的.所以,在了解JVM规范中的Java线程的生命周期之前,我们可以先了解通用的线程生命周期,这有助于我们后续对JVM线程生命周期的理解. 首先,通用的线程生命周期有五种,分别是:新建状态(NEW).可运行状态(RUNNABLE).运行状态(RUN).休眠状态(SLEEP).终止状态(TERMINATED).生命流程如下图所示: 新建状态(NEW).线程在此状态,仅仅是在编程语言层面创建了此线程,而在真正的操作系统中是没有创建的.所以,

从零开始学数据分析,什么程度可以找到工作?( 内附20G、5000分钟数据分析工具教程大合集 )

从零开始学数据分析,什么程度可以找到工作?( 内附20G.5000分钟数据分析工具教程大合集 ) 我现在在Coursera上面学data science 中的R programming,过去很少接触过统计.计算机这两个学科,现在很想转行做数据.问题如下: 1.Data需要学到什么程度可以找工作?2.初级的数据分析会做哪些工作?3.数据分析有什么小方向吗?4.想要深度做数据分析有怎样的建议? 5.统计的学习应该从哪里下手? 本文将给你以上问题所有答案,文末还有UniCareer为大家独家整理的20