小白随机迷宫之A*求解

随机迷宫之A*求解思路:

经过网上学习及参考大佬大部分代码,小白愿与大家一起解析迷宫生成及A*自动寻路思路,共同走进迷宫这一小游戏。极乐世界,影影迷宫。

step1:迷宫随机生成

类BackGroundPanel中,用方法public void paintComponent(Graphics graphics){}
进行划线,画出网格。然后再用方法public static void getYANAiList(){},随机生成60个障碍方块

step2:A*自动求解

在这里插入图片描述

①、先找到A点方块的上下左右四个方块,计算出各个方块的G、H、F值,然后将A方块放入closedlist中(已经扩散过了),将A方块的上下左右四个方块放入openlist中(需要继续向外扩散)。然后遍历openlist集合,向外扩散。

②、以C点方块为例,找到C点方块的上下右三个点(其中左边的A点方块已经在closedlist中,所以不考虑),计算出GHF值后,将上下右三个方块放入openlist中,将C点方块放入closedlist中。

PS:一定要注意,G值,一定是前一个点G值+1得到。

一直向外扩散,当K点方块向外扩散的时候,周围方块包括了终点,将K点方块放入closedlist中,再将B点方块也放入closedlist中。。

此时的closedlist中,必然包括了从A点到B点的路线。那么,接下来就是如何取路线。

在第①步的时候,找到了A点方块的上下左右四个方块,在将这四个方块放入openlist之前添加一步:将这四个方块的父级方块设置为A方块。

在第②步的时候,找到了C点方块的上下右三个方块,也将这三个方块的父级方块设置为C方块,然后再放入openlist中。

同样,每次根据中心方块找到四周的方块的时候,都将四周的方块的父级方块设置为这个中心方块。那么在根据K方块找到四周方块的时候,你会发现,终点B方块的父级方块是K方块。

此时遍历closedlist,找到终点B方块,放入autowaylist中,然后再遍历closedlist,找到B方块的父级方块,让如autowaylist中。一直循环,直到找到起点A方块,放入autowaylist中,那么这个autowaylist集合就是路线。

这两部分一共包括六个类:

1、JPanel类扩展类BackGroundPanel

2、类FangKuaiPosition

3、JPanel类扩展类MyPanel

4、工具类CommonUtil

5、JFrame类扩展类BasePanel

6、类AutoFindWay

小白第一天

上午:背景面板类

类BackGroundPanel包含两个方法:

1、public BackGroundPanel(){}

用于布局管理

public BackGroundPanel(){
        this.setLayout(null);
    }

2、public void paintComponent(Graphics graphics){}

用于划线,生成迷宫网格

public void paintComponent(Graphics graphics){
        super.paintComponent(graphics);
        graphics.drawLine(50, 0, 50, 700);
        graphics.drawLine(100, 0, 100, 700);
        graphics.drawLine(150, 0, 150, 700);
        graphics.drawLine(200, 0, 200, 700);
        graphics.drawLine(250, 0, 250, 700);
        graphics.drawLine(300, 0, 300, 700);
        graphics.drawLine(350, 0, 350, 700);
        graphics.drawLine(400, 0, 400, 700);
        graphics.drawLine(450, 0, 450, 700);
        graphics.drawLine(500, 0, 500, 700);
        graphics.drawLine(550, 0, 550, 700);
        graphics.drawLine(600, 0, 600, 700);
        graphics.drawLine(650, 0, 650, 700);
        graphics.drawLine(700, 0, 700, 700);
        graphics.drawLine(750, 0, 750, 700);
        graphics.drawLine(800, 0, 800, 700);

        graphics.drawLine(0, 50, 850, 50);
        graphics.drawLine(0, 100, 850, 100);
        graphics.drawLine(0, 150, 850, 150);
        graphics.drawLine(0, 200, 850, 200);
        graphics.drawLine(0, 250, 850, 250);
        graphics.drawLine(0, 300, 850, 300);
        graphics.drawLine(0, 350, 850, 350);
        graphics.drawLine(0, 400, 850, 400);
        graphics.drawLine(0, 450, 850, 450);
        graphics.drawLine(0, 500, 850, 500);
        graphics.drawLine(0, 550, 850, 550);
        graphics.drawLine(0, 600, 850, 600);
        graphics.drawLine(0, 650, 850, 650);
    }
下午:方块类

类 FangKuaiPosition包含方法如下:

1、public FangKuaiPosition(int x, int y) {}

根据方块坐标生成方块(这里的坐标指的是网格坐标,不是像素坐标)

@param x, x方向的方块单位(即x方向像素/size)

@param y, y方向的方块单位(即y方向像素/size)

public FangKuaiPosition(int x, int y) {
        this.x = x;
        this.y = y;
    }

2、 public FangKuaiPosition(int x, int y,FangKuaiPosition fk) {}

根据方块坐标生成方块(这里的坐标指的是网格坐标),参数与方法1不同

@param x, x方向的方块单位(即x方向像素/size)

@param y, y方向的方块单位(即y方向像素/size)

@param fk, 前一个方块(即父级方块)

