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/maze.tpp | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 src/app/maze.tpp (limited to 'src/app/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); + } +} -- cgit v1.2.3-18-g5258