From 5dae8f8d10d506abc3c75a1f66c1dfe620c84fc1 Mon Sep 17 00:00:00 2001 From: HampusM Date: Tue, 15 Feb 2022 20:27:51 +0100 Subject: refactor: improve project design --- src/app/app.cpp | 26 ++++++++ src/app/app.hpp | 5 ++ src/app/maze.hpp | 23 +++++++ src/app/maze.tpp | 153 ++++++++++++++++++++++++++++++++++++++++++ src/app/options.cpp | 43 ++++++++++++ src/app/options.hpp | 38 +++++++++++ src/app/stack.hpp | 41 ++++++++++++ src/app/stack.tpp | 45 +++++++++++++ src/conversion.cpp | 41 ++++++++++++ src/conversion.hpp | 13 ++++ src/engine/bounds.cpp | 48 +++++++++++++ src/engine/bounds.hpp | 32 +++++++++ src/engine/matrix.hpp | 9 +-- src/engine/matrix.tpp | 17 ++--- src/engine/vector2.cpp | 23 ++++--- src/engine/vector2.hpp | 20 +++--- src/maze.hpp | 22 ------ src/maze.tpp | 146 ---------------------------------------- src/mazerator.cpp | 170 ++++++++++++++++++++++++++--------------------- src/random_generator.hpp | 2 +- src/stack.hpp | 40 ----------- src/stack.tpp | 42 ------------ src/utils.cpp | 34 ---------- src/utils.hpp | 11 --- 24 files changed, 642 insertions(+), 402 deletions(-) create mode 100644 src/app/app.cpp create mode 100644 src/app/app.hpp create mode 100644 src/app/maze.hpp create mode 100644 src/app/maze.tpp create mode 100644 src/app/options.cpp create mode 100644 src/app/options.hpp create mode 100644 src/app/stack.hpp create mode 100644 src/app/stack.tpp create mode 100644 src/conversion.cpp create mode 100644 src/conversion.hpp create mode 100644 src/engine/bounds.cpp create mode 100644 src/engine/bounds.hpp delete mode 100644 src/maze.hpp delete mode 100644 src/maze.tpp delete mode 100644 src/stack.hpp delete mode 100644 src/stack.tpp delete mode 100644 src/utils.cpp delete mode 100644 src/utils.hpp (limited to 'src') diff --git a/src/app/app.cpp b/src/app/app.cpp new file mode 100644 index 0000000..0942c3e --- /dev/null +++ b/src/app/app.cpp @@ -0,0 +1,26 @@ +#include "app.hpp" + +#include "app/maze.hpp" +#include "engine/bounds.hpp" +#include "engine/matrix.hpp" +#include "engine/vector2.hpp" + +#include +#include + +void app_start(const AppOptions &app_options) +{ + Matrix matrix(*app_options.maze_bounds() * + Bounds({.width = 2U, .height = 2U}) + + Bounds({.width = 1U, .height = 1U})); + + matrix.fill(app_options.wall()); + + auto start_pos = *app_options.start_coords() * Vector2({.x = 2U, .y = 2U}) + + Vector2({.x = 1U, .y = 1U}); + + matrix_to_maze(&matrix, std::make_shared(start_pos), " ", + app_options.random_gen()); + + matrix.print(); +} diff --git a/src/app/app.hpp b/src/app/app.hpp new file mode 100644 index 0000000..d02c405 --- /dev/null +++ b/src/app/app.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include "app/options.hpp" + +void app_start(const AppOptions &app_options); diff --git a/src/app/maze.hpp b/src/app/maze.hpp new file mode 100644 index 0000000..2dd05a5 --- /dev/null +++ b/src/app/maze.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "engine/matrix.hpp" +#include "engine/vector2.hpp" +#include "random_generator.hpp" + +#include +#include + +/** + * Turns a matrix into a maze. + * + * @param matrix A matrix + * @param start_pos The start position in the matrix + * @param space_element A matrix element used to indicate a space in a maze + * @param random_gen A pseudo-random number generator + */ +template +void matrix_to_maze(Matrix *matrix, std::shared_ptr start_pos, + Element space_element, + const std::shared_ptr& random_gen); + +#include "maze.tpp" diff --git a/src/app/maze.tpp b/src/app/maze.tpp new file mode 100644 index 0000000..1ed69ac --- /dev/null +++ b/src/app/maze.tpp @@ -0,0 +1,153 @@ +#pragma once + +#include "maze.hpp" + +#include "app/stack.hpp" + +#include +#include + +/** + * Returns the neighbours of a position in a maze. + * + * @param matrix A matrix + * @param pos A matrix position + * @param space_element A matrix element used to indicate a space in a maze + */ +template +std::vector> get_neighbours(Matrix *matrix, + const std::shared_ptr &pos, + Element space_element) +{ + std::vector> neighbours; + neighbours.reserve(3); + + if (pos->y() != 1U) + { + auto pos_down = (*pos - Vector2({.x = 0U, .y = 2U})).copy(); + + if (matrix->get(*pos_down) != space_element) + { + neighbours.push_back(pos_down); + } + } + + if (pos->y() != matrix->rows() - 2U) + { + auto pos_up = (*pos + Vector2({.x = 0U, .y = 2U})).copy(); + + if (matrix->get(*pos_up) != space_element) + { + neighbours.push_back(pos_up); + } + } + + if (pos->x() != 1U) + { + auto pos_left = (*pos - Vector2({.x = 2U, .y = 0U})).copy(); + + if (matrix->get(*pos_left) != space_element) + { + neighbours.push_back(pos_left); + } + } + + if (pos->x() != matrix->columns() - 2U) + { + auto pos_right = (*pos + Vector2({.x = 2U, .y = 0U})).copy(); + + if (matrix->get(*pos_right) != space_element) + { + neighbours.push_back(pos_right); + } + } + + return neighbours; +} + +/** + * Returns the logical size of a maze for a matrix. + * + * @param matrix A matrix + */ +template +unsigned int get_maze_size(Matrix *matrix) +{ + return ((matrix->columns() - 1U) / 2U) * ((matrix->rows() - 1U) / 2U); +} + +/** + * Makes a position be between two coordinates with a differential vector. + * + * @param between_pos Target position + * @param coord A coordinate + * @param coord A second coordinate that must not be equal too the first + * @param diff A differential vector to apply to the target position + */ +void pos_to_between(const std::shared_ptr &between_pos, unsigned int coord, + unsigned int away_coord, Vector2 diff) +{ + if (away_coord > coord) + { + *between_pos += diff; + } + else + { + *between_pos -= diff; + } +} + +template +void matrix_to_maze(Matrix *matrix, std::shared_ptr start_pos, + Element space_element, + const std::shared_ptr &random_gen) +{ + Stack> path_stack(get_maze_size(matrix)); + + path_stack.push(std::move(start_pos)); + + unsigned int visited_pos_cnt = 0U; + while (true) + { + auto pos = path_stack.peek(); + + matrix->set(*pos, space_element); + + auto neighbours = get_neighbours(matrix, pos, space_element); + + if (neighbours.empty()) + { + if (visited_pos_cnt == get_maze_size(matrix) - 1U) + { + break; + } + + // Go back a step + path_stack.pop(); + continue; + } + + visited_pos_cnt++; + + auto next_pos = neighbours[random_gen->in_range( + 0U, static_cast(neighbours.size()) - 1U)]; + + auto between_pos = pos->copy(); + + if (next_pos->y() != pos->y()) + { + pos_to_between(between_pos, pos->y(), next_pos->y(), + Vector2({.x = 0U, .y = 1U})); + } + + if (next_pos->x() != pos->x()) + { + pos_to_between(between_pos, pos->x(), next_pos->x(), + Vector2({.x = 1U, .y = 0U})); + } + + matrix->set(*between_pos, space_element); + + path_stack.push(next_pos); + } +} diff --git a/src/app/options.cpp b/src/app/options.cpp new file mode 100644 index 0000000..cb6e20e --- /dev/null +++ b/src/app/options.cpp @@ -0,0 +1,43 @@ +#include "options.hpp" + +#include + +std::shared_ptr AppOptions::maze_bounds() const +{ + return _maze_bounds; +} + +void AppOptions::maze_bounds(std::shared_ptr maze_bounds) +{ + _maze_bounds = std::move(maze_bounds); +} + +std::shared_ptr AppOptions::start_coords() const +{ + return _start_coords; +} + +void AppOptions::start_coords(std::shared_ptr start_coords) +{ + _start_coords = std::move(start_coords); +} + +std::string_view AppOptions::wall() const +{ + return _wall; +} + +void AppOptions::wall(std::string_view wall) +{ + _wall = wall; +} + +std::shared_ptr AppOptions::random_gen() const +{ + return _random_gen; +} + +void AppOptions::random_gen(std::shared_ptr random_gen) +{ + _random_gen = std::move(random_gen); +} diff --git a/src/app/options.hpp b/src/app/options.hpp new file mode 100644 index 0000000..0023283 --- /dev/null +++ b/src/app/options.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "engine/bounds.hpp" +#include "engine/vector2.hpp" +#include "random_generator.hpp" + +#include +#include + +/** + * Application options. + */ +class AppOptions +{ +public: + AppOptions() = default; + + [[nodiscard]] std::shared_ptr maze_bounds() const; + void maze_bounds(std::shared_ptr maze_bounds); + + [[nodiscard]] std::shared_ptr start_coords() const; + void start_coords(std::shared_ptr start_coords); + + [[nodiscard]] std::string_view wall() const; + void wall(std::string_view wall); + + [[nodiscard]] std::shared_ptr random_gen() const; + void random_gen(std::shared_ptr random_gen); + +private: + std::shared_ptr _maze_bounds = nullptr; + + std::shared_ptr _start_coords = nullptr; + + std::string_view _wall; + + std::shared_ptr _random_gen = nullptr; +}; diff --git a/src/app/stack.hpp b/src/app/stack.hpp new file mode 100644 index 0000000..11f7405 --- /dev/null +++ b/src/app/stack.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +/** + * A stack data structure. + */ +template +class Stack +{ +public: + /** + * Creates a stack. + * + * @param capacity The capacity of the stack + */ + explicit Stack(uint64_t capacity); + + /** + * Pushes a item onto the stack. + */ + void push(Item item); + + /** + * Pops the topmost item from the stack. + */ + void pop(); + + /** + * Peeks into the stack. + * + * @returns The topmost stack item. + */ + Item peek(); + +private: + std::vector _items; +}; + +#include "stack.tpp" diff --git a/src/app/stack.tpp b/src/app/stack.tpp new file mode 100644 index 0000000..bcdafc0 --- /dev/null +++ b/src/app/stack.tpp @@ -0,0 +1,45 @@ +#pragma once + +#include "stack.hpp" + +#include +#include + +template +Stack::Stack(uint64_t capacity) +{ + _items.reserve(capacity); +} + +template +void Stack::push(Item item) +{ + if (_items.size() == _items.capacity()) + { + throw std::overflow_error("Tried to push when stack is full"); + } + + _items.push_back(item); +} + +template +void Stack::pop() +{ + if (_items.empty()) + { + throw std::underflow_error("Tried to pop when stack size is 0"); + } + + _items.pop_back(); +} + +template +Item Stack::peek() +{ + if (_items.empty()) + { + throw std::underflow_error("Tried to peek when stack size is 0"); + } + + return _items.back(); +} diff --git a/src/conversion.cpp b/src/conversion.cpp new file mode 100644 index 0000000..b8fb942 --- /dev/null +++ b/src/conversion.cpp @@ -0,0 +1,41 @@ +#include "conversion.hpp" + +#include +#include + +unsigned int str_to_uint(std::string str) +{ + if (str.at(0) == '-') + { + throw "Less than 0"; + } + + std::size_t waste_pos = 0; + + uint64_t num = 0; + + try + { + num = std::stoul(str, &waste_pos, NUMBER_BASE); + } + catch (const std::invalid_argument &exc) + { + throw "Not a number"; + } + catch (const std::out_of_range &exc) + { + throw "Out of range"; + } + + if (waste_pos != str.length()) + { + throw "Not a number"; + } + + if (num > UINT_MAX) + { + throw "Out of range"; + } + + return static_cast(num); +} diff --git a/src/conversion.hpp b/src/conversion.hpp new file mode 100644 index 0000000..460a3ac --- /dev/null +++ b/src/conversion.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +constexpr unsigned int NUMBER_BASE = 10U; + +/** + * Converts a string to a unsigned integer. + * + * @param str A string that possibly is a unsigned integer + * @returns A unsigned integer + */ +unsigned int str_to_uint(std::string str); diff --git a/src/engine/bounds.cpp b/src/engine/bounds.cpp new file mode 100644 index 0000000..782fd02 --- /dev/null +++ b/src/engine/bounds.cpp @@ -0,0 +1,48 @@ +#include "bounds.hpp" + +Bounds::Bounds(BoundsOpts opts) : _width(opts.width), _height(opts.height) {} + +unsigned int Bounds::width() const +{ + return _width; +} + +void Bounds::width(unsigned int width) +{ + _width = width; +} + +unsigned int Bounds::height() const +{ + return _height; +} + +void Bounds::height(unsigned int height) +{ + _height = height; +} + +void Bounds::verify_coords(const Vector2 &coords) const +{ + if (coords.x() >= _width) + { + throw "X coordinate cannot be higher than or equal to bounds width"; + } + + if (coords.y() >= _height) + { + throw "Y coordinate cannot be higher than or equal to bounds height"; + } +} + +Bounds Bounds::operator*(const Bounds &bounds) const +{ + return Bounds( + {.width = _width * bounds.width(), .height = _height * bounds.height()}); +} + +Bounds Bounds::operator+(const Bounds &bounds) const +{ + return Bounds( + {.width = _width + bounds.width(), .height = _height + bounds.height()}); +} diff --git a/src/engine/bounds.hpp b/src/engine/bounds.hpp new file mode 100644 index 0000000..964e73a --- /dev/null +++ b/src/engine/bounds.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "engine/vector2.hpp" + +struct BoundsOpts +{ + unsigned int width; + unsigned int height; +}; + +class Bounds +{ +public: + explicit Bounds(BoundsOpts opts); + + [[nodiscard]] unsigned int width() const; + + void width(unsigned int width); + + [[nodiscard]] unsigned int height() const; + + void height(unsigned int height); + + void verify_coords(const Vector2 &coords) const; + + Bounds operator*(const Bounds &bounds) const; + Bounds operator+(const Bounds &bounds) const; + +private: + unsigned int _width = 0U; + unsigned int _height = 0U; +}; diff --git a/src/engine/matrix.hpp b/src/engine/matrix.hpp index ddc1a1c..c243eaf 100644 --- a/src/engine/matrix.hpp +++ b/src/engine/matrix.hpp @@ -1,6 +1,8 @@ #pragma once -#include "vector2.hpp" +#include "engine/bounds.hpp" +#include "engine/vector2.hpp" + #include /** @@ -13,10 +15,9 @@ public: /** * Creates a matrix. * - * @param rows The number of rows of the matrix - * @param columns The number of columns of the matrix + * @param bounds The bounds of the matrix */ - Matrix(unsigned int rows, unsigned int columns); + explicit Matrix(const Bounds &bounds); /** * Fills the matrix with a element. diff --git a/src/engine/matrix.tpp b/src/engine/matrix.tpp index bddf76a..d25de28 100644 --- a/src/engine/matrix.tpp +++ b/src/engine/matrix.tpp @@ -1,14 +1,15 @@ +#pragma once + #include "matrix.hpp" + #include template -Matrix::Matrix(unsigned int rows, unsigned int columns) +Matrix::Matrix(const Bounds &bounds) + : _rows(bounds.height()), _columns(bounds.width()) { - _rows = rows; - _columns = columns; - - _matrix.reserve(rows); - _matrix.assign(_matrix.capacity(), std::vector(columns)); + _matrix.reserve(bounds.height()); + _matrix.assign(_matrix.capacity(), std::vector(bounds.width())); }; template @@ -28,9 +29,9 @@ void Matrix::fill(Element element) template void Matrix::print() { - for (std::vector row : _matrix) + for (const std::vector &row : _matrix) { - for (Element element : row) + for (const Element &element : row) { std::cout << element; } diff --git a/src/engine/vector2.cpp b/src/engine/vector2.cpp index effc8b5..d091ea5 100644 --- a/src/engine/vector2.cpp +++ b/src/engine/vector2.cpp @@ -1,10 +1,8 @@ #include "vector2.hpp" -Vector2::Vector2(unsigned int x, unsigned int y) -{ - _x = x; - _y = y; -} +#include + +Vector2::Vector2(Vector2Opts opts) : _x(opts.x), _y(opts.y) {} unsigned int Vector2::x() const { @@ -28,17 +26,22 @@ void Vector2::y(unsigned int y) std::shared_ptr Vector2::copy() { - return std::shared_ptr(new Vector2(*this)); + return std::make_shared(*this); +} + +Vector2 Vector2::operator*(const Vector2 &vector2) const +{ + return Vector2({.x = _x * vector2.x(), .y = _y * vector2.y()}); } -Vector2 Vector2::operator+(const Vector2 vector2) +Vector2 Vector2::operator+(const Vector2 &vector2) const { - return Vector2(_x + vector2.x(), _y + vector2.y()); + return Vector2({.x = _x + vector2.x(), .y = _y + vector2.y()}); } -Vector2 Vector2::operator-(const Vector2 vector2) +Vector2 Vector2::operator-(const Vector2 &vector2) const { - return Vector2(_x - vector2.x(), _y - vector2.y()); + return Vector2({.x = _x - vector2.x(), .y = _y - vector2.y()}); } Vector2 &Vector2::operator+=(const Vector2 &vector2) diff --git a/src/engine/vector2.hpp b/src/engine/vector2.hpp index 3dc1db1..8423431 100644 --- a/src/engine/vector2.hpp +++ b/src/engine/vector2.hpp @@ -2,6 +2,12 @@ #include +struct Vector2Opts +{ + unsigned int x; + unsigned int y; +}; + /** * A 2D Vector. */ @@ -10,16 +16,13 @@ class Vector2 public: /** * Creates a 2D vector. - * - * @param x A X coordinate - * @param y A Y coordinate */ - Vector2(unsigned int x, unsigned int y); + explicit Vector2(Vector2Opts opts); /** * Returns the X coordinate. */ - unsigned int x() const; + [[nodiscard]] unsigned int x() const; /** * Sets the X coordinate. @@ -31,7 +34,7 @@ public: /** * Returns the Y coordinate. */ - unsigned int y() const; + [[nodiscard]] unsigned int y() const; /** * Sets the Y coordinate. @@ -47,8 +50,9 @@ public: */ std::shared_ptr copy(); - Vector2 operator+(const Vector2 vector2); - Vector2 operator-(const Vector2 vector2); + Vector2 operator*(const Vector2 &vector2) const; + Vector2 operator+(const Vector2 &vector2) const; + Vector2 operator-(const Vector2 &vector2) const; Vector2 &operator+=(const Vector2 &vector2); Vector2 &operator-=(const Vector2 &vector2); diff --git a/src/maze.hpp b/src/maze.hpp deleted file mode 100644 index 650e60a..0000000 --- a/src/maze.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "engine/matrix.hpp" -#include "engine/vector2.hpp" -#include "random_generator.hpp" -#include -#include - -/** - * Turns a matrix into a maze. - * - * @param matrix A matrix - * @param start_pos The start position in the matrix - * @param space_element A matrix element used to indicate a space in a maze - * @param random_gen A pseudo-random number generator - */ -template -void matrix_to_maze(Matrix *matrix, std::shared_ptr start_pos, - Element space_element, - std::shared_ptr random_gen); - -#include "maze.tpp" diff --git a/src/maze.tpp b/src/maze.tpp deleted file mode 100644 index 2edb60d..0000000 --- a/src/maze.tpp +++ /dev/null @@ -1,146 +0,0 @@ -#include "maze.hpp" -#include "stack.hpp" -#include - -/** - * Returns the neighbours of a position in a maze. - * - * @param matrix A matrix - * @param pos A matrix position - * @param space_element A matrix element used to indicate a space in a maze - */ -template -std::vector> get_neighbours(Matrix *matrix, - std::shared_ptr pos, - Element space_element) -{ - std::vector> neighbours; - neighbours.reserve(3); - - if (pos->y() != 1U) - { - auto pos_down = (*pos - Vector2(0U, 2U)).copy(); - - if (matrix->get(*pos_down) != space_element) - { - neighbours.push_back(pos_down); - } - } - - if (pos->y() != matrix->rows() - 2U) - { - auto pos_up = (*pos + Vector2(0U, 2U)).copy(); - - if (matrix->get(*pos_up) != space_element) - { - neighbours.push_back(pos_up); - } - } - - if (pos->x() != 1U) - { - auto pos_left = (*pos - Vector2(2U, 0U)).copy(); - - if (matrix->get(*pos_left) != space_element) - { - neighbours.push_back(pos_left); - } - } - - if (pos->x() != matrix->columns() - 2U) - { - auto pos_right = (*pos + Vector2(2U, 0U)).copy(); - - if (matrix->get(*pos_right) != space_element) - { - neighbours.push_back(pos_right); - } - } - - return neighbours; -} - -/** - * Returns the logical size of a maze for a matrix. - * - * @param matrix A matrix - */ -template -unsigned int get_maze_size(Matrix *matrix) -{ - return ((matrix->columns() - 1U) / 2U) * ((matrix->rows() - 1U) / 2U); -} - -/** - * Makes a position be between two coordinates with a differential vector. - * - * @param between_pos Target position - * @param coord A coordinate - * @param coord A second coordinate that must not be equal too the first - * @param diff A differential vector to apply to the target position - */ -void pos_to_between(std::shared_ptr between_pos, unsigned int coord, - unsigned int away_coord, Vector2 diff) -{ - if (away_coord > coord) - { - *between_pos += diff; - } - else - { - *between_pos -= diff; - } -} - -template -void matrix_to_maze(Matrix *matrix, std::shared_ptr start_pos, - Element space_element, - std::shared_ptr random_gen) -{ - Stack> path_stack(get_maze_size(matrix)); - - path_stack.push(start_pos); - - unsigned int visited_pos_cnt = 0U; - while (1) - { - auto pos = path_stack.peek(); - - matrix->set(*pos, space_element); - - auto neighbours = get_neighbours(matrix, pos, space_element); - - if (neighbours.empty()) - { - if (visited_pos_cnt == get_maze_size(matrix) - 1U) - { - break; - } - - // Go back a step - path_stack.pop(); - continue; - } - - visited_pos_cnt++; - - auto next_pos = neighbours[random_gen->in_range( - 0U, static_cast(neighbours.size()) - 1U)]; - - auto between_pos = pos->copy(); - - if (next_pos->y() != pos->y()) - { - pos_to_between(between_pos, pos->y(), next_pos->y(), Vector2(0U, 1U)); - } - - if (next_pos->x() != pos->x()) - { - pos_to_between(between_pos, pos->x(), next_pos->x(), Vector2(1U, 0U)); - } - - matrix->set(*between_pos, space_element); - - path_stack.push(next_pos); - } -} diff --git a/src/mazerator.cpp b/src/mazerator.cpp index b30124f..81960af 100644 --- a/src/mazerator.cpp +++ b/src/mazerator.cpp @@ -1,35 +1,28 @@ -#include "engine/matrix.hpp" +#include "conversion.hpp" + +#include "app/app.hpp" #include "engine/vector2.hpp" -#include "getopt.h" -#include "maze.hpp" #include "random_generator.hpp" -#include "utils.hpp" + +#include #include #include #include +#include +#include + +constexpr unsigned int DEFAULT_MAZE_WIDTH = 40U; +constexpr unsigned int DEFAULT_MAZE_HEIGHT = 20U; -void optarg_error(int arg, std::string error) +constexpr std::string_view DEFAULT_MAZE_WALL = "█"; + +void optarg_error(int arg, const std::string &error) { std::cout << "Error: Invalid option argument for -" << arg << ". " << error << std::endl; exit(EXIT_FAILURE); } -void validate_start_coords(unsigned int start_x, unsigned int start_y, unsigned int width, - unsigned int height) -{ - if (start_x >= width) - { - throw "The x start coordinate cannot be higher than or equal to the maze's width"; - } - - if (start_y >= height) - { - throw "The y start coordinate cannot be higher than or equal to the maze's " - "height"; - } -} - /** * Parses a unsigned integer command-line argument. * @@ -54,102 +47,135 @@ void parse_uint_arg(unsigned int *num_dst, int arg, bool check_zero = false) } } -const struct option options[] = {{"width", required_argument, NULL, 'w'}, - {"height", required_argument, NULL, 'h'}, - {"wall", required_argument, NULL, 'W'}, - {"seed", required_argument, NULL, 's'}, - {"start-x", required_argument, NULL, 'x'}, - {"start-y", required_argument, NULL, 'y'}, - {"help", no_argument, NULL, 0}, - {NULL, 0, NULL, 0}}; +const std::array options = { + option({"width", required_argument, nullptr, 'w'}), + option({"height", required_argument, nullptr, 'h'}), + option({"wall", required_argument, nullptr, 'W'}), + option({"seed", required_argument, nullptr, 's'}), + option({"start-x", required_argument, nullptr, 'x'}), + option({"start-y", required_argument, nullptr, 'y'}), + option({"help", no_argument, nullptr, 0}), + option({nullptr, 0, nullptr, 0})}; int main(int argc, char *argv[]) { - unsigned int maze_width = 40U; - unsigned int maze_height = 20U; + auto args = std::vector(argv, argv + argc); - std::unique_ptr start_x = nullptr; - std::unique_ptr start_y = nullptr; + AppOptions app_options; - std::shared_ptr random_gen = nullptr; + BoundsOpts default_maze_bounds = {DEFAULT_MAZE_WIDTH, DEFAULT_MAZE_HEIGHT}; - std::string wall = "█"; + app_options.maze_bounds(std::make_shared(default_maze_bounds)); - int arg; - while ((arg = getopt_long(argc, argv, "w:h:W:s:x:y:", options, nullptr)) != -1) + app_options.wall(DEFAULT_MAZE_WALL); + + std::unique_ptr start_x = nullptr; + std::unique_ptr start_y = nullptr; + + int arg = 0; + while ((arg = getopt_long(argc, argv, "w:h:W:s:x:y:", options.data(), nullptr)) != -1) { switch (arg) { case 'w': - parse_uint_arg(&maze_width, arg, true); + { + app_options.maze_bounds()->width( + static_cast(std::stoul(optarg, nullptr, NUMBER_BASE))); break; + } case 'h': - parse_uint_arg(&maze_height, arg, true); + { + app_options.maze_bounds()->height( + static_cast(std::stoul(optarg, nullptr, NUMBER_BASE))); break; + } case 'x': + { start_x = std::make_unique(); + parse_uint_arg(start_x.get(), arg); break; + } case 'y': + { start_y = std::make_unique(); + parse_uint_arg(start_y.get(), arg); break; + } case 'W': - wall = optarg; + { + app_options.wall(optarg); break; + } case 's': - unsigned int seed; + { + unsigned int seed = 0; parse_uint_arg(&seed, arg, true); - random_gen = std::make_shared(seed); + app_options.random_gen(std::make_shared(seed)); break; + } case 0: - std::cout - << "Usage: " << argv[0] - << " [OPTION]...\n\n" - "Options:\n" - " -w, --width WIDTH The width of the maze (Default: 40)\n" - " -h, --height HEIGHT The height of the maze (Default: 20)\n" - " -x, --start-x X The x coordinate for the start " - "position " - "(Default: random)\n" - " -y, --start-y Y The y coordinate for the start " - "position " - "(Default: random)\n" - " -W, --wall WALL Single character used as maze walls " - "(Default: '█')\n" - " -s, --seed SEED The randomization seed used for maze " - "generation\n" - " --help Displays usage information" - << std::endl; + { + std::cout << "Usage: " << args[0] + << " [OPTION]...\n\n" + "Options:\n" + " -w, --width WIDTH The width of the maze (Default: " + << DEFAULT_MAZE_WIDTH + << ")\n" + " -h, --height HEIGHT The height of the maze (Default: " + << DEFAULT_MAZE_HEIGHT + << ")\n" + " -x, --start-x X The x coordinate for the start " + "position " + "(Default: random)\n" + " -y, --start-y Y The y coordinate for the start " + "position " + "(Default: random)\n" + " -W, --wall WALL Single character used as maze walls " + "(Default: '" + << DEFAULT_MAZE_WALL + << "')\n" + " -s, --seed SEED The randomization seed used for maze " + "generation\n" + " --help Displays usage information" + << std::endl; return EXIT_SUCCESS; + } case '?': - std::cout << "\nTry '" << argv[0] << " --help' for more information" + { + std::cout << "\nTry '" << args[0] << " --help' for more information" << std::endl; return EXIT_FAILURE; } + } } - if (random_gen == nullptr) + if (app_options.random_gen() == nullptr) { - random_gen = std::make_shared(); + app_options.random_gen(std::make_shared()); } if (start_x == nullptr) { - start_x = - std::make_unique(random_gen->in_range(0, maze_width - 1U)); + start_x = std::make_unique(app_options.random_gen()->in_range( + 0, app_options.maze_bounds()->width() - 1U)); } if (start_y == nullptr) { - start_y = - std::make_unique(random_gen->in_range(0, maze_height - 1U)); + start_y = std::make_unique(app_options.random_gen()->in_range( + 0, app_options.maze_bounds()->height() - 1U)); } + Vector2Opts start_coords = {.x = *start_x, .y = *start_y}; + + app_options.start_coords(std::make_shared(start_coords)); + try { - validate_start_coords(*start_x, *start_y, maze_width, maze_height); + app_options.maze_bounds()->verify_coords(*app_options.start_coords()); } catch (const char *error) { @@ -157,13 +183,5 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - Matrix matrix(maze_height * 2U + 1U, maze_width * 2U + 1U); - - matrix.fill(wall); - - auto start_pos = std::make_shared(*start_x * 2U + 1U, *start_y * 2U + 1U); - - matrix_to_maze(&matrix, start_pos, " ", random_gen); - - matrix.print(); + app_start(app_options); } diff --git a/src/random_generator.hpp b/src/random_generator.hpp index a4c1331..8788982 100644 --- a/src/random_generator.hpp +++ b/src/random_generator.hpp @@ -14,7 +14,7 @@ public: * * @param seed A number generation seed */ - RandomNumberGenerator(unsigned int seed); + explicit RandomNumberGenerator(unsigned int seed); /** * Creates a pesudo-random number generator. diff --git a/src/stack.hpp b/src/stack.hpp deleted file mode 100644 index 4da5f76..0000000 --- a/src/stack.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include - -/** - * A stack data structure. - */ -template -class Stack -{ -public: - /** - * Creates a stack. - * - * @param capacity The capacity of the stack - */ - Stack(unsigned long capacity); - - /** - * Pushes a item onto the stack. - */ - void push(Item item); - - /** - * Pops the topmost item from the stack. - */ - void pop(); - - /** - * Peeks into the stack. - * - * @returns The topmost stack item. - */ - Item peek(); - -private: - std::vector _items; -}; - -#include "stack.tpp" diff --git a/src/stack.tpp b/src/stack.tpp deleted file mode 100644 index b555a49..0000000 --- a/src/stack.tpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "stack.hpp" -#include -#include - -template -Stack::Stack(unsigned long capacity) -{ - _items.reserve(capacity); -} - -template -void Stack::push(Item item) -{ - if (_items.size() == _items.capacity()) - { - throw std::overflow_error("Tried to push when stack is full"); - } - - _items.push_back(item); -} - -template -void Stack::pop() -{ - if (_items.size() == 0) - { - throw std::underflow_error("Tried to pop when stack size is 0"); - } - - _items.pop_back(); -} - -template -Item Stack::peek() -{ - if (_items.size() == 0) - { - throw std::underflow_error("Tried to peek when stack size is 0"); - } - - return _items.back(); -} diff --git a/src/utils.cpp b/src/utils.cpp deleted file mode 100644 index 480c31f..0000000 --- a/src/utils.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "utils.hpp" -#include -#include - -unsigned int str_to_uint(std::string str) -{ - if (str.at(0) == '-') - throw "Less than 0"; - - std::size_t waste_pos; - - unsigned long num; - - try - { - num = std::stoul(str, &waste_pos, 10); - } - catch (const std::invalid_argument &exc) - { - throw "Not a number"; - } - catch (const std::out_of_range &exc) - { - throw "Out of range"; - } - - if (waste_pos != str.length()) - throw "Not a number"; - - if (num > UINT_MAX) - throw "Out of range"; - - return static_cast(num); -} diff --git a/src/utils.hpp b/src/utils.hpp deleted file mode 100644 index be299c7..0000000 --- a/src/utils.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -/** - * Converts a string to a unsigned integer. - * - * @param str A string that possibly is a unsigned integer - * @returns A unsigned integer - */ -unsigned int str_to_uint(std::string str); -- cgit v1.2.3-18-g5258