// Name: pathfind // Description: Takes several parameters and returns a struct // representing the results of processing that data with // the A* search algorithm. This method attempts to find // a best path to the end node from the start node, // utilizing data available to it from the Monster and // DungeonLevel references // Parameters: Monster monster , a reference to a monster where // pathfinding data specific to that monster is mined // DungeonLevel level , a reference to a DungeonLevel so // that critical information about its layers, in regards to // pathfinding, can be mined // short startXCoord , // short startYCoord , the x and y-coordinate on the // DungeonLayer of the DungeonLevel that the entity is // beginning to pathfind from // short endXCoord , // short endYCoord , the x and y-coordinate on the // DungeonLayer of the DungeonLevel that the entity is // attempting to find a path to // short searchDistance , how far the pathfinder will search // before breaking with either a noPath or a "kinda" path. // The default is 0, or search the entire searchspace // bool guessPath , if true, the pathfinder will try to // return a path to a location near the goal, or if the // pathfinder exits early because of a searchDistance limit, // will return with a path "going toward" the goal. The // default is off (false) // byte algToUse , -1 is Djikstra's algorithm. This is // just a bad choice. 1 is greedy best-first, which is // optimal in the case of known very short paths, as it is // fastest and its inaccuracy is ruled out in short paths. // 0 is A*, which is the fastest longer-distance algorithm // to use, and is also the default // Returns: An instance of a PathfindData struct that carries both the // length of the calculated path, and the direction in which // the first step of the path begins public static PathfindData pathfind(Monster monster, DungeonLevel level, short startXCoord, short startYCoord, short endXCoord, short endYCoord, short searchDistance = 0, bool guesspath = false, byte algorithmToUse = 0) { // Determine if the map we're searching on is larger in some way // than the searchspace previously defined if (Pathfinder.searchSpace == null || Pathfinder.searchSpace.GetLength(0) < level.getDungeonHeight() || Pathfinder.searchSpace.GetLength(1) < level.getDungeonWidth()) { Pathfinder.searchSpace = new short[level.getDungeonHeight(), level.getDungeonWidth(), NUM_OF_ELEMENTS]; } else { clearSearchSpace(); } // Initialize the static end coordinates currentEndXCoord = endXCoord; currentEndYCoord = endYCoord; // Set the start and end nodes in the searchSpace searchSpace[startYCoord, startXCoord, NODE_TYPE_INDEX] = START_NODE; searchSpace[endYCoord, endXCoord, NODE_TYPE_INDEX] = END_NODE; Pathfinder.level = level; Pathfinder.monster = monster; // Initialize the pathfinding heap array NodeHeap.initializePathfindList(level); // Create the first tile, to represent the start tile. The F-Score // is a G-Score of 0 (start node), and the full heuristic PathfindTile currentTile = new PathfindTile(startYCoord, startXCoord, /*0 +*/ calcHeuristic(startYCoord, startXCoord)); #if OPTIM Console.WriteLine(calcHeuristic((short)10,(short)10)); #endif // Do while we are not currently exploring the end node and there is // more in the list do { // Expand the node to explore its neighbors expandNode(currentTile); // Add the tile to the closed list searchSpace[currentTile.yCoord, currentTile.xCoord, LIST_INDEX] = CLOSED_LIST; // Pull the root tile, which should be the most optimal tile // to explore next currentTile = NodeHeap.pullRoot(); #if DEBUG Console.WriteLine(currentTile.fScore); //NodeHeap.printNodeHeap(); //printSearchspace(); #endif } while (searchSpace[currentTile.yCoord, currentTile.xCoord, NODE_TYPE_INDEX] != END_NODE && !NodeHeap.isHeapEmpty()); // If we found the end node, then calculate a path back if (searchSpace[currentTile.yCoord, currentTile.xCoord, NODE_TYPE_INDEX] == END_NODE) { #if DEBUG path = new char[ level.getDungeonHeight() , level.getDungeonWidth()]; for (int i = 0; i < path.GetLength(0); i++) { for (int j = 0; j < path.GetLength(1); j++) { path[i, j] = '.'; } } #endif pathLength = 0; while (searchSpace[currentTile.yCoord, currentTile.xCoord, NODE_TYPE_INDEX] != START_NODE) { lastTile = currentTile; #if DEBUG path[lastTile.yCoord,lastTile.xCoord] = 'O'; if (searchSpace[currentTile.yCoord, currentTile.xCoord, NODE_TYPE_INDEX] == END_NODE) { path[lastTile.yCoord, lastTile.xCoord] = 'E'; } #endif temp = searchSpace[currentTile.yCoord, currentTile.xCoord, PARENT_INDEX_X]; currentTile.yCoord = searchSpace[currentTile.yCoord, currentTile.xCoord, PARENT_INDEX_Y]; currentTile.xCoord = temp; #if DEBUG if (searchSpace[currentTile.yCoord, currentTile.xCoord, NODE_TYPE_INDEX] == START_NODE) { path[currentTile.yCoord, currentTile.xCoord] = 'S'; } #endif pathLength++; } return new PathfindData( Pathfinder.calcDirection(currentTile.xCoord, currentTile.yCoord, lastTile.xCoord, lastTile.yCoord), pathLength); } // Temp code for compilation return new PathfindData((byte)Direction.NORTH, 0); }
// Name: initializePathfindList // Description: Initialize the pathfind list // Parameters: DungeonLevel level , the level size is needed to // initialize the pathfind heap public static void initializePathfindList(DungeonLevel level) { // If it isn't null and is not the right size, resize and initialize if (pathfindList != null && pathfindList.Length != (level.getDungeonWidth() - 1) * (level.getDungeonHeight() - 1)) { pathfindList = new PathfindTile[(level.getDungeonHeight() - 1) * (level.getDungeonWidth() - 1)]; for (short i = 0; i < pathfindList.Length; i++) { pathfindList[i].fScore = Pathfinder.ARB_HIGH; } endTracker = 0; } // If it was null, create and initialize else { pathfindList = new PathfindTile[(level.getDungeonHeight() - 1) * (level.getDungeonWidth() - 1)]; for (short i = 0; i < pathfindList.Length; i++) { pathfindList[i].fScore = Pathfinder.ARB_HIGH; } endTracker = 0; } }