我正在尝试构建一个实现A *的应用程序,但我在处理逻辑方面遇到了麻烦。这里的方法采用4个int(startX / Y,goalX / Y),然后使用A *算法,它将构建一个ArrayList并返回它,因此main方法可以迭代并显示路径A * builds。但我得到的是一条跳跃的路径,最终建立一条通往目标节点的非常厚的路径。任何人都可以找出我的错误所在。
注意:open和closed是优先级队列,而Tile实现了可比性。
public ArrayList<Tile> findPath(int sX, int sY, int gX, int gY)
{
ArrayList<Tile> path = new ArrayList<Tile>();
open.offer(gameMap[sX][sY]);
Tile currentNode = gameMap[sX][sY];
Tile goalNode = gameMap[gX][gY];
int cX;
int cY;
while(open.size() > 0){
currentNode = open.poll();
closed.offer(currentNode);
path.add(currentNode);
cX = currentNode.getX();
cY = currentNode.getY();
if(currentNode == goalNode){
break;
}
if((cX > 0 && cX < gameMap.length - 1) && (cY > 0 && cY < gameMap.length -1)){
for(int i = -1; i < 2; i++){
for(int j = 1; j > -2; j--){
if(i == 0 && j == 0){}
else{
if((gameMap[cX + i][cX + j].type != 1) && !closed.contains(gameMap[cX + i][cX + j])){
if(!open.contains(gameMap[cX + i][cX + j])){
open.offer(gameMap[cX + i][cX + j]);
gameMap[cX + i][cX + j].parent = currentNode;
}
}
}
}
}
}
}
// while(currentNode != gameMap[sX][sY]){
// path.push(currentNode);
// currentNode = currentNode.parent;
// }
return path;
}
答案 0 :(得分:1)
首先,我不认为您的closed
集需要成为优先级队列。它只是一组已经查看过的节点。
你似乎错过了A *如何工作的核心部分,这就是为什么我认为这个路径查找器不适合你。
以下是主要想法:
具有启发式功能,可以猜测目的地的距离。理想情况下,该功能将可接受,这意味着它永远不会高估距离。
对于平铺网格,可以使用曼哈顿距离(x差+ y差)完成,因为这是最小距离,因此它始终是可接受的。
每当您从打开的列表中取出一个磁贴并将其添加到闭合集时,您需要更新相邻磁贴的距离的已知值(保持最低的已知值)。由于您已将已放入封闭集的区块的已知值,因此您只需向所有邻居添加1即可。已知值。
通过更新这些值,打开列表可能会改变顺序(这就是为什么优先级队列在这里是一个不错的选择)。启发式值可能保持不变,但已知值将更加精确。
到达目的地后,您将拥有一组已知距离的已关闭节点。然后,您从目的地回溯,查看也在闭合集中的每个邻居,并选择已知距离最小的邻居。
就如何实现这一点而言,您可能需要考虑将Tile
类包含在另一个名为SearchTile
的类中或类似的类中。它看起来像这样:
//You may not want to use public variables, depending on your needs
public class SearchTile implements Comparable<SearchTile> {
public final Tile tile;
//These need to be updated
public int knownDistance = 0;
public int heuristicDistance = 0;
public SearchTile(final Tile tile) {
this.tile = tile;
}
@Override
public int compareTo(final SearchTile other) {
if (knownDistance + heuristicDistance > other.knownDistance + other.heuristicDistance) {
return 1;
} else if (knownDistance + heuristicDistance < other.knownDistance + other.heuristicDistance) {
return -1;
} else {
return 0;
}
}
}
关于A *的一个很酷的事情是,在理想的情况下,它应该直接到达目的地。在有墙的情况下,它会采取最佳猜测,只要启发式是可以接受的,它就会提出最佳解决方案。
答案 1 :(得分:0)
我还没有完全详细介绍您的实施细节,但我想到您在OPEN中插入节点的方式可能会导致问题:
if(!open.contains(gameMap[cX + i][cX + j])){
open.offer(gameMap[cX + i][cX + j]);
gameMap[cX + i][cX + j].parent = currentNode;
}
您的目标是管理在OPEN列表中避免重复的元素,但有时您可能需要更换元素,因为您遇到了以更高的成本达到它的方式。在这种情况下,您需要删除已插入OPEN的节点,并以较低的成本重新引入该节点(因此具有最高优先级)。如果你不允许这样做,你可能会产生次优路径,因为它似乎是你的情况。
另外,该算法的一些逻辑缺失。您应该为您创建的每个节点将累计成本从开始G和目标H的估计成本存储起来。 OPEN列表是根据G + H的值排序的,我在代码中没有注意到这样做。无论如何,我建议你看看ForEach-Object之类的existing implementation of A*,以了解其工作原理的详细信息。
希望我的回答有所帮助!