对A-Star寻路算法的粗略研究
首先来看看完成后的效果:
其中灰色代表路障,绿色是起点和移动路径,红色代表终点
// = openArray[i+1].F) {
minNode = openArray[i+1];
}
}
start = minNode;
//将新开始点加入关闭列表
close.push(start);
//将新开始点从开启列表中移除
for(i = 0; i ';
for(var j = -3; j '+j+','+i+'
';
} else {
html += '
';
}
}
html += '
';
}
t.innerHTML = html;
}
function addStone() {
var tdCollections = $('td');
for(var i = 0; i
为了继续学习,需要明白几个概念。
曼哈顿距离
曼哈顿距离的定义是,两个物体南北方向的距离与东西方向的距离之和。看起来就好像是直角三角形的两条边之和。
用代码表示出来就是:
/**
* 计算两点间的曼哈顿距离
* @param goalNode {Object} 终点坐标
* @param startNode {Object} 起点坐标
* @returns {number} 两点间的曼哈顿距离
*/
function Manhattan(goalNode,startNode) {
return Math.abs(goalNode.x - startNode.x) + Math.abs(goalNode.y - startNode.y);
}
公式F=G+H
G:从起点开始,沿着计算出的可能路径,移动到该路径上的移动消耗。
H:计算出的可能路径到终点的移动预估消耗。
F:G与H之和。
以下图来说明:
起点S周围有四个可选路径a、b、c、d(为简单起见,不考虑对角线也可行走的情况),先来看路径a。
从起点S到达a的移动耗费是1格,故G=1。而从a到达终点G的移动耗费估算是5格,故H=5。F是G与H的值相加,为6。
经过观察,a、b、c三个路径的F值是一样的。而d路径的F值为4。可见F值越小,到达终点的花费越少,因此应该选择d路径作为下一步。
到达d路径后,重复前面的过程,搜索周围的路径,找到F值最小的作为下一步,同时将这个路径作为新的起始点。因为接下来每个路径的F值
都是参照这个新起始点来计算的。
由上图可知,S通往G的最佳路径是d、e、f。
但是还有一种情况,比如下图(灰色表示路障,无法通行):
e和f的F值是一样的,这时候选择哪个呢?其实都可以,一般选择最后一个被计算出来的路径即可。
具体实现
上述方法虽然可行,但是如果不加以限制,会造成一些不良后果。当从起点S到达新路径d时,d仍需要对周围的路径进行探索,起点S也将包含其中,很显然这是不必要而且浪费的。对此,我们需要维护两个列表,一个称为路径开启列表,一个称为路径关闭列表。
var open = []; //开启列表
var close = []; //关闭列表
open列表的职责是探索周围路径时,将可通行的路径(无路障的路径)加入列表中;
close列表则负责将各个新起点加入其中(相应的这些新起点也要从open列表中移除),下一次执行路径探索时,如果该路径存在这个列表中,则忽略它。
当终点G被包含在close列表中时,搜索结束。
需要注意的是,为每个新起点标记它的父结点,即它是从哪里过来的(比如d路径的父结点就是S,e的父结点是d),这样在到达终点G时,就能够根据它的父结点
一级一级地返回,从而找到这条“通路”的所有坐标,有点像链表这种数据结构。
完整代码:
html部分
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>a-star</title>
<style>
body{
margin: 0;
font-size:12px;
}
table{border-collapse: collapse;
width: 100%; table-layout: fixed}
table th,table td{border:1px solid #000}
#t{
width: 831px;
}
#t td{
width: 30px;
height: 30px;
text-align: center;
}
.start{background-color: #00fc5f}
.block{background-color:#cacaca}
.goal{background-color: #ff2211}
.visited{background-color: #009921;}
</style>
<script src="../jquery-2.1.3.js"></script>
</head>
<body>
<input id="start" type="button" value="开始寻路"/>
<script src="a-star.js"></script>
<script>
$('#start').bind('click',function() {
move(start,end);
});
</script>
</body>
</html>
js部分
var open = [], //开启列表
close = [], //关闭列表
start = {}, //起点坐标
end = {}; //终点坐标
var d = document; /**
* 检查待测坐标是否在坐标集合内
* @param toBeCheckNode {Object} 待检查坐标 {x,y}
* @param sourceNode {Array} 坐标集合
* @returns {boolean} 待测坐标是否在坐标集合内
*/
function isNodeExists(toBeCheckNode,sourceNode) {
for(var i in sourceNode) {
if (sourceNode.hasOwnProperty(i)) {
if (parseInt(toBeCheckNode.x) === sourceNode[i].x && parseInt(toBeCheckNode.y) === sourceNode[i].y) return true;
}
}
return false;
} /**
* 返回数组中的某个元素
* @param el 待返回元素
* @param arr 数组
* @returns {Object} 返回该元素
*/
function getElementInArray(el,arr) {
for(var i in arr) {
if(arr.hasOwnProperty(i)) {
if(parseInt(el.x) === arr[i].x && parseInt(el.y) === arr[i].y) {
return arr[i];
}
}
}
return null;
} /**
* 计算两点间的曼哈顿距离
* @param goalNode {Object} 终点坐标
* @param startNode {Object} 起点坐标
* @returns {number} 两点间的曼哈顿距离
*/
function Manhattan(goalNode,startNode) {
return Math.abs(goalNode.x - startNode.x) + Math.abs(goalNode.y - startNode.y);
} /**
* 选择最佳路径作为新起始点
* @param openArray {Array} 开启列表
* @returns {Object} 返回新起始点
*/
function selectNewStart(openArray) {
var minNode = openArray[0],i;
for(i = 0,len = openArray.length - 1; i < len; i++) {
if(minNode.F >= openArray[i+1].F) {
minNode = openArray[i+1];
}
}
start = minNode;
//将新开始点加入关闭列表
close.push(start); //将新开始点从开启列表中移除
for(i = 0; i < openArray.length; i++) {
if(minNode.x === openArray[i].x && minNode.y === openArray[i].y) {
openArray.splice(i,1);
break;
}
}
return start;
} /**
* 遍历周围节点并加入开启列表
* @param node {Object} 一个起始点
*/
function searchAround(node) {
for(var i = -1; i <= 1;i++) {
for(var j = -1; j <= 1; j++) {
var x = node.x + i,
y = node.y + j;
//判断是否为有效的路径点
var nodeExsits = findCurrentPositionInfo(x,y) != null;
if(!nodeExsits) continue;
var t = parseInt(findCurrentPositionInfo(x,y).getAttribute('type')); if(!(x !== node.x && y !== node.y)) {
if(x!== node.x || y !== node.y) {
var curNode = {x:x,y:y,type:t}; //如果该坐标无法通行,则加入关闭列表中
if(curNode.type === 4 ||
curNode.type === 0 ||
curNode.type === 44) {
if(isNodeExists(curNode,close)) continue;
close.push(curNode);
} //如果该坐标已在关闭列表中,略过
if(isNodeExists(curNode,close)) continue; //如果该坐标已在开启列表中,则重新计算它的G值
if(isNodeExists(curNode,open)) {
var new_GValue = Manhattan(curNode,start),
//在开启列表中取出这个元素
inOpenNode = getElementInArray(curNode,open),
//取出旧的G值
old_GValue = inOpenNode.G; //如果G值更小,则意味着当前到达它的路径比上一次的好,更新它的父结点
//以及G值,并重新计算它的F值
if(new_GValue < old_GValue) {
inOpenNode.parent = start;
inOpenNode.G = new_GValue;
inOpenNode.F = inOpenNode.G + inOpenNode.H;
}
continue;
} //设置父节点
curNode.parent = {x:node.x,y:node.y};
curNode.G = Manhattan(curNode,node);
curNode.H = Manhattan(end,curNode);
//估算值
curNode.F = curNode.G + curNode.H;
//将坐标加入开启列表中
open.push(curNode);
}
}
}
}
} function findCurrentPositionInfo(x, y) {
var tds = $('td'),
s = x + "," + y;
for(var i = 0; i < tds.length; i++) {
if(tds[i].innerHTML === s) return tds[i];
}
return null;
} function generateMap() {
var t = d.createElement('table');
t.id = 't';
d.body.appendChild(t); var html = '';
for(var i = -3; i < 10; i++) {
html += '<tr>';
for(var j = -3; j < 15; j++) {
if(i === 0 && j === 0) {
html += '<td class="start" type="1">'+j+','+i+'</td>';
} else {
html += '<td type="1">'+j+','+i+'</td>';
}
}
html += '</tr>';
}
t.innerHTML = html;
} function addStone() {
for(var i = 0; i < 50; i++) {
var r = Math.ceil(Math.random() * 233);
if(r === 57) continue;
var res = tdCollections.eq(r).addClass('block');
res.attr('type',0);
}
} function setGoal() {
var r = Math.ceil(Math.random() * 233);
if(r === 57 || tdCollections.eq(r).hasClass('block')) return setGoal();
var res = tdCollections.eq(r).addClass('goal'); //var res = tdCollections.eq(24).addClass('goal'); return {
x:res.html().split(',')[0],
y:res.html().split(',')[1]
}
} function setColor(start) {
var x = start.x,
y = start.y,
el = findCurrentPositionInfo(x,y); $(el).addClass('visited');
} function move(s,e) {
searchAround(s);
s = selectNewStart(open);
setColor(s);
if(!isNodeExists(e,close)) {
setTimeout(function() {
log();
return move(s,e);
},100);
}
} function init() {
open = [];
close = [];
start = {};
end = {};
} function log() {
console.log('当前起点:',start);
console.log('开启列表:',open);
console.log('关闭列表:',close);
} generateMap();
var tdCollections = $('td');
addStone();
end = setGoal();
start = {x:0,y:0,type:1};
close.push(start); //move(start,end);
//
对A-Star寻路算法的粗略研究的更多相关文章
- A*(也叫A star, A星)寻路算法Java版
寻路算法有非常多种,A*寻路算法被公觉得最好的寻路算法. 首先要理解什么是A*寻路算法,能够參考这三篇文章: http://www.gamedev.net/page/resources/_/techn ...
- 算法:Astar寻路算法改进
早前写了一篇<RCP:gef智能寻路算法(A star)> 出现了一点问题. 在AStar算法中,默认寻路起点和终点都是N x N的方格,但如果用在路由上,就会出现问题. 如果,需要连线的 ...
- A*寻路算法的探寻与改良(三)
A*寻路算法的探寻与改良(三) by:田宇轩 第三分:这部分内容基于树.查找算法等对A*算法的执行效率进行了改良,想了解细 ...
- 无递归 A星寻路算法
整理硬盘的时候,发现我早些年写的A星寻路算法.特放上来,待有缘人拿去,无递归噢,性能那是杠杠的. 码上伺候 public class Node { public int X { get; set; } ...
- [转] A*寻路算法C++简单实现
参考文章: http://www.policyalmanac.org/games/aStarTutorial.htm 这是英文原文<A*入门>,最经典的讲解,有demo演示 http: ...
- 《C++游戏开发》十六 游戏中的寻路算法(二):迷宫&A*算法基础
本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/10289253 作者:七十一雾央 新浪微博:http: ...
- 不再依赖A*,利用C++编写全新寻路算法
一,说在前面的话 大概在半年前,看见一到信息竞赛题:在任意方格阵中设置障碍物,确定起始点后,求这两点之间路径.当时觉得蛮有意思的,但是没有时间去做,今天花了两个小时来实现它.据说有一个更高级的寻路算法 ...
- 一种高效的寻路算法 - B*寻路算法
在此把这个算法称作B* 寻路算法(Branch Star 分支寻路算法,且与A*对应),本算法适用于游戏中怪物的自动寻路,其效率远远超过A*算法,经过测试,效率是普通A*算法的几十上百倍. 通过引入该 ...
- 数据结构和算法总结(三):A* 寻路算法
前言 复习下寻路相关的东西,而且A star寻路在游戏开发中应用挺多的,故记录下. 正文 迪杰斯特拉算法 说起A*得先谈谈Dijkstra算法,它是在BFS基础上的一种带权值的两点最短寻路贪心算法. ...
随机推荐
- mysql 游标的使用总结
一.游标的基本概念 游标:游标是一个存储在Mysql服务器上的数据库查询,它不是一条select语句,而是被该语句检索出来的结果集. 本人,学习游标中,曾遇到一个问题,循环总是最后多执行一次.下面分析 ...
- 洛谷P3625 - [APIO2009]采油区域
Portal Description 给出一个\(n\times m(n,m\leq1500)\)的矩阵,从中选出\(3\)个互不相交的\(k\times k\)方阵,使得被选出的数的和最大. Sol ...
- 仔细瞄一下HashMap是怎么干活的
以下分析基于jdk11.0.2 1. 创建HashMap时发生了什么? HashMap(),HashMap(int initialCapacity),HashMap(int initialCapaci ...
- batch.bat explaination
1.Echo 命令 打开回显或关闭请求回显功能,或显示消息.如果没有任何参数,echo 命令将显示当前回显设置. 语法 echo [{on|off}] [message] Sample篅echo of ...
- 【Visual Studio】error LNK2038: 检测到“_MSC_VER”的不匹配项: 值“1600”不匹配值“1800” (转)
1.案例一 _MSC_VER 定义编译器的版本.下面是一些编译器版本的_MSC_VER值:MS VC++ 10.0 _MSC_VER = 1600MS VC++ 9.0 _MSC_VER = 1500 ...
- https的简单学习
HTTPS介绍: (全称:Hyper Text Transfer Protocol over Secure Socket Layer 或 Hypertext Transfer Protocol Sec ...
- docker部署 mysql redis问题
问题:(ubuntu不报错,centos报错) ERROR: : starting container process caused "process_linux.go:402: conta ...
- js中加“var”和不加“var”的区别,看完觉得这么多年js白学了
Javascript声明变量的时候,虽然用var关键字声明和不用关键字声明,很多时候运行并没有问题,但是这两种方式还是有区别的.可以正常运行的代码并不代表是合适的代码. var num = 1: 是在 ...
- TensorFlow——tensorflow指定CPU与GPU运算
1.指定GPU运算 如果安装的是GPU版本,在运行的过程中TensorFlow能够自动检测.如果检测到GPU,TensorFlow会尽可能的利用找到的第一个GPU来执行操作. 如果机器上有超过一个可用 ...
- 深入理解Atomic原子类
Atomic是基于unsafe类和自旋操作实现的,下面以AtomicInteger类为例进行讲解. 要理解Atomic得先了解CAS CAS CAS全程Compare And Swap ,是条并发原语 ...