diff --git a/source/Worlds/BiomeGenerator.cpp b/source/Worlds/BiomeGenerator.cpp index 2a800a43..2435dedc 100644 --- a/source/Worlds/BiomeGenerator.cpp +++ b/source/Worlds/BiomeGenerator.cpp @@ -1,14 +1,18 @@ /** + * This file is part of the Fall 2023, CSE 491 course project. * @file BiomeGenerator.cpp - * @author Paul Schulte, Milan Mihailovic, ChatGPT + * @author Paul Schulte, Milan Mihailovic (some code assisted with ChatGPT) */ -#include +#include "BiomeGenerator.hpp" +#include "Agents/AgentLibary.hpp" + #include -#include #include -#include -#include "BiomeGenerator.hpp" +#include + +using namespace group6; +using namespace cse491; using std::vector; @@ -19,78 +23,96 @@ using std::vector; * @param height The height of the grid * @param seed The seed used for random number generation */ -BiomeGenerator::BiomeGenerator(BiomeType biome, unsigned int width, unsigned int height, unsigned int seed) : biome(biome), width(width), height(height) { - - if (biome == BiomeType::Maze) - { - setTiles(' ', '#'); +BiomeGenerator::BiomeGenerator(BiomeType biome, unsigned int width, unsigned int height, unsigned int seed) : biome(biome), width(width), height(height), seed(seed) { + if (biome == BiomeType::Maze) { + setTiles(floor_id, wall_id); + } else if (biome == BiomeType::Grasslands) { + setTiles(grass_id, dirt_id); } - else if (biome == BiomeType::Grasslands) - { - setTiles('M', '~'); + else if (biome == BiomeType::Ocean) { + setTiles(water_id, sand_id); } perlinNoise = PerlinNoise(seed); - grid = vector>(height, vector(width)); + grid.Resize(width, height); +} + +void BiomeGenerator::setWorld(WorldBase *world) { + worldPtr = world; } /** * Generates the grid with two types of tiles */ void BiomeGenerator::generate() { - - char tile1 = tiles[0]; - char tile2 = tiles[1]; + size_t tile1 = tiles[0]; + size_t tile2 = tiles[1]; for (unsigned int y = 0; y < height; y++) { for (unsigned int x = 0; x < width; x++) { - const double val = perlinNoise.noise2D(x * frequency / width, y * frequency / height); - grid.at(y).at(x) = val < 0 ? tile1 : tile2; + // Give 5x5 clear space in top left corner + // TODO: Replace with putting player in valid room instead of 0,0 + if (x > 4 || y > 4) { + const double val = perlinNoise.noise2D(x * frequency / width, y * frequency / height); + grid.At(x, y) = val < 0 ? tile1 : tile2; + } } } - if (biome == BiomeType::Maze) - { - placeSpecialTiles(tile1, 'X', 0.02); // Placing spike tiles - placeSpecialTiles(tile1, 'O', 0.05); // Placing tar tiles - placeDoorTile('D'); // placing door tile - placeKeyTile('K'); // placing key tile + if (biome == BiomeType::Maze) { + placeSpecialTiles(tile1, spike_id, 0.05); // Placing spike tiles + placeSpecialTiles(tile1, tar_id, 0.08); // Placing tar tiles + placeTileRandom(key_id, floor_id); // placing key tile + + vector path = clearPath(); + applyPathToGrid(path); + placeDoorTile(door_id); // placing door tile + grid.At(keyLocation) = key_id; + + } + + if (biome == BiomeType::Grasslands) { + placeTileRandom(hole_id, grass_id); // placing hole tile } + + if (biome == BiomeType::Ocean) { + oceanHandler(); + placeTileRandom(hole_id, sand_id); + } + } /** - * Generates random coordinate to place Key tile - * @param keyTile Door Tile + * Generates random coordinates to place the given tile + * + * @param tile The tile being placed + * @param spawnTile The type of tile allowed to be replaced */ - void BiomeGenerator::placeKeyTile(const char &keyTile) -{ - bool counter = false; - while( counter == false ) - { - std::random_device rd; - std::mt19937 gen(rd()); - - std::uniform_int_distribution x_distribution(width/2, width-1); - std::uniform_int_distribution y_distribution(height/2, height-1); - int random_x = x_distribution(gen); - int random_y = y_distribution(gen); - - if( grid[random_y][random_x] == ' ' ) - { - grid[random_y][random_x] = keyTile; - counter = true; - } - } +void BiomeGenerator::placeTileRandom(const size_t &tile, const size_t &spawnTile) { + bool counter = false; + while (!counter) { + int random_x = (int)worldPtr->GetRandom(width / 2.0, width - 1); + int random_y = (int)worldPtr->GetRandom(height / 2.0, height - 1); + + if (grid.At(random_x, random_y) == spawnTile) { + grid.At(random_x, random_y) = tile; + + if (tile == key_id) { + keyLocation = GridPosition(random_x, random_y); + } + + counter = true; + } + } } /** - * Generates door tile on grid at [0][0] + * Generates door tile on grid at [1][1] * @param doorTile Door Tile */ -void BiomeGenerator::placeDoorTile(const char &doorTile) -{ - grid[1][1] = doorTile; +void BiomeGenerator::placeDoorTile(const size_t &doorTile) { + grid.At(2, 2) = doorTile; } /** @@ -99,78 +121,82 @@ void BiomeGenerator::placeDoorTile(const char &doorTile) * @param specialTile The special tile to generate * @param percentage Chance of special tile generating on the generic tile */ -void BiomeGenerator::placeSpecialTiles(const char &genericTile, const char &specialTile, double percentage) { +void BiomeGenerator::placeSpecialTiles(const size_t &genericTile, const size_t &specialTile, double percentage) { std::vector> floorPositions; - for (int i = 0; i < height; ++i) { - for (int j = 0; j < width; ++j) { - if (grid[i][j] == genericTile) { - floorPositions.push_back({j, i}); + for (unsigned int x = 0; x < width; ++x) { + for (unsigned int y = 0; y < height; ++y) { + if (grid.At(x, y) == genericTile) { + floorPositions.emplace_back(x, y); } } } - int numSpikes = floorPositions.size() * percentage; - std::random_device rd; - std::mt19937 g(rd()); - std::shuffle(floorPositions.begin(), floorPositions.end(), g); + int numSpikes = (int)round((int)floorPositions.size() * percentage); // Convert some generic floor tiles to special tiles for (int i = 0; i < numSpikes; ++i) { - grid[floorPositions[i].second][floorPositions[i].first] = specialTile; + int pos = (int)round(worldPtr->GetRandom((int)floorPositions.size() - 1)); + grid.At(floorPositions [pos].first, floorPositions[pos].second) = specialTile; + + floorPositions.erase(floorPositions.begin() + pos); } } /** * Clears a randomized path from the top left of the * grid, to any point on the rightmost side of the map - * @return A vector of points necessary for this path + * @return A vector of GridPositions necessary for this path */ -std::vector BiomeGenerator::clearPath() const { - std::vector path; +vector BiomeGenerator::clearPath() const { + vector path; - Point current(0, 0); + GridPosition current(0, 0); // Starting point path.push_back(current); - while (current.x < width - 1) { - int randDirection = rand() % 3; // 0: Right, 1: Up, 2: Down + // Continue until we reach the KeyLocation + while (current != keyLocation) { + std::vector possibleMoves; - // Choose next point based on random direction - Point next = current; - if (randDirection == 0) - { - next.x++; + // Always add right movement if not aligned horizontally + if (current.GetX() < keyLocation.GetX()) { + possibleMoves.push_back(current.ToRight()); } - else if (randDirection == 1) - { - if (next.y > 0) // Ensure within grid bounds - next.y--; + // Add down movement if above the target and within grid bounds + if (current.GetY() < keyLocation.GetY() && current.GetY() < height - 1) { + possibleMoves.push_back(current.Below()); } - else { - if (next.y < height - 1) // Ensure within grid bounds - next.y++; + // Add up movement if below the target and within grid bounds + if (current.GetY() > keyLocation.GetY() && current.GetY() > 0) { + possibleMoves.push_back(current.Above()); } - // If the next point is the same as the current, then we chose an invalid direction - // (like trying to go up at the top edge), so just skip this iteration. - if (next != current) { - path.push_back(next); - current = next; + // Randomly choose one of the possible moves + if (!possibleMoves.empty()) { + GridPosition next = possibleMoves[int(worldPtr->GetRandom(0, 2)) % possibleMoves.size()]; + + // Check if we have made a valid move, if so, update the path and current position + if (next != current) { + path.push_back(next); + current = next; + } } } return path; } + + /** * Clears the walls out of the grid, guaranteeing a path from the * left of the grid, to any point on the rightmost side of the map - * @param path A vector of points necessary for this path + * @param path A vector of GridPositions necessary for this path */ -void BiomeGenerator::applyPathToGrid(const std::vector& path) { - for (const Point& p : path) { - grid[p.y][p.x] = ' '; +void BiomeGenerator::applyPathToGrid(const vector &path) { + for (const GridPosition &p: path) { + grid.At(p) = floor_id; } } @@ -179,14 +205,25 @@ void BiomeGenerator::applyPathToGrid(const std::vector& path) { * @param filename The filename the grid will be saved to */ void BiomeGenerator::saveToFile(const std::string &filename) const { - std::ofstream out(filename); - for (const auto &row : grid) { - for (const auto &cell : row) { - out << cell; - } - out << "\n"; - } - out.close(); + type_options_t types = type_options_t(); + + types.push_back(CellType{"floor", "Floor that you can easily walk over.", ' '}); + types.push_back(CellType{"wall", "Impenetrable wall that you must find a way around.", '#'}); + types.push_back(CellType{"spike", "Dangerous spike that resets the game.", 'X'}); + types.push_back(CellType{"tar", "Slow tile that makes you take two steps to get through it", 'O'}); + types.push_back(CellType{"key", "item that can be picked up to unlock door and escape maze", 'K'}); + types.push_back(CellType{"door", "Door that can be walked through only with possession of key to leave maze", 'D'}); + types.push_back(CellType{"grass", "Grass you can walk on.", 'M'}); + types.push_back(CellType{"dirt", "Dirt you can walk on.", '~'}); + types.push_back(CellType{"tree", "A tree that blocks the way.", 't'}); + types.push_back(CellType{"hole", "A hole that you can fall into the maze from.", '8'}); + + types.push_back(CellType{"water","Water that you may be able to swim on.", 'W'}); + types.push_back(CellType{"sand", "Sand you can walk on.", '-'}); + + + grid.Write(filename, types); + } /** @@ -194,10 +231,59 @@ void BiomeGenerator::saveToFile(const std::string &filename) const { * @param firstTile Tile #1 for the biome * @param secondTile Tile #2 for the biome */ -void BiomeGenerator::setTiles(const char& firstTile, const char& secondTile) -{ +void BiomeGenerator::setTiles(const size_t &firstTile, const size_t &secondTile) { tiles.clear(); tiles.push_back(firstTile); tiles.push_back(secondTile); } +/** + * Places trees on the grid + */ +void BiomeGenerator::placeTrees() { + // Iterates through each tile in the grid with a margin of 1 tile to prevent out of bounds access + for (unsigned int y = 1; y < height - 1; ++y) { + for (unsigned int x = 1; x < width - 1; ++x) { + // Only place trees on grass tiles and ensure we have space for a 3x3 tree + if (grid.At(x, y) == grass_id && + grid.At(x-1, y) == grass_id && grid.At(x+1, y) == grass_id && + grid.At(x, y-1) == grass_id && grid.At(x, y+1) == grass_id && + grid.At(x-1, y-1) == grass_id && grid.At(x+1, y-1) == grass_id && + grid.At(x-1, y+1) == grass_id && grid.At(x+1, y+1) == grass_id) { + // Use a random chance to place a tree + if (worldPtr->GetRandom( 100) < 10) { // 10% chance to place a tree + // Place a 3x3 block of tree tile characters for the tree + for (int i = -1; i <= 1; ++i) { + for (int j = -1; j <= 1; ++j) { + grid.At(x + i, y + j) = tree_id; + } + } + } + } + } + } +} + +/** + * Handles logic for Ocean biome + */ +void BiomeGenerator::oceanHandler(){ + for (unsigned int y = 1; y < height - 1; ++y) { + for (unsigned int x = 1; x < width - 1; ++x) { + if (grid.At(x, y) == water_id) { + if (worldPtr->GetRandom(100) < 15) { + for (int i = -1; i <= 1; ++i) { + for (int j = -1; j <= 1; ++j) { + if (x + i > 0 && x + i < width - 1 && y + j > 0 && y + j < height - 1) { + grid.At(x + i, y + j) = sand_id; + } + } + } + } + } + } + } +} + + + diff --git a/source/Worlds/BiomeGenerator.hpp b/source/Worlds/BiomeGenerator.hpp index c83e087d..3c00f448 100644 --- a/source/Worlds/BiomeGenerator.hpp +++ b/source/Worlds/BiomeGenerator.hpp @@ -6,83 +6,91 @@ */ #pragma once + #include #include #include - #include "PerlinNoise.hpp" +#include "../core/WorldBase.hpp" #include "../core/WorldGrid.hpp" +#include "../core/Data.hpp" -using siv::PerlinNoise; -using cse491::WorldGrid; - -enum class BiomeType { - Maze, - Grasslands -}; +namespace group6 { + using siv::PerlinNoise; + using cse491::WorldGrid, cse491::WorldBase, cse491::GridPosition; -/** - * Holds coordinate position on the grid - * @param biome The biome of the grid - */ -struct Point { - int x; ///< The x coordinate on the grid) - int y; ///< The y coordinate on the grid) + enum class BiomeType { + Maze, + Grasslands, + Ocean + }; /** - * Creates an (x,y) coordinate point - * @param _x The x-coordinate - * @param _y The y-coordinate + * Generates a new grid based on a specified biome */ - Point(int _x, int _y): x(_x), y(_y) {} + class BiomeGenerator { + private: + const double frequency = 8.0; ///< [0.1, 64.0] + const int octaves = 8; ///< [1, 16] - /** - * Comparison operator between 2 equal points - * @param other The other point that this point is being compared to - * @return True if the 2 points are equal, false otherwise - */ - bool operator==(const Point& other) const { return x == other.x && y == other.y; } + PerlinNoise perlinNoise; ///< The Perlin Noise procedural generation algorithm - /** - * Comparison operator between 2 different points - * @param other The other point that this point is being compared to - * @return True if the 2 points are different, false otherwise - */ - bool operator!=(const Point& other) const { return !(*this == other); } -}; + BiomeType biome; ///< Biome for the gird -/** - * Generates a new grid based on a specified biome - */ -class BiomeGenerator { -private: - const double frequency = 8.0; ///< [0.1, 64.0] - // const int octaves = 8; ///< [1, 16] + unsigned int width; ///< Width of the grid + unsigned int height; ///< Height of the grid + WorldGrid grid; ///< Grid of all tiles + + WorldBase *worldPtr = nullptr; ///< Pointer to world + + unsigned int seed; ///< Seed used for RNG + + GridPosition keyLocation; + + std::vector tiles; ///< Vector to store tiles + + size_t floor_id = 0; + size_t wall_id = 1; + + size_t spike_id = 2; + size_t tar_id = 3; + size_t key_id = 4; + size_t door_id = 5; + + size_t grass_id = 6; + size_t dirt_id = 7; + + size_t tree_id = 8; + + size_t sand_id = 11; + size_t water_id = 10; + + size_t hole_id = 9; + + public: + BiomeGenerator(BiomeType biome, unsigned int width, unsigned int height, unsigned int seed); + ~BiomeGenerator() = default; - PerlinNoise perlinNoise; ///< The Perlin Noise procedural generation algorithm + void setWorld(WorldBase *world); - BiomeType biome; ///< Biome for the gird - std::vector tiles; ///< Vector to store tiles + void generate(); + void saveToFile(const std::string &filename) const; + void placeSpecialTiles(const size_t &genericTile, const size_t &specialTile, double percentage); - unsigned int width; ///< Width of the grid - unsigned int height; ///< Height of the grid - std::vector> grid; ///< Grid of all tiles + [[nodiscard]] unsigned int getSeed() const { return seed; } -public: - BiomeGenerator(BiomeType biome, unsigned int width, unsigned int height, unsigned int seed); - ~BiomeGenerator() = default; + void setTiles(const size_t &firstTile, const size_t &secondTile); + [[nodiscard]] BiomeType getBiome() const { return biome; } - void generate(); - void saveToFile(const std::string &filename) const; - void placeSpecialTiles(const char& genericTile, const char& specialTile, double percentage); + void placeDoorTile(const size_t &doorTile); + void placeTileRandom(const size_t& tile, const size_t& spawnTile); - void setTiles(const char &firstTile, const char &secondTile); - [[nodiscard]] BiomeType getBiome() const { return biome; } + [[nodiscard]] std::vector clearPath() const; + void applyPathToGrid(const std::vector& path); - void placeDoorTile(const char &doorTile); - void placeKeyTile(const char &keyTile); + void placeTrees(); - [[nodiscard]] std::vector clearPath() const; - void applyPathToGrid(const std::vector& path); -}; + void oceanHandler(); + }; +} diff --git a/source/Worlds/GenerativeWorld.hpp b/source/Worlds/GenerativeWorld.hpp index 271bbec8..8ddfe89f 100644 --- a/source/Worlds/GenerativeWorld.hpp +++ b/source/Worlds/GenerativeWorld.hpp @@ -8,133 +8,440 @@ #include +#include "BiomeGenerator.hpp" #include "../core/WorldBase.hpp" +#include "../Agents/AStarAgent.hpp" -namespace cse491 { - -class GenerativeWorld : public WorldBase { - protected: - enum ActionType { REMAIN_STILL = 0, MOVE_UP, MOVE_DOWN, MOVE_LEFT, MOVE_RIGHT }; - - size_t floor_id; ///< Easy access to floor CellType ID. - size_t wall_id; ///< Easy access to wall CellType ID. - - size_t spike_id; ///< Easy access to spike CellType ID. - size_t tar_id; ///< Easy access to tar CellTypeID - size_t key_id; ///< Easy access to key CellTypeID - size_t door_id; ///< Easy access to door CellTypeID - - size_t grass_id; - size_t dirt_id; - - /// Provide the agent with movement actions. - void ConfigAgent(AgentBase & agent) override { - agent.AddAction("up", MOVE_UP); - agent.AddAction("down", MOVE_DOWN); - agent.AddAction("left", MOVE_LEFT); - agent.AddAction("right", MOVE_RIGHT); - agent.SetProperty("key_property", 0.0); //if key is set to 0, agent does not have possession of key. If it is 1, agent has possession of key and can exit through door - agent.SetProperty("tar_property", 5.0); //if it is set to 5.0, agent is free to move. if it is set to 6.0, agent is stuck - } - - public: - explicit GenerativeWorld(unsigned int seed = time(nullptr)) : WorldBase(seed) { - floor_id = AddCellType("floor", "Floor that you can easily walk over.", ' '); - wall_id = AddCellType("wall", "Impenetrable wall that you must find a way around.", '#'); - - spike_id = AddCellType("spike", "Dangerous spike that resets the game.", 'X'); - - tar_id = AddCellType("tar", "Slow tile that makes you take two steps to get through it", 'O'); - key_id = AddCellType("key", "item that can be picked up to unlock door and escape maze", 'K'); - door_id = AddCellType("door", "Door that can be walked through only with possession of key to leave maze", 'D'); - - grass_id = AddCellType("grass", "Grass you can walk on.", 'M'); - dirt_id = AddCellType("dirt", "Dirt you can walk on.", '~'); - - main_grid.Read("../assets/grids/generated_maze.grid", type_options); - } - - ~GenerativeWorld() = default; - - /// Allow the agents to move around the maze. - int DoAction(AgentBase & agent, size_t action_id) override { - // Determine where the agent is trying to move. - GridPosition currentPosition = agent.GetPosition(); - GridPosition new_position; - switch (action_id) { - case REMAIN_STILL: new_position = agent.GetPosition(); break; - case MOVE_UP: new_position = agent.GetPosition().Above(); break; - case MOVE_DOWN: new_position = agent.GetPosition().Below(); break; - case MOVE_LEFT: new_position = agent.GetPosition().ToLeft(); break; - case MOVE_RIGHT: new_position = agent.GetPosition().ToRight(); break; - } - - // Don't let the agent move off the world or into a wall. - if (!main_grid.IsValid(new_position)) { return false; } - if (main_grid.At(new_position) == wall_id) { return false; } - - // Check if the agent is trying to move onto a spike. - if (main_grid.At(new_position) == spike_id) { - std::cout << "Game over, try again!" << std::endl; - exit(0); // Halting the program - - return false; - } - - // agent is moving onto a key tile and picking it up - if( main_grid.At(new_position) == key_id ) - { - agent.SetProperty("key_property", 1.0); - main_grid.At(new_position) = floor_id; - } - - // player is exiting through door with key and ending game - if( main_grid.At(new_position) == door_id && agent.GetProperty("key_property") == 1.0 ) - { - std::cout << "You successfully exited maze!" << std::endl; - exit(0); - } - - // player is on tar tile and trying to move to a tar tile - if( main_grid.At(new_position) == tar_id && main_grid.At(currentPosition) == tar_id) - { - if( agent.GetProperty("tar_property") == 6.0 ) //player is stuck on tar - { - agent.SetProperty("tar_property", 5.0); - new_position = currentPosition; - return false; +namespace group6 { + using namespace cse491; + using std::vector, std::string; + + class GenerativeWorld : public WorldBase { + protected: + enum ActionType { + REMAIN_STILL = 0, MOVE_UP, MOVE_DOWN, MOVE_LEFT, MOVE_RIGHT + }; + + size_t floor_id; ///< Easy access to floor CellType ID. + size_t wall_id; ///< Easy access to wall CellType ID. + + size_t spike_id; ///< Easy access to spike CellType ID. + size_t tar_id; ///< Easy access to tar CellTypeID + size_t key_id; ///< Easy access to key CellTypeID + size_t door_id; ///< Easy access to door CellTypeID + size_t teleporter_id; ///< Easy access to teleporter CellTypeId + size_t armory_id; ///< Easy access to armory CellTypeID + + size_t tree_id; ///< Easy access to tree CellTypeID + size_t grass_id; ///< Easy access to grass CellTypeID + size_t dirt_id; ///< Easy access to dirt CellTypeID + size_t hole_id; ///< Easy access to hole CellTypeID + + size_t water_id; ///< Easy access to water CellTypeID + size_t sand_id; ///< Easy access to sand CellTypeID + + unsigned int world_width; + unsigned int world_height; + + /** + * Provide the agent with movement actions. + * @param agent The agent being configured + */ + void ConfigAgent(AgentBase &agent) override { + agent.AddAction("up", MOVE_UP); + agent.AddAction("down", MOVE_DOWN); + agent.AddAction("left", MOVE_LEFT); + agent.AddAction("right", MOVE_RIGHT); + agent.SetProperty("key_property",0.0); ///< If key is set to 0, agent does not have possession of key. If it is 1, agent has possession of key and can exit through door + agent.SetProperty("tar_property",5.0); ///< If it is set to 5.0, agent is free to move. if it is set to 6.0, agent is stuck } - else - { - agent.SetProperty("tar_property", 6.0); + + /** + * Ends game if agent is the player + * @param agent The agent being damaged + */ + void DamageAgent(AgentBase &agent) { + if (agent.IsInterface()) { + EndGame(false); + } + } + + /** + * Creates a new grid with the given parameters + * + * @param biome The BiomeType being used + * @param width The width of the grid + * @param height The height of the grid + * @param seed The seed used for random generation + * @param file The file to save the grid to. Full path is "../assets/grids/generated_[file].grid" + */ + void CreateGrid(BiomeType biome, unsigned int width, unsigned int height, unsigned int seed, const string &file) { + BiomeGenerator biomeGenerator(biome, width, height, seed); + biomeGenerator.setWorld(this); + biomeGenerator.generate(); + + string filePath = "../assets/grids/generated_" + file + ".grid"; + + biomeGenerator.saveToFile(filePath); + + main_grid.Read(filePath, type_options); + } + + public: + explicit GenerativeWorld(BiomeType biome, unsigned int width, unsigned int height, unsigned int seed) + : WorldBase(seed) { + floor_id = AddCellType("floor", "Floor that you can easily walk over.", ' '); + wall_id = AddCellType("wall", "Impenetrable wall that you must find a way around.", '#'); + + spike_id = AddCellType("spike", "Dangerous spike that resets the game.", 'X'); + + tar_id = AddCellType("tar", "Slow tile that makes you take two steps to get through it", 'O'); + key_id = AddCellType("key", "item that can be picked up to unlock door and escape maze", 'K'); + door_id = AddCellType("door", "Door that can be walked through only with possession of key to leave maze",'D'); + teleporter_id = AddCellType("teleporter", "Teleports agent to other teleporter", 'T'); + + armory_id = AddCellType("armory", "Armory tile that repairs damaged inventory items", 'A'); + tree_id = AddCellType("tree", "A tree that blocks the way.", 't'); + grass_id = AddCellType("grass", "Grass you can walk on.", 'M'); + dirt_id = AddCellType("dirt", "Dirt you can walk on.", '~'); + hole_id = AddCellType("hole", "A hole that you can fall into the maze from.", '8'); + + water_id = AddCellType("water", "Water that you may be able to swim on.", 'W'); + sand_id = AddCellType("sand", "Sand you can walk on.", '-'); + + world_width = width; + world_height = height; + + CreateGrid(biome, width, height, seed, "maze"); + } + + ~GenerativeWorld() override = default; + + /** + * Adds teleporters to map + */ + void AddTeleporters() { + main_grid.At(53, 6) = teleporter_id; + main_grid.At(18, 18) = teleporter_id; + } + + /** + * Ends the game + * @param win True if the game is ending in a win. False if it is a loss + */ + [[noreturn]] void EndGame(bool win) { + run_over = true; + + if (win) { + std::cout << "You successfully exited maze!" << std::endl; + } else { + std::cout << "Game over, try again!" << std::endl; + } + std::exit(0); + } + + /** + * Adds armory tiles to grid + */ + void AddArmory() { + bool counter = false; + while (!counter) { + //generate random location in bottom left quarter of map + int random_y = (int)GetRandom((double)main_grid.GetHeight() / 2, (double)main_grid.GetHeight() - 1); + int random_x = (int)GetRandom(0, (double)main_grid.GetWidth() / 2); + + if (main_grid.At(random_x, random_y) == floor_id) { + main_grid.At(random_x, random_y) = armory_id; + counter = true; + } + } + + counter = false; + while (!counter) { + //generate random location in top right quarter of map + int random_y = (int)GetRandom(0, (double)main_grid.GetHeight() / 2); + int random_x = (int)GetRandom((double)main_grid.GetWidth() / 2, (double)main_grid.GetWidth() - 1); + + if (main_grid.At(random_x, random_y) == floor_id) { + main_grid.At(random_x, random_y) = armory_id; + counter = true; + } + } + } + + [[nodiscard]] static vector FindTiles(WorldGrid grid, size_t tile_id) { + vector result; + + for (size_t x = 0; x < grid.GetWidth(); ++x) { + for (size_t y = 0; y < grid.GetHeight(); ++y) { + if (grid.At(x, y) == tile_id) { + result.emplace_back(x, y); + } + } + } + + return result; + } + + /** + * Handles logic for agent movement + * @param agent The agent attempting to do an action + * @param action_id The action being performed + * @return 1 if the action was successful. 0 if it was not + */ + int DoAction(AgentBase &agent, size_t action_id) override { + AgentCollisionHelper(agent); + + // Skip turn if stuck on tar + if (agent.GetProperty("tar_property") == 6.0) { + agent.SetProperty("tar_property", 5.0); + return true; + } + + // Determine where the agent is trying to move. + GridPosition new_position; + switch (action_id) { + case REMAIN_STILL: + new_position = agent.GetPosition(); + break; + case MOVE_UP: + new_position = agent.GetPosition().Above(); + break; + case MOVE_DOWN: + new_position = agent.GetPosition().Below(); + break; + case MOVE_LEFT: + new_position = agent.GetPosition().ToLeft(); + break; + case MOVE_RIGHT: + new_position = agent.GetPosition().ToRight(); + break; + } + + // Don't let the agent move off the world or into a wall. + if (!main_grid.IsValid(new_position)) { return false; } + if (main_grid.At(new_position) == wall_id) { return false; } + + //check to see if player is going onto armory tile + if (main_grid.At(new_position) == armory_id) { + ArmoryTileHelper(agent); + } + // check to see if player is moving onto spike tile + if (main_grid.At(new_position) == spike_id) { + SpikeTileHelper(agent); + } + // check to see if player is moving onto a tar tile + else if (main_grid.At(new_position) == tar_id) { + TarTileHelper(agent); + } + // check to see if player is moving onto teleporter + else if (main_grid.At(new_position) == teleporter_id) { + TeleporterHelper(new_position); + } + // check to see if player is moving onto key tile + else if (main_grid.At(new_position) == key_id) { + KeyTileHelper(agent, new_position); + } + // check to see if the player is moving onto door tile + else if (main_grid.At(new_position) == door_id) { + DoorTileHelper(agent); + } + // check to see if player is moving onto a hole tile + else if (main_grid.At(new_position) == hole_id) { + HoleTileHelper(agent, new_position); + } + + //recalculate AStarAgent's path when player moves + AStarAgentHelper(agent); + + //check to see if agent is walking on an item + ItemHelper(agent, new_position); + + // Set the agent to its new position. agent.SetPosition(new_position); + return true; } - } - // determining if player is moving onto a tar tile and setting tar property to 6.0 if so - if( main_grid.At(new_position) == tar_id ) - { - agent.SetProperty("tar_property", 6.0); - } - - //Determining if agent is stuck on tar or not - if( main_grid.At(currentPosition) == tar_id ) - { - if( agent.GetProperty("tar_property") == 6.0 ) //player is stuck on tar - { - agent.SetProperty("tar_property", 5.0); - new_position = currentPosition; - return false; + + /** + * Helper function for armory functionality + * @param agent agent performing action + */ + void ArmoryTileHelper(AgentBase &agent) { + for (const auto &pair: item_map) { + //if agent has the item in its inventory, heal it back to full health + if (agent.HasItem(pair.first)) { + pair.second->SetProperty("Health", 4.0); + } + } + } + + /** + * Helper function for spike tile functionality + * @param agent agent performing action + */ + void SpikeTileHelper(AgentBase &agent) { + bool spike_immune = false; + + //check to see if player has shield on + for (const auto &pair: item_map) { + if (agent.HasItem(pair.first) && pair.second->GetName() == "Shield") { + //agent's shield has enough health and will protect player from spike tile + if (pair.second->GetProperty("Health") > 0) { + pair.second->SetProperty("Health", pair.second->GetProperty("Health") - 1); + spike_immune = true; + + break; + } + } + } + + // Damage agent if not immune to spike + if (!spike_immune) { + DamageAgent(agent); + } + } + + /** + * Helper function for tar tile functionality + * @param agent agent performing action + */ + void TarTileHelper(AgentBase &agent) { + bool tar_immune = false; + + //check to see if player has boots on + for (const auto &pair: item_map) { + if (agent.HasItem(pair.first) && pair.second->GetName() == "Boots") { + //agent's boots have enough health and will protect player from tar + if (pair.second->GetProperty("Health") > 0) { + pair.second->SetProperty("Health", pair.second->GetProperty("Health") - 1); + tar_immune = true; + + break; + } + } + } + + // Slow agent if not immune to tar + if (!tar_immune) { + agent.SetProperty("tar_property", 6.0); + } + } + + /** + * Helper function for armory functionality + * @param agent agent performing action + * @param new_position new position agent is moving to + */ + void TeleporterHelper(GridPosition &new_position) { + vector teleporters = FindTiles(main_grid, teleporter_id); + + for (GridPosition teleporter: teleporters) { + if (new_position != teleporter) { + new_position = teleporter; + + break; + } + } } - } + /** + * Helper function for key tile functionality + * @param agent agent performing action + * @param new_position new position agent is moving to + */ + void KeyTileHelper(AgentBase &agent, GridPosition &new_position) { + // Only player can pick up keys + if (agent.IsInterface()) { + agent.SetProperty("key_property", 1.0); + main_grid.At(new_position) = floor_id; + } + } - // Set the agent to its new postion. - agent.SetPosition(new_position); + /** + * Helper function for door tile functionality + * @param agent agent performing action + */ + void DoorTileHelper(AgentBase &agent) { + // Only player with key can win game + if (agent.IsInterface() && agent.GetProperty("key_property") == 1.0) { + EndGame(true); + } + } - return true; - } + /** + * Helper function for hole tile functionality + * @param agent agent performing action + * @param new_position new position agent is moving to + */ + void HoleTileHelper(AgentBase &agent, GridPosition &new_position) { + if (agent.IsInterface()) { + CreateGrid(BiomeType::Maze, world_width, world_height, ++seed, "maze2"); + new_position.Set(0, 0); + } + } -}; + /** + * Helper function for item pickup functionality + * @param agent agent performing action + * @param new_position new position agent is moving to + */ + void ItemHelper(AgentBase &agent, GridPosition &new_position) { + for (const auto &pair: item_map) { + //check to see if items position is same as the position the player is moving to + if (pair.second->GetPosition() == new_position) { + //Add item to inventory + agent.AddItem(pair.first); + break; + } + } + } + + /** + * Collision testing functionality + * @param agent agent performing action + */ + void AgentCollisionHelper(AgentBase &agent) { + //if player is on same position as agent, game ends + for (const auto &temp_agent: agent_map) { + //check to see if the two agents positions being compared are equal, + //as well as if one agent is a player and one agent is an enemy + if (temp_agent.second->GetPosition() == agent.GetPosition() && + ((agent.GetName() == "Player" && temp_agent.second->GetName() != "Player") || + (agent.GetName() != "Player" && temp_agent.second->GetName() == "Player"))) + EndGame(false); + } + } + + + /** + * Boolean function for traversable tiles in maze + * @param agent agent performing action + * @param pos position of tile + */ + bool IsTraversable(const AgentBase & /*agent*/, cse491::GridPosition pos) const override { + size_t tileType = main_grid.At(pos); + return !(tileType == wall_id || tileType == spike_id || tileType == tar_id || tileType == armory_id || tileType == teleporter_id); + } + + void AStarAgentHelper(AgentBase &agent) + { + if( agent.GetName() == "AStar1" ) + { + for( const auto &temp_agent : agent_map ) + { + //updating AStarAgent's path to the players current location + if( temp_agent.second->GetName() == "Player" ) + { + auto &astar_agent = dynamic_cast(agent); + astar_agent.SetGoalPosition(temp_agent.second->GetPosition()); + astar_agent.RecalculatePath(); + astar_agent.SetActionResult(1); + break; + } + } + } + } + }; -} // End of namespace cse491 +} // End of namespace group6 diff --git a/source/group_3_main.cpp b/source/group_3_main.cpp index af5c62d7..1d7ea40e 100644 --- a/source/group_3_main.cpp +++ b/source/group_3_main.cpp @@ -45,7 +45,7 @@ int main() { world_1.Run(); static const unsigned int SEED = 973; - BiomeGenerator biomeGenerator(BiomeType::Maze, 110, 25, SEED); + group6::BiomeGenerator biomeGenerator(group6::BiomeType::Maze, 110, 25, SEED); biomeGenerator.generate(); srand(time(NULL)); @@ -53,12 +53,12 @@ int main() { biomeGenerator.applyPathToGrid(path); biomeGenerator.saveToFile("../assets/grids/generated_maze.grid"); - cse491::GenerativeWorld world_2(SEED); - world_2.AddAgent("Pacer 1").SetPosition(3, 1); - world_2.AddAgent("Pacer 2").SetPosition(6, 1); - world_2.AddAgent("Interface2").SetProperty("symbol", '@'); - - world_2.Run(); +// cse491::GenerativeWorld world_2(SEED); +// world_2.AddAgent("Pacer 1").SetPosition(3, 1); +// world_2.AddAgent("Pacer 2").SetPosition(6, 1); +// world_2.AddAgent("Interface2").SetProperty("symbol", '@'); +// +// world_2.Run(); cse491_team8::ManualWorld world_3; world_3.AddItem("Axe", "Uses", 5, "symbol", 'P').SetPosition(37, 3); diff --git a/source/group_5_client_main.cpp b/source/group_5_client_main.cpp index 701eacdb..ded9e2eb 100644 --- a/source/group_5_client_main.cpp +++ b/source/group_5_client_main.cpp @@ -105,7 +105,11 @@ bool runGenerativeWorldDemo(std::istream &is, const std::string &ipString, unsig manager.setUpdatePort(initPort); manager.setupGameUpdateSocket(socket); std::string interfaceName = "Interface2"; - cse491::GenerativeWorld world; + auto biome = group6::BiomeType::Maze; // change world biome type here + int width = 110; // change world width here + int height = 25; // change world height here + static const unsigned int SEED = 973; // change world seed here + group6::GenerativeWorld world(biome, width, height, SEED); world.Deserialize(is, &manager); clientKillPort = port; clientKillIP = ipString; diff --git a/source/group_5_server_main.cpp b/source/group_5_server_main.cpp index 2491b101..7b7212f3 100644 --- a/source/group_5_server_main.cpp +++ b/source/group_5_server_main.cpp @@ -165,14 +165,14 @@ int runGenerativeWorldDemo() { // load world static const unsigned int SEED = 973; - BiomeGenerator biomeGenerator(BiomeType::Maze, 110, 25, SEED); + group6::BiomeGenerator biomeGenerator(group6::BiomeType::Maze, 110, 25, SEED); biomeGenerator.generate(); srand(time(NULL)); auto path = biomeGenerator.clearPath(); biomeGenerator.applyPathToGrid(path); - cse491::GenerativeWorld world(SEED); + group6::GenerativeWorld world(group6::BiomeType::Maze, 110, 25, SEED); int startX = 0, startY = 0; // Add agents @@ -238,4 +238,4 @@ int main(int argc, char *argv[]) { // World type not in list std::cout << "Invalid world type, choose a number between 0 and 3" << std::endl; return 1; -} \ No newline at end of file +} diff --git a/source/group_6_main.cmake b/source/group_6_main.cmake index c83d1564..c7883136 100644 --- a/source/group_6_main.cmake +++ b/source/group_6_main.cmake @@ -2,4 +2,16 @@ # Example: The CMake file for my_main.cpp would be my_main.cmake in the same directory # Here, add one .cpp per line. Only the strings should +add_source_to_target(${EXE_NAME} "source/Interfaces/MainInterface.cpp") +add_source_to_target(${EXE_NAME} "source/Interfaces/TextureHolder.cpp") +add_source_to_target(${EXE_NAME} "source/Interfaces/Component.cpp") +add_source_to_target(${EXE_NAME} "source/Interfaces/Component.hpp") +add_source_to_target(${EXE_NAME} "source/Interfaces/Container.cpp") +add_source_to_target(${EXE_NAME} "source/Interfaces/Container.hpp") +add_source_to_target(${EXE_NAME} "source/Interfaces/Button.cpp") +add_source_to_target(${EXE_NAME} "source/Interfaces/Menu.cpp") add_source_to_target(${EXE_NAME} "source/Worlds/BiomeGenerator.cpp") +add_source_to_target(${EXE_NAME} "source/Interfaces/TextBox.cpp") +add_source_to_target(${EXE_NAME} "source/Interfaces/MessageBoard.cpp") +add_source_to_target(${EXE_NAME} "source/Interfaces/Inventory.cpp") +add_source_to_target(${EXE_NAME} "source/Interfaces/Inventory.hpp") \ No newline at end of file diff --git a/source/group_6_main.cpp b/source/group_6_main.cpp index e9f86ca8..c1e7a0d2 100644 --- a/source/group_6_main.cpp +++ b/source/group_6_main.cpp @@ -7,23 +7,116 @@ // Include the modules that we will be using. #include "Agents/PacingAgent.hpp" #include "Interfaces/TrashInterface.hpp" +#include "Interfaces/MainInterface.hpp" #include "Worlds/GenerativeWorld.hpp" #include "Worlds/BiomeGenerator.hpp" +#include "Agents/AStarAgent.hpp" +#include +#include +#include -int main() { - static const unsigned int SEED = 973; - BiomeGenerator biomeGenerator(BiomeType::Maze, 110, 25, SEED); - biomeGenerator.generate(); +using namespace group6; + +void timer(GenerativeWorld& world, int duration) { + std::this_thread::sleep_for(std::chrono::seconds(duration)); + world.EndGame(false); +} + +void runWorld(BiomeType biome, int width, int height, const unsigned int SEED, int timerDuration = -1) { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution distribution(0, 1000000); + int random_number = distribution(gen); + GenerativeWorld world(biome, width, height, random_number); + world.AddTeleporters(); + + if (biome == BiomeType::Grasslands) { + // Using TrashInterface since MainInterface does not support Grasslands biome + world.AddAgent("Interface2").SetProperty("symbol", '@').SetName("Player"); + } + + else if (biome == BiomeType::Maze) { + bool counter = false; + while (!counter) { + int random_x = world.GetRandom(18, 53); + int random_y = world.GetRandom(6, 18); + + if (world.GetGrid().At(random_x, random_y) == 1) { + world.AddItem("Boots", "symbol", 'B').SetPosition(random_x, random_y).SetName("Boots").SetProperty("Health", 3.0); + counter = true; + } + } + counter = false; + while (!counter) { + int random_x = world.GetRandom(18, 53); + int random_y = world.GetRandom(6, 18); + + if (world.GetGrid().At(random_x, random_y) == 1) { + world.AddItem("Shield", "symbol", 'S').SetPosition(random_x, random_y).SetName("Shield").SetProperty("Health", 3.0); + counter = true; + } + } - srand(time(NULL)); - auto path = biomeGenerator.clearPath(); - biomeGenerator.applyPathToGrid(path); - biomeGenerator.saveToFile("../assets/grids/generated_maze.grid"); + world.AddAgent("Pacer 1").SetPosition(5, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 2").SetPosition(8, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 3").SetPosition(10, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 4").SetPosition(12, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 5").SetPosition(15, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 6").SetPosition(18, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 7").SetPosition(20, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 8").SetPosition(22, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 9").SetPosition(25, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 10").SetPosition(28, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 11").SetPosition(30, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 12").SetPosition(32, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 13").SetPosition(35, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 14").SetPosition(38, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 15").SetPosition(40, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 16").SetPosition(42, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 17").SetPosition(45, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 18").SetPosition(48, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 19").SetPosition(50, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 20").SetPosition(52, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 21").SetPosition(55, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 22").SetPosition(58, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 23").SetPosition(60, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 24").SetPosition(62, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 25").SetPosition(65, int(world.GetRandom(0, height-1))); + world.AddAgent("Pacer 26").SetPosition(70, int(world.GetRandom(0, height-1))); + auto & astar_agent = static_cast(world.AddAgent("AStar1")); + astar_agent.SetPosition(4, 4); + astar_agent.SetGoalPosition(1, 1); + astar_agent.RecalculatePath(); + + world.AddAgent("Interface2").SetProperty("symbol", '@').SetName("Player"); + + world.AddArmory(); + } + + else if (biome == BiomeType::Ocean) { + world.AddAgent("Interface").SetProperty("symbol", '@'); + } + + // Start an optional timer + if (timerDuration != -1) { + std::thread timerThread(timer, std::ref(world), timerDuration); + world.Run(); + timerThread.join(); + } + + else { + world.Run(); + } +} + +int main() { + auto biome = BiomeType::Maze; // change world biome type here + int width = 70; // change world width here + int height = 24; // change world height here + static const unsigned int SEED = 19; // change world seed here + int timerDuration = -1; // an optional timer length, set to -1 for no timer - cse491::GenerativeWorld world(SEED); - world.AddAgent("Pacer 1").SetPosition(3, 1); - world.AddAgent("Pacer 2").SetPosition(6, 1); - world.AddAgent("Interface").SetProperty("char", '@'); + runWorld(biome, width, height, SEED, timerDuration); - world.Run(); + return 0; } diff --git a/tests/unit/Worlds/BiomeGenerator.cmake b/tests/unit/Worlds/BiomeGenerator.cmake new file mode 100644 index 00000000..31d487de --- /dev/null +++ b/tests/unit/Worlds/BiomeGenerator.cmake @@ -0,0 +1 @@ +add_source_to_target(${EXE_NAME} "source/Worlds/BiomeGenerator.cpp") \ No newline at end of file diff --git a/tests/unit/Worlds/BiomeGenerator.cpp b/tests/unit/Worlds/BiomeGenerator.cpp new file mode 100644 index 00000000..e03579c2 --- /dev/null +++ b/tests/unit/Worlds/BiomeGenerator.cpp @@ -0,0 +1,69 @@ +/** + * This file is part of the Fall 2023, CSE 491 course project. + * @brief Unit tests for Data.hpp in source/core + **/ + +// Catch2 +#define CATCH_CONFIG_MAIN +#include + +// Class project +#include "Worlds/BiomeGenerator.hpp" +#include "Worlds/GenerativeWorld.hpp" + +using namespace std; +using namespace group6; +using namespace cse491; + +TEST_CASE("Constructor", "[worlds][biome]") { + static const unsigned int SEED = 5; + + auto biome = BiomeType::Maze; + BiomeGenerator biomeGenerator(biome, 50, 50, SEED); + + CHECK(biomeGenerator.getBiome() == biome); + CHECK(biomeGenerator.getSeed() == SEED); +} + +string fileToString(const string& fileName) { + ifstream file(fileName); + + string line; + string result; + while (getline(file, line)) { + result.append(line); + } + + return result; +} + +TEST_CASE("Generation", "[worlds][biome]") { + static const unsigned int SEED = 5; + static const string GRID_BASE = "../assets/grids/generated_maze"; + auto biome = BiomeType::Maze; + + GenerativeWorld world(biome, 50, 50, SEED); + + BiomeGenerator biomeGenerator(biome, 50, 50, SEED); + biomeGenerator.setWorld(&world); + biomeGenerator.generate(); + biomeGenerator.saveToFile(GRID_BASE + "1.grid"); + + GenerativeWorld world2(biome, 50, 50, SEED); + + BiomeGenerator biomeGenerator2(biome, 50, 50, SEED); + biomeGenerator2.setWorld(&world2); + biomeGenerator2.generate(); + biomeGenerator2.saveToFile(GRID_BASE + "2.grid"); + + CHECK(fileToString(GRID_BASE + "1.grid") == fileToString(GRID_BASE + "2.grid")); + + GenerativeWorld world3(biome, 50, 50, SEED + 1); + + BiomeGenerator biomeGenerator3(biome, 50, 50, SEED + 1); + biomeGenerator3.setWorld(&world3); + biomeGenerator3.generate(); + biomeGenerator3.saveToFile(GRID_BASE + "3.grid"); + + CHECK_FALSE(fileToString(GRID_BASE + "1.grid") == fileToString(GRID_BASE + "3.grid")); +} diff --git a/tests/unit/Worlds/GenerativeWorld.cmake b/tests/unit/Worlds/GenerativeWorld.cmake new file mode 100644 index 00000000..31d487de --- /dev/null +++ b/tests/unit/Worlds/GenerativeWorld.cmake @@ -0,0 +1 @@ +add_source_to_target(${EXE_NAME} "source/Worlds/BiomeGenerator.cpp") \ No newline at end of file diff --git a/tests/unit/Worlds/GenerativeWorld.cpp b/tests/unit/Worlds/GenerativeWorld.cpp new file mode 100644 index 00000000..a38d3cb9 --- /dev/null +++ b/tests/unit/Worlds/GenerativeWorld.cpp @@ -0,0 +1,139 @@ +/** + * This file is part of the Fall 2023, CSE 491 course project. + * @brief Unit tests for GenerativeWorld.hpp in source/Worlds + **/ + +// Catch2 +#define CATCH_CONFIG_MAIN +#include + +// Class project +#include "Worlds/GenerativeWorld.hpp" +#include "Worlds/BiomeGenerator.hpp" +#include "Agents/PacingAgent.hpp" + +using namespace std; +using namespace group6; +using namespace cse491; + +TEST_CASE("Constructor", "[worlds][generative]") { + static const unsigned int SEED = 5; + BiomeType biome = BiomeType::Maze; + + GenerativeWorld world(biome, 50, 50, SEED); + CHECK(world.GetSeed() == SEED); +} + +TEST_CASE("ItemHelper", "[worlds][generative]") { + static const unsigned int SEED = 5; + BiomeType biome = BiomeType::Maze; + + GenerativeWorld world(biome, 50, 50, SEED); + AgentBase &agent = world.AddAgent("Agent"); + world.AddItem("Boots", "symbol", 'B').SetPosition(1, 1).SetName("Boots").SetProperty("Health", 3.0); + size_t itemId = world.GetItemID("Boots"); + GridPosition pos(0, 0); + + world.ItemHelper(agent, pos); + CHECK_FALSE(agent.HasItem(itemId)); + + pos = GridPosition(1, 1); + world.ItemHelper(agent, pos); + CHECK(agent.HasItem(itemId)); +} + +TEST_CASE("SpikeTileHelper", "[worlds][generative]") { + static const unsigned int SEED = 5; + BiomeType biome = BiomeType::Maze; + + GenerativeWorld world(biome, 50, 50, SEED); + AgentBase &agent = world.AddAgent("Agent"); + world.AddItem("Shield", "symbol", 'B').SetPosition(1, 1).SetName("Shield").SetProperty("Health", 3.0); + + GridPosition pos(1, 1); + world.ItemHelper(agent, pos); + + world.SpikeTileHelper(agent); + ItemBase &item = world.GetItem(world.GetItemID("Shield")); + CHECK(item.GetProperty("Health") == 2.0); +} + +TEST_CASE("TarTileHelper", "[worlds][generative]") { + static const unsigned int SEED = 5; + BiomeType biome = BiomeType::Maze; + + GenerativeWorld world(biome, 50, 50, SEED); + AgentBase &agent = world.AddAgent("Agent"); + world.AddItem("Boots", "symbol", 'B').SetPosition(1, 1).SetName("Boots").SetProperty("Health", 3.0); + + world.TarTileHelper(agent); + CHECK(agent.HasProperty("tar_property")); + + agent.RemoveProperty("tar_property"); + GridPosition pos(1, 1); + world.ItemHelper(agent, pos); + + world.TarTileHelper(agent); + CHECK_FALSE(agent.HasProperty("tar_property")); +} + +TEST_CASE("KeyTileHelper", "[worlds][generative]") { + static const unsigned int SEED = 5; + BiomeType biome = BiomeType::Maze; + + GenerativeWorld world(biome, 50, 50, SEED); + AgentBase &agent = world.AddAgent("Agent"); + GridPosition pos(1, 1); + + world.KeyTileHelper(agent, pos); + CHECK(agent.GetProperty("key_property") == 0.0); + + AgentBase &agent2 = world.AddAgent("Interface"); + + world.KeyTileHelper(agent2, pos); + CHECK(agent2.GetProperty("key_property") == 1.0); +} + +TEST_CASE("FindTiles", "[worlds][generative]") { + size_t wall_id = 1; + size_t spike_id = 2; + size_t door_id = 3; + + WorldGrid grid(10, 10); + + vector walls {GridPosition(1, 1), GridPosition(2, 1), GridPosition(3, 1)}; + vector spikes {GridPosition(5, 5), GridPosition(8, 5)}; + vector doors {GridPosition(8, 8)}; + + grid.At(walls[0]) = wall_id; + grid.At(walls[1]) = wall_id; + grid.At(walls[2]) = wall_id; + + grid.At(spikes[0]) = spike_id; + grid.At(spikes[1]) = spike_id; + + grid.At(doors[0]) = door_id; + + CHECK(GenerativeWorld::FindTiles(grid, wall_id) == walls); + CHECK(GenerativeWorld::FindTiles(grid, spike_id) == spikes); + CHECK(GenerativeWorld::FindTiles(grid, door_id) == doors); +} + +TEST_CASE("TeleporterHelper", "[worlds][generative]") { + static const unsigned int SEED = 5; + BiomeType biome = BiomeType::Maze; + + GenerativeWorld world(biome, 100, 100, SEED); + world.AddTeleporters(); + + GridPosition pos1(53, 6); + GridPosition pos2(18, 18); + GridPosition newPos = pos1; + + world.TeleporterHelper(newPos); + CHECK(newPos == pos2); + + newPos = pos2; + world.TeleporterHelper(newPos); + CHECK(newPos == pos1); +}