UNPKG

abstract-astar

Version:

Versatile A* (A Star) implementation in TypeScript.

128 lines (127 loc) 5.16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.aStar = exports.MinHeap = void 0; const parent = (i) => Math.floor((i - 1) / 2); const left = (i) => 2 * i + 1; const right = (i) => 2 * i + 2; class MinHeap { constructor(_getValue) { this._getValue = _getValue; this._heapArray = []; this._itemToIndex = new Map(); } upsert(item) { const existingIndex = this._itemToIndex.get(item); if (existingIndex !== undefined) this._heapifyDownFromIndex(existingIndex); else this.insert(item); } contains(item) { return this._itemToIndex.get(item) !== undefined; } insert(item) { const { _getValue, _heapArray, _itemToIndex } = this; // add to the end _heapArray.push(item); let index = this._heapArray.length - 1; _itemToIndex.set(item, index); // swap it up the heap until the invariant condition restored while (index !== 0) { const parentIndex = parent(index); const parentItem = _heapArray[parentIndex]; if (_getValue(parentItem) <= _getValue(_heapArray[index])) return; this._swapIndexes(index, parentIndex); index = parentIndex; } } removeMinimum() { const { _heapArray, _itemToIndex } = this; if (_heapArray.length <= 0) return undefined; if (_heapArray.length === 1) { _itemToIndex.delete(_heapArray[0]); return _heapArray.pop(); } const root = _heapArray[0]; _itemToIndex.delete(root); // put the last item in the first position and restore invariant _heapArray[0] = _heapArray.pop(); this._heapifyDownFromIndex(0); return root; } _swapIndexes(firstIndex, secondIndex) { const { _heapArray, _itemToIndex } = this; const firstItem = _heapArray[firstIndex]; const secondItem = _heapArray[secondIndex]; _itemToIndex.set(firstItem, secondIndex); _itemToIndex.set(secondItem, firstIndex); _heapArray[firstIndex] = secondItem; _heapArray[secondIndex] = firstItem; } /** Recursively ensures the item at the given index is less than its children. */ _heapifyDownFromIndex(index) { const { _heapArray, _getValue } = this; const leftIndex = left(index); const rightIndex = right(index); let smallestIndex = index; const size = _heapArray.length; if (leftIndex < size && _getValue(_heapArray[leftIndex]) < _getValue(_heapArray[index])) smallestIndex = leftIndex; if (rightIndex < size && _getValue(_heapArray[rightIndex]) < _getValue(_heapArray[smallestIndex])) smallestIndex = rightIndex; if (smallestIndex !== index) { this._swapIndexes(index, smallestIndex); this._heapifyDownFromIndex(smallestIndex); } } } exports.MinHeap = MinHeap; function aStar({ start, goal, estimateFromNodeToGoal, neighborsAdjacentToNode, actualCostToMove: costToMove, }) { var _a; const cameFromMap = new Map(); const cheapestActualCostFrom = new Map(); cheapestActualCostFrom.set(start, 0); const cheapestEstimatedCostToGoalFrom = new Map(); cheapestEstimatedCostToGoalFrom.set(start, estimateFromNodeToGoal(start)); const openMinHeap = new MinHeap((node) => { var _a; return (_a = cheapestEstimatedCostToGoalFrom.get(node)) !== null && _a !== void 0 ? _a : Infinity; }); openMinHeap.upsert(start); while (true) { const current = openMinHeap.removeMinimum(); // no more to explore, failed to find path if (current === undefined) return undefined; // reached goal if (current === goal) return reconstructPath(cameFromMap, current); const cheapestActualCostToCurrent = (_a = cheapestActualCostFrom.get(current)) !== null && _a !== void 0 ? _a : Infinity; neighborsAdjacentToNode(current).forEach((neighbor) => { var _a; const actualCostToNeighbor = cheapestActualCostToCurrent + costToMove(cameFromMap, current, neighbor); const cheapestActualCostToNeighbor = (_a = cheapestActualCostFrom.get(neighbor)) !== null && _a !== void 0 ? _a : Infinity; if (actualCostToNeighbor < cheapestActualCostToNeighbor) { cameFromMap.set(neighbor, current); cheapestActualCostFrom.set(neighbor, actualCostToNeighbor); cheapestEstimatedCostToGoalFrom.set(neighbor, actualCostToNeighbor + estimateFromNodeToGoal(neighbor)); openMinHeap.upsert(neighbor); } }); } } exports.aStar = aStar; function reconstructPath(cameFrom, current) { const total = [current]; while (true) { const newCurrent = cameFrom.get(current); if (newCurrent === undefined) return total.reverse(); else { total.push(newCurrent); current = newCurrent; } } }