A* 寻路 +寻路演示(js)

效果

在这里插入图片描述

每个单元格内文字:
(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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/507011.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pc端无法ping android模拟器_【内附下载方式】PC端最新宝可梦 Lets Go去皮去伊模拟器+最新dlc+mod...

游戏名称&#xff1a;精灵宝可梦 Let‘s go 皮卡丘 伊布游戏总大小&#xff1a;9GB游戏简介&#xff1a;《精灵宝可梦 Lets Go 皮卡丘/伊布》是宝可梦系列全新作品&#xff0c;也是该系列首次登上Switch主机&#xff0c;为后续作品的开发奠定了基础。本作可以视为是对第一世代红…

自定义处理网页选区字符并实时显示(js)

概述 浏览网页的时候&#xff0c;可能需要去数一下某段文字的字符数量&#xff0c;或者需要对选中内容做些实时翻译&#xff0c;比如进制的转化&#xff0c;可以使用脚本做个简单的实时翻译。 效果 选中内容&#xff0c;并且鼠标移动时会在左下角显示翻译后的结果。示例为翻…

css文本行高是哪个属性_CSS中的line-height行高属性的使用技巧小结

CSS中的line-hight属性是用来控制文本行之间的空隙的。它一般情况下没有单位的设定(如&#xff1a;line-height:1.4;)所以这是一个按字体尺寸的比例来计算。这对印刷版来说是一个很重要的属性。线条过低就会挤在一起&#xff0c;线条过高就会相距甚远&#xff0c;这两种情况都会…

模2运算及模2运算式计算 (js)

概述 学习二维码生成的时候卡在纠错码部分&#xff0c;然后就接触到了伽罗华域&#xff0c;了解到模2运算&#xff0c;恰好前不久刚了解了波兰表达式&#xff0c;就尝试写一个支持模2运算的算式解析计算。 结果 10011*101101.M2Calc() >"1011010" 涉及内容 模…

react配合python_部署React前端和Django后端的3种方法

Python部落(python.freelycode.com)组织翻译&#xff0c;禁止转载&#xff0c;欢迎转发。2020年4月7日星期二&#xff0c;马修西格尔类别&#xff1a;Django如果您要用Django REST开发web应用程序后端&#xff0c;并使用React或Vue开发应用程序前端。有很多方法实现。你需要做出…

伽罗瓦域(256) 生成指定纠错码字的生成多项式 (js)

效果 使用 复制code 保存为html 涉及内容 1、有限域、伽罗瓦域(256) 2、对数反对数 3、XOR 异或 两个因式各项相乘&#xff0c;当系数项相乘时&#xff0c;指数相加并mod(255) 合并同类项时&#xff0c;相同项的系数合并为 XOR操作&#xff1b; 伽罗瓦域依旧有些犯懵&…

python贪心算法求删数问题_贪心算法删数问题

删数问题给定n位正整数a&#xff0c;去掉其中任意k个数字后&#xff0c;剩下的数字按原次序排列组成一个新的正整数。对于给定的n和k&#xff0c;设计一个算法&#xff0c;找出剩下数字组成的新数最少的删数方案。输入示例&#xff1a; 178543 4输出&#xff1a; 13输入示例&am…

二维码-纠错码生成(js)

概要 本文主要为计算&#xff0c;而非对伽罗瓦域等数学内容的深入解析&#xff0c;在知道消息多项式以及纠错数量的情况下通过程序生成对应的纠错码。详细的二维码生成原理参考一个详细全面的二维码生成解析 效果 数据码: 长度16的示例数据码&#xff08;正常获取的是示例的…

一步一步学python爬虫_初学Python之爬虫的简单入门

初学Python之爬虫的简单入门一、什么是爬虫&#xff1f;1.简单介绍爬虫爬虫的全称为网络爬虫&#xff0c;简称爬虫&#xff0c;别名有网络机器人&#xff0c;网络蜘蛛等等。网络爬虫是一种自动获取网页内容的程序&#xff0c;为搜索引擎提供了重要的数据支撑。搜索引擎通过网络…

实现二维码-完整三种编码流程加代码解析(javascript)

效果 输入内容&#xff1a;XXXwedewed生日//&sss乐❤XXXwedewed生日//&sss乐❤ 完整的演示效果为&#xff0c;输入内容后会将解码绘制的每一步都展示&#xff08;有点长就不全截图了&#xff0c;可以直接移至最后复制代码到本地运行&#xff09;&#xff1a; 原理…

可视化排班管理_小白经理的思考日记-可视化看板和走动管理

前言&#xff1a;所谓勤于思&#xff0c;敏于行&#xff0c;实践能提升思考的深度&#xff0c;反思也能更好的指导实践。基于这样的认知&#xff0c;我将过往所学所思所行进行了专题整理&#xff0c;对内化知识和技能结构颇有益。又承蒙付老师抬爱和鼓励&#xff0c;推荐我看《…

headerIP php_PHP curl伪造IP地址和header信息代码实例

原标题&#xff1a;PHP curl伪造IP地址和header信息代码实例curl虽然功能强大&#xff0c;但是只能伪造$_SERVER["HTTP_X_FORWARDED_FOR"]&#xff0c;对于大多数IP地址检测程序来说&#xff0c;$_SERVER["REMOTE_ADDR"]很难被伪造&#xff1a;首先是clien…

c++ 使用nacos_为什么选用Nacos?虎牙直播微服务改造实践

原标题&#xff1a;为什么选用Nacos&#xff1f;虎牙直播微服务改造实践“相比文字和图片&#xff0c;直播提供了人与人之间更丰富的沟通形式&#xff0c;其对平台稳定性的考验很大&#xff0c;那么倡导“以技术驱动娱乐”的虎牙直播如何在技术上赋能娱乐&#xff1f;本文将分为…

three.js 拖动场景中物体(原生|拖拽控制器)

非控制器版 拖动场景中的物体实际上是在一个平行于窗口的平面中进行拖动&#xff0c;确定这个平面并确定鼠标在该平面中的位置变化&#xff0c;就将问题转换成简单的2d移动物体了 <!DOCTYPE html> <html> <head><meta charsetutf-8><title>015-…

bch纠错码 码长8_从HDMI视频数据带有BCH纠错码讨论线材对画质的影响

一直感觉几十元的HDMI线已经可以正常传输视频信号,那么几百元应该是接近完美了,再听人说换上几千块的线就让画质黑位什么的提升一大截,认为非常不可思议.但别人又信誓旦旦的说自己是感觉到了明显改变,对这种情况产生的争论最终不会得到正确答案,所以数据党还是从科学的角度先分…

element-ui 可复选树型表格

效果 思路 自定义模板当点击某个行复选框时&#xff0c;其所有后代复选框都要同步状态&#xff0c;且其直系父辈状态需要根据所点击复选框的状态来修正点击全选复选框时&#xff0c;批量同步所有行内复选框状态 代码 非封装组件&#xff0c;按需自行改写 确保引入element-ui…

linux 删除含有关键词的文件_linux下查找包含关键字的文件

在linux下如果要查找包含某个关键字的文件&#xff0c;如要在nagios目录下搜索带有关键字“store-rd-sys”的文件&#xff0c;在终端下运行命令&#xff1a;/usr/local/nagios/etc/* (nagios目录)法1&#xff1a;grep -r “关键字” 路径[rootnagios01 ~]#grep -r "store-…

树形可拖拽排序配置组件

效果 使用场景 vue2下自定义表格表头配置: 列排序&#xff0c;显示/隐藏等。确保表头以配置项的形式加载&#xff0c;这样表格才能对修改后的配置作响应 思路 1、表格使用render函数加载(如有疑问可私信)&#xff0c;通过类似如下的columns配置表头 columns: [{ label: 姓名…

maven 打包指定依赖包_Maven打包成Jar文件时依赖包的问题

我们项目中使用到第三方的库文件&#xff0c;这些jar库文件并没有放到Maven中央库上&#xff0c;导致我们需要在项目中自己配置使用。我们的两三个开发人员对Java都是很熟&#xff0c;因此在使用中遇到了一些问题&#xff0c;表现在&#xff1a;在本地中引入第三方jar包后&…