@fabienjuif/astar
Version:
> an A\* algorithm in javascript
166 lines (131 loc) • 3.38 kB
JavaScript
// https://fr.wikipedia.org/wiki/Algorithme_A*
// A* Search Algorithm
// node {
// x,
// y,
// cost,
// h,
// parentNode,
// }
function sortNodes(node1, node2) {
if (node1.h > node2.h) return 1
if (node1.h < node2.h) return -1
return 0
}
function getFinalPath(graph, end) {
const node = graph.get(end.x).get(end.y)
const mappedNode = {
x: node.x,
y: node.y,
cost: node.cost,
}
if (!node.parentNode) return [mappedNode]
return [...getFinalPath(graph, node.parentNode), mappedNode]
}
function defaultSameNode(node1, node2) {
return node1.x === node2.x && node1.y === node2.y
}
function defaultGetNeighbours(graph, node) {
const neighbours = []
function getAndAdd(x, y) {
if (!graph.has(x)) return
const graphNode = graph.get(x).get(y)
if (!graphNode) return
neighbours.push(graphNode)
}
// left
getAndAdd(node.x - 1, node.y)
// right
getAndAdd(node.x + 1, node.y)
// top
getAndAdd(node.x, node.y - 1)
// bottom
getAndAdd(node.x, node.y + 1)
return neighbours
}
function defaultDistance(node, end) {
const x = end.x - node.x
const y = end.y - node.y
return x * x + y * y
}
function defaultMapGraph(graph) {
const rows = new Map()
graph.forEach((cell) => {
const row = rows.get(cell.x) || new Map()
if (!rows.has(cell.x)) rows.set(cell.x, row)
row.set(cell.y, { ...cell })
})
return rows
}
module.exports = function getClosestPath(
graph,
start,
end,
{
sameNode = defaultSameNode,
mapGraph = defaultMapGraph,
getNeighbours = defaultGetNeighbours,
distance = defaultDistance,
heuristic = () => 1,
maxLoops = Infinity,
} = {},
) {
const mappedGraph = mapGraph(graph)
const closedList = []
const openList = []
function getNode({ x, y }) {
return mappedGraph.get(x).get(y)
}
function updateNode(node) {
mappedGraph.get(node.x).set(node.y, node)
return node
}
openList.push(updateNode({ ...start, cost: 0 }))
let loop = -1
while (openList.length > 0 && loop++ < maxLoops) {
const current = getNode(openList.shift())
if (current.cost === Infinity) {
return {
status: 'not_found',
path: [],
loops: loop,
}
}
if (sameNode(current, end)) {
return {
status: 'success',
path: getFinalPath(mappedGraph, current),
loops: loop,
}
}
const neighbours = getNeighbours(mappedGraph, current)
for (let i = 0; i < neighbours.length; i += 1) {
const neighbour = getNode(neighbours[i])
const known = neighbour.cost !== undefined
if (closedList.find((n) => sameNode(n, neighbour))) continue
const newCost =
(current.cost || 0) + heuristic({ ...current }, { ...neighbour })
if (known && neighbour.cost < newCost) continue
const newNeighbour = updateNode({
...neighbour,
cost: newCost,
h: newCost + distance(neighbour, end),
parentNode: {
x: current.x,
y: current.y,
},
})
if (!known) openList.push(newNeighbour)
openList.sort(sortNodes)
}
closedList.push(current)
}
if (loop >= maxLoops) {
return {
status: 'not_optimized',
path: getFinalPath(mappedGraph, openList[0]),
loops: loop,
}
}
return { status: 'not_found', path: [], loops: loop }
}