@excaliburjs/plugin-pathfinding
Version:
excalibur-pathfinding provides ability to use A* and Dijkstra's Algorithm for pathfinding and with Excalibur.js
1 lines • 6.77 kB
JavaScript
import{TileMap}from"excalibur";class ExcaliburGraph{duration=0;starttime=0;endtime=0;key=!1;nodes=new Map;edges=new Map;addNode(t){this.nodes.set("number"==typeof t.id?t.id.toString():t.id,t)}addEdge(t,e=!1){this.edges.set(t.name,t),e&&(e={name:t.name+"_reverse",from:t.to,to:t.from},t.value&&Object.assign(e,{value:t.value}),this.addEdge(e))}resetGraph(){this.nodes=new Map,this.edges=new Map}addTileMap(i,o=!1){i.tiles.forEach((t,e)=>{t.collider||this.addNode({id:""+e,value:0})}),i.tiles.forEach((t,e)=>{var s;i.tiles[e].collider||(s=[],i.tiles[e-1]&&e%i.cols!=0&&s.push({index:e-1,diagonal:!1}),i.tiles[e+1]&&e%i.cols!=i.cols-1&&s.push({index:e+1,diagonal:!1}),i.tiles[e-i.cols]&&s.push({index:e-i.cols,diagonal:!1}),i.tiles[e+i.cols]&&s.push({index:e+i.cols,diagonal:!1}),o&&(i.tiles[e-i.cols-1]&&e%i.cols!=0&&s.push({index:e-i.cols-1,diagonal:!0}),i.tiles[e-i.cols+1]&&e%i.cols!=i.cols-1&&s.push({index:e-i.cols+1,diagonal:!0}),i.tiles[e+i.cols-1]&&e%i.cols!=0&&s.push({index:e+i.cols-1,diagonal:!0}),i.tiles[e+i.cols+1])&&e%i.cols!=i.cols-1&&s.push({index:e+i.cols+1,diagonal:!0}),s.forEach(t=>{1!=i.tiles[t.index].collider&&this.addEdge({name:e+"_"+t.index,from:this.nodes.get(""+e),to:this.nodes.get(""+t.index),value:t.diagonal?1.41:1})}))})}getNodes(){return this.nodes}getEdges(){return this.edges}getAdjacentNodes(t){t=this.getAdjacentEdges(t).map(t=>t.to);return Array.from(new Set(t.map(t=>t.id))).map(t=>this.nodes.get("number"==typeof t?t.toString():t))}getAdjacentEdges(e){return[...this.edges.values()].filter(t=>t.from===e).map(t=>t)}getAdjacentEdgesTo(e){return[...this.edges.values()].filter(t=>t.to===e).map(t=>t)}bfs(t,e){this.starttime=performance.now();for(var s=[t],i=new Set;0<s.length;){var o=s.shift();if(i.has(o)||(i.add(o),s.push(...this.getAdjacentNodes(o))),o===e)return this.endtime=performance.now()-this.starttime,this.duration=this.endtime,!0}return this.endtime=performance.now()-this.starttime,this.duration=this.endtime,!1}dfs(t,e,s=new Set){this.starttime=performance.now();s.add(t);var i,t=this.getAdjacentNodes(t);for(i of t){if(i===e)return!0;if(!s.has(i)&&this.dfs(i,e,s))return this.endtime=performance.now()-this.starttime,this.duration=this.endtime,!0}return this.endtime=performance.now()-this.starttime,this.duration=this.endtime,!1}dijkstra(e){this.starttime=performance.now();let o=[],r=[],d=[];this.nodes.forEach(t=>r.push(t)),this.nodes.forEach(t=>d.push({node:t,distance:1/0,previous:null}));var s=d.findIndex(t=>t.node===e);if(-1===s)return[];d[s].distance=0,o.push(e),r.splice(r.indexOf(e),1);let n=e,h=this.getAdjacentEdges(n);for(let t=0;t<h.length;t++){let e=h[t];var i=d.findIndex(t=>t.node===e.to);if(-1===s)return[];d[i].distance=e.value,d[i].previous=n}for(;0<r.length;){var a;let s=1/0,i=-1;if(0<(a=d.filter(t=>r.includes(t.node)).map(t=>t.node)).length)for(let t=0;t<a.length;t++){let e=a[t];var l=d.findIndex(t=>t.node===e);d[l].distance<s&&(s=d[l].distance,i=l)}else{s=1/0,i=-1;for(let t=0;t<r.length;t++){let e=r[t];var c=d.findIndex(t=>t.node===e);d[c].distance<s&&(s=d[c].distance,i=c)}}if(-1===i)break;n=d[i].node,h=(h=this.getAdjacentEdges(n)).filter(t=>!o.includes(t.from)&&!o.includes(t.to)),o.push(n),r.splice(r.indexOf(n),1);for(let t=0;t<h.length;t++){let e=h[t];var p=d.findIndex(t=>t.node===e.to),u=d.findIndex(t=>t.node===e.from),u=d[u].distance+e.value;u<d[p].distance&&(d[p].distance=u,d[p].previous=n)}}return this.endtime=performance.now()-this.starttime,this.duration=this.endtime,d}shortestPath(t,e){var s=this.dijkstra(t),i=[];let o=e;for(;null!=o&&(i.push(o),null!=(o=s.find(t=>t.node==o)?.previous)););return i.reverse(),i}}class ExcaliburAStar{tilemap={cols:0,rows:0};grid=[];currentNode=null;currentIndex=0;checkedNodes=[];openNodes=[];startnode=null;endnode=null;goalReached=!1;path=[];duration=0;starttime=0;endtime=0;constructor(t){this.tilemap.cols=t instanceof TileMap?t.columns:t.cols,this.tilemap.rows=t.rows;let e=0;for(var s of t.tiles)t instanceof TileMap?this.grid.push({id:e,collider:!!s.solid,gCost:0,hCost:0,fCost:0,x:e%this.tilemap.cols,y:Math.floor(e/this.tilemap.cols),checked:!1,parent:null}):this.grid.push({id:e,collider:!!s.collider,gCost:0,hCost:0,fCost:0,x:e%this.tilemap.cols,y:Math.floor(e/this.tilemap.cols),checked:!1,parent:null}),e++}setCost(){if(null!==this.startnode&&null!==this.endnode&&0!==this.grid.length)for(var t of this.grid)t.gCost=this.getGCost(t,this.startnode),t.hCost=this.getHCost(t,this.endnode),t.fCost=this.getFCost(t)}astar(t,e,s=!1){for(this.starttime=performance.now(),this.startnode=t,this.endnode=e,this.goalReached=!1,this.checkedNodes=[],this.openNodes=[],this.setCost(),this.openNodes.push(this.startnode);0<this.openNodes.length;){this.currentNode=this.openNodes[0],this.currentIndex=0;for(var i of this.openNodes)i.fCost<this.currentNode.fCost&&(this.currentNode=i,this.currentIndex=this.openNodes.indexOf(i));if(this.openNodes.splice(this.currentIndex,1),this.checkedNodes.push(this.currentNode),this.currentNode===this.endnode){for(this.path=[];this.path.push(this.currentNode),this.currentNode=this.currentNode.parent,this.currentNode!==this.startnode;);return this.path=this.path.reverse(),this.goalReached=!0,this.endtime=performance.now(),this.duration=this.endtime-this.starttime,this.path}var o;for(o of this.getNeighbors(this.currentNode,s))this.checkedNodes.includes(o)||this.openNodes.includes(o)||o.collider||(o.parent=this.currentNode,this.openNodes.push(o))}return this.endtime=performance.now(),this.duration=this.endtime-this.starttime,[]}getNeighbors(t,e){var s=[],t="string"==typeof t.id?parseInt(t.id):t.id;return this.grid[t-1]&&t%this.tilemap.cols!=0&&s.push(this.grid[t-1]),this.grid[t+1]&&t%this.tilemap.cols!=this.tilemap.cols-1&&s.push(this.grid[t+1]),this.grid[t-this.tilemap.cols]&&s.push(this.grid[t-this.tilemap.cols]),this.grid[t+this.tilemap.cols]&&s.push(this.grid[t+this.tilemap.cols]),e&&(this.grid[t-this.tilemap.cols-1]&&t%this.tilemap.cols!=0&&s.push(this.grid[t-this.tilemap.cols-1]),this.grid[t-this.tilemap.cols+1]&&t%this.tilemap.cols!=this.tilemap.cols-1&&s.push(this.grid[t-this.tilemap.cols+1]),this.grid[t+this.tilemap.cols-1]&&t%this.tilemap.cols!=0&&s.push(this.grid[t+this.tilemap.cols-1]),this.grid[t+this.tilemap.cols+1])&&t%this.tilemap.cols!=this.tilemap.cols-1&&s.push(this.grid[t+this.tilemap.cols+1]),s}getNodeByIndex(t){return this.grid[t]}getNodeByCoord(t,e){return this.grid[e*this.tilemap.cols+t]}getGCost(t,e){return Math.abs(t.x-e.x)+Math.abs(t.y-e.y)}getHCost(t,e){return Math.abs(t.x-e.x)+Math.abs(t.y-e.y)}getFCost(t){return t.gCost+t.hCost}resetGrid(){for(var t of this.grid)t.checked=!1,t.gCost=0,t.hCost=0,t.fCost=0;this.checkedNodes=[],this.startnode=null,this.endnode=null,this.goalReached=!1,this.duration=0}}export{ExcaliburGraph,ExcaliburAStar};