@@ -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