A星寻路算法
A星寻路算法
1.准备一个close关闭列表(存放已被检索的点),一个open开启列表(存放未被检索的点),一个当前点的对象cur
2.将cur设成开始点
3.从cur起,将cur点放入close表中,然后将cur点四周不为障碍物的点放入open表中,并计算四周点的F、G、H值,设置点的父级为cur
4.依次检索open表中F值最小的点,如果出现多个F值最小的点,取离cur最近的点,并更新cur为最小的点,并将其从open表中删除
5.重复3-4步
结束条件:结束点被放入close表中,或者open表为空
寻路:
从结束点开始,依次查找其父级,直至查找到起始点
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>A*寻路算法</title>
<style>
body, dl, dd { margin:0; padding:0; }
html { font-size:12px; background:#F5F5F5; }
.block { border:1px solid #eaeaea; width:30px; background:#FFF; }
.menu { position:fixed; left:0; bottom:0; width:100%; background:#00aeff; background:-webkit-linear-gradient(rgba(0, 186, 255, .8), rgba(0, 130, 255, .8)); border-top:1px solid #009cff; padding:20px 0; box-shadow:2px -2px 12px rgba(167, 213, 255, .8); }
.menu dd { float:left; padding:0 10px; }
.menu span { padding:0 10px; color:#FFF; }
.menu input { border-radius:4px; }
.menu .txt { width:100px; border:1px solid #0085ff; padding:0 5px; width:60px; height:20px; color:#0085ff; background:#ffffff; }
.menu .btn { border:1px solid #0085ff; color:#FFF; background:#0071d1; background:-webkit-linear-gradient(rgba(80, 170, 255, .8), rgba(0, 132, 255, .8)); height:22px; cursor:pointer; }
.menu .btn:hover { background-color:#32a1ff; background:-webkit-linear-gradient(rgba(0, 132, 255, .8), rgba(80, 170, 255, .8)); border-color:#1988ff; }
.menu .btn.dashed, .menu .btn.dashed:hover { background:#1988ff; color:#b2dcff; cursor:default; }
.start, .start.path { background-color:#007de7; }
.end, .end.path { background-color:#333; }
.fence { background-color:#d1d1d1; }
.path { background-color:#57e700; -webkit-transition:.5s all ease; }
</style>
<script>
window.onload = function () {
var oBox = document.getElementById('box'); // 主容器
var oBtnStart = document.getElementById('setStart'); // 设置起始点按钮
var oBtnEnd = document.getElementById('setEnd'); // 设置结束点按钮
var oBtnCalcu = document.getElementById('calcuPath'); // 计算路径按钮
var oBtnReplay = document.getElementById('calcuReplay'); // 重新计算 var oFrag = document.createDocumentFragment(); // 存放临时文档碎片
var oStart = null; // 起始点
var oEnd = null; // 结束点 var aPoint = oBox.getElementsByTagName('div'); // 存放地图元素
var aMaps = []; // 存放地图数据
var maps = [
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 1, 1, 1],
[0, 0, 0, 0, 1, 1, 0, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
]; // 此maps调试用 var maps = []; var iRnd = parseInt(document.getElementById('setRnd').value); // 随机障碍
var iRow = parseInt(document.getElementById('setRow').value); // 存放行数
var iCol = parseInt(document.getElementById('setCol').value); // 存放列数
var iWidth = parseInt(document.getElementById('setWidth').value); // 单元格宽
var iHeight = parseInt(document.getElementById('setHeight').value); // 单元格高
var iTime = parseInt(document.getElementById('setTime').value); // 动画延时 function render() {
var n = 0;
oBox.innerHTML = ''; for (var i = 0; i < iRow; i++) {
for (var j = 0; j < iCol; j++ ) {
var oBlock = document.createElement('div');
oBlock.className = 'block';
oBlock.row = i;
oBlock.col = j;
oBlock.style.position = 'absolute';
oBlock.style.left = iWidth * j + 'px';
oBlock.style.top = iHeight * i + 'px';
oBlock.style.width = iWidth - 1 + 'px';
oBlock.style.height = iHeight - 1 + 'px'; oFrag.appendChild(oBlock);
}
} oBox.appendChild(oFrag); oBox.style.width = iWidth * iCol + 'px';
oBox.style.height = iHeight * iRow + 'px';
oBox.style.position = 'absolute';
oBox.style.left = '50%';
oBox.style.top = '50%';
oBox.style.marginLeft = -oBox.offsetWidth / 2 + 'px';
oBox.style.marginTop = -oBox.offsetHeight / 2 + 'px';
} function module() {
aMaps = [];
aPoint = oBox.getElementsByTagName('div'); for (var i = 0; i < iRow; i++) {
aMaps[i] = [];
for (var j = 0; j < iCol; j++) {
aMaps[i][j] = aPoint[i * iCol + j];
}
}
} render();
module();
rndFence(aMaps, iRnd, maps); /* // 此处起始点调试用
oStart = aMaps[0][1];
oStart.className += ' start'
oEnd = aMaps[3][8];
oEnd.className += ' end'; findway(aMaps, oStart, oEnd);*/ oBox.onclick = function (ev) {
var ev = ev || window.event;
var target = ev.target || ev.srcElement; if (oBtnCalcu.disabled) return; // 设置起点、设置终点、设置障碍物
if (/(\b|\s)+block(\b|\s)+/i.test(target.className)) {
if (!oStart && target.val != 1) target.start = true, target.className += ' start', oStart = target;
else if (oStart && !oEnd) {
if (!target.start && target.val != 1) target.end = true, target.className += ' end', oEnd = target;
else if (target.start) return alert('起止点不能相同点');
} else if (oStart && oEnd) {
if (!target.start && !target.end && target.val != 1) {
target.val = 1;
target.className += ' fence';
}
}
} module();
}; oBtnCalcu.onclick = function () {
if (oStart && oEnd) {
var path = findway(aMaps, oStart, oEnd, 0);
if (!path) alert('无路可走');
else {
for (var i = 0; i < path.length; i++) {
~function (i) {
var timer = null;
timer = setTimeout(function () {
path[i].className += ' path';
clearTimeout(timer);
}, i * iTime)}(i);
}
}
} if (!oStart && !oEnd) {
alert('请选择起始点');
} else if (oStart && !oEnd) {
alert('请选择结束点');
} else {
this.disabled = true;
this.className += ' dashed';
}
} oBtnReplay.onclick = function () {
oStart = null;
oEnd = null;
oBtnCalcu.disabled = false;
oBtnCalcu.className = oBtnCalcu.className.replace(/(\s|\b)+dashed(\s|\b)+/ig, ''); iWidth = parseInt(document.getElementById('setWidth').value);
iHeight = parseInt(document.getElementById('setHeight').value);
iRow = parseInt(document.getElementById('setRow').value);
iCol = parseInt(document.getElementById('setCol').value);
iRnd = parseInt(document.getElementById('setRnd').value);
iTime = parseInt(document.getElementById('setTime').value); render();
module();
rndFence(aMaps, iRnd);
};
}; // 随机生成障碍物
function rndFence(points, num, maps) {
var total = points.length * points[0].length;
var index = 0;
var col = 0;
var row = 0;
var n = 0;
var arr = []; if (!maps || !maps.length) {
while (n < num) {
index = rnd(0, total);
row = parseInt(index / points[0].length);
col = index % points[0].length; if (!points[row][col].val) {
points[row][col].val = 1;
points[row][col].className += ' fence';
n++;
} else {
continue;
}
}
} else {
for (var i = 0; i < maps.length; i++) {
for (var j = 0; j < maps[0].length; j++) {
if (maps[i][j] == 1) {
points[i][j].val = 1;
points[i][j].className += ' fence';
}
}
}
}
} // 生成随机数
function rnd(begin, end){
return Math.floor(Math.random() * (end - begin)) + begin;
} // 获取四周点
function getRounds(points, current) {
var u = null;
var l = null;
var d = null;
var r = null; var rounds = []; // 上
if (current.row - 1 >= 0) {
u = points[current.row - 1][current.col];
rounds.push(u);
} // 左
if (current.col - 1 >= 0) {
l = points[current.row][current.col - 1];
rounds.push(l);
} // 下
if (current.row + 1 < points.length) {
d = points[current.row + 1][current.col];
rounds.push(d);
} // 右
if (current.col + 1 < points[0].length) {
r = points[current.row][current.col + 1];
rounds.push(r);
} return rounds;
} // 检测是否在列表中
function inList(list, current) {
for (var i = 0, len = list.length; i < len; i++) {
if ((current.row == list[i].row && current.col == list[i].col) || (current == list[i])) return true;
}
return false;
} function findway(points, start, end) {
var opens = []; // 存放可检索的方块
var closes = []; // 存放已检索的方块
var cur = null; // 当前指针
var bFind = true; // 是否检索
var n = 0; // 设置开始点的F、G为0并放入opens列表
start.F = 0;
start.G = 0;
start.H = 0; // 将起点压入closes数组,并设置cur指向起始点
closes.push(start);
cur = start; // 如果起始点紧邻结束点则不计算路径直接将起始点和结束点压入closes数组
if (Math.abs(start.row - end.row) + Math.abs(start.col - end.col) == 1) {
end.P = start;
closes.push(end);
bFind = false;
} // 计算路径
while (cur && bFind) {
//while (n < 19) {
// 先把当前点加入closes中
//if (n == 10) console.log(cur);
if (!inList(closes, cur)) closes.push(cur);
//alert(n + '次:运行');
// 然后获取当前点四周点
var rounds = getRounds(points, cur);
//if (n == 18) console.log(rounds); // 调试用
// 当四周点不在opens数组中并且可移动,设置G、H、F和父级P,并压入opens数组
for (var i = 0; i < rounds.length; i++) {
if (rounds[i].val == 1 || inList(closes, rounds[i]) || inList(opens, rounds[i])) continue;
else if (!inList(opens, rounds[i]) && rounds[i].val != 1) {
rounds[i].G = cur.G + 1;
rounds[i].H = Math.abs(rounds[i].col - end.col) + Math.abs(rounds[i].row - end.row);
rounds[i].F = rounds[i].G + rounds[i].H;
rounds[i].P = cur; opens.push(rounds[i]); //rounds[i].style.backgroundColor = 'yellow';
//rounds[i].innerHTML = 'F=' + rounds[i].F + '<br />G=' + rounds[i].G + '<br />H=' + rounds[i].H + '<br />N=' + n;
}
} /* // 此for调试用
for (var i = 0; i < opens.length; i++) {
opens[i].style.backgroundColor = 'yellow';
opens[i].innerHTML = 'F=' + opens[i].F + '<br />G=' + opens[i].G + '<br />H=' + opens[i].H + '<br />N=' + n;
}*/ //alert(n + '次:计算open数组后'); // 如果获取完四周点后opens列表为空,则代表无路可走,此时推出循环
if (!opens.length) {
cur = null;
opens = [];
closes = [];
break;
} // 按照F值由小到达将opens数组排序
opens.sort(function (a, b) {
return a.F - b.F;
}); // 取出opens数组中F值最小的元素,即opens数组中的第一个元素
var oMinF = opens[0]; var aMinF = []; // 存放opens数组中F值最小的元素集合 // 循环opens数组,查找F值和cur的F值一样的元素,并压入aMinF数组。即找出和最小F值相同的元素有多少
for (var i = 0; i < opens.length; i++) {
if (opens[i].F == oMinF.F) aMinF.push(opens[i]);
} // 如果最小F值有多个元素
if (aMinF.length > 1) {
// 计算元素与cur的曼哈顿距离
for (var i = 0; i < aMinF.length; i++) {
//aMinF[i].D = Math.abs(aMinF[i].row - cur.row) + Math.abs(aMinF[i].col - cur.col);
aMinF[i].D = Math.abs(aMinF[i].row - cur.row) + Math.abs(aMinF[i].col - cur.col);
} // 将aMinF按照D由小到大排序
aMinF.sort(function (a, b) {
return a.D - b.D;
}); // 将cur指向D值最小的元素
oMinF = aMinF[0];
} cur = oMinF; // 将cur压入closes数组
if (!inList(closes, cur)) closes.push(cur); // 将cur从opens数组中删除
for (var i = 0; i < opens.length; i++) {
if (opens[i] == cur) {
opens.splice(i, 1);
break;
}
} /* // 此处样式调试用
cur.style.backgroundColor = 'green';
cur.innerHTML = 'F=' + cur.F + '<br />G=' + cur.G + '<br />H=' + cur.H + '<br />N=' + n;*/ //alert(n + '次:选取cur后'); // 找到最后一点,并将结束点压入closes数组
if (cur.H == 1) {
end.P = cur;
closes.push(end);
cur = null;
} n++;
} if (closes.length) {
// 从结尾开始往前找
var dotCur = closes[closes.length - 1];
var path = []; // 存放最终路径 while (dotCur) {
path.unshift(dotCur); // 将当前点压入path数组
dotCur = dotCur.P; // 设置当前点指向父级 // 找到第一个点后把起点添加进路径
if (!dotCur.P) {
path.unshift(start);
dotCur = null;
}
} return path;
} else {
return false;
}
}
</script>
</head> <body>
<div id="box" class="box"></div>
<dl class="menu">
<dd><span>动画延时(毫秒)</span><input type="text" id="setTime" value="30" class="txt" /></dd>
<dd><span>随机障碍(整数)</span><input type="text" id="setRnd" value="20" class="txt" /></dd>
<dd><span>设置行数(整数)</span><input type="text" id="setRow" value="10" class="txt" /></dd>
<dd><span>设置列数(整数)</span><input type="text" id="setCol" value="10" class="txt" /></dd>
<dd><span>设置宽度(像素)</span><input type="text" id="setWidth" value="50" class="txt" /></dd>
<dd><span>设置高度(像素)</span><input type="text" id="setHeight" value="50" class="txt" /></dd>
<dd><input type="button" id="calcuPath" value="计算路径" class="btn" /></dd>
<dd><input type="button" id="calcuReplay" value="重新计算" class="btn" /></dd>
</dl>
</body>
</html>
A星寻路算法的更多相关文章
- A星寻路算法介绍
你是否在做一款游戏的时候想创造一些怪兽或者游戏主角,让它们移动到特定的位置,避开墙壁和障碍物呢? 如果是的话,请看这篇教程,我们会展示如何使用A星寻路算法来实现它! 在网上已经有很多篇关于A星寻路算法 ...
- cocos2d-x学习日志(13) --A星寻路算法demo
你是否在做一款游戏的时候想创造一些怪兽或者游戏主角,让它们移动到特定的位置,避开墙壁和障碍物呢?如果是的话,请看这篇教程,我们会展示如何使用A星寻路算法来实现它! A星算法简介: A*搜寻算法俗称A星 ...
- 无递归 A星寻路算法
整理硬盘的时候,发现我早些年写的A星寻路算法.特放上来,待有缘人拿去,无递归噢,性能那是杠杠的. 码上伺候 public class Node { public int X { get; set; } ...
- A星寻路算法(A* Search Algorithm)
你是否在做一款游戏的时候想创造一些怪兽或者游戏主角,让它们移动到特定的位置,避开墙壁和障碍物呢? 如果是的话,请看这篇教程,我们会展示如何使用A星寻路算法来实现它! 在网上已经有很多篇关于A星寻路算法 ...
- A星寻路算法入门(Unity实现)
最近简单学习了一下A星寻路算法,来记录一下.还是个萌新,如果写的不好,请谅解.Unity版本:2018.3.2f1 A星寻路算法是什么 游戏开发中往往有这样的需求,让玩家控制的角色自动寻路到目标地点, ...
- A星寻路算法-Mind&Hand(C++)
//注1:Mind & Hand,MIT校训,这里指的理解与实现(动脑也动手) //注2:博文分为两部分:(1)理解部分,为参考其他优秀博文的摘要梳理:(2)代码部分,是C++代码实现的,源码 ...
- 【Android】基于A星寻路算法的简单迷宫应用
简介 基于[漫画算法-小灰的算法之旅]上的A星寻路算法,开发的一个Demo.目前实现后退.重新载入.路径提示.地图刷新等功能.没有做太多的性能优化,算是深化对A星寻路算法的理解. 界面预览: 初始化: ...
- [转载]A星寻路算法介绍
转载自:http://www.raywenderlich.com/zh-hans/21503/a%E6%98%9F%E5%AF%BB%E8%B7%AF%E7%AE%97%E6%B3%95%E4%BB% ...
- 基于Unity的A星寻路算法(绝对简单完整版本)
前言 在上一篇文章,介绍了网格地图的实现方式,基于该文章,我们来实现一个A星寻路的算法,最终实现的效果为: 项目源码已上传Github:AStarNavigate 在阅读本篇文章,如果你对于里面提到的 ...
随机推荐
- POJ 2464 Brownie Points II --树状数组
题意: 有点迷.有一些点,Stan先选择某个点,经过这个点画一条竖线,Ollie选择一个经过这条直接的点画一条横线.Stan选这两条直线分成的左下和右上部分的点,Ollie选左上和右下部分的点.Sta ...
- HDU 2065 “红色病毒”问题 --指数型母函数
这种有限制的类棋盘着色问题一般可以用指数型母函数来解决,设Hn表示这样的着色数,首先H0=1,则Hn等于四个字母的(A,B,C,D)的多重集合的n排列数,其中每个字母的重数是无穷,且要求A,C出现的次 ...
- 第20章 DLL高级技术(1)
20.1 DLL模块的显式载入和符号链接 20.1.1 显式载入DLL模块 (1)构建DLL时,如果至少导出一个函数/变量,那么链接器会同时生成一个.lib文件,但这个文件只是在隐式链接DLL时使用( ...
- JMeter学习(二十五)HTTP属性管理器HTTP Cookie Manager、HTTP Request Defaults
Test Plan的配置元件中有一些和HTTP属性相关的元件:HTTP Cache Manager.HTTP Authorization Manager.HTTP Cookie Manager.HTT ...
- JMeter学习(十八)JMeter测试Java(二)
实例: 服务为:将输入的两个参数通过IO存入文件: 1.打开MyEclipse,编写Java代码 服务: package test; import java.io.File; import java. ...
- TestLink学习六:TestLink1.9.13工作使用小结
Testlink是一款强大的用例追踪和管理工具.测试管理注重的实际上就是一个流程. 1.默认当测试用例同名时,就会有提示.(以前版本需要修改配置) 2.测试用例序号:(缺点) 1)删除一个测试用例之后 ...
- Android优化——UI优化(五) Listview 重用convertView
1.重用convertView 我们对convertView添加判断,如果存在我们就直接使用,否则初始化一个convertView的实例.(如下图) 2.使用viewHolder 使用viewHold ...
- C++中的运算符重载
首先思考以下几个问题: 1.什么是运算符重载? 2.为什么要重载运算符,它有什么用? 3.可以重载哪些运算符? 4.重载运算符有哪些规则? 一.基本概念 我们在程序中使用各种操作符,比如加(+).赋值 ...
- 会报编译器警告的Xcode 6.3新特性:Nullability Annotations
最近在用Xcode 6.3写代码,一些涉及到对象的代码会报如下编译器警告: 1 Pointer is missing a nullability type specifier (__nonnull o ...
- Java 操作 Redis 高级
/Users/sherry/WorkPath/Git/Web/redisDemo/src/main/java/org/zln/utils/JedisUtils.java package org.zln ...