效果

每个单元格内文字:
(F) (Price)
(G) (H)

原理

原理是参考另一篇csdn博文,不过忘记收藏找不到了

  1. 初始化 open_setclose_set
  2. 起点 加入open_set中,并设置优先级为0(优先级最高)。
  3. 如果open_set不为空,则从open_set中选取优先级最高的节点n
    1. 如果节点n为终点,则:从终点开始逐步追踪prev(前一个)节点,一直达到起点;返回找到的结果路径,算法结束
    2. 如果节点n不是终点,则:将节点nopen_set中删除,并加入close_set中;遍历节点n所有的邻近节点
      1. 如果邻近节点mclose_set中,则:跳过,选取下一个邻近节点;
      2. 如果邻近节点mopen_set中,则:判断节点n到节点mF(n) + cost[n,m] 值是否 <节点mF(m) 。来尝试更新该点,重新设置f值和父节点等数据;
      3. 如果邻近节点m也不在open_set中,则:设置节点mprev为节点n 计算节点m的优先级将节点m加入open_set中。

个人理解

1、不用考虑死胡同情况,不同于普通的按照方向搜索,a* 遍历临近节点,即使碰到死胡同,也不需要回归到非死胡同节点。

代码

直接保存以下为html执行

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>a*</title>
</head>
<body>
<canvas id="cv" width="1000px" height="1000px"></canvas>
<script>/*** 输入起始、目标 坐标,返回寻路结果。* 每个单元格都有代价值*///初始点let sx, sy;//目标点let es, ey;//网格数量let mx = 20, my = 20;//格子大小let cell_size = 40;//初始化网格数据let cells = [];//不可逾越代价let notAllowPrice = 10;//代价上限let priceLimit = 11;//节点对象function cell(x, y, price) {this.x = x;this.y = y;//关联的之前单元this.prev = null;//关联单元格坐标this.relation = new Array(8);this.relation[0] = y == 0 ? null : [x, y - 1];this.relation[1] = (y == 0 || x == mx - 1) ? null : [x + 1, y - 1];this.relation[2] = (x == mx - 1) ? null : [x + 1, y];this.relation[3] = (x == mx - 1 || y == my - 1) ? null : [x + 1, y + 1];this.relation[4] = (y == my - 1) ? null : [x, y + 1];this.relation[5] = (x == 0 || y == my - 1) ? null : [x - 1, y + 1];this.relation[6] = (x == 0) ? null : [x - 1, y];this.relation[7] = (x == 0 || y == 0) ? null : [x - 1, y - 1];//通行代价this.price = ~~(price);}//初始化画布let el = document.getElementById('cv');let context = el.getContext("2d");let canvas_width = el.width;let canvas_height = el.height;//初始化单元数据function loadGridData() {cells = [];for (let i = 0; i < my; i++) {for (let j = 0; j < mx; j++) {cells.push(new cell(j, i, Math.random() * priceLimit))}}}//设置初始点,目标点function setStartAndEnd(x0, y0, x1, y1) {if (x0 != null && y0 != null && x1 != null && y1 != null) {sx = x0;sy = y0;ex = x1;ey = y1;} else {//设置初始点sx = ~~(Math.random() * mx);sy = ~~(Math.random() * my);// sx = 1;// sy = 17;//设置目标点ex = ~~(Math.random() * mx);ey = ~~(Math.random() * my);}let start = cells[sy * my + sx],end = cells[ey * my + ex];//确保起始点\终点是可通行start.price = 0;end.price = 0;start.f = 0;end.f = 0;start.g = 0;return {start: start, end: end}}//绘制画布function paint() {//清空之前context.fillStyle = '#fff';context.fillRect(0,0,canvas_width,canvas_height);context.stokeStyle = `#999`;context.fillStyle = `#000`;for (let i = 0, cell; (cell = cells[i]) != null; i++) {if (cell.price < notAllowPrice) {context.rect(cell.x * cell_size, cell.y * cell_size, cell_size, cell_size)} else {context.fillRect(cell.x * cell_size, cell.y * cell_size, cell_size, cell_size)}}context.stroke();}//开始寻路async function start(x0,y0,x1,y1) {//初始单元数据loadGridData();//绘制画板paint();//初始let {start, end} = setStartAndEnd(...arguments);//绘制起始点context.fillStyle = "green";context.fillRect(start.x * cell_size, start.y * cell_size, cell_size, cell_size);context.fillStyle = "red";context.fillRect(end.x * cell_size, end.y * cell_size, cell_size, cell_size);console.log('start:', start);console.log('end:', end);//寻路结果let result = [];//检测列表let open_set = [];//关闭列表let close_set = [];//从起始点开始检测open_set.push(start);let bestCell;while (open_set.length) {open_set.sort((a, b) => a.f - b.f);bestCell = open_set[0];if (bestCell == end) {while (bestCell.prev) {result.push(bestCell);bestCell = bestCell.prev;}console.log('result:', result);break;} else {close_set.push(open_set.shift());let relCell;for (let i = 0; i < bestCell.relation.length; i++) {relCell = bestCell.relation[i];if (relCell) {relCell = cells[relCell[1] * my + relCell[0]];//临近点不可通过|临近点已被关闭|临界点为夹角点不可到达if (relCell.price >= notAllowPrice || close_set.includes(relCell) || !angleAllowCell(bestCell, relCell)) {continue;}if (open_set.includes(relCell)) {if (bestCell.f + cost(bestCell, relCell) < relCell.f) {relCell.prev = bestCell;relCell.g = ~~(relCell.prev.g+cost(relCell.prev,relCell));relCell.f = ~~(relCell.g + cost(relCell, end)+relCell.price);relCell.h = ~~cost(relCell,end);}continue}relCell.prev = bestCell;//g的计算方式为 前一个单元 的G + 当前单元与前一个单元的距离relCell.g = ~~(relCell.prev.g+cost(relCell.prev,relCell));relCell.f = ~~(relCell.g + cost(relCell, end)+relCell.price);relCell.h = ~~cost(relCell,end);open_set.push(relCell);//标记为检测await delay(5).then(() => {context.fillStyle = "rgba(50,238,255,0.1)";context.fillRect(relCell.x * cell_size, relCell.y * cell_size, cell_size, cell_size);paintRelArrow(relCell, relCell.prev);paintCellText(relCell, relCell.f, 2, 10);//FpaintCellText(relCell,relCell.g,2,cell_size -2);//GpaintCellText(relCell,relCell.h,cell_size - 13,cell_size-2);//HpaintCellText(relCell,relCell.price,cell_size - 10,10);//price})}}}}if(result.length){//标记路线result.shift();//移出终点result.forEach(d => {context.fillStyle = "rgba(255,253,114,0.6)";context.fillRect(d.x * cell_size, d.y * cell_size, cell_size, cell_size);});}else{console.log('目标不可到达')}}/*================================utils=======================================*//*** 计算两点间曼哈顿距离(可以换成欧拉)*/function cost(a, b) {return Math.abs(a.x - b.x) + Math.abs(a.y - b.y)}/*** 绘制先后关系指向箭头,由后者指向前者并在后者中标注* @param child 子单元* @param parent*/function paintRelArrow(after, prev) {let directions = ['↑', '↗', '→', '↘', '↓', '↙', '←', '↖'];for (let i = 0; i < 8; i++) {if (after.relation[i] && after.relation[i][0] == prev.x && after.relation[i][1] == prev.y) {//←↑→↓↖↙↗↘↕let text = directions[i];paintCellText(after, text, cell_size / 2 - 5, cell_size / 2 + 5);break}}}/*** tb 相对 fa 的方向t* @param fa cell* @param tb cell* @param isStrict 是否严格要求相邻*/function directionType(fa, tb, isStrict) {if (isStrict) {for (let i = 0; i < 8; i++) {if (fa.relation[i] && fa.relation[i][0] == tb.x && fa.relation[i][1] == tb.y) {//←↑→↓↖↙↗↘↕return i}}} else {let dx = tb.x - fa.x,dy = tb.y - fa.y;if (dx < 0) {return dy < 0 ? 7 : (dy == 0 ? 6 : 5)}if (dx == 0) {return dy < 0 ? 0 : (dy == 0 ? -1 : 4)}if (dx > 0) {return dy < 0 ? 1 : (dy == 0 ? 2 : 3)}}}/*** 根据x,y 坐标获取实际的cell对象* @param x* @param y*/function getCellByXY(x, y) {return cells[y * my + x];}/*** 目标临近点是否是当前节点的夹角点,判断标准为临近点为当前点的对角点且对角两侧为不可通过点*/function angleAllowCell(a, b) {let relation = a.relation;if (relation[1] && b == getCellByXY(...relation[1])) {return relation[0].price < notAllowPrice || relation[2].price < notAllowPrice}if (relation[3] && b == getCellByXY(...relation[3])) {return relation[2].price < notAllowPrice || relation[4].price < notAllowPrice}if (relation[5] && b == getCellByXY(...relation[5])) {return relation[4].price < notAllowPrice || relation[6].price < notAllowPrice}if (relation[7] && b == getCellByXY(...relation[7])) {return relation[6].price < notAllowPrice || relation[0].price < notAllowPrice}return true}/*** 给指定单元格的指定位置绘制文字* @param cell* @param text* @param x* @param y*/function paintCellText(cell, text, x, y) {context.font = '12px';context.fillStyle = '#000';context.fillText(text, cell.x * cell_size + x, cell.y * cell_size + y);}/*** 延时*/function delay(time) {time = time || 1000;return new Promise((res, rej) => {setTimeout(res, time)})}start();/*** 7  0  1* 6  *  2* 5  4  3*初始化open_set和close_set;* 将起点加入open_set中,并设置优先级为0(优先级最高);* 如果open_set不为空,则从open_set中选取优先级最高的节点n:*   如果节点n为终点,则:*     从终点开始逐步追踪parent节点,一直达到起点;*     返回找到的结果路径,算法结束;*   如果节点n不是终点,则:*      将节点n从open_set中删除,并加入close_set中;*      遍历节点n所有的邻近节点:*         如果邻近节点m在close_set中,则:*            跳过,选取下一个邻近节点*         如果邻近节点m在open_set中,则:*            判断节点n到节点m的 F(n) + cost[n,m] 值是否 < 节点m的 F(m) 。*            来尝试更新该点,重新设置f值和父节点等数据*         如果邻近节点m也不在open_set中,则:*            设置节点m的parent为节点n*            计算节点m的优先级*            将节点m加入open_set中**/
</script>
</body>
</html>

