A星探路 |六角握把
A-Star Pathfinding | Hexagonal Grip
我是新手,遇到这么尴尬的问题:
.
算法似乎多吞了一个十六进制。
有人能指出我的错误吗?
代码如下:
(1和2中出现的'ob'是'obstacles'的集合)
1.Pathfinding 函数:
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.Neighbors:
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.Heuristic:
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.And 以防万一这里是节点和比较器 类:
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);
}
}
TreeSet
不适合这种用法。它是根据比较器定义的(忽略 equals
的实现),但在开放集中有多个节点(确实需要存在)具有相同的 F 分数是可能的,也是常见的。这将导致本应存在的节点丢失,以及重复项的无意义存在。
顺便说一下,在插入节点之前删除节点并不是一个真正的解决方案,这只是一个(显然)使其在某些时候起作用的快速破解。您不应将该建议视为可接受的解决方案,应进行根本性的更改。
一种通常提到的方法是维护链接的优先级队列和哈希图,其中优先级队列更新哈希图以跟踪具有给定坐标的节点在队列中出现的位置。这使得可以快速查询 "contains" 的开放集(hashmap),从队列中快速删除具有给定坐标的节点(hashmap,然后告诉队列删除给定索引处的节点),并且仍然给出快速访问 F 分数最低的节点(像往常一样)。
这种安排不能用内置的有序容器进行,因为队列需要知道散列映射并更新它。幸运的是,二叉堆并不难实现,而且也已经完成,因此您可以借用现有的实现。
我是新手,遇到这么尴尬的问题:
算法似乎多吞了一个十六进制。 有人能指出我的错误吗?
代码如下:
(1和2中出现的'ob'是'obstacles'的集合)
1.Pathfinding 函数:
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.Neighbors:
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.Heuristic:
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.And 以防万一这里是节点和比较器 类:
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);
}
}
TreeSet
不适合这种用法。它是根据比较器定义的(忽略 equals
的实现),但在开放集中有多个节点(确实需要存在)具有相同的 F 分数是可能的,也是常见的。这将导致本应存在的节点丢失,以及重复项的无意义存在。
顺便说一下,在插入节点之前删除节点并不是一个真正的解决方案,这只是一个(显然)使其在某些时候起作用的快速破解。您不应将该建议视为可接受的解决方案,应进行根本性的更改。
一种通常提到的方法是维护链接的优先级队列和哈希图,其中优先级队列更新哈希图以跟踪具有给定坐标的节点在队列中出现的位置。这使得可以快速查询 "contains" 的开放集(hashmap),从队列中快速删除具有给定坐标的节点(hashmap,然后告诉队列删除给定索引处的节点),并且仍然给出快速访问 F 分数最低的节点(像往常一样)。
这种安排不能用内置的有序容器进行,因为队列需要知道散列映射并更新它。幸运的是,二叉堆并不难实现,而且也已经完成,因此您可以借用现有的实现。