Algorithm 07 - Analysis and Implementation of Game Pathfinding Algorithm A-star

Algorithm 07-A-star

1. Introduction

The A-Star (A*) algorithm is the most effective method for solving the shortest path in a static road network. It uses the evaluation function to represent the distance when walking to the next point, and then selects the point with the smallest distance from the points that can be walked, and starts walking until it reaches the end point. The evaluation function formula is expressed as: f(n)=g(n)+h(n), where f(n) is the evaluation function of node n from the initial point to the target point, and g(n) is the state space from the initial point to the target point. The actual cost of node to node n, h(n) is the estimated cost of the best path from n to the target node.

A-Star algorithm is divided into simplified version and full version.

Application of A-Star algorithm: game wayfinding.

2. A-Star simplified version

1. Basic idea

The simplified version of the A-Star algorithm only needs to mark the points passed by as 1. When the end point is reached, all the points with a value of 1 are the shortest path. It is suitable for situations without concave obstacles, and the efficiency is higher than that of the full version.

Steps:

  1. Starting from the starting point, through the evaluation function f(n), determine the point A with the shortest path among the 8 surrounding points, and mark this point as 1 (representing walking);
  2. Using A as the new starting point, repeat step 1 until the end point is reached.

2. Key code

/**
 * @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;
}

3. A-Star full version

1. Basic idea

The full version of the A-Star algorithm selects the smallest point A from all the points that can be walked to start walking, and then uses A as the node to dynamically update the estimated distance of all the points that can be walked, and saves the previous node of A. When you reach the key point, you can go back to the actual shortest route through the saved last node.

The full version of the A-Star algorithm is suitable for situations with concave obstacles.

Steps:

  1. Declare openList to save all points that can be walked, declare closeList to save all points that can be walked, and add the starting point start to openList
  2. Start walking from the smallest point A in the openList, add A to the closeList, and then re-find all the points that can be walked. If the found point p was not in the openList before, add it directly to the openList; if p was in the openList before, judge the size of the estimated distance L1 of p when passing through node A and the estimated distance L2 of p when walking in the original direction, if L1

2. Code implementation

/**
 * @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);
        }
    }
}

The demo has been uploaded to gitee, and students who need it can download it!

Previous: Algorithm 06 - Dynamic Programming

Next:Algorithm 08 - Backtracking

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325473316&siteId=291194637