public FangKuaiPosition(int x, int y,FangKuaiPosition fk){
        this.x = x;
        this.y = y;
        this.previousFK = fk;
    }

3、public FangKuaiPosition(MyPanel myPpanel) {}

根据jp生成方块

@param myPpanel mypanel对象

 public FangKuaiPosition(MyPanel myPpanel) {
        this.x = myPpanel.getX() / MyPanel.size;
        this.y = myPpanel.getY() / MyPanel.size;
    }

4、一些get和set方法获取:

x方向的方块单位(即x方向像素/size);

y方向的方块单位(即y方向像素/size);

F,和值,G+H;

G,该点到出发点的移动量;

H,该点到目的点的估算移动量;

previousFK,父节点

public int getX () {
        return x;
    }

    public void setX ( int x){
        this.x = x;
    }

    public int getY () {
        return y;
    }

    public void setY ( int y){
        this.y = y;
    }
    
public int getF() {
        return F;
    }

    public void setF(int f) {
        F = f;
    }
    
    public int getG() {
        return G;
    }

    public void setG(int g) {
        G = g;
    }

    public int getH() {
        return H;
    }

    public void setH(int h) {
        H = h;
    }

    public FangKuaiPosition getPreviousFK() {
        return previousFK;
    }

    public void setPreviousFK(FangKuaiPosition previousFK) {
        this.previousFK = previousFK;
    }

5、public boolean equals(Object obj) {}

对equals方法重写,用于判断两个网格是否相等,以及比较这两个网格的X和Y是否相等

@Override
    public boolean equals(Object obj) {
        if(((FangKuaiPosition)obj).getX() == this.x && ((FangKuaiPosition)obj).getY() == this.y){
            return true;
        } else {
            return false;
        }
    }

小白第二天

上午:类MyPanel

MyPanel类包含两个方法:

1、public MyPanel(int x, int y){}

根据方块的位置设置panel的位置属性

@param x 方块的X单位

@param y 方块的Y单位

public MyPanel(int x, int y){
        this.setBounds(x * size, y * size, size, size);
    }

2、public MyPanel(FangKuaiPosition fk){}

根据类Fangkuaiposition设置panel位置属性

@param fk Fangkuaiposition对象

public MyPanel(FangKuaiPosition fk){
        this.setBounds(fk.getX() * size, fk.getY() * size, size, size);
    }
下午:工具类CommonUtil

类CommonUtil包含一个方法:

public static Dimension getScreenSize(){}

获取屏幕尺寸

@return Dimension

Dimension.getWidth()

Dimension.getHeight()

public static Dimension getScreenSize(){
        return Toolkit.getDefaultToolkit().getScreenSize();
    }

小白第三天

上午:类BasePanel–step1

类BasePanel包含:

1、定义一些必要变量如起点cat、终点fish 对象bgp,

集合如openList–需要向外扩散的方块集合,openList–已走过路线集合,YANList–地图上的障碍物或不可穿越地方的集合

    private static final int frameWidth = 815;//jf的宽
    public static int frameHeight;//jf的高
    public static int width;//内部panel的宽

    static {
        frameHeight = 635;
        width = 800;
    }

    private static final int height = 600;//内部panel的高
    public static int widthLength = 16;//方块单位的y方向最大值
    public static int heightLength = 12;//方块单位的x方向最大值
    public static BackGroundPanel bgp = new BackGroundPanel();//容器panel,所有的方块都放入这个panel中,然后将这个panel添加到jf中

    public static MyPanel cat = new MyPanel(0,0);//起点
    public static MyPanel fish = new MyPanel(ThreadLocalRandom.current().nextInt(widthLength),ThreadLocalRandom.current().nextInt(heightLength));//随机生成终点
    public static List<FangKuaiPosition> YANList = new ArrayList<>(); //地图上的障碍物/不可穿越地方的集合
    public static List<FangKuaiPosition> closedList; //已走过路线集合

    static {
        closedList = new ArrayList<>();
    }

    public static List<FangKuaiPosition> openList = new ArrayList<>(); //需要向外扩散的方块的集合

下午:类BasePanel–step2

2、方法1: public BasePanel(){}

 public BasePanel(){
        //获取屏幕尺寸相关信息
        Dimension dimension = CommonUtil.getScreenSize();
        //设定JFrame基础属性
        //jf的x坐标
        int beginX = (int) (dimension.getWidth() / 2 - 400);
        //jf的y坐标
        int beginY = (int) (dimension.getHeight() / 2 - 300);
        this.setBounds(beginX, beginY, frameWidth, frameHeight);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLayout(null);

        //起点panel
        cat.setBackground(Color.green);
        //终点panel
        fish.setBackground(Color.red);

        bgp.setBounds(0, 0, width, height);
        bgp.add(cat);
        bgp.add(fish);

        //将障碍物生成panel
        for(FangKuaiPosition fk : YANList){
            MyPanel panel = new MyPanel(fk);
            panel.setBackground(Color.gray);
            bgp.add(panel);
        }

        this.add(bgp);
        this.repaint();
        this.setVisible(true);
    }

3、主函数:

public static void main(String[] args) throws InterruptedException {}

public static void main(String[] args) throws InterruptedException {
        getYANAiList();
        BasePanel bp = new BasePanel();
        AutoFindWay afw = new AutoFindWay();
        List<FangKuaiPosition> wayList = afw.getWayLine(cat, fish);
        bp.movePanel(wayList);
    }

4、方法 2:

public void movePanel(List wayList) throws InterruptedException{}

方块移动

@param wayList 移动路线

@throws InterruptedException

public void movePanel(List<FangKuaiPosition> wayList) throws InterruptedException{

        if(wayList == null || wayList.size() == 0){
            System.out.println("无法 到达终点 !");
            return;
        }

        for(int i = wayList.size() - 2; i >= 0; i--){
            FangKuaiPosition fk = wayList.get(i);
            //向上
            //方块自动移动的间隔时间
            long sleepTime = 10;
            while(cat.getY() > fk.getY() * MyPanel.size){
                cat.setBounds(cat.getX(), cat.getY() - 2, MyPanel.size, MyPanel.size);
                Thread.sleep(sleepTime);
            }

            //向下
            while(cat.getY() < fk.getY() * MyPanel.size){
                cat.setBounds(cat.getX(), cat.getY() + 2, MyPanel.size, MyPanel.size);
                Thread.sleep(sleepTime);
            }

            //向左
            while(cat.getX() > fk.getX() * MyPanel.size){
                cat.setBounds(cat.getX() - 2, cat.getY(), MyPanel.size, MyPanel.size);
                Thread.sleep(sleepTime);
            }

            //向右
            while(cat.getX() < fk.getX() * MyPanel.size){
                cat.setBounds(cat.getX() + 2, cat.getY(), MyPanel.size, MyPanel.size);
                Thread.sleep(sleepTime);
            }

        }
        System.out.println("寻路结束!");
    }

5、方法3:

public static void getYANAiList(){}

随机生成障碍方块

public static void getYANAiList(){

        //随机生成60个障碍方块
        while(YANList.size() < 60){

            int x = ThreadLocalRandom.current().nextInt(widthLength);
            int y = ThreadLocalRandom.current().nextInt(heightLength);
            /*
             * 根据方块坐标生成方块(这里的坐标指的是网格坐标,不是像素坐标)
             * @param x	x方向的方块单位(即x方向像素/size)
             * @param y	y方向的方块单位(即y方向像素/size)
             */
            FangKuaiPosition fk = new FangKuaiPosition(x,y);
            //新生成的方块不能已存在YANlist中,也不能和起点/终点重合
            if(YANList.contains(fk) || (cat.getX() == fk.getX()*MyPanel.size && cat.getY() == fk.getY() * MyPanel.size)
                    || (fish.getX() == fk.getX() * MyPanel.size && fish.getY() == fk.getY() * MyPanel.size)){
                continue;
            }
            YANList.add(fk);
        }
    }

小白第四天

上午:类AutoFindWay–step1

类AutoFindWay包含:

1、定义必要变量beginFk、endFk

  public static FangKuaiPosition beginFk = null;
  public static FangKuaiPosition endFk = null;

2、主函数:

public static void main(String[] args) {}

public static void main(String[] args) {
        AutoFindWay afw = new AutoFindWay();
        MyPanel cat = new MyPanel(4,6);
        MyPanel fish = new MyPanel(10,10);
        afw.getWayLine(cat,fish);
}
下午:类AutoFindWay–step2

3、方法1:

public List getWayLine(MyPanel cat, MyPanel fish){}

获取路线方法入口

@param cat 起点

@param fish 终点

@return 路线集合List