A* 寻路 +寻路演示(js)相关推荐

  1. python强化学习实例:寻路Q-Learn演示

    文章目录 python强化学习-寻路Q-Learn演示 1. 简介 1.1 项目简介 1.2 运行方式 1.3 参考 2 Q-Learn 2.1 简介 2.2 项目里的Q表 3. 演示 4. 后记 p ...

  2. 多通路迷宫最短路径(寻路加找宝箱)

    多通路迷宫最短路径找宝箱版 图示 寻路需求 演示代码 演示 图示 寻路需求 这次我们在一个多路径迷宫中放置几个宝箱 ,从起点出发找到所有的宝箱之后再寻路到终点. 演示代码 #!/usr/bin/pyt ...

  3. 探索迷局:解密广度寻路算法

    ================================ 专栏文章,自下而上 数据结构与算法--二叉搜索树 数据结构与算法--深度寻路算法 数据结构与算法--二叉树+实现表达式树 数据结构与算 ...

  4. 34、JS/AJAX

      1)回顾JS中核心内容 2)了解WEB1.0和WEB2.0时代的技术与特点 3)理解AJAX的产生背景.工作原理与特点 4)掌握AJAX常用API及应用   声明:服务端使用Servlet技术 一 ...

  5. JavaScript---网络编程(12)--DHTML技术演示(5)-form表单验证技术(正则)

    这里不进行很复杂的后台验证以及JavaScript的正则表达式,只是简单的介绍下这个技术,简单的后台接收与跳转,大概了解怎么验证的就可以.具体的技术,我后面还会继续写博客的.本人也还在学习中. 表单验 ...

  6. 100多个经典常用的PHP功能插件大全实例演示和下载

    版权声明:转载请注明原创地址 https://blog.csdn.net/u013032788/article/details/74011578 推荐特效 PHP功能插件  更多 > 09-07 ...

  7. JS实现b站动态评论区抽奖(含去重)

    目录 新前言 补充 视频演示 旧前言 教程 1.访问页面 2.打开"检查" 3.贴入代码 2.0版本多人抽取 3.0新版本单人抽取(适应b站动态改动) 3.1新版本多人抽取(适应b ...

  8. Ext JS框架入门

    Ext JS框架入门 一.概述: ExtJS可以用来开发RIA也即富客户端的AJAX应用,是一个用javascript写的,主要用于创建前端用户界面,是一个与后台技术无关的前端ajax框架. 功能丰富 ...

  9. 案例:js实现关闭淘宝二维码

    案例:js实现关闭淘宝二维码 先看效果演示: js实现过程: 第1步.获取页面元素 var x = document.getElementById('x');//id比较好用,因为id具有唯一性// ...

最新文章

  1. 基于 Nginx+lua+Memcache 实现灰度发布
  2. ActivityRouter
  3. P4424-[HNOI/AHOI2018]寻宝游戏【结论】
  4. 值对于 int32 太大或太小_怎样将视频文件变小却对画质没有太大影响呢?
  5. win10计算机从桌面消失了,Windows10家庭版程序窗口在桌面上消失了解决方法
  6. html中使用js、jQuery展示页面小结
  7. supersocke接收不到数据_豪横吗?易查分除了上传电子表格,复制粘贴也能上传数据啦!...
  8. shell 查看Linux 进程 是否存在
  9. ORACLE start with… connect by prior 子句用法
  10. 2019一注结构成绩_2019年福建地区计算机考研汇总分析
  11. C++ RapidXml快速入门
  12. ELK日志分析Elasticsearch模块——语法基础CRUD
  13. python自己做课程表_Python课程表II
  14. [转载]ExtJs4 笔记(8) Ext.slider 滚轴控件、 Ext.ProgressBar 进度条控件、 Ext.Editor 编辑控件...
  15. ibm tivoli_使用表单认证通过Tivoli Access Manager电子商务WebSEAL启用Microsoft Office Sharepoint Server客户机集成
  16. 安徽师范大学计算机学院导师,安徽师范大学 数字计算机学院 耿焕同老师简介 联系方式 手机电话 邮箱...
  17. win10开启键盘灯的步骤
  18. VS编译器提示:C4996 ‘scanf‘: This function or variable may be unsafe. Consider using scanf_s instead.的解决方法
  19. [sign in和sign up哪个是注册?哪个是登入?]
  20. 迷你扣扣的java实现

热门文章

  1. Android之事件分发机制
  2. 《Python快速入门》基础知识扫盲课
  3. 【每日SQL打卡】​​​​​​​​​​​​​​​DAY 4丨员工薪水中位数【难度困难】
  4. Swagger 注解~用于模型
  5. java 反射获取对象_使用Java反射机制获取对象
  6. Modbus协议栈实现Modbus RTU多主站支持
  7. hash和一致性hash
  8. React Native新手引导
  9. PKU 学生反馈 2009 - 4
  10. oracle 异常返回值,oracle - java.sql.SQLException:无效的列类型:调用具有行类型返回值的函数时为1111 - 堆栈内存溢出...