【问题标题】:A-Star Pathfinding | Hexagonal GripA-Star 寻路 |六角握把
【发布时间】:2017-08-26 11:28:27
【问题描述】:

我是新手,遇到这么尴尬的问题:

.

算法似乎吞噬了一个额外的十六进制。 有人能指出我的错误吗?

代码如下:

(出现在1和2中的'ob'是一组'障碍')

1.寻路功能:

private static Node pathFinding(Vector2i startHex, Vector2i goalHex, HashSet<Vector2i> ob, int movesLeft) {
    start = new Node(startHex);
    goal = new Node(goalHex);

    if (distance(start, goal) > movesLeft) return null;

    TreeSet<Node> openSet = new TreeSet<>(new NodeComparator());
    HashSet<Node> closedSet = new HashSet<>();

    start.cameFrom = null;
    start.g = 0;
    start.h = distance(start, goal);
    start.f = start.g + start.h;
    openSet.add(start);

    float tentativeScore;

    while (!openSet.isEmpty()) {
        current = openSet.pollFirst();
        if (current.coords.equals(goal.coords)) {
            return current;
        }
        closedSet.add(current);
        for (Node node : getNeighbors(current, ob)) {
            node.g = distance(start, node);
            tentativeScore = current.g + distance(current, node);

            if (!closedSet.contains(node) || tentativeScore < node.g) {
                node.g = tentativeScore;
                node.h = distance(node, goal);
                node.f = node.g + node.h;
                node.cameFrom = current;
                openSet.add(node);
            }
        }
    }
    return null;
}

2.邻居:

private static final Vector2i[] directions2i = {
        new Vector2i(0,-1), new Vector2i(1,-1), new Vector2i(1,0),
        new Vector2i(0,1), new Vector2i(-1,1), new Vector2i(-1,0)
};

private static List<Node> getNeighbors(Node origin, HashSet<Vector2i> ob) {
    List<Node> list = new ArrayList<>();
    for (int i = 0; i < 6; i++) {
        Vector2i dir = new Vector2i(origin.coords.x + directions2i[i].x, origin.coords.y + directions2i[i].y);
        if (!ob.contains(dir))
            list.add(new Node(dir));
    }
    return list;
}

3.启发式:

static float distance(Node a, Node b) {
    return (Math.abs(a.coords.x - b.coords.x) + Math.abs(a.coords.y - b.coords.y) + Math.abs(a.coords.x +a.coords.y -b.coords.x -b.coords.y)) / 2;
}

4. 以防万一这里是 Node 和 Comparator 类:

public class Node {
public Node cameFrom;
public Vector2i coords;
float g, h, f;

@Override
public int hashCode() {
    return coords.x * 100000 + coords.y;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (getClass() != o.getClass()) return false;
    Node oth = (Node) o;
    return this.coords.equals(oth.coords);
}

Node(Vector2i coords) {
    this.coords = new Vector2i(coords);
}
}

private static class NodeComparator implements Comparator<Node> {
    @Override
    public int compare(Node o1, Node o2) {
        return Float.compare(o1.f, o2.f);
    }
}

【问题讨论】:

  • 不是答案,而是:代码有点难以阅读和理解,尤其是很难仅仅通过查看来“调试”。但以防万一你自己没有找到它:redblobgames.com/grids/hexagons 上有一个关于六边形网格的优秀资源(包括邻居搜索、距离计算,甚至有障碍物的路径查找)
  • openSet.add 是否返回 false
  • @Marco13 感谢您提供有用的信息!
  • @harold 我想是的。每次 set 已经包含该元素时,它都会返回 false
  • 这很糟糕,这意味着您不会更新您找到更好路线的节点的父/G。您可以尝试在添加之前删除节点。

标签: java algorithm path-finding a-star hexagonal-tiles


【解决方案1】:

TreeSet 不适合这种用法。它是根据比较器定义的(忽略 equals 的实现),但在 Open 集中具有相同 F 分数的多个节点(确实需要存在)是可能且常见的。这将导致本应存在的节点丢失,并且重复出现毫无意义。

顺便说一句,在插入节点之前删除它并不是一个真正的解决方案,只是一个快速破解(显然)使它在某些时候工作。您不应该将该建议视为可接受的解决方案,应该应用根本性的改变。

通常提到的方法是维护链接的优先级队列和哈希映射,其中优先级队列更新哈希映射以跟踪具有给定坐标的节点在队列中出现的位置。这可以快速查询 Open 集合中的“包含”(哈希图),快速从队列中删除带有给定坐标的节点(哈希图,然后告诉队列删除给定索引处的节点),并且仍然可以快速访问到 F 分数最低的节点(和往常一样)。

这种安排不能用内置的有序容器进行,因为队列需要知道哈希映射并更新它。幸运的是,二叉堆实现起来并不难,而且它也已经实现了,因此您可以借用现有的实现。

【讨论】:

  • 我考虑过使用简单的优先级队列,因为我不需要“包含”和“删除具有给定坐标的节点”来打开集。对吗?
  • @GrastaSsS 那么开放集中将有“死节点”,这些节点对应于次优前缀,因此永远不会对最优路由做出贡献,并且将浪费时间评估这些路径。但是,它仍然应该找到最佳路径。
  • 好的,我知道了。
猜你喜欢
  • 2011-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-27
  • 1970-01-01
  • 2020-06-06
  • 2012-02-06
相关资源
最近更新 更多