MCPcopy
hub / github.com/KilledByAPixel/LittleJS / aStarSearch

Method aStarSearch

plugins/pathFinder.js:225–308  ·  view source on GitHub ↗

Core A* search loop. Expects buildNodeData() to have been called first. * Marks node.parent for path reconstruction. Returns true if endNode was * reached; false on disconnected goal or maxLoop exhaustion. * @param {PathFinderNode} startNode * @param {PathFinderNode} endNode

(startNode, endNode)

Source from the content-addressed store, hash-verified

223 * @returns {boolean}
224 * @private */
225 aStarSearch(startNode, endNode)
226 {
227 ASSERT(startNode && endNode, 'aStarSearch needs both endpoints');
228 ASSERT(startNode !== endNode, 'aStarSearch: start and end must differ — caller should handle trivial case');
229 ASSERT(startNode.walkable && endNode.walkable, 'aStarSearch: endpoints must be walkable');
230
231 const openList = [startNode];
232 startNode.isOpen = true;
233 let loopCount = 0;
234
235 while (openList.length > 0)
236 {
237 // Find the open node with the smallest f score (linear scan).
238 // Same as the C++ — fine up to a few thousand nodes.
239 let bestIndex = 0;
240 let bestF = openList[0].f;
241 for (let i = 1; i < openList.length; ++i)
242 {
243 if (openList[i].f < bestF)
244 {
245 bestF = openList[i].f;
246 bestIndex = i;
247 }
248 }
249 const current = openList[bestIndex];
250
251 if (current === endNode) break;
252 if (++loopCount > this.maxLoop) break;
253
254 // Move current from open to closed.
255 current.isOpen = false;
256 openList.splice(bestIndex, 1);
257 current.isClosed = true;
258
259 if (this.debug && this.debugTime > 0)
260 debugRect(current.posWorld, PATHFINDER_TILE_VEC, rgb(1, 1, 1, 0.05), this.debugTime);
261
262 // Expand all 8 neighbors.
263 for (let dy = -1; dy <= 1; ++dy)
264 for (let dx = -1; dx <= 1; ++dx)
265 {
266 if (dx === 0 && dy === 0) continue;
267 const neighbor = this.getNode(current.pos.x + dx, current.pos.y + dy);
268 if (!neighbor || !neighbor.walkable || neighbor.isClosed) continue;
269
270 let stepCost = 1;
271 if (dx !== 0 && dy !== 0)
272 {
273 // Diagonal step: refuse if either cardinal neighbor is
274 // blocked. Prevents cutting through walls at corners.
275 // (Costed-but-walkable cardinals do not block — diagonal
276 // movement around expensive terrain is standard A*.)
277 const card1 = this.getNode(current.pos.x + dx, current.pos.y);
278 if (!card1 || !card1.walkable) continue;
279 const card2 = this.getNode(current.pos.x, current.pos.y + dy);
280 if (!card2 || !card2.walkable) continue;
281 stepCost = PATHFINDER_DIAGONAL_COST;
282 }

Callers 2

findPathMethod · 0.95

Calls 4

getNodeMethod · 0.95
rgbFunction · 0.85
ASSERTFunction · 0.50
debugRectFunction · 0.50

Tested by

no test coverage detected