我正在研究A *搜索算法,我遵循了这个:A* search algorithm
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MapData.Interfaces;
namespace MapData.Algorithm {
public class AStar {
IDMap map;
List<Location2D> openset;
List<Location2D> closedset;
List<Location2D> path;
Dictionary<Location2D, Double> g_scores;
Dictionary<Location2D, Double> h_scores;
Dictionary<Location2D, Double> f_scores;
Dictionary<Location2D, Location2D> came_from;
int[] ArrayX = { 0, -1, -1, -1, 0, 1, 1, 1 };
int[] ArrayY = { 1, 1, 0, -1, -1, -1, 0, 1 };
/// <summary>
/// Initing the Algorithm with MapInfo
/// </summary>
/// <param name="map">DMap info.</param>
public AStar(IDMap map) {
this.map = map;
}
/// <summary>
/// Start searching untile it's find it's goal or return false.
/// </summary>
/// <param name="start">Starting Node.</param>
/// <param name="goal">Ending Node.</param>
public bool Find( Location2D start, Location2D goal) {
openset = new List<Location2D>(); // The set of tentative nodes to be evaluated, initially containing the start node.
closedset = new List<Location2D>(); // The set of nodes already evaluated.
path = new List<Location2D>(); // The path result.
g_scores = new Dictionary<Location2D, Double>(); // Cost from start along best known path.
h_scores = new Dictionary<Location2D, Double>(); // Estimated cost to get from a node to the goal.
f_scores = new Dictionary<Location2D, Double>(); // Estimated total cost from start to goal.
came_from = new Dictionary<Location2D, Location2D>(); // The navigated nodes.
openset.Add(start);
g_scores[start] = 0;
h_scores[start] = GetHValue(start, goal);
f_scores[start] = g_scores[start] + h_scores[start];
while (openset.Count > 0) { // while openset is not empty
Location2D current = CheckBestNode(); //the node in openset having the lowest f_score[] value
if (current.Equals(goal)) {
ReconstructPathRecursive(current);
return true;
}
Location2D neighbor = new Location2D();
for (int i = 0; i < 8; i++) { // neighbor nodes.
neighbor.X = (ushort)(current.X + ArrayX[i]);
neighbor.Y = (ushort)(current.Y + ArrayY[i]);
bool tentative_is_better = false;
if (closedset.Contains(neighbor)) continue;
if (!map.Cells[neighbor.X, neighbor.Y].Walkable) continue;
Double tentative_g_score = g_scores[current] + DistanceBetween(current, neighbor);
if (!openset.Contains(neighbor)) {
openset.Add(neighbor);
h_scores[neighbor] = GetHValue(neighbor, goal);
tentative_is_better = true;
} else if (tentative_g_score < g_scores[neighbor]) {
tentative_is_better = true;
} else {
tentative_is_better = false;
}
if (tentative_is_better) {
if (came_from.ContainsKey(neighbor)) {
came_from[neighbor] = current;
} else {
came_from[neighbor] = current;
g_scores[neighbor] = tentative_g_score;
f_scores[neighbor] = g_scores[neighbor] + h_scores[neighbor];
}
}
}
}
return false;
}
/// <summary>
/// Check the best node that has the smallest f value;
/// </summary>
/// <returns>The best Node.</returns>
Location2D CheckBestNode() {
var BestNode = f_scores.OrderBy(x => x.Value).First().Key;
f_scores.Remove(BestNode);
openset.Remove(BestNode);
closedset.Add(BestNode);
return BestNode;
}
private void ReconstructPathRecursive(Location2D current_node) {
Location2D temp;
if (came_from.TryGetValue(current_node, out temp)) {
ReconstructPathRecursive(temp);
path.Add(temp);
} else {
path.Add(current_node);
}
}
int GetHValue(Location2D start, Location2D goal) {
int nDx = Math.Abs(start.X - goal.X);
int nDy = Math.Abs(start.Y - goal.Y);
if (nDx > nDy)
return 10 * nDx + 6 * nDy;
return 10 * nDy + 6 * nDx;
}
readonly Double SQRT_2 = Math.Sqrt(2);
protected Double DistanceBetween(Location2D inStart, Location2D inEnd) {
int diffX = Math.Abs(inStart.X - inEnd.X);
int diffY = Math.Abs(inStart.Y - inEnd.Y);
switch (diffX + diffY) {
case 1: return 1;
case 2: return SQRT_2;
case 0: return 0;
default:
throw new ApplicationException();
}
}
public List<Location2D> Path {
get {
return path;
}
}
}
}
现在我有两个问题:
如果我检查了步行区域,Astar会无限循环,永远不会找到路径也不会中断。
如果我评论了检查可行走区域的部分,它会找到路径但不准确,就像目标位置是 - &gt; (444,444),路径终止于 - &gt; (444,443)或(443,444)。
自从我遵循维基百科指南以来,我无法看到这里出了什么问题。
答案 0 :(得分:0)
现在,如果我错了,请纠正我,但似乎就像从查看其他代码一样,你希望for循环中的if语句迭代当前节点的邻居看看像这样:
if (!closedset.Contains(neighbor)) continue;
if (map.Cells[neighbor.X, neighbor.Y].Walkable) continue;
不喜欢这样:
if (closedset.Contains(neighbor)) continue;
if (!map.Cells[neighbor.X, neighbor.Y].Walkable) continue;
您的原始代码仅评估邻居是否在关闭列表中并且它不可步行,我认为您不想要。更新后的代码仅在相邻节点不在闭合集上并且可以步行时才评估它。
要进一步了解A *算法及其实际工作原理的详细说明,请查看this link。
编辑:您说您的代码提供了一条不准确的路径,即一条不会一直引导到目标方块的路径。通过查看ReconstructPathRecursive()
函数,您似乎犯了一个至关重要的错误。因为您的列表包含遍历的每个节点的父节点,所以除了目标方之外,您将最终得到所有节点,因为它不是任何东西的父节点。在开始向后工作重建当前节点的父节点(目标方块)之前,目标方格应该是添加到列表中的第一个方格。
要解决此问题,我建议当您发现当前节点等于目标节点时,首先将目标节点添加到路径列表中,然后在完成之后调用ReconstructPathRecursive()
函数以获取最后的名单。