aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2022-01-09 21:47:23 +0100
committerHampusM <hampus@hampusmat.com>2022-01-09 21:47:23 +0100
commit8ceb79db1d0687bba005cef4a77bb889bf7ec3c3 (patch)
treeb7c13359f652506d60c8556ea386ae8d50bfc5bc /src
parent097aa95c1f0cb159e7d9d0a3edf9284c421ee298 (diff)
refactor: rewrite to c++
Diffstat (limited to 'src')
-rw-r--r--src/grid.c55
-rw-r--r--src/grid.h58
-rw-r--r--src/matrix.hpp67
-rw-r--r--src/matrix.tpp62
-rw-r--r--src/maze.c124
-rw-r--r--src/maze.h15
-rw-r--r--src/maze.hpp24
-rw-r--r--src/maze.tpp131
-rw-r--r--src/mazerator.c176
-rw-r--r--src/mazerator.cpp184
-rw-r--r--src/position.c8
-rw-r--r--src/position.h12
-rw-r--r--src/position_stack.c76
-rw-r--r--src/position_stack.h26
-rw-r--r--src/stack.hpp43
-rw-r--r--src/stack.tpp36
-rw-r--r--src/utils.c45
-rw-r--r--src/utils.cpp34
-rw-r--r--src/utils.h21
-rw-r--r--src/utils.hpp15
-rw-r--r--src/vector2.cpp58
-rw-r--r--src/vector2.hpp62
22 files changed, 716 insertions, 616 deletions
diff --git a/src/grid.c b/src/grid.c
deleted file mode 100644
index 1ad01cd..0000000
--- a/src/grid.c
+++ /dev/null
@@ -1,55 +0,0 @@
-#include "grid.h"
-#include "utils.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-Grid grid_create(unsigned int width, unsigned int height, char *fill)
-{
- unsigned int mem_height = height * sizeof(char **);
- unsigned int mem_width = width * sizeof(char *);
-
- Dimensions dimens = {.width = width, .height = height};
-
- Grid grid = {.grid = malloc_s(mem_height), .dimens = dimens};
-
- // Fill the grid
- for (unsigned int y = 0U; y < height; y++)
- {
- grid.grid[y] = malloc_s(mem_width);
-
- for (unsigned int x = 0U; x < width; x++)
- grid.grid[y][x] = fill;
- }
-
- return grid;
-}
-
-char *grid_get(Grid grid, Position pos)
-{
- return grid.grid[pos.y][pos.x];
-}
-
-void grid_set(Grid grid, Position pos, char *value)
-{
- grid.grid[pos.y][pos.x] = value;
-}
-
-void grid_print(Grid grid)
-{
- for (unsigned int y = 0U; y < grid.dimens.height; y++)
- {
- for (unsigned int x = 0U; x < grid.dimens.width; x++)
- printf("%s", grid.grid[y][x]);
-
- printf("\n");
- }
-}
-
-void grid_destroy(Grid grid)
-{
- // Deallocate the memory of the grid
- for (unsigned int y = 0U; y < grid.dimens.height; y++)
- free(grid.grid[y]);
-
- free(grid.grid);
-}
diff --git a/src/grid.h b/src/grid.h
deleted file mode 100644
index 262035e..0000000
--- a/src/grid.h
+++ /dev/null
@@ -1,58 +0,0 @@
-#ifndef GRID_H
-#define GRID_H
-
-#include "position.h"
-
-typedef struct Dimensions
-{
- unsigned int width;
- unsigned int height;
-} Dimensions;
-
-typedef struct Grid
-{
- char ***grid;
- Dimensions dimens;
-} Grid;
-
-/**
- * Returns a grid.
- *
- * @param width The grid width
- * @param height The grid height
- * @param fill A string to fill the new grid with
- */
-Grid grid_create(unsigned int width, unsigned int height, char *fill);
-
-/*
- * Returns a value from a position in a grid.
- *
- * @param grid A grid
- * @param pos A grid position
- */
-char *grid_get(Grid grid, Position pos);
-
-/*
- * Sets the value of a position in a grid.
- *
- * @param grid A grid
- * @param pos A grid position
- * @param value A new value
- */
-void grid_set(Grid grid, Position pos, char *value);
-
-/**
- * Prints a grid.
- *
- * @param grid A grid
- */
-void grid_print(Grid grid);
-
-/**
- * Destroys a grid.
- *
- * @param grid A grid
- */
-void grid_destroy(Grid grid);
-
-#endif
diff --git a/src/matrix.hpp b/src/matrix.hpp
new file mode 100644
index 0000000..83f9fc2
--- /dev/null
+++ b/src/matrix.hpp
@@ -0,0 +1,67 @@
+#ifndef MATRIX_HPP
+#define MATRIX_HPP
+
+#include "vector2.hpp"
+#include <vector>
+
+/**
+ * A Matrix.
+ */
+template <typename Element>
+class Matrix
+{
+public:
+ /**
+ * Creates a matrix.
+ *
+ * @param rows The number of rows of the matrix
+ * @param columns The number of columns of the matrix
+ */
+ Matrix(unsigned int rows, unsigned int columns);
+
+ /**
+ * Fills the matrix with a element.
+ *
+ * @param element A element
+ */
+ void fill(Element element);
+
+ /**
+ * Prints the matrix.
+ */
+ void print();
+
+ /**
+ * Returns a element of the matrix.
+ *
+ * @param pos The position of a element
+ */
+ Element get(Vector2 pos);
+
+ /**
+ * Sets a element of the matrix.
+ *
+ * @param pos The position of a element
+ * @param element A new element
+ */
+ void set(Vector2 pos, Element element);
+
+ /**
+ * Returns the number of rows the matrix has.
+ */
+ unsigned int rows();
+
+ /**
+ * Returns the number of columns the matrix has.
+ */
+ unsigned int columns();
+
+private:
+ std::vector<std::vector<Element>> _matrix;
+ unsigned int _rows;
+ unsigned int _columns;
+};
+
+#include "matrix.tpp"
+
+#endif
diff --git a/src/matrix.tpp b/src/matrix.tpp
new file mode 100644
index 0000000..b9fa495
--- /dev/null
+++ b/src/matrix.tpp
@@ -0,0 +1,62 @@
+#include "matrix.hpp"
+#include <iostream>
+
+template <typename Element>
+Matrix<Element>::Matrix(unsigned int rows, unsigned int columns)
+{
+ _rows = rows;
+ _columns = columns;
+
+ _matrix.reserve(rows);
+ _matrix.assign(_matrix.capacity(), std::vector<Element>(columns));
+};
+
+template <typename Element>
+void Matrix<Element>::fill(Element element)
+{
+ for (unsigned int row = 0U; row < _matrix.capacity(); row++)
+ {
+ std::vector<Element> row_vector = _matrix[row];
+
+ for (unsigned int column = 0U; column < row_vector.capacity(); column++)
+ _matrix[row][column] = element;
+ }
+}
+
+template <typename Element>
+void Matrix<Element>::print()
+{
+ for (std::vector<Element> row : _matrix)
+ {
+ for (Element element : row)
+ std::cout << element;
+
+ std::cout << "\n";
+ }
+
+ std::cout << std::flush;
+}
+
+template <typename Element>
+Element Matrix<Element>::get(Vector2 pos)
+{
+ return _matrix[pos.y()][pos.x()];
+}
+
+template <typename Element>
+void Matrix<Element>::set(Vector2 pos, Element element)
+{
+ _matrix[pos.y()][pos.x()] = element;
+}
+
+template <typename Element>
+unsigned int Matrix<Element>::rows()
+{
+ return _rows;
+}
+
+template <typename Element>
+unsigned int Matrix<Element>::columns()
+{
+ return _columns;
+}
diff --git a/src/maze.c b/src/maze.c
deleted file mode 100644
index 76775b2..0000000
--- a/src/maze.c
+++ /dev/null
@@ -1,124 +0,0 @@
-#include "maze.h"
-#include "grid.h"
-#include "position.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-int is_pos_empty(Grid grid, Position pos)
-{
- return strcmp(grid_get(grid, pos), " ") != 0;
-}
-
-void add_neighbour(Position neighbours[3], unsigned int *neighbour_cnt,
- Position neighbour_pos)
-{
- neighbours[*neighbour_cnt] = neighbour_pos;
- (*neighbour_cnt)++;
-}
-
-void get_neighbours(Grid grid, Position pos, Position neighbours[3],
- unsigned int *neighbour_cnt)
-{
- if (pos.y != 1U)
- {
- Position pos_down = position_create(pos.x, pos.y - 2U);
-
- if (is_pos_empty(grid, pos_down))
- add_neighbour(neighbours, neighbour_cnt, pos_down);
- }
-
- if (pos.y != grid.dimens.height - 2U)
- {
- Position pos_up = position_create(pos.x, pos.y + 2U);
-
- if (is_pos_empty(grid, pos_up))
- add_neighbour(neighbours, neighbour_cnt, pos_up);
- }
-
- if (pos.x != 1U)
- {
- Position pos_left = position_create(pos.x - 2U, pos.y);
-
- if (is_pos_empty(grid, pos_left))
- add_neighbour(neighbours, neighbour_cnt, pos_left);
- }
-
- if (pos.x != grid.dimens.width - 2U)
- {
- Position pos_right = position_create(pos.x + 2U, pos.y);
-
- if (is_pos_empty(grid, pos_right))
- add_neighbour(neighbours, neighbour_cnt, pos_right);
- }
-}
-
-unsigned int get_maze_size(Grid grid)
-{
- return ((grid.dimens.width - 1U) / 2U) * ((grid.dimens.height - 1U) / 2U);
-}
-
-int is_whole_maze_visited(Grid grid, unsigned int visited_pos_cnt)
-{
- return visited_pos_cnt == get_maze_size(grid) - 1U;
-}
-
-void grid_to_maze(Grid grid, Position start_pos)
-{
- PositionStack *path = pos_stack_create(get_maze_size(grid));
-
- pos_stack_push(path, start_pos);
-
- unsigned int visited_pos_cnt = 0U;
- while (1)
- {
- Position pos = pos_stack_peek(path);
-
- grid_set(grid, pos, " ");
-
- Position neighbours[3];
- unsigned int neighbour_cnt = 0U;
-
- get_neighbours(grid, pos, neighbours, &neighbour_cnt);
-
- if (neighbour_cnt == 0U)
- {
- if (is_whole_maze_visited(grid, visited_pos_cnt))
- break;
-
- // Go back a step
- pos_stack_pop(path);
- continue;
- }
-
- visited_pos_cnt++;
-
- Position next_pos = neighbours[rand() % neighbour_cnt];
-
- Position between_pos = position_create(pos.x, pos.y);
-
- if (next_pos.y != pos.y)
- {
- if (next_pos.y > pos.y)
- between_pos.y += 1U;
- else
- between_pos.y -= 1U;
- }
-
- if (next_pos.x != pos.x)
- {
- if (next_pos.x > pos.x)
- between_pos.x += 1U;
- else
- between_pos.x -= 1U;
- }
-
- grid_set(grid, between_pos, " ");
-
- pos_stack_push(path, next_pos);
-
- // grid_print(grid);
- }
-
- pos_stack_destroy(path);
-}
diff --git a/src/maze.h b/src/maze.h
deleted file mode 100644
index a3c932c..0000000
--- a/src/maze.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef MAZE_H
-#define MAZE_H
-
-#include "grid.h"
-#include "position_stack.h"
-
-/**
- * Creates a maze from a grid
- *
- * @param grid A grid
- * @param start_pos Start position
- */
-void grid_to_maze(Grid grid, Position start_pos);
-
-#endif
diff --git a/src/maze.hpp b/src/maze.hpp
new file mode 100644
index 0000000..0ff1d06
--- /dev/null
+++ b/src/maze.hpp
@@ -0,0 +1,24 @@
+#ifndef MAZE_HPP
+#define MAZE_HPP
+
+#include "matrix.hpp"
+#include "vector2.hpp"
+#include <memory>
+#include <random>
+#include <string>
+
+/**
+ * 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 <typename Element>
+void matrix_to_maze(Matrix<Element> *matrix, std::shared_ptr<Vector2> start_pos,
+ Element space_element, std::mt19937 random_gen);
+
+#include "maze.tpp"
+
+#endif
diff --git a/src/maze.tpp b/src/maze.tpp
new file mode 100644
index 0000000..6c4fd71
--- /dev/null
+++ b/src/maze.tpp
@@ -0,0 +1,131 @@
+#include "matrix.hpp"
+#include "maze.hpp"
+#include "stack.hpp"
+#include <vector>
+
+/**
+ * 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 <typename Element>
+std::vector<std::shared_ptr<Vector2>> get_neighbours(Matrix<Element> *matrix,
+ std::shared_ptr<Vector2> pos,
+ Element space_element)
+{
+ std::vector<std::shared_ptr<Vector2>> 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 <typename Element>
+unsigned int get_maze_size(Matrix<Element> *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<Vector2> between_pos, unsigned int coord,
+ unsigned int away_coord, Vector2 diff)
+{
+ if (away_coord > coord)
+ *between_pos += diff;
+ else
+ *between_pos -= diff;
+}
+
+template <typename Element>
+void matrix_to_maze(Matrix<Element> *matrix, std::shared_ptr<Vector2> start_pos,
+ Element space_element, std::mt19937 random_gen)
+{
+ Stack<std::shared_ptr<Vector2>> 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);
+
+ std::vector<std::shared_ptr<Vector2>> 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 random_dist =
+ std::uniform_int_distribution<unsigned int>(0U, neighbours.size() - 1UL);
+
+ auto next_pos = neighbours[random_dist(random_gen)];
+
+ 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.c b/src/mazerator.c
deleted file mode 100644
index 9926c1e..0000000
--- a/src/mazerator.c
+++ /dev/null
@@ -1,176 +0,0 @@
-#include "grid.h"
-#include "maze.h"
-#include "position.h"
-#include "utils.h"
-#include <getopt.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-void optarg_error(char arg, char *error)
-{
- printf("Error: Invalid option argument for -%c. %s\n", arg, error);
- exit(EXIT_FAILURE);
-}
-
-void validate_start_coords(unsigned int start_x, unsigned int start_y, unsigned int width,
- unsigned int height)
-{
- char *error_format =
- "Error: The %s start coordinate is not allowed to be higher than the maze's %s\n";
-
- if (start_x > width)
- {
- printf(error_format, "x", "width");
- exit(EXIT_FAILURE);
- }
-
- if (start_y > height)
- {
- printf(error_format, "y", "height");
- exit(EXIT_FAILURE);
- }
-}
-
-void get_seed(unsigned int *seed_dst)
-{
- FILE *urandom = fopen("/dev/urandom", "r");
- fread(seed_dst, sizeof(*seed_dst), 1, urandom);
- fclose(urandom);
-}
-
-const struct option options[] = {{"width", required_argument, NULL, 'w'},
- {"heigth", 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}};
-
-int main(int argc, char *argv[])
-{
- unsigned int maze_width = 40U;
- unsigned int maze_height = 20U;
-
- unsigned int *start_x = NULL;
- unsigned int *start_y = NULL;
-
- unsigned int *seed = NULL;
-
- char *wall = "█";
-
- int arg;
- while ((arg = getopt_long(argc, argv, "w:h:W:s:x:y:", options, NULL)) != -1)
- {
- char *err = NULL;
-
- switch (arg)
- {
- case 'w':
- maze_width = str_to_uint(optarg, &err);
-
- if (err != NULL)
- optarg_error(arg, err);
-
- if (maze_width == 0)
- optarg_error(arg, "It should not be 0");
-
- break;
- case 'h':
- maze_height = str_to_uint(optarg, &err);
-
- if (err != NULL)
- optarg_error(arg, err);
-
- if (maze_height == 0)
- optarg_error(arg, "It should not be 0");
-
- break;
- case 'x':
- start_x = malloc_s(sizeof(unsigned int *));
- *start_x = str_to_uint(optarg, &err);
-
- if (err != NULL)
- optarg_error(arg, err);
-
- break;
- case 'y':
- start_y = malloc_s(sizeof(unsigned int *));
- *start_y = str_to_uint(optarg, &err);
-
- if (err != NULL)
- optarg_error(arg, err);
-
- break;
- case 'W':
- wall = optarg;
- break;
- case 's':
- seed = malloc_s(sizeof(unsigned int *));
- *seed = str_to_uint(optarg, &err);
-
- if (err != NULL)
- optarg_error(arg, err);
-
- if (*seed == 0)
- optarg_error(arg, "It should not be 0");
-
- break;
- case 0:
- printf("Usage: %s [OPTION]...\n\n"
- "Options:\n"
- " -w, --width WIDTH The width of the maze (Default: 40)\n"
- " -h, --heigth HEIGHT The heigth 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\n",
- argv[0]);
- exit(0);
- case '?':
- printf("\nTry '%s --help' for more information\n", argv[0]);
- exit(EXIT_FAILURE);
- }
- }
-
- if (seed == NULL)
- {
- seed = malloc_s(sizeof(unsigned int *));
- get_seed(seed);
- }
-
- srand(*seed);
- free(seed);
-
- if (start_x == NULL)
- {
- start_x = malloc_s(sizeof(unsigned int *));
- *start_x = rand() % maze_width;
- }
-
- if (start_y == NULL)
- {
- start_y = malloc_s(sizeof(unsigned int *));
- *start_y = rand() % maze_height;
- }
-
- validate_start_coords(*start_x, *start_y, maze_width, maze_height);
-
- Position start_pos = position_create(*start_x * 2U + 1U, *start_y * 2U + 1U);
-
- Grid grid = grid_create(maze_width * 2U + 1U, maze_height * 2U + 1U, wall);
-
- grid_to_maze(grid, start_pos);
-
- grid_print(grid);
-
- grid_destroy(grid);
-
- free(start_x);
- free(start_y);
-}
diff --git a/src/mazerator.cpp b/src/mazerator.cpp
new file mode 100644
index 0000000..0927b4c
--- /dev/null
+++ b/src/mazerator.cpp
@@ -0,0 +1,184 @@
+#include "fmt/core.h"
+#include "getopt.h"
+#include "matrix.hpp"
+#include "maze.hpp"
+#include "stack.hpp"
+#include "utils.hpp"
+#include "vector2.hpp"
+#include <iostream>
+#include <memory>
+#include <random>
+#include <string>
+
+void optarg_error(char arg, std::string error)
+{
+ std::cout << fmt::format("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)
+{
+ std::string error_format =
+ "The {} start coordinate cannot be higher than or equal to the maze's {}";
+
+ if (start_x >= width)
+ throw fmt::format(error_format, "x", "width");
+
+ if (start_y >= height)
+ throw fmt::format(error_format, "y", "height");
+}
+
+/**
+ * Parses a unsigned integer command-line argument.
+ *
+ * @param num_dst A pointer to a place to store the result value
+ * @param arg The command-line argument character
+ * @param check_zero Whether or not to make sure that the result is not zero
+ */
+void parse_uint_arg(unsigned int *num_dst, char arg, bool check_zero = false)
+{
+ try
+ {
+ *num_dst = str_to_uint(std::string(optarg));
+
+ if (check_zero && *num_dst == 0)
+ throw "It should not be 0";
+ }
+ catch (const char *error)
+ {
+ optarg_error(arg, std::string(error));
+ }
+}
+
+/**
+ * Parses a unsigned integer command-line argument.
+ *
+ * @param num_dst A shared pointer to a place to store the result value
+ * @param arg The command-line argument character
+ */
+void parse_uint_arg(std::shared_ptr<unsigned int> num_dst, char arg)
+{
+ num_dst = std::make_shared<unsigned int>();
+
+ parse_uint_arg(num_dst.get(), arg);
+}
+
+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}};
+
+int main(int argc, char *argv[])
+{
+ unsigned int maze_width = 40U;
+ unsigned int maze_height = 20U;
+
+ std::shared_ptr<unsigned int> start_x = nullptr;
+ std::shared_ptr<unsigned int> start_y = nullptr;
+
+ std::unique_ptr<std::mt19937> random_gen = nullptr;
+
+ std::string wall = "█";
+
+ int arg;
+ while ((arg = getopt_long(argc, argv, "w:h:W:s:x:y:", options, nullptr)) != -1)
+ {
+ switch (arg)
+ {
+ case 'w':
+ parse_uint_arg(&maze_width, arg, true);
+ break;
+ case 'h':
+ parse_uint_arg(&maze_height, arg, true);
+ break;
+ case 'x':
+ parse_uint_arg(start_x, arg);
+ break;
+ case 'y':
+ parse_uint_arg(start_y, arg);
+ break;
+ case 'W':
+ wall = optarg;
+ break;
+ case 's':
+ unsigned int seed;
+ parse_uint_arg(&seed, arg, true);
+
+ random_gen = std::make_unique<std::mt19937>(seed);
+ break;
+ case 0:
+ std::cout
+ << fmt::format(
+ "Usage: {} [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",
+ argv[0])
+ << std::endl;
+ exit(0);
+ case '?':
+ std::cout << fmt::format("\nTry '{} --help' for more information", argv[0])
+ << std::endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (random_gen == nullptr)
+ {
+ std::random_device random_device;
+ random_gen = std::make_unique<std::mt19937>(random_device());
+ }
+
+ if (start_x == nullptr)
+ {
+ auto random_dist =
+ std::uniform_int_distribution<unsigned int>(0, maze_width - 1U);
+
+ start_x = std::make_unique<unsigned int>(random_dist(*random_gen));
+ }
+
+ if (start_y == nullptr)
+ {
+ auto random_dist =
+ std::uniform_int_distribution<unsigned int>(0, maze_height - 1U);
+
+ start_y = std::make_unique<unsigned int>(random_dist(*random_gen));
+ }
+
+ try
+ {
+ validate_start_coords(*start_x, *start_y, maze_width, maze_height);
+ }
+ catch (std::string error)
+ {
+ std::cout << "Error: " << error << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ Matrix<std::string> matrix(maze_height * 2U + 1U, maze_width * 2U + 1U);
+
+ matrix.fill(wall);
+
+ auto start_pos = std::make_shared<Vector2>(*start_x * 2U + 1U, *start_y * 2U + 1U);
+
+ matrix_to_maze<std::string>(&matrix, start_pos, " ", *random_gen);
+
+ matrix.print();
+}
diff --git a/src/position.c b/src/position.c
deleted file mode 100644
index a7465ab..0000000
--- a/src/position.c
+++ /dev/null
@@ -1,8 +0,0 @@
-#include "position.h"
-
-Position position_create(unsigned int x, unsigned int y)
-{
- Position pos = {.x = x, .y = y};
-
- return pos;
-}
diff --git a/src/position.h b/src/position.h
deleted file mode 100644
index 05d21fa..0000000
--- a/src/position.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef POSITION_H
-#define POSITION_H
-
-typedef struct Position
-{
- unsigned int x;
- unsigned int y;
-} Position;
-
-Position position_create(unsigned int x, unsigned int y);
-
-#endif
diff --git a/src/position_stack.c b/src/position_stack.c
deleted file mode 100644
index 0b4895a..0000000
--- a/src/position_stack.c
+++ /dev/null
@@ -1,76 +0,0 @@
-#include "position_stack.h"
-#include "utils.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-// Error handler for stack errors
-void stack_error(int err)
-{
- switch (err)
- {
- case STACK_ERR_OVERFLOW:
- printf("Error: Stack overflow\nBe kind and report this problem.");
- break;
- case STACK_ERR_UNDERFLOW:
- printf("Error: Stack underflow\nBe kind and report this problem.");
- break;
- }
-
- exit(1);
-}
-
-// Creates a new stack
-PositionStack *pos_stack_create(unsigned int capacity)
-{
- PositionStack *pos_stack = malloc_s(sizeof(PositionStack));
-
- pos_stack->capacity = capacity;
- pos_stack->top = -1;
- pos_stack->items = malloc_s(sizeof(Position) * capacity);
-
- return pos_stack;
-}
-
-void pos_stack_destroy(PositionStack *pos_stack)
-{
- free(pos_stack->items);
- free(pos_stack);
-}
-
-// Adds a new item to a stack
-void pos_stack_push(PositionStack *pos_stack, Position pos)
-{
- // Avoid a overflow by checking if the stack is full
- if (pos_stack->top == pos_stack->capacity - 1U)
- {
- stack_error(STACK_ERR_OVERFLOW);
- }
-
- // Add an element and increase the top index
- pos_stack->items[++pos_stack->top] = pos;
-}
-
-// Returns the topmost item of a stack
-Position pos_stack_peek(PositionStack *pos_stack)
-{
- // Avoid a underflow by checking if the stack is empty
- if (pos_stack->top == -1)
- {
- stack_error(STACK_ERR_UNDERFLOW);
- }
-
- return pos_stack->items[pos_stack->top];
-}
-
-// Deletes the topmost item of a stack
-Position pos_stack_pop(PositionStack *pos_stack)
-{
- // Avoid a underflow by checking if the stack is empty
- if (pos_stack->top == -1)
- {
- stack_error(STACK_ERR_UNDERFLOW);
- }
-
- // Decrease the stack size by 1 and return the popped element
- return pos_stack->items[pos_stack->top--];
-}
diff --git a/src/position_stack.h b/src/position_stack.h
deleted file mode 100644
index 674da23..0000000
--- a/src/position_stack.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef POSITION_STACK_H
-#define POSITION_STACK_H
-
-#include "position.h"
-
-#define STACK_ERR_OVERFLOW 0xFFF01
-#define STACK_ERR_UNDERFLOW 0xFFF02
-
-typedef struct PositionStack
-{
- unsigned int capacity;
- int top;
- Position *items;
-} PositionStack;
-
-PositionStack *pos_stack_create(unsigned int capacity);
-
-void pos_stack_destroy(PositionStack *pos_stack);
-
-void pos_stack_push(PositionStack *pos_stack, Position pos);
-
-Position pos_stack_peek(PositionStack *pos_stack);
-
-Position pos_stack_pop(PositionStack *pos_stack);
-
-#endif
diff --git a/src/stack.hpp b/src/stack.hpp
new file mode 100644
index 0000000..b156242
--- /dev/null
+++ b/src/stack.hpp
@@ -0,0 +1,43 @@
+#ifndef STACK_HPP
+#define STACK_HPP
+
+#include <vector>
+
+/**
+ * A stack data structure.
+ */
+template <typename Item>
+class Stack
+{
+public:
+ /**
+ * Creates a stack.
+ *
+ * @param capacity The capacity of the stack
+ */
+ Stack(int 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<Item> _items;
+};
+
+#include "stack.tpp"
+
+#endif
diff --git a/src/stack.tpp b/src/stack.tpp
new file mode 100644
index 0000000..958d6ca
--- /dev/null
+++ b/src/stack.tpp
@@ -0,0 +1,36 @@
+#include "stack.hpp"
+#include <iostream>
+#include <stdexcept>
+
+template <typename Item>
+Stack<Item>::Stack(int capacity)
+{
+ _items.reserve(capacity);
+}
+
+template <typename Item>
+void Stack<Item>::push(Item item)
+{
+ if (_items.size() == _items.capacity())
+ throw std::overflow_error("Tried to push when stack is full");
+
+ _items.push_back(item);
+}
+
+template <typename Item>
+void Stack<Item>::pop()
+{
+ if (_items.size() == 0)
+ throw std::underflow_error("Tried to pop when stack size is 0");
+
+ _items.pop_back();
+}
+
+template <typename Item>
+Item Stack<Item>::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.c b/src/utils.c
deleted file mode 100644
index fa33cb0..0000000
--- a/src/utils.c
+++ /dev/null
@@ -1,45 +0,0 @@
-#include "utils.h"
-#include <ctype.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-void *malloc_s(unsigned long amount)
-{
- void *memory = malloc(amount);
-
- if (memory == NULL)
- {
- printf("Error: Memory allocation failed");
- exit(EXIT_FAILURE);
- }
-
- return memory;
-}
-
-unsigned int str_to_uint(char *str, char **err)
-{
- if (*str == '-')
- {
- *err = "Less than 0";
- return 0;
- }
-
- char *str_waste;
- unsigned long num = strtoul(str, &str_waste, 10);
-
- if (strlen(str_waste) != 0UL)
- {
- *err = "Not a number";
- return 0;
- }
-
- if (num > (unsigned long)UINT_MAX)
- {
- *err = "Too large";
- return 0;
- }
-
- return (unsigned int)num;
-}
diff --git a/src/utils.cpp b/src/utils.cpp
new file mode 100644
index 0000000..30ff4ec
--- /dev/null
+++ b/src/utils.cpp
@@ -0,0 +1,34 @@
+#include "utils.hpp"
+#include <climits>
+#include <stdexcept>
+
+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 > (unsigned long)UINT_MAX)
+ throw "Out of range";
+
+ return (unsigned int)num;
+}
diff --git a/src/utils.h b/src/utils.h
deleted file mode 100644
index 2f36034..0000000
--- a/src/utils.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef UTILS_H
-#define UTILS_H
-
-/**
- * Safely allocates memory to the heap.
- *
- * @param amount The amount of memory to allocate
- * @returns The allocated memory.
- */
-void *malloc_s(unsigned long amount);
-
-/**
- * Converts a string to a unsigned integer.
- *
- * @param str A string
- * @param err A error destination
- * @returns A unsigned integer.
- */
-unsigned int str_to_uint(char *str, char **err);
-
-#endif
diff --git a/src/utils.hpp b/src/utils.hpp
new file mode 100644
index 0000000..91a1d8e
--- /dev/null
+++ b/src/utils.hpp
@@ -0,0 +1,15 @@
+#ifndef UTILS_HPP
+#define UTILS_HPP
+
+#include <memory>
+#include <string>
+
+/**
+ * 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);
+
+#endif
diff --git a/src/vector2.cpp b/src/vector2.cpp
new file mode 100644
index 0000000..effc8b5
--- /dev/null
+++ b/src/vector2.cpp
@@ -0,0 +1,58 @@
+#include "vector2.hpp"
+
+Vector2::Vector2(unsigned int x, unsigned int y)
+{
+ _x = x;
+ _y = y;
+}
+
+unsigned int Vector2::x() const
+{
+ return _x;
+}
+
+void Vector2::x(unsigned int x)
+{
+ _x = x;
+}
+
+unsigned int Vector2::y() const
+{
+ return _y;
+}
+
+void Vector2::y(unsigned int y)
+{
+ _y = y;
+}
+
+std::shared_ptr<Vector2> Vector2::copy()
+{
+ return std::shared_ptr<Vector2>(new Vector2(*this));
+}
+
+Vector2 Vector2::operator+(const Vector2 vector2)
+{
+ return Vector2(_x + vector2.x(), _y + vector2.y());
+}
+
+Vector2 Vector2::operator-(const Vector2 vector2)
+{
+ return Vector2(_x - vector2.x(), _y - vector2.y());
+}
+
+Vector2 &Vector2::operator+=(const Vector2 &vector2)
+{
+ _x += vector2.x();
+ _y += vector2.y();
+
+ return *this;
+}
+
+Vector2 &Vector2::operator-=(const Vector2 &vector2)
+{
+ _x -= vector2.x();
+ _y -= vector2.y();
+
+ return *this;
+}
diff --git a/src/vector2.hpp b/src/vector2.hpp
new file mode 100644
index 0000000..ccfa75b
--- /dev/null
+++ b/src/vector2.hpp
@@ -0,0 +1,62 @@
+#ifndef VECTOR2_HPP
+#define VECTOR2_HPP
+
+#include <memory>
+
+/**
+ * A 2D Vector.
+ */
+class Vector2
+{
+public:
+ /**
+ * Creates a 2D vector.
+ *
+ * @param x A X coordinate
+ * @param y A Y coordinate
+ */
+ Vector2(unsigned int x, unsigned int y);
+
+ /**
+ * Returns the X coordinate.
+ */
+ unsigned int x() const;
+
+ /**
+ * Sets the X coordinate.
+ *
+ * @param x A new X coordinate
+ */
+ void x(unsigned int x);
+
+ /**
+ * Returns the Y coordinate.
+ */
+ unsigned int y() const;
+
+ /**
+ * Sets the Y coordinate.
+ *
+ * @param Y A new Y coordinate
+ */
+ void y(unsigned int y);
+
+ /**
+ * Creates a copy of the 2D vector.
+ *
+ * @returns A identical 2D vector.
+ */
+ std::shared_ptr<Vector2> copy();
+
+ Vector2 operator+(const Vector2 vector2);
+ Vector2 operator-(const Vector2 vector2);
+
+ Vector2 &operator+=(const Vector2 &vector2);
+ Vector2 &operator-=(const Vector2 &vector2);
+
+private:
+ unsigned int _x;
+ unsigned int _y;
+};
+
+#endif