我一直在阅读一些文章来学习A *寻路,我能够根据它制作一个测试程序,但我所做的程序是给出了一条奇怪的路径,这些路径并不是目标节点的最短路径。
在图像中,绿色方块单位表示起始节点,红色单位表示目标节点,蓝色单位表示无法通过的图块(墙壁),紫色单位表示从起始节点到目标节点的路径
如果有人发现路径查找源代码有问题,我会非常感激。我因为试图知道是什么原因导致它变得奇怪而感到筋疲力尽。
允许切角并走向对角线
package com.streak324.pathfinding;
import java.util.Comparator;
import java.util.HashSet;
import java.util.PriorityQueue;
import com.badlogic.gdx.utils.Array;
public class PathFinder {
private boolean foundTarget;
private int width, height;
//list of nodes that leads from starting node to target node is stored here
private Array<PathNode> path;
//all nodes stored in this array
private PathNode[][] nodes;
private PriorityQueue<PathNode> open;
private HashSet<PathNode> closed;
//nodes that must be referenced
private PathNode start, target, current;
//how far the current node can reach for other nodes from its own position
private int range = 1;
public PathFinder(int width, int height, boolean map[][]) {
this.width = width;
this.height = height;
nodes = new PathNode[width][height];
for(int i=0; i<width; i++) {
for(int j=0; j<height; j++) {
nodes[i][j] = new PathNode(i, j);
//if wall tile is spotted, mark the node unwalkable
if(map[i][j] != true) { nodes[i][j].passable = false; }
}
}
open = new PriorityQueue<PathNode>(new CostComparator());
closed = new HashSet<PathNode>();
}
public Array<PathNode> getPath(int sx, int sy ,int tx, int ty) {
path = new Array<PathNode>();
open.clear();
closed.clear();
start = nodes[sx][sy];
start.movementCost = 0;
addToOpenList(start);
target = nodes[tx][ty];
while(foundTarget != true) {
if(open.size() == 0) { return null; }
current = open.poll();
addToClosedList(current);
checkNeighbors(current);
}
traceBack();
return path;
}
// makes its way back adding the parent node until start
private void traceBack() {
while(current != start) {
path.add(current);
current = current.parent;
}
}
//checks for nodes within certain range
private void checkNeighbors(PathNode node) {
//continues loop if i or j goes out of bounds of nodes array
for(int i = node.x - range; i <= (node.x + range); i++) {
if(i >= width || i < 0) { continue; }
for(int j = node.y - range; j <= (node.y + range); j++) {
if( j >= height || j < 0) { continue; }
if((i == node.x && j == node.y) ) { continue; }
PathNode neighbor = nodes[i][j];
identifyNode(neighbor);
}
}
}
//if node is not on open list, add node and calculate it
private void identifyNode(PathNode node) {
if(!node.passable || closed.contains(node) ) return;
if(node == target) {
foundTarget = true;
System.out.println("Target Found: " + node.x + ", " + node.y);
return;
}
else if(!open.contains(node)) {
addToOpenList(node);
calcHeuristic(node);
updateNode(node, current);
}
else {
checkForReparenting(node);
}
}
//is the movement cost less to go from the current node?
private void checkForReparenting(PathNode node) {
float cost = node.movementCost;
float reCost = calcMovementCost(node, current);
if(reCost < cost) {
System.out.println("Reparenting");
updateNode(node, current);
}
}
//updates parent and cost
private void updateNode(PathNode child, PathNode parent) {
child.parent = parent;
child.movementCost = calcMovementCost(child, parent);
child.totalCost = child.movementCost + child.heuristic;
}
private float calcMovementCost(PathNode n1, PathNode n2) {
float dx = n1.x - n2.x;
float dy = n1.y - n2.y;
return (float) Math.sqrt( (dx*dx + dy*dy) ) + n2.movementCost;
}
private float calcHeuristic(PathNode node) {
float dx = node.x - target.x;
float dy = node.y - target.y;
return (float) Math.sqrt( (dx*dx + dy*dy) );
}
private void addToOpenList(PathNode node) {
if(!open.contains(node) && !closed.contains(node)) {
open.add(node);
}
}
private void addToClosedList(PathNode node) {
if(!closed.contains(node)) {
closed.add(node);
}
}
public class PathNode {
public int x, y;
public PathNode parent;
//g, h and f
public float movementCost, heuristic, totalCost;
public boolean passable;
public PathNode(int x, int y) {
this.x = x;
this.y = y;
passable = true;
}
}
private class CostComparator implements Comparator<PathNode> {
@Override
public int compare(PathNode a, PathNode b) {
if(a.totalCost < b.totalCost) return 1;
else return -1;
}
}
}
没有评论 http://pastebin.com/rSv7pUrB
我猜测优先级队列排序元素的方式有问题,或者我可能没有正确计算totalCost,movementCost和启发式变量,但我认为没有错。
非常感谢能够指出我可能出现的问题或解决方案正确方向的人
答案 0 :(得分:1)
您的代码存在以下问题:
你从未真正使用过启发式方法。以下语句(对calcHeuristic
的唯一调用)只是“将结果抛弃”。
calcHeuristic(node);
仅此一项不能成为错误,因为它是一个有效的可接受的启发式方法,可以将目标的距离猜测为0
。然而,算法以这种方式退化(我认为是Dijkstra算法)。
您永远不会更新优先级队列中节点的位置。这意味着具有更新totalDistance
的节点永远不会在proirity队列中向上移动,即使它totalCost
变得小于另一个节点的totalCost
。您必须删除该节点并再次添加它才能使用PriorityQueue
:
open.remove(node);
// ... update totalDistance
open.add(node);
totalDistance
等于实际距离,对于目标的扩展邻居 IF 你使用启发式;这里距离实际距离因sqrt(2)
或1
而异。一般来说,最后一步的距离启发式可能是任意不好的(这里很糟糕,请参阅(1.))并且你只能确定找到了真正的解决方案,如果你将算法运行到扩展的程度目标节点。答案 1 :(得分:0)
Estimated Streak324:
现在你的A *实现工作正常,我建议你在Internet上快速搜索java搜索库。您的代码看起来会更简单,可扩展和模块化,并且实现非常高效且经过充分测试。这将是您使用Hipster的代码:
//HERE YOU DEFINE THE SEARCH PROBLEM
// The maze is a 2D map, where each tile defined by 2D coordinates x and y
// can be empty or occupied by an obstacle. We have to define de transition
// function that tells the algorithm which are the available movements from
// a concrete tile point.
SearchProblem p = ProblemBuilder.create()
.initialState(origin)
.defineProblemWithoutActions()
.useTransitionFunction(new StateTransitionFunction<Point>() {
@Override
public Iterable<Point> successorsOf(Point state) {
// The transition function returns a collection of transitions.
// A transition is basically a class Transition with two attributes:
// source point (from) and destination point (to). Our source point
// is the current point argument. We have to compute which are the
// available movements (destination points) from the current point.
// Class Maze has a helper method that tell us the empty points
// (where we can move) available:
//TODO: FILL WITH YOUR CODE GENERATING THE NEIGHBORS, FILTERING
//THOSE WHICH ARE NOT ACCESIBLE DUE TO OBSTACLES IN YOUR MAP
return [...]
}
})
.useCostFunction(new CostFunction<Void, Point, Double>() {
// We know now how to move (transitions) from each tile. We need to define the cost
// of each movement. A diagonal movement (for example, from (0,0) to (1,1)) is longer
// than a top/down/left/right movement. Although this is straightforward, if you don't
// know why, read this http://www.policyalmanac.org/games/aStarTutorial.htm.
// For this purpose, we define a CostFunction that computes the cost of each movement.
// The CostFunction is an interface with two generic types: S - the state, and T - the cost
// type. We use Points as states in this problem, and for example doubles to compute the distances:
@Override
public Double evaluate(Transition<Void, Point> transition) {
Point source = transition.getFromState();
Point destination = transition.getState();
// The distance from the source to de destination is the euclidean
// distance between these two points http://en.wikipedia.org/wiki/Euclidean_distance
return source.distance(destination);
}
})
.build();
//HERE YOU INSTANTIATE THE ALGORITHM AND EXECUTE THE SEARCH
//MazeSearch.printSearch(Hipster.createAStar(p).iterator(), maze);
System.out.println(Hipster.createAStar(p).search(goal));
如您所见,您只需要定义要在搜索问题中使用的组件,然后执行算法。图书馆将为您完成剩余的操作。
此外,该库是开源的,并且是知道的Apache2。您可以访问几个有助于您开始使用库的示例。
在您的情况下,当您使用自定义2D网格时,您唯一需要调整的是过渡功能,它会检查您的网格以过滤因障碍而无法访问的邻居。
使用此实现的一个好处是,除了代码的可伸缩性和模块性之外,避免实例化路径中的所有节点,因为库将动态地为您执行,减少内存并提高性能(特别是在情况下)巨大的网格。)
我希望我的答案有所帮助,