项目三:基于 A*搜索算法迷宫游戏开发

1、项目概述

1.1项目目标和主要内容

  1. 学习图形界面的设计,利用 MFC 应用程序(Java swing 或 QT 框架,或 C#)创建基于对话框的应用程序,添加按钮、编辑框等控件;
  2. 能通过设计的按钮控件输入并实现简单算术运算,要求表达式在编辑框中显示,能将运算结果,输出在编辑框内显示;并保存历史的表达式运算记录。
  3. 也能够实现混合运算的算术表达式求解,算术表达式中包括加、减、乘、除、括号等运算符;并且能够识别括号,优先级正确。

1.2项目的主要功能

  1. 计算器能够实现混合运算的算术表达式求解;
  2. 输入时有足够的容错机制,来尽量避免用户输入不合规的算术表达式。

2、项目设计

一个类实现将txt文件加载到idea变成字节流,再将字节流转换成地图,实现通路和墙体:

1·导入文件迷宫转化成地图

3·绘制地图并计数的函数

package maze_solution;
import java.io.File;
import java.util.ArrayList;
import java.util.Scanner;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class MazeSolution {

    public static void main(String[] args) throws Exception{
        Scanner in = new Scanner(System.in);
        System.out.println("Please enter the size(不能大于20) and name of your maze(数据的文件名)");
        int n = in.nextInt();
//        String filename = in.next();
//        File file=new File(filename);
        FileReader file = null;
        try {
            file = new FileReader(".//src//map.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
//      读取文件里面的数据,将其存入map二维数组中
        Scanner input = new Scanner(file);
        int[][] map = new int[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                map[i][j] = input.nextInt();
            }
        }

//		这是开始时间,用于记录搜索耗费的时间
        long startTime = System.currentTimeMillis();

//		定义地图的基本数据,长宽高,大小
        MapStructure QuestionMap = new MapStructure(map, map.length, new Point(0, 0),
                new Point(map.length-1, map.length-1));

        new Algorithm().Go(QuestionMap);
//		打印最后的map
        printAnswer(map);
//		记录结束时间
        long endTime=System.currentTimeMillis();
        System.out.println("This program takes " + (endTime - startTime) + "ms.");
    }

一个类实现地图绘制

public static void printAnswer(int[][] maps)
    {
        for (int i = 0; i < maps.length; i++)
        {
            for (int j = 0; j < maps[i].length; j++)
            {
                if (maps[i][j] == 1) {
//					这个代表墙体
                    System.out.print("🟦");
                }
                if (maps[i][j] == 0) {
//					这个代表路
                    System.out.print("0️⃣");
                }
                if (maps[i][j] == 2) {
//					这个代表最终的路径
                    System.out.print("*️⃣");
                }
//				System.out.print(maps[i][j] + " ");
            }
            System.out.println();
        }
    }

}

一个类判断是否找到终点

class Coord{
    int x;
    int y;

    Coord(int x, int y){
        this.x = x;
        this.y = y;
    }
    //判断是否已经到达了终点
    boolean isEquals(Coord goal){
        if(goal.x == x && goal.y == y)
            return true;
        return false;
    }
}

一个类表示节点(当前点,墙体,持有点)坐标

class Point{
    Coord coord;
    Point last;
    //	A*算法的两个参数
    int Gx;	//移动距离
    int Hx;	//离终点的距离
    int air;

    //节点坐标
    Point(int x, int y){
        this.coord = new Coord(x,y);
    }

    Point(Coord coord, Point parent, int Gx, int Hx){
        this.coord = coord;
        this.last = parent;
        this.Gx = Gx;
        this.Hx = Hx;
    }
    Point(int air){
        this.air = air;
    }
}

一个类初始化地图

//MapStructure定义结构
class MapStructure{
    int[][] map;
    int n;	//地图的宽和高
    Point start;
    Point end;

    MapStructure(int [][]map, int n, Point start,Point end){
        this.map = map;
        this.n = n;
        this.start = start;
        this.end = end;
    }

    void setMapStructure(Point start,Point end){
        this.start = start;
        this.end = end;
    }
}

一个类完成A*算法核心查找终点功能

//完成A*算法,并求出路径
class Algorithm{
    //	1代表墙
    final static int bar = 1;
    //	2代表选中,走的路径
    final static int sign = 2;
    final static int value = 1; //只能上下左右移动

    ArrayList<Point> openList = new ArrayList<Point>();		//放入可移动的路径
    ArrayList<Point> closeList = new ArrayList<Point>();	//放入走过的路径

    boolean isEndCoord(Coord coord, Coord end){
        if(coord != null && end.isEquals(coord))
//			已经到达终点
            return true;
        return false;
    }
    //是否能加入移动路径
    boolean canAddopenList(MapStructure map, int x, int y){
        if(x<0 || x>=map.n || y<0 || y>=map.n)
//			不能越界
            return false;
        if(map.map[y][x] == bar)
//			不能等于墙
            return false;
        if(isIncloseList(x, y))
            return false;
        if(isInopenList(x, y))
            return false;
//		以上判断判断都不符合,可以加入
        return true;
    }
    //	是否在放入可移动的路径
    boolean isInopenList(int x, int y){
        for(Point point : openList){
            if(x == point.coord.x && y == point.coord.y)
                return true;
        }
        return false;
    }
    //  是否在放入走过的路径
    boolean isIncloseList(int x, int y){
        if(closeList.isEmpty())
            return false;
        for(Point point : closeList){
            if(x == point.coord.x && y == point.coord.y)
                return true;
        }
        return false;
    }

    int calcHx(Coord goal, Coord end){
//		欧式距离
        return Math.abs(goal.x - end.x) + Math.abs(goal.y - end.y);
    }

    //	增加可移动的路径列表
    void addPointInopenList(MapStructure map, Point current, int x, int y){
        if(canAddopenList(map, x, y)){
            Point end = map.end;
            Point goal = new Point(x, y);
//			当前的距离加1
            int Gx = current.Gx + value;
//			这是与目标的距离
            int Hx = calcHx(current.coord, end.coord);
//			判断是否在终点
            if(isEndCoord(goal.coord, end.coord)){
                goal = end;
                goal.last = current;
                goal.Gx = Gx;
                goal.Hx = calcHx(goal.coord, end.coord);
            }
            else
//				如果不在,继续寻找
                goal = new Point(goal.coord, current, Gx, Hx);
//			加入放入可移动的路径
            openList.add(goal);
        }
    }

    void addPointInopenList(MapStructure map, Point current){
        int x = current.coord.x;
        int y = current.coord.y;

        addPointInopenList(map, current, x-1, y);	//左
        addPointInopenList(map, current, x+1, y);	//右
        addPointInopenList(map, current, x, y-1);	//上
        addPointInopenList(map, current, x, y+1);	//下
    }

    void drawPath(int[][] map, Point end){
        while(end != null){
            map[end.coord.y][end.coord.x] = sign;
            end = end.last;
        }
    }
    //获得路径信息
    void getPathInformation(int[][] map, Point end){
        int path = 1;
        System.out.println("The shortest path needs " + end.Gx + " steps.");
        while(end != null){
//			可移动路径进行遍历
            for(Point point : openList){
                if(point.Hx == end.Hx && point.Gx == end.Gx){
                    path++;
                }
            }
            map[end.coord.y][end.coord.x] = sign;
            end = end.last;
        }
        System.out.println("The shortest path number is " + path);
    }

    //从地图左上角为所有的0赋予
    void addBreathbyLeft(MapStructure map){
        int i = 0;
        int j = 0;
        Point zero = new Point(j, i);
        for (i = 1; i < map.n-1; i++) {
            for (j = 1; j < map.n-1; j++) {
                zero.air = 0;
                if(map.map[i][j] == 0){
                    if(map.map[i][j+1] == 0)
                        zero.air++;
                    if(j >= 1 && map.map[i][j-1] == 0)
                        zero.air++;
                    if(map.map[i+1][j] == 0)
                        zero.air++;
                    if(i >= 1 && map.map[i-1][j] == 0)
                        zero.air++;
                }

                if(zero.air <= 1){		//判断迷宫中只有一格气的点,说明它是死胡同,将它改为1
                    map.map[i][j] = bar;
//					System.out.println("0➡1");
                }
            }
        }
    }

    //从地图右下角为所有的0赋予气
    void addBreathbyRight(MapStructure map){
        int i = 0;
        int j = 0;
        Point zero = new Point(j, i);
        for (i = map.n-2; i > 0; i--) {
            for (j = map.n-2; j > 0; j--) {
                zero.air = 0;
                if(map.map[i][j] == 0){
                    if(map.map[i][j+1] == 0)
                        zero.air++;
                    if(j >= 1 && map.map[i][j-1] == 0)
                        zero.air++;
                    if(map.map[i+1][j] == 0)
                        zero.air++;
                    if(i >= 1 && map.map[i-1][j] == 0)
                        zero.air++;
                }
                if(zero.air <= 1){		//判断迷宫中只有一格气的点,说明它是死胡同,将它改为1
                    //System.out.println("0➡1");
                    map.map[i][j] = bar;

                }
            }
        }
    }

    //从左上和右下两次遍历,将所有死胡同堵死
    void changeBreath(MapStructure map){
        for (int i = 0; i < map.map.length-1; i++) {
            addBreathbyLeft(map);
            addBreathbyRight(map);
        }
        System.out.println("Get rid of all dead paths (they don't have extra breath)");
    }

一个类进行执行,完成路径数,查找时间,和最佳路线的输出

 void Go(MapStructure map){
        if(map == null){
//			初始化失败
            System.out.println("The map structure is wrong!");
            return;
        }
        openList.clear();
        closeList.clear();

        changeBreath(map);

        openList.add(map.start);
        movePoint(map);
        getPathInformation(map.map, map.end);
        System.out.println("This is all maze paths (The sign of the pathway is '2'):");
    }

    void Back(MapStructure map){
        openList.clear();
        closeList.clear();
        openList.add(map.start);
        movePoint(map);
    }

    Point findMinInopenList(){
        Point goal = openList.get(0);
        for(Point point : openList){
            if(point.Gx + point.Hx < goal.Gx + goal.Hx)
                goal = point;
        }
        return goal;
    }

    void movePoint(MapStructure map){
        while(!openList.isEmpty()){
            Point goal = findMinInopenList();
            openList.remove(goal);
            closeList.add(goal);
            addPointInopenList(map, goal);
            if(isIncloseList(map.end.coord.x, map.end.coord.y)){
                drawPath(map.map, map.end);
                break;
            }
        }
    }
}

2.2关键算法分析

深度遍历的算法描述

更新访问点数组信息,在邻接矩阵当中找到该访问点的最近的邻接点,访问该邻接点,输出遍历顶点信息,重复此步骤。

当无法继续深度遍历的时候,在访问点数组中往后依次退1,直到能有一个顶点能继续深度遍历。

当起点都无法继续深度遍历的时候,对图的深度遍历已完成

实际就是从第n个顶点开始、标记该顶点已被访问,然后查找该顶点第一个未访问的邻接点第i个顶点,再去第i个顶点 深度遍历。就是一个递归的过程。

广度优先搜索

又被称为宽度优先搜索,是最常见的图搜索方法之一。

广度优先搜索指从某个节点(源点)出发,一次性访问所有未被访问的邻接点,再依次从这些已访问过的邻接点出发,一层一层地访问。

BFS以起点A为圆心,先搜索A周围的所有点,形成一个类似圆的搜索区域,再扩大搜索半径,进一步搜索其它没搜索到的区域,直到终点B进入搜索区域内被找到。

再看一下深度优先搜索,这里的深度优先搜索不是所有路径都搜索而是沿着B点方向搜索。(图片来源网上)。DFS则是让搜索的区域离A尽量远,离B尽量近,比如现在你在一个陌生的大学校园里,你知道校门口在你的北方,虽然你不知道你和校门口之间的路况如何,地形如何,但是你会尽可能的往北方走,总能找到校门口。

比起BFS,DFS因为尽量靠近终点的原则,其实是用终点相对与当前点的方向为导向,所以有一个大致的方向,就不用盲目地去找了,这样,就能比BFS能快地找出来最短路径,但是这种快速寻找默认起点A终点B之间没有任何障碍物,地形的权值也都差不多。如果起点终点之间有障碍物,那么DFS就会出现绕弯的情况。

图中DFS算法使电脑一路往更右下方的区域探索,可以看出,在DFS遇到障碍物时,其实没有办法找到一条最优的路径,只能保证DFS会提供其中的一条路径(如果有的话)。

BFS保证的是从起点到达路线上的任意点花费的代价最小(但是不考虑这个过程是否要搜索很多格子);DFS保证的是通过不断矫正行走方向和终点的方向的关系,使发现终点要搜索的格子更少(但是不考虑这个过程是否绕远)。

A*算法的设计同时融合了BFS和DFS的优势,既考虑到了从起点通过当前路线的代价(保证了不会绕路),又不断的计算当前路线方向是否更趋近终点的方向(保证了不会搜索很多图块),是一种静态路网中最有效的直接搜索算法。

3、项目实现及结果分析

1)迷宫界面展示:

4、实验总结

1·地图需要自己提前设计二位数组来分辨路径和墙体,如果需要多付地图则需要更多的文件。

2·自己需要将地图导入进入程序之中

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落不心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值