public List<FangKuaiPosition> getWayLine(MyPanel cat, MyPanel fish){
        //定义返回的结果
        List<FangKuaiPosition> wayList = new ArrayList<>();
        //中心方块的四周方块集合
        List<FangKuaiPosition> tmpList;

        //将起点和终点转换为fangkuaiposition对象
        beginFk = new FangKuaiPosition(cat);
        beginFk.setG(0);
        endFk = new FangKuaiPosition(fish);

        //获取中心方块(起点)四周的方块

        tmpList = aroundFk(beginFk);
        //如果四周没有符合条件的方块,则说明是死路
        if(tmpList == null || tmpList.size() == 0){
            return wayList;
        }
        //放入openlist中,作为向外扩散的中心方块
        BasePanel.openList.addAll(tmpList);

        //遍历openlist,以每个方块作为中心方块,向外扩散
        for(int i = 0; i < BasePanel.openList.size(); i++){
            //获取新的中心方块,并获取四周方块
            FangKuaiPosition tmpFk = BasePanel.openList.get(i);
            tmpList = aroundFk(tmpFk);

            //周围方块为空,说明 是死路,继续下一个 方块
            if(tmpList == null || tmpList.size() == 0){
                //如果openlist已经遍历完了,都没有四周方块,则要在for循环外面判断waylist是否包含终点,
                //如果不包含,则到达不了终点
                continue;
            }

            //如果周围方块包括终点方块,则结束寻路
            if(tmpList.contains(endFk)){
                //如果四周方块包含终点,则将终点添加到closelist中,并跳出openlist循环(已经到达终点)
                for(FangKuaiPosition obj : tmpList){
                    if(obj.equals(endFk)){
                        BasePanel.closedList.add(obj);
                        break;
                    }
                }
                break;

小白第五天

上午:类AutoFindWay–step2
             /*
             * 将中心方块的周围方块添加到openlist集合
             * 注意:如果openlist中已经存在,则 需要将消耗最少的方块更新到 openlist中
             */
            for(FangKuaiPosition fk : tmpList){
                if(BasePanel.openList.contains(fk)){
                    for(FangKuaiPosition openFk : BasePanel.openList){
                        if(openFk.equals(fk)){
                            if(openFk.getG() > fk.getG()){
                                openFk.setG(fk.getG());
                                openFk.setF(openFk.getG() + openFk.getH());
                                openFk.setPreviousFK(fk.getPreviousFK());
                                break;
                            }
                        }
                    }
                }else{
                    BasePanel.openList.add(fk);
                }
            }

            //删掉openlist中的当前中心方块,继续获取并处理下一个
            BasePanel.openList.remove(i);
            i--;
        }
         /*
         * 从 closedlist中获取到路线
         * 先获取终点,然后根据fangkuaiposition.previousFk获取上一个方块,一直获取到起点
         */
        for(int i = 0; i < BasePanel.closedList.size(); i++){
            //如果wayList<=0,说明还没有获取到第一个方块(终点);如果wayList>0,说明已经获取到第一个方块(终点),则不用再执行下一个if语句
            if(wayList.size() > 0){
                if(wayList.get(wayList.size() - 1).getPreviousFK().equals(BasePanel.closedList.get(i))){
                    wayList.add(BasePanel.closedList.get(i));
                    //如果获取到的方块是起点,则跳出for循环
                    if(BasePanel.closedList.get(i).equals(beginFk)){
                        break;
                    }
                    //获取到一个方块后,将该方块从closedlist中删除,然后从0开始遍历closedlist找到第一个方块的previousfk。
                    //所以需要赋值i=-1,因为continue的时候会执行一次i++
                    BasePanel.closedList.remove(BasePanel.closedList.get(i));
                    i = -1;

                }
                continue;
            }

            //第一个方块为终点,获取到一个方块后,将该方块从closedlist中删除,然后从0开始遍历closedlist找到第一个方块的previousfk。
            //所以需要赋值i=-1,因为continue的时候会执行一次i++
            if(BasePanel.closedList.get(i).equals(endFk)){
                wayList.add(BasePanel.closedList.get(i));
                BasePanel.closedList.remove(BasePanel.closedList.get(i));
                i = -1;
            }
        }
        return wayList;
}
下午:类AutoFindWay–step3

4、方法2:

public List aroundFk(FangKuaiPosition fk){}

获取周围方块

①判断是否超越边界

②判断是否是障碍物/已计算过的方块

@param fk 中心方块

@return 周围方块结集合

    public List<FangKuaiPosition> aroundFk(FangKuaiPosition fk){
        if(fk.getX() == 10 && fk.getY() == 11){
            System.out.println(".....");
        }
        List<FangKuaiPosition> list = new ArrayList<>();
        //判断上面的方块是否符合条件
        //判断是否超过越边界
        if(fk.getY() - 1 >= 0){
            FangKuaiPosition tmpFk = new FangKuaiPosition(fk.getX(), fk.getY() - 1, fk);
            //判断是否是障碍物/已计算过的方块
            if(!BasePanel.YANList.contains(tmpFk)
                    && !BasePanel.closedList.contains(tmpFk)){
                list.add(tmpFk);
            }
        }

        //判断下面的方块是否符合条件
        if(fk.getY() + 1 < BasePanel.heightLength){
            FangKuaiPosition tmpFk = new FangKuaiPosition(fk.getX(), fk.getY() + 1, fk);
            if(!BasePanel.YANList.contains(tmpFk)
                    && !BasePanel.closedList.contains(tmpFk)){
                list.add(tmpFk);
            }
        }

        //判断左面的方块是否符合条件
        if(fk.getX() - 1 >= 0){
            FangKuaiPosition tmpFk = new FangKuaiPosition(fk.getX() - 1, fk.getY(), fk);
            if(!BasePanel.YANList.contains(tmpFk)
                    && !BasePanel.closedList.contains(tmpFk)){
                list.add(tmpFk);
            }
        }
        //判断右面的方块是否符合条件
        if(fk.getX() + 1 < BasePanel.widthLength){
            FangKuaiPosition tmpFk = new FangKuaiPosition(fk.getX() + 1, fk.getY(), fk);
            if(!BasePanel.YANList.contains(tmpFk)
                    && !BasePanel.closedList.contains(tmpFk)){
                list.add(tmpFk);
            }
        }

        //将中心方块添加到已处理过的集合中
        BasePanel.closedList.add(fk);
        getFGH(list,fk);
        return list;
    }

小白第六天

上午:类AutoFindWay–step3

5、方法3:

public void getFGH(List list,FangKuaiPosition currFk){}

给集合中的每个方块计算出FGH的值

@param list

   public void getFGH(List<FangKuaiPosition> list,FangKuaiPosition currFk){
        if(list != null && list.size() > 0){
            for(FangKuaiPosition fk : list){
                fk.setG(currFk.getG() + 1);
                fk.setH(toGetH(fk,endFk));
                fk.setF(fk.getG() + fk.getH());
            }
        }
    }

6、方法4:

public int toGetH(FangKuaiPosition currentFangKuai,FangKuaiPosition targetFangKuai){}

获取从一个方块到另一个方块的移动量(按方块个数计算)

@param currentFangKuai

@param targetFangKuai

@return

public int toGetH(FangKuaiPosition currentFangKuai,FangKuaiPosition targetFangKuai){
        int h = 0;
        h += Math.abs(currentFangKuai.getX() - targetFangKuai.getX());
        h += Math.abs(currentFangKuai.getY() - targetFangKuai.getY());
        return h;
    }
}
下午:运行查看

