// 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; } }
// 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: Main (unit test) // Description: This is the unit test of the pathfinder. It will be // used to exhaustively test the features of the pathfinder // to ensure that not only does it work correctly with // optimal input data, but handles gracefully bad input // data, and does it all in an extremely efficient, // optimized, sexy-speedy manner static void Main(string[] args) { // Define constants for the size of the window so that window- //dependant things can be easily updated const int WINDOW_WIDTH = 80; const int WINDOW_HEIGHT = 55; #if GUI // Display an initializing message to the console Console.WriteLine("Initilizing game window..."); // Create the new libtcod output window. This needs to be done only // once, but before anything else. The first value is the width in // cells the window is, the second is the height in cells, and the // third is the name of the window TCODConsole.initRoot(WINDOW_WIDTH, WINDOW_HEIGHT, "Hello World!"); // Set the maximum graphical update speed. This is important to // massively reduce CPU use with unnecessary updates TCODSystem.setFps(30); // Set the default background color of the root console with black TCODConsole.root.setBackgroundColor(TCODColor.black); // Clear is a function that overwrites every cell in the window with // a plain, empty cell of the color dictated by setBackgroundColor() TCODConsole.root.clear(); // Flush writes the graphical data that is pending to the screen. // Any graphics updates are not shown to the screen until flush is // called. Try to minimize this call TCODConsole.flush(); TCODKey Key = TCODConsole.checkForKeypress(); #endif // Create dungeon objects eventually to be used for pathfinding test Monster monster = new Monster(); // Set the bit for isPlayer in the bitfield monster.setFlag(Monster.AttributeFlags.isPlayer); DungeonLevel dungeon = new DungeonLevel(TCODConsole.root); dungeon.addMonsterToDungeon(monster); // PATHFINDING TESTS #if TEST Stopwatch st = new Stopwatch(); for (int i = 0; i < 500000; i++) { st.Start(); #endif //Console.WriteLine( pathfind(monster, dungeon, monster.XCoord, monster.YCoord, (short)30, (short)40);//.directionOfPath); #if TEST st.Stop(); } Console.WriteLine(st.ElapsedMilliseconds/500000.0); #endif // PATHFINDING TESTS #if DEBUG for (int i = 0; i < path.GetLength(0); i++) { for (int j = 0; j < path.GetLength(1); j++) { Console.Write(path[i,j]); } } Console.WriteLine("Search Space:"); printSearchspace(); NodeHeap.printNodeHeap(); #endif #if GUI // While the user has not closed the window and while the user has // not pressed escape, do stuff while (!TCODConsole.isWindowClosed()) { dungeon.printToScreen(); TCODConsole.flush(); dungeon.doAction(); } #endif }