Skip to content

Commit 94115fc

Browse files
author
LoneWandererProductions
committed
improve Pathfinder
1 parent 42d3571 commit 94115fc

3 files changed

Lines changed: 45 additions & 25 deletions

File tree

Pathfinder/Node.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ internal Node(int x, int y, int g, int h, int movementCost, Node parent = null)
9292
/// <value>
9393
/// The F cost.
9494
/// </value>
95-
private int F => G + H;
95+
internal int F => G + H;
9696

9797
/// <inheritdoc />
9898
/// <summary>

Pathfinder/NodeComparer.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,23 @@ public sealed class NodeComparer : IComparer<Node>
2828
/// </returns>
2929
public int Compare(Node? x, Node? y)
3030
{
31-
var result = (x.G + x.H).CompareTo(y.G + y.H);
31+
if (ReferenceEquals(x, y)) return 0;
32+
if (x is null) return -1;
33+
if (y is null) return 1;
34+
35+
// 1. Total Cost is always the king of A*
36+
int result = x.F.CompareTo(y.F);
37+
3238
if (result == 0)
3339
{
40+
// 2. Revert to your logic: Compare G.
41+
// This ensures we respect the diagonal and movement costs
42+
// when the heuristic guess is identical.
3443
result = x.G.CompareTo(y.G);
44+
3545
if (result == 0)
3646
{
47+
// 3. Final unique identifier for the SortedSet
3748
result = x.X.CompareTo(y.X);
3849
if (result == 0)
3950
{

Pathfinder/Pathfinding.cs

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -69,20 +69,25 @@ public Pathfinding(int[,] grid, int straightCost = 1, int diagonalCost = 2)
6969
public PathfinderResults FindPath(int startX, int startY, int goalX, int goalY, int rangeLimit)
7070
{
7171
var result = new PathfinderResults();
72+
73+
// We use SortedSet for picking the best node, BUT...
7274
var openList = new SortedSet<Node>(new NodeComparer());
75+
76+
// ...we use a Dictionary to find if a coordinate is already in the open list instantly!
77+
var openLookUp = new Dictionary<(int, int), Node>();
78+
7379
var closedList = new HashSet<(int, int)>();
74-
var nodeIds = new Dictionary<Node, int>();
75-
var nodeIdCounter = 0;
7680

77-
// Initialize start node with its movement cost
7881
var startNode = new Node(startX, startY, 0, Heuristic(startX, startY, goalX, goalY), _grid[startX, startY]);
82+
7983
openList.Add(startNode);
80-
nodeIds[startNode] = nodeIdCounter++;
84+
openLookUp[(startX, startY)] = startNode;
8185

8286
while (openList.Count > 0)
8387
{
8488
var currentNode = openList.Min;
85-
_ = openList.Remove(currentNode);
89+
openList.Remove(currentNode);
90+
openLookUp.Remove((currentNode.X, currentNode.Y));
8691

8792
if (currentNode.X == goalX && currentNode.Y == goalY)
8893
{
@@ -95,26 +100,27 @@ public PathfinderResults FindPath(int startX, int startY, int goalX, int goalY,
95100

96101
foreach (var neighbor in GetNeighbors(currentNode, _grid, goalX, goalY))
97102
{
98-
if (closedList.Contains((neighbor.X, neighbor.Y)))
103+
if (closedList.Contains((neighbor.X, neighbor.Y))) continue;
104+
105+
// INSTANT LOOKUP - No more FirstOrDefault linear scans!
106+
if (openLookUp.TryGetValue((neighbor.X, neighbor.Y), out var existingOpenNode))
99107
{
100-
continue;
101-
}
108+
if (neighbor.G < existingOpenNode.G)
109+
{
110+
// To update a value in a SortedSet, you MUST remove and re-add
111+
// because the sort order has changed.
112+
openList.Remove(existingOpenNode);
102113

103-
neighbor.H = Heuristic(neighbor.X, neighbor.Y, goalX, goalY);
104-
neighbor.Parent = currentNode;
114+
existingOpenNode.G = neighbor.G;
115+
existingOpenNode.Parent = currentNode;
105116

106-
var openNode = openList.FirstOrDefault(n => n.X == neighbor.X && n.Y == neighbor.Y);
107-
if (openNode == null)
108-
{
109-
openList.Add(neighbor);
110-
nodeIds[neighbor] = nodeIdCounter++;
117+
openList.Add(existingOpenNode);
118+
}
111119
}
112-
else if (neighbor.G < openNode.G)
120+
else
113121
{
114-
openList.Remove(openNode);
115-
openNode.G = neighbor.G;
116-
openNode.Parent = currentNode;
117-
openList.Add(openNode);
122+
openList.Add(neighbor);
123+
openLookUp[(neighbor.X, neighbor.Y)] = neighbor;
118124
}
119125
}
120126
}
@@ -151,11 +157,14 @@ private static List<Node> BuildPath(Node goalNode)
151157
/// <param name="x2">The x-coordinate of the second point.</param>
152158
/// <param name="y2">The y-coordinate of the second point.</param>
153159
/// <returns>The heuristic estimate of the cost.</returns>
154-
private static int Heuristic(int x1, int y1, int x2, int y2)
160+
private int Heuristic(int x1, int y1, int x2, int y2)
155161
{
156-
return Math.Abs(x1 - x2) + Math.Abs(y1 - y2);
157-
}
162+
int dx = Math.Abs(x1 - x2);
163+
int dy = Math.Abs(y1 - y2);
158164

165+
// Move diagonally as much as possible, then move straight for the remaining distance
166+
return _straightCost * (dx + dy) + (_diagonalCost - 2 * _straightCost) * Math.Min(dx, dy);
167+
}
159168
/// <summary>
160169
/// Gets the neighboring nodes for a given node based on the grid and movement costs.
161170
/// </summary>

0 commit comments

Comments
 (0)