日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時(shí)間:2024/8/23 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 A* 寻路 +寻路演示(js) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

效果

每個(gè)單元格內(nèi)文字:
(F) (Price)
(G) (H)

原理

原理是參考另一篇csdn博文,不過忘記收藏找不到了

  • 初始化 open_set和 close_set。
  • 起點(diǎn) 加入open_set中,并設(shè)置優(yōu)先級為0(優(yōu)先級最高)。
  • 如果open_set不為空,則從open_set中選取優(yōu)先級最高的節(jié)點(diǎn)n
  • 如果節(jié)點(diǎn)n為終點(diǎn),則:從終點(diǎn)開始逐步追蹤prev(前一個(gè))節(jié)點(diǎn),一直達(dá)到起點(diǎn);返回找到的結(jié)果路徑,算法結(jié)束
  • 如果節(jié)點(diǎn)n不是終點(diǎn),則:將節(jié)點(diǎn)n從open_set中刪除,并加入close_set中;遍歷節(jié)點(diǎn)n所有的鄰近節(jié)點(diǎn)
  • 如果鄰近節(jié)點(diǎn)m在close_set中,則:跳過,選取下一個(gè)鄰近節(jié)點(diǎn);
  • 如果鄰近節(jié)點(diǎn)m在open_set中,則:判斷節(jié)點(diǎn)n到節(jié)點(diǎn)m的F(n) + cost[n,m] 值是否 <節(jié)點(diǎn)m的 F(m) 。來嘗試更新該點(diǎn),重新設(shè)置f值和父節(jié)點(diǎn)等數(shù)據(jù);
  • 如果鄰近節(jié)點(diǎn)m也不在open_set中,則:設(shè)置節(jié)點(diǎn)m的prev為節(jié)點(diǎn)n 計(jì)算節(jié)點(diǎn)m的優(yōu)先級將節(jié)點(diǎn)m加入open_set中。
  • 個(gè)人理解

    1、不用考慮死胡同情況,不同于普通的按照方向搜索,a* 遍歷臨近節(jié)點(diǎn),即使碰到死胡同,也不需要回歸到非死胡同節(jié)點(diǎn)。

    代碼

    直接保存以下為html執(zhí)行

    <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>a*</title> </head> <body> <canvas id="cv" width="1000px" height="1000px"></canvas> <script>/*** 輸入起始、目標(biāo) 坐標(biāo),返回尋路結(jié)果。* 每個(gè)單元格都有代價(jià)值*///初始點(diǎn)let sx, sy;//目標(biāo)點(diǎn)let es, ey;//網(wǎng)格數(shù)量let mx = 20, my = 20;//格子大小let cell_size = 40;//初始化網(wǎng)格數(shù)據(jù)let cells = [];//不可逾越代價(jià)let notAllowPrice = 10;//代價(jià)上限let priceLimit = 11;//節(jié)點(diǎn)對象function cell(x, y, price) {this.x = x;this.y = y;//關(guān)聯(lián)的之前單元this.prev = null;//關(guān)聯(lián)單元格坐標(biāo)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];//通行代價(jià)this.price = ~~(price);}//初始化畫布let el = document.getElementById('cv');let context = el.getContext("2d");let canvas_width = el.width;let canvas_height = el.height;//初始化單元數(shù)據(jù)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))}}}//設(shè)置初始點(diǎn),目標(biāo)點(diǎn)function setStartAndEnd(x0, y0, x1, y1) {if (x0 != null && y0 != null && x1 != null && y1 != null) {sx = x0;sy = y0;ex = x1;ey = y1;} else {//設(shè)置初始點(diǎn)sx = ~~(Math.random() * mx);sy = ~~(Math.random() * my);// sx = 1;// sy = 17;//設(shè)置目標(biāo)點(diǎn)ex = ~~(Math.random() * mx);ey = ~~(Math.random() * my);}let start = cells[sy * my + sx],end = cells[ey * my + ex];//確保起始點(diǎn)\終點(diǎn)是可通行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) {//初始單元數(shù)據(jù)loadGridData();//繪制畫板paint();//初始let {start, end} = setStartAndEnd(...arguments);//繪制起始點(diǎn)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);//尋路結(jié)果let result = [];//檢測列表let open_set = [];//關(guān)閉列表let close_set = [];//從起始點(diǎn)開始檢測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]];//臨近點(diǎn)不可通過|臨近點(diǎn)已被關(guān)閉|臨界點(diǎn)為夾角點(diǎn)不可到達(dá)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的計(jì)算方式為 前一個(gè)單元 的G + 當(dāng)前單元與前一個(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);//標(biāo)記為檢測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){//標(biāo)記路線result.shift();//移出終點(diǎn)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('目標(biāo)不可到達(dá)')}}/*================================utils=======================================*//*** 計(jì)算兩點(diǎn)間曼哈頓距離(可以換成歐拉)*/function cost(a, b) {return Math.abs(a.x - b.x) + Math.abs(a.y - b.y)}/*** 繪制先后關(guān)系指向箭頭,由后者指向前者并在后者中標(biāo)注* @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 是否嚴(yán)格要求相鄰*/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)}}}/*** 根據(jù)x,y 坐標(biāo)獲取實(shí)際的cell對象* @param x* @param y*/function getCellByXY(x, y) {return cells[y * my + x];}/*** 目標(biāo)臨近點(diǎn)是否是當(dāng)前節(jié)點(diǎn)的夾角點(diǎn),判斷標(biāo)準(zhǔn)為臨近點(diǎn)為當(dāng)前點(diǎn)的對角點(diǎn)且對角兩側(cè)為不可通過點(diǎn)*/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);}/*** 延時(shí)*/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;* 將起點(diǎn)加入open_set中,并設(shè)置優(yōu)先級為0(優(yōu)先級最高);* 如果open_set不為空,則從open_set中選取優(yōu)先級最高的節(jié)點(diǎn)n:* 如果節(jié)點(diǎn)n為終點(diǎn),則:* 從終點(diǎn)開始逐步追蹤parent節(jié)點(diǎn),一直達(dá)到起點(diǎn);* 返回找到的結(jié)果路徑,算法結(jié)束;* 如果節(jié)點(diǎn)n不是終點(diǎn),則:* 將節(jié)點(diǎn)n從open_set中刪除,并加入close_set中;* 遍歷節(jié)點(diǎn)n所有的鄰近節(jié)點(diǎn):* 如果鄰近節(jié)點(diǎn)m在close_set中,則:* 跳過,選取下一個(gè)鄰近節(jié)點(diǎn)* 如果鄰近節(jié)點(diǎn)m在open_set中,則:* 判斷節(jié)點(diǎn)n到節(jié)點(diǎn)m的 F(n) + cost[n,m] 值是否 < 節(jié)點(diǎn)m的 F(m) 。* 來嘗試更新該點(diǎn),重新設(shè)置f值和父節(jié)點(diǎn)等數(shù)據(jù)* 如果鄰近節(jié)點(diǎn)m也不在open_set中,則:* 設(shè)置節(jié)點(diǎn)m的parent為節(jié)點(diǎn)n* 計(jì)算節(jié)點(diǎn)m的優(yōu)先級* 將節(jié)點(diǎn)m加入open_set中**/ </script> </body> </html>

    總結(jié)

    以上是生活随笔為你收集整理的A* 寻路 +寻路演示(js)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。