算法07-游戏寻路算法A-star的分析与实现

算法07-A-star

一、介绍

A-Star(A*)算法是一种静态路网中求解最短路最有效的方法。它使用估价函数表示走下一个点时的路程,然后从能走的点中选取路程最小的那个点开始走,直到走到终点。估价函数公式表示为:f(n)=g(n)+h(n),其中f(n)是节点n从初始点到目标点的估价函数,g(n)是在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目标节点最佳路径的估计代价。

A-Star算法分为简化版和完整版。

A-Star算法的应用:游戏找路。

二、A-Star简化版

1、基本思想

A-Star算法简化版只需把走过的点标记为1,当到达终点时,所有为1的点,即为最短路径。适用于没有凹形障碍物的情形,效率比完整版高。

操作步骤:

  1. 从起点开始,通过估价函数f(n),判断周围8个点中,使得路径最短的那个点A,将这个点标记为1(代表走过);
  2. 把A当作新的起点,重复步骤1,直到到达终点。

2、关键代码

/**
 * @param map  用一个二维数组表示地图。其中0表示走得通,1表示走过,2表示终点,3表示不能走。
 * @param star 起点
 * @param end  终点
 */
public static void AStarSort(int[][] map, Point star, Point end) {
    if (star == null && end == null) {
        return;
    }
    if (star.x >= map.length - 1 || star.y >= map[0].length - 1) {
        return;
    }
    //到达终点
    if (star.x == end.x && star.y == end.y) {
        return;
    }
    // 标记当前位置已走过
    map[star.x][star.y] = 1;
    //查找下一个能走的点
    Point next = lookup(map, star, end);
    //以next为起点,重新找寻
    AStarSort(map, next, end);
}

/**
 * 查找下一个要走的点
 *
 * @param map   地图
 * @param point 当前点
 * @param end   终点
 * @return 下一个要走的点
 */
public static Point lookup(int[][] map, Point point, Point end) {
    //保存所有可走的点
    List<Point> result = new ArrayList<>();
    // 表示1维数组下标
    int x = point.x;
    // 表示2维数组下标
    int y = point.y;
    // 如果左边可以走,就添加到result
    if (map[x - 1][y] != 1 && map[x - 1][y] != 3) {
        result.add(new Point(x - 1, y));
    }
    // 如果上边可以走,就添加到result
    if (map[x][y - 1] != 1 && map[x][y - 1] != 3) {
        result.add(new Point(x, y - 1));
    }
    // 如果右边可以走,就添加到result
    if (map[x + 1][y] != 1 && map[x + 1][y] != 3) {
        result.add(new Point(x + 1, y));
    }
    // 如果下边可以走,就添加到result
    if (map[x][y + 1] != 1 && map[x][y + 1] != 3) {
        result.add(new Point(x, y + 1));
    }
    // 如果左上可以走,就添加到result
    if (map[x - 1][y - 1] != 1 && map[x - 1][y - 1] != 3) {
        result.add(new Point(x - 1, y - 1));
    }
    // 如果右下可以走,就添加到result
    if (map[x + 1][y - 1] != 1 && map[x + 1][y - 1] != 3) {
        result.add(new Point(x + 1, y - 1));
    }
    // 如果左下可以走,就添加到result
    if (map[x - 1][y + 1] != 1 && map[x - 1][y + 1] != 3) {
        result.add(new Point(x - 1, y + 1));
    }
    // 如果右下可以走,就添加到result
    if (map[x + 1][y + 1] != 1 && map[x + 1][y + 1] != 3) {
        result.add(new Point(x + 1, y + 1));
    }

    //如果没有可走的点,就退出寻路
    if (result.size() == 0) {
        return null;
    }

    int index = 0;
    int dstance = Integer.MAX_VALUE;
    for (int i = 0; i < result.size(); i++) {
        //使用估价函数,估算两点的距离
        int l = getDistance(point, result.get(i), end);
        //找到距离最小的那个点
        if (dstance > l) {
            index = i;
            dstance = l;
        }
    }
    return result.get(index);
}

/**
 * 估价函数:起点与节点的距离+节点与终点的距离
 *
 * @param start 起点
 * @param node  节点
 * @param end   终点
 * @return 估价的距离
 */
public static int getDistance(Point start, Point node, Point end) {
    return getDistance(start, node) + getDistance(node, end);
}

/**
 * 计算两点距离
 *
 * @param start 起点
 * @param end   终点
 * @return 两点距离
 */
public static int getDistance(Point start, Point end) {
    if (start == null || end == null) {
        return Integer.MAX_VALUE;
    }
    int a = end.x - start.x;
    int b = end.y - start.y;
    int c = (int) Math.sqrt(a * a + b * b);
    return c;
}

三、A-Star完整版

1、基本思想

