UNPKG

astar-core

Version:

A-Star Algorithm Core

174 lines (155 loc) 5.18 kB
import GridNode from './GridNode'; import Graph from './Graph'; import BinaryHeap from './BinaryHeap'; /** * @name pathTo * @description 到对应节点位置的路径 * @param node 节点 * @returns 路径数组 */ function pathTo(node: GridNode): Array<GridNode> { let curr = node; const path = []; while (curr.parent) { path.unshift(curr); curr = curr.parent; } return path; } export interface AStarResult { path: Array<GridNode>; currentNodes: Map<number, GridNode>; neighborNodes: Map<number, Array<GridNode>>; } /** * @name Heuristics * @description 启发式算法 manhattan-曼哈顿距离,diagonal-对角距离 */ export const Heuristics = { manhattan: function(pos0: GridNode, pos1: GridNode): number { var d1 = Math.abs(pos1.x - pos0.x); var d2 = Math.abs(pos1.y - pos0.y); return d1 + d2; }, diagonal: function(pos0: GridNode, pos1: GridNode): number { var D = 1; var D2 = Math.sqrt(2); var d1 = Math.abs(pos1.x - pos0.x); var d2 = Math.abs(pos1.y - pos0.y); return (D * (d1 + d2)) + ((D2 - (2 * D)) * Math.min(d1, d2)); } } /** * AStar算法 */ export default class AStar { /** * @name cleanNode * @description 清理节点数据 * @param node 节点 * @returns {void} */ public static cleanNode(node: GridNode): void { node.f = 0; node.g = 0; node.h = 0; node.visited = false; node.closed = false; node.parent = null; } /** * @name search * @description 开始搜索最短路径 * @param graph 图 * @param start 起始节点 * @param end 终点节点 * @param options 配置 */ public static search (graph: Graph, start: GridNode, end: GridNode, options:any): AStarResult { graph.cleanDirty(); options = options || {}; // 启发算法,默认用曼哈顿距离作为启发算法 const heuristic = options.heuristic || Heuristics.manhattan; // 如果目标节点不可达,是否返回最接近目标的节点 const closest = options.closest || false; const openHeap = new BinaryHeap(function(node: GridNode) { return node.f; }); // 将起始节点作为当前最近节点 let closestNode = start; start.h = heuristic(start, end); graph.markDirty(start); openHeap.push(start); let iterNum = 0; const neighborNodes: Map<number, Array<GridNode>> = new Map(); const currentNodes: Map<number, GridNode> = new Map(); while (openHeap.size() > 0) { // 取对顶元素,也就是F最小的。堆帮我们排好序了 let currentNode = openHeap.pop(); // 如果当前节点是终点,则返回路径 if (currentNode === end) { const result: AStarResult = { path: pathTo(currentNode), currentNodes, neighborNodes, }; return result; } // 将当前节点放入关闭列表,这里用的是closed标志,然后处理其他相邻节点 currentNode.closed = true; // 找到当前节点的所有相邻节点 const neighbors = graph.neighbors(currentNode); for (var i = 0, il = neighbors.length; i < il; ++i) { const neighbor = neighbors[i]; if (neighbor.closed || neighbor.isWall()) { // 如果这个相邻节点在关闭列表或者是墙,则跳过处理下一个 continue; } // g为节点到起点的最短距离,相邻节点的g等于当前节点的g加上与相邻节点的距离 // 我们需要校验到达相邻节点的路径是否是当前最短的路径 const gScore = currentNode.g + neighbor.getCost(currentNode); const beenVisited = neighbor.visited; // 如果相邻节点未被访问到或者其新的g值小于旧的g值则重新计算 if (!beenVisited || gScore < neighbor.g) { neighbor.visited = true; neighbor.parent = currentNode; neighbor.h = neighbor.h || heuristic(neighbor, end); neighbor.g = gScore; neighbor.f = neighbor.g + neighbor.h; graph.markDirty(neighbor); if (closest) { // 如果相邻节点比当前最近节点还靠近终点,则将其置为当前最近节点 if (neighbor.h < closestNode.h || (neighbor.h === closestNode.h && neighbor.g < closestNode.g)) { closestNode = neighbor; } } if (!beenVisited) { // 将相邻节点入栈,栈会根据其f值将其放到正确的位置 openHeap.push(neighbor); } else { // 因为之前访问过此相邻节点,则需要重新排序堆的位置 openHeap.rescoreElement(neighbor); } } } neighborNodes[iterNum] = neighbors; currentNodes[iterNum] = currentNode; iterNum++; } if (closest) { const result: AStarResult = { path: pathTo(closestNode), currentNodes, neighborNodes, }; return result; } // 找不到路径,返回空 const result: AStarResult = { path: [], currentNodes, neighborNodes, }; return result; } }