A*寻路
最近做东西可能会用到寻路的功能,就先用Javascript写个A*扔这里吧。
关于A*算法,有兴趣的同学可参考:http://www.policyalmanac.org/games/aStarTutorial.htm
中文版:http://blog.vckbase.com/panic/archive/2005/03/20/3778.html
不过个人觉得A*算法的性能确实不太高,还有需要优化的地方
OK,这里是JS版的AStar:
/**
* A*类
* @param {Array} range 查寻范围
* @param {Number} scale 比例
* @param {Array} blockList 障碍物列表
* @param {Array} src 起始点
* @param {Array} des 目标点
*/
function AStar(range,scale,blockList,src,des){
var srcNode={
parent:null,
G:0,
H:0,
coord:src || [10,10]
};
var desNode={
parent:null,
G:0,
H:0,
coord:des || [90,90]
};
var closedList=[];
var openList=[srcNode];
function init(){
range=range || [[0,0],[100,100]];
scale=scale || 10;
}
/**
* 检测障碍物
* @param {Array} p
*/
function checkBlock(p){
var i=blockList.length;
while(i--){
if(blockList[i][0]===p[0] && blockList[i][1]===p[1]){
return true;
}
}
return false;
}
/**
* 从列表中移除节点
* @param {Object} p
* @param {Array} list
*/
function removeFromList(p,list){
var i=list.length;
while(i--){
if(list[i]===p){
list.splice(i,1);
}
}
}
/**
* 从列表中获取节点
* @param {Object} p
* @param {Object} list
*/
function getFromList(p,list){
var i=list.length;
var ret;
while(i--){
if(list[i].coord[0]===p[0] && list[i].coord[1]===p[1]){
ret=list[i];
break;
}
}
return ret;
}
/**
* 获取节点的H值
* @param {Array} crd1
* @param {Array} crd2
*/
function getH(crd1,crd2){
return Math.abs(crd2[0]-crd1[0]) + Math.abs(crd2[1]-crd1[1])*scale;
}
/**
* 获取节点的G值
* @param {Array} crd
* @param {Object} p
*/
function getG(crd,p){
var crd2=p.coord;
return Math.floor(Math.sqrt(Math.pow(crd[0]-crd2[0],2)+Math.pow(crd[1]-crd2[1],2))*scale) + p.G;
}
/**
* 根据指定节点获取其相邻的节点集合
* @param {Object} p
*/
function getNearPoints(p){
var i=3;
var j;
var points=[];
var x;
var y;
var cp;
var otp;
while(i--){
x=p.coord[0] + (1-i);
j=3;
while(j--){
y=p.coord[1] + (1-j);
if ((x !== p.coord[0] || y !== p.coord[1])
&& !checkBlock([x,y])
&& (x > range[0][0] && x < range[1][0])
&& (y > range[0][1] && y < range[1][1])
&& !getFromList([x,y],closedList)
){
cp={
parent:null,
coord: [x, y],
G: 0,
H: getH([x, y], desNode.coord)
};
points.push(cp);
}
}
}
return points;
}
/**
* 从open列表中获取当前离目标点最近的节点
* @param {Array} openList
*/
function getCurrentPoint(openList){
var ol=openList;
var i=ol.length;
var minF=openList[0].G + openList[0].H;
var F;
var ret;
while(i--){
F=ol[i].G + ol[i].H;
if(F <= minF){
minF=F;
ret=ol[i];
}
}
return ret;
}
/**
* 检查节点的父节点是否需要更新为更短路径
* @param {Object} p
* @param {Object} crtP
*/
function checkParent(p,crtP){
var crd1=p.coord;
var crd2=crtP.coord;
var crtD=Math.floor(Math.sqrt(Math.pow(crd1[0]-crd2[0],2)+Math.pow(crd1[1]-crd2[1],2))*scale) + crtP.G;
if(crtD < p.G){
p.G=crtD;
p.parent=crtP;
}
}
/**
* 搜寻路径
*/
function search(){
var p;
var np;
var nearPoints=[];
var currentPoint;
var i;
var ret;
do {
currentPoint=getCurrentPoint(openList);
closedList.push(currentPoint);
removeFromList(currentPoint,openList);
if(getFromList(desNode.coord,closedList)){
ret=closedList;
break;
}
nearPoints=getNearPoints(currentPoint);
i=nearPoints.length;
while(i--){
np=nearPoints[i];
if(!getFromList(np.coord,openList)){
np.parent=currentPoint;
np.G=getG(np.coord,currentPoint);
openList.push(np);
}
checkParent(np,currentPoint);
}
}while (openList.length);
return ret;
}
var me={
/**
* 获取找寻到的路径
* @param {Array} src
* @param {Array} des
*/
getPath:function(src,des){
var path=[];
var list;
var node;
if (src) {
srcNode.coord=src;
openList = [srcNode];
}
des && (desNode.coord=des);
list=search();
if(list){
node=closedList[closedList.length-1];
path.push(node);
do{
node=node.parent;
path.push(node);
}while(node!==srcNode);
}
return path;
}
};
init();
return me;
}
* A*类
* @param {Array} range 查寻范围
* @param {Number} scale 比例
* @param {Array} blockList 障碍物列表
* @param {Array} src 起始点
* @param {Array} des 目标点
*/
function AStar(range,scale,blockList,src,des){
var srcNode={
parent:null,
G:0,
H:0,
coord:src || [10,10]
};
var desNode={
parent:null,
G:0,
H:0,
coord:des || [90,90]
};
var closedList=[];
var openList=[srcNode];
function init(){
range=range || [[0,0],[100,100]];
scale=scale || 10;
}
/**
* 检测障碍物
* @param {Array} p
*/
function checkBlock(p){
var i=blockList.length;
while(i--){
if(blockList[i][0]===p[0] && blockList[i][1]===p[1]){
return true;
}
}
return false;
}
/**
* 从列表中移除节点
* @param {Object} p
* @param {Array} list
*/
function removeFromList(p,list){
var i=list.length;
while(i--){
if(list[i]===p){
list.splice(i,1);
}
}
}
/**
* 从列表中获取节点
* @param {Object} p
* @param {Object} list
*/
function getFromList(p,list){
var i=list.length;
var ret;
while(i--){
if(list[i].coord[0]===p[0] && list[i].coord[1]===p[1]){
ret=list[i];
break;
}
}
return ret;
}
/**
* 获取节点的H值
* @param {Array} crd1
* @param {Array} crd2
*/
function getH(crd1,crd2){
return Math.abs(crd2[0]-crd1[0]) + Math.abs(crd2[1]-crd1[1])*scale;
}
/**
* 获取节点的G值
* @param {Array} crd
* @param {Object} p
*/
function getG(crd,p){
var crd2=p.coord;
return Math.floor(Math.sqrt(Math.pow(crd[0]-crd2[0],2)+Math.pow(crd[1]-crd2[1],2))*scale) + p.G;
}
/**
* 根据指定节点获取其相邻的节点集合
* @param {Object} p
*/
function getNearPoints(p){
var i=3;
var j;
var points=[];
var x;
var y;
var cp;
var otp;
while(i--){
x=p.coord[0] + (1-i);
j=3;
while(j--){
y=p.coord[1] + (1-j);
if ((x !== p.coord[0] || y !== p.coord[1])
&& !checkBlock([x,y])
&& (x > range[0][0] && x < range[1][0])
&& (y > range[0][1] && y < range[1][1])
&& !getFromList([x,y],closedList)
){
cp={
parent:null,
coord: [x, y],
G: 0,
H: getH([x, y], desNode.coord)
};
points.push(cp);
}
}
}
return points;
}
/**
* 从open列表中获取当前离目标点最近的节点
* @param {Array} openList
*/
function getCurrentPoint(openList){
var ol=openList;
var i=ol.length;
var minF=openList[0].G + openList[0].H;
var F;
var ret;
while(i--){
F=ol[i].G + ol[i].H;
if(F <= minF){
minF=F;
ret=ol[i];
}
}
return ret;
}
/**
* 检查节点的父节点是否需要更新为更短路径
* @param {Object} p
* @param {Object} crtP
*/
function checkParent(p,crtP){
var crd1=p.coord;
var crd2=crtP.coord;
var crtD=Math.floor(Math.sqrt(Math.pow(crd1[0]-crd2[0],2)+Math.pow(crd1[1]-crd2[1],2))*scale) + crtP.G;
if(crtD < p.G){
p.G=crtD;
p.parent=crtP;
}
}
/**
* 搜寻路径
*/
function search(){
var p;
var np;
var nearPoints=[];
var currentPoint;
var i;
var ret;
do {
currentPoint=getCurrentPoint(openList);
closedList.push(currentPoint);
removeFromList(currentPoint,openList);
if(getFromList(desNode.coord,closedList)){
ret=closedList;
break;
}
nearPoints=getNearPoints(currentPoint);
i=nearPoints.length;
while(i--){
np=nearPoints[i];
if(!getFromList(np.coord,openList)){
np.parent=currentPoint;
np.G=getG(np.coord,currentPoint);
openList.push(np);
}
checkParent(np,currentPoint);
}
}while (openList.length);
return ret;
}
var me={
/**
* 获取找寻到的路径
* @param {Array} src
* @param {Array} des
*/
getPath:function(src,des){
var path=[];
var list;
var node;
if (src) {
srcNode.coord=src;
openList = [srcNode];
}
des && (desNode.coord=des);
list=search();
if(list){
node=closedList[closedList.length-1];
path.push(node);
do{
node=node.parent;
path.push(node);
}while(node!==srcNode);
}
return path;
}
};
init();
return me;
}
完整版示例(其中绿色为范围,黑色为障碍物,蓝色为起始点,红色为目标点,白色为寻找到的路径):
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器