总代码:

BackGroundPanel.java

package Labyrinth_APP;

import java.awt.Graphics;
import javax.swing.JPanel;

public class BackGroundPanel extends JPanel{
    public BackGroundPanel(){
        this.setLayout(null);
    }

    public void paintComponent(Graphics graphics){
        super.paintComponent(graphics);
        graphics.drawLine(50, 0, 50, 700);
        graphics.drawLine(100, 0, 100, 700);
        graphics.drawLine(150, 0, 150, 700);
        graphics.drawLine(200, 0, 200, 700);
        graphics.drawLine(250, 0, 250, 700);
        graphics.drawLine(300, 0, 300, 700);
        graphics.drawLine(350, 0, 350, 700);
        graphics.drawLine(400, 0, 400, 700);
        graphics.drawLine(450, 0, 450, 700);
        graphics.drawLine(500, 0, 500, 700);
        graphics.drawLine(550, 0, 550, 700);
        graphics.drawLine(600, 0, 600, 700);
        graphics.drawLine(650, 0, 650, 700);
        graphics.drawLine(700, 0, 700, 700);
        graphics.drawLine(750, 0, 750, 700);
        graphics.drawLine(800, 0, 800, 700);

        graphics.drawLine(0, 50, 850, 50);
        graphics.drawLine(0, 100, 850, 100);
        graphics.drawLine(0, 150, 850, 150);
        graphics.drawLine(0, 200, 850, 200);
        graphics.drawLine(0, 250, 850, 250);
        graphics.drawLine(0, 300, 850, 300);
        graphics.drawLine(0, 350, 850, 350);
        graphics.drawLine(0, 400, 850, 400);
        graphics.drawLine(0, 450, 850, 450);
        graphics.drawLine(0, 500, 850, 500);
        graphics.drawLine(0, 550, 850, 550);
        graphics.drawLine(0, 600, 850, 600);
        graphics.drawLine(0, 650, 850, 650);
    }
}

FangKuaiPosition.java

package Labyrinth_APP;

public class FangKuaiPosition {
    /*
     * 根据方块坐标生成方块(这里的坐标指的是网格坐标,不是像素坐标)
     * @param x	x方向的方块单位(即x方向像素/size)
     * @param y	y方向的方块单位(即y方向像素/size)
     */
    public FangKuaiPosition(int x, int y) {
        this.x = x;
        this.y = y;
    }

    /*
     * 根据方块坐标生成方块(这里的坐标指的是网格坐标)
     * @param x	x方向的方块单位(即x方向像素/size)
     * @param y	y方向的方块单位(即y方向像素/size)
     * @param fk	前一个方块(父级方块)
     */
    public FangKuaiPosition(int x, int y,FangKuaiPosition fk) {
        this.x = x;
        this.y = y;
        this.previousFK = fk;
    }

    /*
     * 根据jp生成方块
     * @param myPpanel	mypanel对象
     */
    public FangKuaiPosition(MyPanel myPpanel) {
        this.x = myPpanel.getX() / MyPanel.size;
        this.y = myPpanel.getY() / MyPanel.size;
    }

    static public final int size = 50;//一个方块单位为50像素
    private int x;//x方向的方块单位(即x方向像素/size)
    private int y;//y方向的方块单位(即y方向像素/size)
    private int F;//和值,G+H
    private int G;//该点到出发点的移动量
    private int H;//该点到目的点的估算移动量
    private FangKuaiPosition previousFK;//父节点

    public int getF() {
        return F;
    }

    public void setF(int f) {
        F = f;
    }
    public int getG() {
        return G;
    }

    public void setG(int g) {
        G = g;
    }

    public int getH() {
        return H;
    }

    public void setH(int h) {
        H = h;
    }

    public FangKuaiPosition getPreviousFK() {
        return previousFK;
    }

    public void setPreviousFK(FangKuaiPosition previousFK) {
        this.previousFK = previousFK;
    }

