@nichathan-gaming/map-generator
Version:
Creates and generates a 2 dimensional array with various path generation functions.
105 lines (87 loc) • 4.1 kB
JavaScript
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;