private static List<Node> getAdjacentNodes(Tile startingTile, Map map, List<Node> closedList, Node destinationNode) { List<Node> adjacentNodes = new List<Node>(); List<Tile> tilesToCheck = new List<Tile>(); Node startingNode = new Node(startingTile); int startingX = startingTile.xPos; int startingY = startingTile.yPos; if (map.coordinatesOnMap(startingX, startingY + 1)) tilesToCheck.Add(map.tiles[startingX, startingY + 1]); if (map.coordinatesOnMap(startingX, startingY - 1)) tilesToCheck.Add(map.tiles[startingX, startingY - 1]); if (map.coordinatesOnMap(startingX + 1, startingY)) tilesToCheck.Add(map.tiles[startingX + 1, startingY]); if (map.coordinatesOnMap(startingX - 1, startingY)) tilesToCheck.Add(map.tiles[startingX - 1, startingY]); foreach (Tile tile in tilesToCheck) { //make sure the tile isnt occupied and isnt in the checked list. if (!tile.isOccupied && !(closedList.FindIndex(node => node.nodeTile == tile) >= 0)) { Node newNode = new Node(tile); newNode.FValue = calculateFValue(newNode, destinationNode); newNode.parentNode = startingNode; adjacentNodes.Add(newNode); } } return adjacentNodes; }
//calculate the heuristic value, currently using "Manhattan" method - distance formula, deltaX + deltaY private static int calculateHeuristic(Node currentNode, Node destinationNode) { int horizDist = Math.Abs(destinationNode.nodeTile.xPos - currentNode.nodeTile.xPos); int vertDist = Math.Abs(destinationNode.nodeTile.yPos - currentNode.nodeTile.yPos); return horizDist + vertDist; }
public static List<Tile> shortestPath(Map map, Tile startingTile, Tile destinationTile) { List<Tile> path = new List<Tile>(); List<Node> openNodes = new List<Node>(); List<Node> closedNodes = new List<Node>(); List<Node> adjacentNodes = new List<Node>(); bool reachedDestination = false; //initialize. Set starting tile to be the first open node. Node startingNode = new Node(startingTile); Node destinationNode = new Node(destinationTile); startingNode.FValue = calculateFValue(startingNode, destinationNode); openNodes.Add(startingNode); Node nodeToEval = startingNode; while (openNodes.Count > 0) // while nodeToEval != destinationNode ? { //get adjacent nodes and add them to openNodes adjacentNodes = getAdjacentNodes(nodeToEval.nodeTile, map, closedNodes, destinationNode); openNodes.AddRange(adjacentNodes); openNodes.Remove(nodeToEval); closedNodes.Add(nodeToEval); //remove nodeToEval from open list, add to closed list if (nodeToEval.nodeTile.xPos == destinationNode.nodeTile.xPos && nodeToEval.nodeTile.yPos == destinationNode.nodeTile.yPos) { reachedDestination = true; break; } //Now we find the NEXT node to evaluate //We search for the lowest F value in the whole open list after adding the ajacent nodes above int lowestFValue = openNodes.Min(node => node.FValue); //Now get the node with that lowest value - thats our next node to examine. Node nextNodeToEval = openNodes.Find(node => node.FValue == lowestFValue); nodeToEval = nextNodeToEval; } if (reachedDestination == true) { //done! collect the destination and all parent nodes in a list path.Add(nodeToEval.nodeTile); Node nextParentNode = nodeToEval.parentNode; while (nextParentNode != null) { path.Add(nextParentNode.nodeTile); //find parent node in the closed list Node currentParentNode = closedNodes.Find(node => node.nodeTile.xPos == nextParentNode.nodeTile.xPos && node.nodeTile.yPos == nextParentNode.nodeTile.yPos); nextParentNode = currentParentNode.parentNode; } } // find adjacent available nodes and add nodeToEval to the closed list afterwards. //adjacentNodes = getAdjacentNodes(nodeToEval.nodeTile, map, closedNodes, destinationNode); //openNodes.Remove(nodeToEval); // openNodes.AddRange(adjacentNodes); return path; }
//Returns the selection number F (F = G + H) to find the most likely step in the shortest path. private static int calculateFValue(Node nodeToEval, Node destinationNode) { return Node.baseCost + calculateHeuristic(nodeToEval, destinationNode); }
public static List<Tile> shortestPath(Map map, Tile startingTile, Tile destinationTile) { List<Tile> path = new List<Tile>(); List<Node> openNodes = new List<Node>(); List<Node> closedNodes = new List<Node>(); List<Node> adjacentNodes = new List<Node>(); bool reachedDestination = false; //initialize. Set starting tile to be the first open node. Node startingNode = new Node(startingTile); Node destinationNode = new Node(destinationTile); startingNode.FValue = calculateFValue(startingNode, destinationNode); openNodes.Add(startingNode); Node nodeToEval = startingNode; while (openNodes.Count > 0) // while nodeToEval != destinationNode ? { //get adjacent nodes and add them to openNodes adjacentNodes = getAdjacentNodes(nodeToEval.nodeTile, map, closedNodes, destinationNode); openNodes.AddRange(adjacentNodes); openNodes.Remove(nodeToEval); closedNodes.Add(nodeToEval); //remove nodeToEval from open list, add to closed list if (nodeToEval.nodeTile.xPos == destinationNode.nodeTile.xPos && nodeToEval.nodeTile.yPos == destinationNode.nodeTile.yPos) { reachedDestination = true; break; } //Now we find the NEXT node to evaluate //We search for the lowest F value in the whole open list after adding the ajacent nodes above int lowestFValue = openNodes.Min(node => node.FValue); //Now get the node with that lowest value - thats our next node to examine. Node nextNodeToEval = openNodes.Find(node => node.FValue == lowestFValue); //this method will give us boring L-shaped paths if two adjacent nodes have the same F value. //lets try forcing the game to prioritize the one that indicates a change in direction! if((openNodes.FindAll(node => node.FValue == lowestFValue).Count > 1)) { List<Node> potentialNextNodesToEval = openNodes.FindAll(node => node.FValue == lowestFValue); foreach(Node node in potentialNextNodesToEval) { if (nodeToEval.parentNode != null){ if (nodeToEval.parentNode.nodeTile.xPos == nodeToEval.nodeTile.xPos && nodeToEval.nodeTile.xPos != node.nodeTile.xPos) { //change of direction in x, prioritize this node and exit nextNodeToEval = node; break; } else if (nodeToEval.parentNode.nodeTile.yPos == nodeToEval.nodeTile.yPos && nodeToEval.nodeTile.yPos != node.nodeTile.yPos) { //change of direction in y, prioritize this node and exit nextNodeToEval = node; break; } } } } nodeToEval = nextNodeToEval; } if (reachedDestination == true) { //done! collect the destination and all parent nodes in a list path.Add(nodeToEval.nodeTile); Node nextParentNode = nodeToEval.parentNode; while (nextParentNode != null) { path.Add(nextParentNode.nodeTile); //find parent node in the closed list Node currentParentNode = closedNodes.Find(node => node.nodeTile.xPos == nextParentNode.nodeTile.xPos && node.nodeTile.yPos == nextParentNode.nodeTile.yPos); nextParentNode = currentParentNode.parentNode; } } // find adjacent available nodes and add nodeToEval to the closed list afterwards. //adjacentNodes = getAdjacentNodes(nodeToEval.nodeTile, map, closedNodes, destinationNode); //openNodes.Remove(nodeToEval); // openNodes.AddRange(adjacentNodes); return path; }