// Name: addMonster // Description: Adds the monster reference to the monster layer list // Parameters: Monster monster , the monster to be added to the list public void addMonster(Monster monster) { // All temp code. As a reminder, quite a few things need to be // initialized when creating a monster monster.XCoord = 10; monster.YCoord = 10; monster.Color = TCODColor.white; monster.ASCIICode = (short)('@'); monster.MonsterLayer = this; // End temp code monsterLayer.Add(monster); }
// 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 }
// 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: addMonsterToDungeon // Description: Takes a Monster reference and adds it to the Monster // Layer of this dungeon level. For now the location is // random, but later the randomness is limited to being // outside the scope of the LoS of the player, to maintain // belief. Also, later an overloaded version of this method // will need to exist such that spawn coordinates can be // given // Parameters: Monster monster , a reference to the monster to be added public void addMonsterToDungeon(Monster monster) { this.monsterLayer.addMonster( monster ); }