#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); } }