findShortestPath = (map, start, isTarget, getCost, getHeuristic = () => 0) => {
const queue = [start]
const alreadyEnqueued = {}
const visited = {}
const tentativeScores = {[start]: 0}
const heuristicsAdjustedScores = {[start]: getHeuristic(start)}
const parents = {}
const getScore = pos => tentativeScores[hashPosition(pos)] ?? Infinity
while (queue.length) {
queue.sort((a, b) => heuristicsAdjustedScores[a] - heuristicsAdjustedScores[b])
const current = queue.shift()
if (isTarget(current)) {
return reconstruct(current, parents)
}
const validNeighbors = getOrthogonalNeighbors(current)
.filter(isInMap(map.size))
.filter(pos => !visited[pos])
for (const neighbor of validNeighbors) {
const cost = getCost(current, neighbor)
if (cost === Infinity) continue
const score = tentativeScores[current] + cost
if (score < (tentativeScores[neighbor] ?? Infinity)) {
tentativeScores[neighbor] = score
heuristicsAdjustedScores[neighbor] = score + getHeuristic(neighbor)
parents[neighbor] = current
}
if (!alreadyEnqueued[neighbor]) {
queue.push(neighbor)
alreadyEnqueued[neighbor] = true
}
}
visited[current] = true
}
}