    /*
     * 重写equals方法,判断两个网格是否相等,比较这两个网格的X和Y是否相等
     */
    @Override
    public boolean equals(Object obj) {
        if(((FangKuaiPosition)obj).getX() == this.x && ((FangKuaiPosition)obj).getY() == this.y){
            return true;
        } else {
            return false;
        }

    }

    public int getX () {
        return x;
    }

    public void setX ( int x){
        this.x = x;
    }

    public int getY () {
        return y;
    }

    public void setY ( int y){
        this.y = y;
    }
}

MyPanel.java

package Labyrinth_APP;
import javax.swing.JPanel;

public class MyPanel extends JPanel{
    //定义panel的默认size为50像素
    public final static int size = 50;
    /*
     * 根据方块的位置设置panel的位置属性
     * @param x	方块的X单位
     * @param y	方块的Y单位
     */
    public MyPanel(int x, int y){
        this.setBounds(x * size, y * size, size, size);
    }

    /*
     * 根据Fangkuaiposition类设置panel的位置属性
     * @param fk	Fangkuaiposition对象
     */
    public MyPanel(FangKuaiPosition fk){
        this.setBounds(fk.getX() * size, fk.getY() * size, size, size);
    }

}

CommonUtil.java


package Labyrinth_APP;
import java.awt.Dimension;
import java.awt.Toolkit;

public class CommonUtil {
    /*
     * 获取屏幕尺寸
     * @return	Dimension
     * Dimension.getWidth()
     * Dimension.getHeight()
     */
    public static Dimension getScreenSize(){
        return Toolkit.getDefaultToolkit().getScreenSize();
    }
}

BasePanel.java

package Labyrinth_APP;
import java.awt.Color;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import javax.swing.JFrame;

public class BasePanel extends JFrame{
    private static final int frameWidth = 815;//jf的宽
    public static int frameHeight;//jf的高
    public static int width;//内部panel的宽

    static {
        frameHeight = 635;
        width = 800;
    }

    private static final int height = 600;//内部panel的高
    public static int widthLength = 16;//方块单位的y方向最大值
    public static int heightLength = 12;//方块单位的x方向最大值
    public static BackGroundPanel bgp = new BackGroundPanel();//容器panel,所有的方块都放入这个panel中,然后将这个panel添加到jf中

    public static MyPanel cat = new MyPanel(0,0);//起点
    public static MyPanel fish = new MyPanel(ThreadLocalRandom.current().nextInt(widthLength),ThreadLocalRandom.current().nextInt(heightLength));//随机生成终点

    public static List<FangKuaiPosition> YANList = new ArrayList<>(); //地图上的障碍物/不可穿越地方的集合
    public static List<FangKuaiPosition> closedList; //已走过路线集合

    static {
        closedList = new ArrayList<>();
    }

    public static List<FangKuaiPosition> openList = new ArrayList<>(); //需要向外扩散的方块的集合

    public BasePanel(){
        //获取屏幕尺寸相关信息
        Dimension dimension = CommonUtil.getScreenSize();
        //设定JFrame基础属性
        //jf的x坐标
        int beginX = (int) (dimension.getWidth() / 2 - 400);
        //jf的y坐标
        int beginY = (int) (dimension.getHeight() / 2 - 300);
        this.setBounds(beginX, beginY, frameWidth, frameHeight);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLayout(null);

        //起点panel
        cat.setBackground(Color.green);
        //终点panel
        fish.setBackground(Color.red);

        bgp.setBounds(0, 0, width, height);
        bgp.add(cat);
        bgp.add(fish);

        //将障碍物生成panel
        for(FangKuaiPosition fk : YANList){
            MyPanel panel = new MyPanel(fk);
            panel.setBackground(Color.gray);
            bgp.add(panel);
        }

        this.add(bgp);
        this.repaint();
        this.setVisible(true);
    }

    public static void main(String[] args) throws InterruptedException {
        getYANAiList();
        BasePanel bp = new BasePanel();
        AutoFindWay afw = new AutoFindWay();
        List<FangKuaiPosition> wayList = afw.getWayLine(cat, fish);
        bp.movePanel(wayList);
    }

    /*
     * 方块移动
     * @param wayList	移动路线
     * @throws InterruptedException
     */
    public void movePanel(List<FangKuaiPosition> wayList) throws InterruptedException{

        if(wayList == null || wayList.size() == 0){
            System.out.println("无法 到达终点 !");
            return;
        }

        for(int i = wayList.size() - 2; i >= 0; i--){
            FangKuaiPosition fk = wayList.get(i);
            //向上
            //方块自动移动的间隔时间
            long sleepTime = 10;
            while(cat.getY() > fk.getY() * MyPanel.size){
                cat.setBounds(cat.getX(), cat.getY() - 2, MyPanel.size, MyPanel.size);
                Thread.sleep(sleepTime);
            }

            //向下
            while(cat.getY() < fk.getY() * MyPanel.size){
                cat.setBounds(cat.getX(), cat.getY() + 2, MyPanel.size, MyPanel.size);
                Thread.sleep(sleepTime);
            }

            //向左
            while(cat.getX() > fk.getX() * MyPanel.size){
                cat.setBounds(cat.getX() - 2, cat.getY(), MyPanel.size, MyPanel.size);
                Thread.sleep(sleepTime);
            }

            //向右
            while(cat.getX() < fk.getX() * MyPanel.size){
                cat.setBounds(cat.getX() + 2, cat.getY(), MyPanel.size, MyPanel.size);
                Thread.sleep(sleepTime);
            }

        }
        System.out.println("寻路结束!");
    }