A-Star算法完整版每次从所有能走的点中选取最小的点A开始走,然后以A为节点动态更新所有能走的点的估价距离,并且保存A的上一个节点。当走到重点时,就可以通过保存的上一个节点,回退实际的最短路线。

A-Star算法完整版适用于有凹形障碍物的情形。

操作步骤:

  1. 声明openList保存所有能走的点,声明closeList保存所有走过的点,把起点start添加到openList中
  2. 从openList中最小的点A开始走,把A添加到closeList中,然后重新查找所有可走的点。如果找到的点p之前不在openList中,把它直接添加到openList中;如果p之前就在openList中,判断经过节点A时p的估价L1与走原来方向时p的估价距离L2的大小,如果L1

2、代码实现

/**
 * @param mapInfo 地图
 * @return 实际的最短路线
 */
public static Stack<Node> AStarSort(MapInfo mapInfo) {
    if (mapInfo == null) {
        return null;
    }
    // 使用自动排序队列表示能走的点
    Queue<Node> openList = new PriorityQueue<>();
    // 已经走过的点
    List<Node> closeList = new ArrayList<>();
    openList.add(mapInfo.start);

    //如果有路可以走
    while (!openList.isEmpty()) {
        // 到达终点
        if (isColsed(closeList, mapInfo.end)) {
            //展示路线
            Stack<Node> result = drawPath(mapInfo);
            return result;
        }

        // 获取最小的节点
        Node now = openList.poll();
        //标记now已走过
        closeList.add(now);
        //寻找下一个可走的点
        addNext(mapInfo, openList, closeList, now);
    }
    return null;
}

/**
 * 寻找下一个可走的点
 *
 * @param mapInfo   地图
 * @param openList  能走的点
 * @param closeList 走过的点
 * @param now       当前点
 */
private static void addNext(MapInfo mapInfo, Queue<Node> openList, List<Node> closeList, Node now) {
    //一维数组的下标
    int x = now.p.x;
    //二维数组的下标
    int y = now.p.y;
    addNext(mapInfo, openList, closeList, now, new Point(x - 1, y), DIRECT_VALUE);// 左
    addNext(mapInfo, openList, closeList, now, new Point(x, y - 1), DIRECT_VALUE);// 上
    addNext(mapInfo, openList, closeList, now, new Point(x + 1, y), DIRECT_VALUE);// 右
    addNext(mapInfo, openList, closeList, now, new Point(x, y + 1), DIRECT_VALUE);// 下
    addNext(mapInfo, openList, closeList, now, new Point(x - 1, y - 1), OBLIQUE_VALUE);// 左上
    addNext(mapInfo, openList, closeList, now, new Point(x + 1, y - 1), OBLIQUE_VALUE);// 右上
    addNext(mapInfo, openList, closeList, now, new Point(x - 1, y + 1), OBLIQUE_VALUE);// 左下
    addNext(mapInfo, openList, closeList, now, new Point(x + 1, y + 1), OBLIQUE_VALUE);// 右下
}


/**
 * 寻找下一个可走的点
 *
 * @param mapInfo   地图
 * @param openList  能走的点
 * @param closeList 走过的点
 * @param now       当前点
 * @param p         下一个准备走的点
 * @param value     从now到p的距离
 */
private static void addNext(MapInfo mapInfo, Queue<Node> openList, List<Node> closeList, Node now, Point p, int value) {
    //先判断当前点能否添加到openList中:如果已经走过或者是障碍物,就不添加到openList中
    if (canAddOpen(mapInfo, closeList, p)) {
        Node end = mapInfo.end;
        // 从起点到p的距离
        int g = now.g + value;
        //从openList找p
        Node child = findOpen(openList, p);
        //如果是第一次遇到这个点
        if (child == null) {
            //p到终点的距离
            int h = calcH(end.p, p) * DIRECT_VALUE;
            //如果到达终点,就更新end的值,然后添加到openList中
            if (p.equals(end.p)) {
                child = end;
                //设置parent是为从end节点推路线图
                child.parent = now;
                child.g = g;
                child.h = h;
                //如果不是终点,就新建一个对象
            } else {
                child = new Node(p, now, g, h);
            }
            //添加到openList中
            openList.add(child);
            //如果p已经在openList中,走当前路线的距离小于原来路线的距离,就用当前的g值替换原来的g值
        } else if (child.g > g) {
            child.g = g;
            //从now节点走,比原来的路线近,所以将它的parent设为now
            child.parent = now;
            // 重新排序,以便选择距离最小的点
            openList.add(child);
        }
    }
}

demo已上传gitee,需要的同学可以下载!

上一篇:算法06-动态规划

下一篇:算法08-回溯法

扫描二维码关注公众号,回复: 122568 查看本文章

猜你喜欢

转载自blog.csdn.net/dshdhsd/article/details/80002167