UNPKG

@nichathan-gaming/map-generator

Version:

Creates and generates a 2 dimensional array with various path generation functions.

105 lines (87 loc) 4.1 kB
import mapGenerator from '../mapGenerator.js'; import getCostToGoal from './getCostToGoal.js'; import compareIndexes from '../helpers/compareIndexes.js'; import getConnectedIndexes from '../helpers/getConnectedIndexes.js'; import assertIndex from '../helpers/assertIndex.js'; import { assertInstanceOf } from '@nichathan-gaming/assertions'; /** * Attempts to find a path between 2 indexes * @param {mapGenerator} map The current game map element * @param {[number, number]} startingIndex The index that the search begins at * @param {[number, number]} endingIndex The index that the search ends at * @returns {[number, number][]} The path between 2 indexes or null if the path is unreachable */ const getAStarPath = (map, startingIndex, endingIndex) => { assertInstanceOf(map, mapGenerator); assertIndex(startingIndex); assertIndex(endingIndex); //get the cost to the goal for the startingIndex const startingCostToGoal = getCostToGoal(startingIndex, endingIndex); //create the staringNode const startingNode = { index: startingIndex, costFromStart: 0, costToGoal: startingCostToGoal, totalCost: startingCostToGoal }; //create an array to store nodes that haven't been travelled to const openNodes = [startingNode]; const closedIndexes = []; //generate the path while we have more openNodes while(openNodes.length > 0){ //sort the open nodes to find the shortest path openNodes.sort((firstNode, secondNode) => firstNode.totalCost - secondNode.totalCost); //take the first node const currentNode = openNodes.shift(); //if the goal is reached, reconstruct the path if(compareIndexes(currentNode.index, endingIndex)){ const path = []; let current = currentNode; while(current){ //add the index of the current node to the front of the path path.unshift(current.index); //get the next node from the previous parent current = current.parent; }; //return the path return path; }; //the index isn't the end of the path, add it to the closed array closedIndexes.push(currentNode.index); // explore neighbors getConnectedIndexes(map, currentNode.index).forEach(el=>{ //don't continue if we are already tracking this index or if it isn't a valid value if(closedIndexes.includes(el) || map.isIndexUnwalkable(el)) return; //get the new values for this connected node const costFromStart = currentNode.costFromStart + 1; const costToGoal = getCostToGoal(el, endingIndex); const totalCost = costFromStart + costToGoal; //see if we have an openNode at this index const existingNode = openNodes.find(openEL => { return compareIndexes(openEL.index, el); }); //we either don't have have an existing node or the existing node now has a lesser value if(!existingNode || costFromStart < existingNode.costFromStart) { if(!existingNode){ //we don't have a node yet at this index const neighborNode = { index: el, costFromStart: costFromStart, costToGoal: costToGoal, totalCost: totalCost, parent: currentNode }; openNodes.push(neighborNode); } else { //update the existing node existingNode.costFromStart = costFromStart; existingNode.totalCost = totalCost; existingNode.parent = currentNode; }; }; }); }; //the goal was not reached return null; }; export default getAStarPath;