A星算法,排序是利用了array.sort来取最小代价,其实可以利用二叉树或者优先级链表,在数据添加就处理好位置排列,关闭列表和开放列表其实就是节点状态的表示,也可以用节点的一个状态属性来表示,不需要专门用一个集合来存储检查,还有就是代价因子在实际的地图应用中,是需要根据你真实的地形来计算的。
var map = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 2, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 3, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]
var paths = findPath([2, 3], [6, 4], map);
let newMap = map.slice();
paths.forEach(p => {
newMap[p[1]][p[0]] = 4;
});
console.log("path:",paths)
console.log("map:",newMap)
function findPath(startPoint, targetPoint, map, configs) {
// 配置
configs = configs || {};
// 判断地图格子是不是障碍
var getIsObstacle = configs.getIsObstacle || ((tileValue) => {
return tileValue == 1;
});
// 代价因子
var getCostFactor = configs.getCostFactor || ((tileValue,currentPoint, parentPoint) => {
if (!parentPoint) {
return 1;
}
return currentPoint[0] != parentPoint[0] && currentPoint[1] !== parentPoint[1] ? 1.5: 1;
});
var
row = map.length, //地图行高
col = map[0].length, //地图列宽
openList = [],//开放列表,存放待处理的节点
closeList = [],//关闭列表,存放已处理的节点
mapSet = map.map(d => new Array(col));
// 创建节点
function createNode(point, parentNode) {
let x2 = point[0], y2 = point[1];
let currentNode = mapSet[y2][x2];//获取当前节点
// // 如果已存在,就不在创建
// if (currentNode) {
// // return currentNode;
// }
currentNode = {};
let tileValue = map[y2][x2];// 地图对应的tile值
let cost = getCostFactor(tileValue,point, parentNode ? parentNode.point : null);// 代价因子
let isObstacle = getIsObstacle(tileValue, x2, y2);//是否障碍物
//G值 = 父节点的G值 + 父节点到当前点的移动代价
let g = parentNode ? (parentNode.g+cost): 0;//计算当前点与起始点的距离
// let g = parentNode ? (Math.abs(parentNode.x - x2) + Math.abs(parentNode.y - y2) * cost) + parentNode.g : 0;//计算当前点与起始点的距离
//H值 = 当前点到结束点的曼哈顿距离
let h = Math.abs(targetPoint[0] - x2) + Math.abs(targetPoint[1] - y2);//计算当前点与目标点的距离
let f = g + h;//代价
Object.assign(currentNode, {
value: tileValue,
isObstacle: isObstacle,
g: g,
h: h,
f: f,
x: x2,
y: y2,
point: point,
parent: parentNode
});
mapSet[y2][x2] = currentNode;
return currentNode;
}
// 获取目标节点
function getTargetNodePath(currentNode) {
// 从左上角开始计算
//从行到列
rowLoop:
for (let r = currentNode.y - 1, rlen = r + 3; r < rlen; r++) {
colLoop:
for (let c = currentNode.x - 1, clen = c + 3; c < clen; c++) {
// 如果不在范围内,跳过
if (!(c >= 0 && c < col && r >= 0 && r < row)) {
continue colLoop;
}
let point = [c, r];
let node = createNode(point, currentNode);
// 如果节点等于目标就直接返回
if (node.x == targetPoint[0] && node.y == targetPoint[1]) {
return node;
}
// 如果不存在openlist列表中或closelist列表,并且是非障碍
if (!node.isObstacle && openList.indexOf(node) == -1 && closeList.indexOf(node) == -1) {
openList.push(node);
}
}
}
//按最小代价升序排列
openList.sort((a, b) => {
return a.f - b.f;
})
}
var startNode = createNode(startPoint, null), targetNode;
openList.push(startNode);
while (openList.length) {
current = openList.shift();//获取当前列表最小代价节点
closeList.push(current);
targetNode = getTargetNodePath(current);
if (targetNode) {
break;
}
}
// 如果不等空,就找出所有路径
var paths = [];
if (targetNode) {
let current = targetNode.parent;
while (current) {
if (current !== startNode) {
paths.unshift(current.point);
}
current = current.parent;
}
}
return paths;
}