    /*
     * 随机生成障碍方块
     */
    public static void getYANAiList(){

        //随机生成60个障碍方块
        while(YANList.size() < 60){

            int x = ThreadLocalRandom.current().nextInt(widthLength);
            int y = ThreadLocalRandom.current().nextInt(heightLength);
            /*
             * 根据方块坐标生成方块(这里的坐标指的是网格坐标,不是像素坐标)
             * @param x	x方向的方块单位(即x方向像素/size)
             * @param y	y方向的方块单位(即y方向像素/size)
             */
            FangKuaiPosition fk = new FangKuaiPosition(x,y);
            //新生成的方块不能已存在YANlist中,也不能和起点/终点重合
            if(YANList.contains(fk) || (cat.getX() == fk.getX()*MyPanel.size && cat.getY() == fk.getY() * MyPanel.size)
                    || (fish.getX() == fk.getX() * MyPanel.size && fish.getY() == fk.getY() * MyPanel.size)){
                continue;
            }
            YANList.add(fk);
        }
    }
}

AutoFindWay.java

package Labyrinth_APP;

import java.util.ArrayList;
import java.util.List;

public class AutoFindWay {
    public static FangKuaiPosition beginFk = null;
    public static FangKuaiPosition endFk = null;
    public static void main(String[] args) {
        AutoFindWay afw = new AutoFindWay();
        MyPanel cat = new MyPanel(4,6);
        MyPanel fish = new MyPanel(10,10);
        afw.getWayLine(cat,fish);
    }

    /*
     * 获取路线方法入口
     * @param cat	起点
     * @param fish	终点
     * @return	路线集合List<FangKuaiPosition>
     */
    public List<FangKuaiPosition> getWayLine(MyPanel cat, MyPanel fish){
        //定义返回的结果
        List<FangKuaiPosition> wayList = new ArrayList<>();
        //中心方块的四周方块集合
        List<FangKuaiPosition> tmpList;

        //将起点和终点转换为fangkuaiposition对象
        beginFk = new FangKuaiPosition(cat);
        beginFk.setG(0);
        endFk = new FangKuaiPosition(fish);

        //获取中心方块(起点)四周的方块

        tmpList = aroundFk(beginFk);
        //如果四周没有符合条件的方块,则说明是死路
        if(tmpList == null || tmpList.size() == 0){
            return wayList;
        }
        //放入openlist中,作为向外扩散的中心方块
        BasePanel.openList.addAll(tmpList);

        //遍历openlist,以每个方块作为中心方块,向外扩散
        for(int i = 0; i < BasePanel.openList.size(); i++){
            //获取新的中心方块,并获取四周方块
            FangKuaiPosition tmpFk = BasePanel.openList.get(i);
            tmpList = aroundFk(tmpFk);

            //周围方块为空,说明 是死路,继续下一个 方块
            if(tmpList == null || tmpList.size() == 0){
                //如果openlist已经遍历完了,都没有四周方块,则要在for循环外面判断waylist是否包含终点,
                //如果不包含,则到达不了终点
                continue;
            }

            //如果周围方块包括终点方块,则结束寻路
            if(tmpList.contains(endFk)){
                //如果四周方块包含终点,则将终点添加到closelist中,并跳出openlist循环(已经到达终点)
                for(FangKuaiPosition obj : tmpList){
                    if(obj.equals(endFk)){
                        BasePanel.closedList.add(obj);
                        break;
                    }
                }
                break;
            }

            /*
             * 将中心方块的周围方块添加到openlist集合
             * 注意:如果openlist中已经存在,则 需要将消耗最少的方块更新到 openlist中
             */
            for(FangKuaiPosition fk : tmpList){
                if(BasePanel.openList.contains(fk)){
                    for(FangKuaiPosition openFk : BasePanel.openList){
                        if(openFk.equals(fk)){
                            if(openFk.getG() > fk.getG()){
                                openFk.setG(fk.getG());
                                openFk.setF(openFk.getG() + openFk.getH());
                                openFk.setPreviousFK(fk.getPreviousFK());
                                break;
                            }
                        }
                    }
                }else{
                    BasePanel.openList.add(fk);
                }
            }

            //删掉openlist中的当前中心方块,继续获取并处理下一个
            BasePanel.openList.remove(i);
            i--;
        }

        /*
         * 从 closedlist中获取到路线
         * 先获取终点,然后根据fangkuaiposition.previousFk获取上一个方块,一直获取到起点
         */
        for(int i = 0; i < BasePanel.closedList.size(); i++){
            //如果wayList<=0,说明还没有获取到第一个方块(终点);如果wayList>0,说明已经获取到第一个方块(终点),则不用再执行下一个if语句
            if(wayList.size() > 0){
                if(wayList.get(wayList.size() - 1).getPreviousFK().equals(BasePanel.closedList.get(i))){
                    wayList.add(BasePanel.closedList.get(i));
                    //如果获取到的方块是起点,则跳出for循环
                    if(BasePanel.closedList.get(i).equals(beginFk)){
                        break;
                    }
                    //获取到一个方块后,将该方块从closedlist中删除,然后从0开始遍历closedlist找到第一个方块的previousfk。
                    //所以需要赋值i=-1,因为continue的时候会执行一次i++
                    BasePanel.closedList.remove(BasePanel.closedList.get(i));
                    i = -1;

                }
                continue;
            }

            //第一个方块为终点,获取到一个方块后,将该方块从closedlist中删除,然后从0开始遍历closedlist找到第一个方块的previousfk。
            //所以需要赋值i=-1,因为continue的时候会执行一次i++
            if(BasePanel.closedList.get(i).equals(endFk)){
                wayList.add(BasePanel.closedList.get(i));
                BasePanel.closedList.remove(BasePanel.closedList.get(i));
                i = -1;
            }
        }
        return wayList;
    }

