1. 程式人生 > >A*尋路

A*尋路

/* * 1.佈局 * 2.估計函式 f(n) = g(n) + h(n); * 3.open佇列和close佇列 * 4.掛載估算值和父節點 * 5.繪製路線 * */ var oUl1 = document.getElementById('ul1'); var aLi = oUl1.getElementsByTagName('li'); var oInput1 = document.getElementById('input1'); // open佇列,裡面存放的是可以走的值 var openArr = []; // close佇列,裡面存放的是不可以走的值,走過的 + 障礙物
var closeArr = []; // 定義地圖 // 1 代表出發點 // 2 代表終點 // 3 代表障礙物 var map = [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,1,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0, 3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0
,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]; // 開方求正方形的邊長 var length = Math.sqrt(map.length); // 設定格子的寬高 var wide = 20; init(); // 起點和終點的查詢一定要放在init()之後,不然可能找不到 // 起點 var startLi = document.getElementsByClassName('sty1')[0]; // 終點 var endLi = document.getElementsByClassName('sty2')[0]; // 初始化 function init() { createGrid(); // 當點選按鈕時,開始尋路 oInput1.onclick = function () { openFn(); } } // 建立格子 function createGrid() { // 設定oUl1的width // 20個格子 * (格子的邊長 + 右側1px的邊框) + oUl1左側1px的邊框 oUl1.style.width = 20 * ( wide + 1 ) + 1 + 'px'; // 設定格子的樣式 for ( var i = 0; i < map.length; i++ ) { var oGrid = document.createElement('li'); oGrid.style.width = wide + 'px'; oGrid.style.height = wide + 'px'; if(map[i] === 1){ oGrid.className = 'sty1'; // 把起始點放入open佇列中 openArr.push(oGrid); } else if ( map[i] === 2 ) { oGrid.className = 'sty2'; } else if ( map[i] === 3 ) { oGrid.className = 'sty3'; // 把障礙物放入close佇列中 closeArr.push(oGrid); } oUl1.appendChild(oGrid); } } // 估價函式 function f(nodeLi){ return g(nodeLi) + h(nodeLi); } // 起始點到n節點的距離 function g(nodeLi) { var a = startLi.offsetLeft - nodeLi.offsetLeft; var b = startLi.offsetTop - nodeLi.offsetTop; return Math.sqrt( a*a + b*b ); } // n節點到終點的距離 function h(nodeLi) { var a = endLi.offsetLeft - nodeLi.offsetLeft; var b = endLi.offsetTop - nodeLi.offsetTop; return Math.sqrt( a*a + b*b ); } // open佇列函式 function openFn(){ // 剔除open佇列陣列中的第一個值,將它新增到close佇列陣列中去 var nowLi = openArr.shift(); // 當nowLi等於終點的時候,停止遞迴 if( nowLi === endLi ){ // 顯示路線 showLine(); return; } closeFn(nowLi); // 找到當前元素,周圍可以走的路 findLi(nowLi); // 找出最小值 openArr.sort(function (li1, li2){ return li1.num - li2.num; }); // 遞迴 arguments.callee(); } // close佇列函式 function closeFn(nowLi){ closeArr.push(nowLi); } // 找到當前元素,周圍可以走的路,然後新增到open佇列陣列中去 /* * 過濾條件 * 1.不能是close佇列陣列中的元素(障礙物+走過的路) * 2.不能是open佇列陣列中已經存在的值 * 3.只能是當前元素鄰居(只相差一個單元格) * */ function findLi(nowLi){ var result = []; // 1.不能是close佇列陣列中的元素(障礙物+走過的路) // 2.不能是open佇列陣列中已經存在的值 for ( var i = 0; i < aLi.length; i++ ) { if( filter(closeArr, aLi[i]) && filter(openArr, aLi[i]) ) { result.push(aLi[i]); } } function filter(arr, li){ // 剔除open佇列陣列或者close佇列陣列有的值 for ( var i = 0; i < arr.length; i++ ) { if ( arr[i] === li ){ return false; } } return true; } // 3.只能是當前元素鄰居(只相差一個單元格) for ( var i = 0; i < result.length; i++ ) { if(Math.abs(nowLi.offsetLeft - result[i].offsetLeft) <= (wide + 1) && Math.abs(nowLi.offsetTop - result[i].offsetTop) <= (wide + 1)){ // 將估價值掛載到元素 result[i].num = f(result[i]); // 掛載父節點,以便之後查詢 result[i].parent = nowLi; openArr.push(result[i]); //result[i].style.background = 'yellow'; } } } // 展示最短的路徑 function showLine(){ // 其實close佇列裡就有最短的路徑 var result = []; // 找到close佇列陣列最後一個值 var lastLi = closeArr.pop(); var iNow = 0; findParent(lastLi); /* * 通過找父節點,當父節點等於起點的時候就停止遞迴 * */ function findParent(nodeLi){ result.unshift(nodeLi); // 如果當前節點的父節點為起點,那麼停止遞迴 if(nodeLi === startLi ) { return; } findParent(nodeLi.parent); } var timer = setInterval(function (){ result[iNow++].style.background = 'red'; if(iNow === result.length){ clearInterval(timer); } }, 500); }