Skip to content

Commit

Permalink
Merge remote-tracking branch 'group_6/main' into development
Browse files Browse the repository at this point in the history
  • Loading branch information
FergusonAJ committed Dec 8, 2023
2 parents 9d550c2 + 61aee22 commit ffe3252
Show file tree
Hide file tree
Showing 12 changed files with 1,017 additions and 297 deletions.
280 changes: 183 additions & 97 deletions source/Worlds/BiomeGenerator.cpp
Original file line number Diff line number Diff line change
@@ -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 <fstream>
#include "BiomeGenerator.hpp"
#include "Agents/AgentLibary.hpp"

#include <cmath>
#include <set>
#include <tuple>
#include <random>
#include "BiomeGenerator.hpp"
#include <queue>

using namespace group6;
using namespace cse491;

using std::vector;

Expand All @@ -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<vector<char>>(height, vector<char>(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<GridPosition> 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<int> x_distribution(width/2, width-1);
std::uniform_int_distribution<int> 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;
}

/**
Expand All @@ -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<std::pair<int, int>> 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<Point> BiomeGenerator::clearPath() const {
std::vector<Point> path;
vector<GridPosition> BiomeGenerator::clearPath() const {
vector<GridPosition> 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<GridPosition> 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<Point>& path) {
for (const Point& p : path) {
grid[p.y][p.x] = ' ';
void BiomeGenerator::applyPathToGrid(const vector<GridPosition> &path) {
for (const GridPosition &p: path) {
grid.At(p) = floor_id;
}
}

Expand All @@ -179,25 +205,85 @@ void BiomeGenerator::applyPathToGrid(const std::vector<Point>& 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);

}

/**
* Sets the tile vector for the biome
* @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;
}
}
}
}
}
}
}
}



Loading

0 comments on commit ffe3252

Please sign in to comment.