    /*
     * 获取周围方块
     * ①判断是否超越边界
     * ②判断是否是障碍物/已计算过的方块
     * @param fk	中心方块
     * @return	周围方块结集合
     */
    public List<FangKuaiPosition> aroundFk(FangKuaiPosition fk){
        if(fk.getX() == 10 && fk.getY() == 11){
            System.out.println(".....");
        }
        List<FangKuaiPosition> list = new ArrayList<>();
        //判断上面的方块是否符合条件
        //判断是否超过越边界
        if(fk.getY() - 1 >= 0){
            FangKuaiPosition tmpFk = new FangKuaiPosition(fk.getX(), fk.getY() - 1, fk);
            //判断是否是障碍物/已计算过的方块
            if(!BasePanel.YANList.contains(tmpFk)
                    && !BasePanel.closedList.contains(tmpFk)){
                list.add(tmpFk);
            }
        }

        //判断下面的方块是否符合条件
        if(fk.getY() + 1 < BasePanel.heightLength){
            FangKuaiPosition tmpFk = new FangKuaiPosition(fk.getX(), fk.getY() + 1, fk);
            if(!BasePanel.YANList.contains(tmpFk)
                    && !BasePanel.closedList.contains(tmpFk)){
                list.add(tmpFk);
            }
        }

        //判断左面的方块是否符合条件
        if(fk.getX() - 1 >= 0){
            FangKuaiPosition tmpFk = new FangKuaiPosition(fk.getX() - 1, fk.getY(), fk);
            if(!BasePanel.YANList.contains(tmpFk)
                    && !BasePanel.closedList.contains(tmpFk)){
                list.add(tmpFk);
            }
        }
        //判断右面的方块是否符合条件
        if(fk.getX() + 1 < BasePanel.widthLength){
            FangKuaiPosition tmpFk = new FangKuaiPosition(fk.getX() + 1, fk.getY(), fk);
            if(!BasePanel.YANList.contains(tmpFk)
                    && !BasePanel.closedList.contains(tmpFk)){
                list.add(tmpFk);
            }
        }

        //将中心方块添加到已处理过的集合中
        BasePanel.closedList.add(fk);
        getFGH(list,fk);
        return list;
    }

    /*
     * 给集合中的每个方块计算出FGH的值
     * @param list
     */
    public void getFGH(List<FangKuaiPosition> list,FangKuaiPosition currFk){
        if(list != null && list.size() > 0){
            for(FangKuaiPosition fk : list){
                fk.setG(currFk.getG() + 1);
                fk.setH(toGetH(fk,endFk));
                fk.setF(fk.getG() + fk.getH());
            }
        }
    }

    /*
     * 获取从一个方块到另一个方块的移动量(按方块个数计算)
     * @param currentFangKuai
     * @param targetFangKuai
     * @return
     */
    public int toGetH(FangKuaiPosition currentFangKuai,FangKuaiPosition targetFangKuai){
        int h = 0;
        h += Math.abs(currentFangKuai.getX() - targetFangKuai.getX());
        h += Math.abs(currentFangKuai.getY() - targetFangKuai.getY());
        return h;
    }
}

运行效果:

1、随机迷宫之寻走不通:
在这里插入图片描述
在这里插入图片描述
2、随机迷宫之查路成功

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

迷宫万千,算法各异。求解迷宫方法有多种,广度优先搜索算法、dijkstra(迪杰斯特拉)算法、Greed-Best-First(最好优先贪婪算法)、A*算法。

其中最佳选择路径算法,则是A*算法,能求出迷宫最优解。

实随机迷宫生成及自动求解思路比较清晰,但是实际算法实现对于小白来说就十分困难。不过难度大就会迫使我们向优秀的大佬们学习,查看与之相关资料和学习视频。

不断学习积累,熟练一定程序语言与算法,小白会飞快成长,离成功之塔就更近一步。小白愿与大家学习交流,共同进步,欢迎大家留言!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值