#include #include #include "rand.hpp" #include "constants.hpp" #include "maze.hpp" using std::string; using matrix::Matrix; inline size_t rand(size_t i, size_t j) { if(i < j) { return Random::uniform(i, j); } else if(j < i) { return Random::uniform(j, i); } else { return i; } } inline bool complete(Matrix& maze) { size_t width = matrix::width(maze); size_t height = matrix::height(maze); for(size_t row = 1; row < height; row += 2) { for(size_t col = 1; col < width; col += 2) { if(maze[row][col] != 0) return false; } } return true; } std::vector neighborsAB(Matrix& maze, Point on) { std::vector result; std::array points{{ {on.x, on.y - 2}, {on.x, on.y + 2}, {on.x - 2, on.y}, {on.x + 2, on.y} }}; for(auto point : points) { if(matrix::inbounds(maze, point.x, point.y)) { result.push_back(point); } } return result; } std::vector neighbors(Matrix& maze, Point on) { std::vector result; std::array points{{ {on.x, on.y - 2}, {on.x, on.y + 2}, {on.x - 2, on.y}, {on.x + 2, on.y} }}; for(auto point : points) { if(matrix::inbounds(maze, point.x, point.y)) { if(maze[point.y][point.x] == WALL_VALUE) { result.push_back(point); } } } return result; } inline std::pair find_coord(Matrix& maze) { size_t width = matrix::width(maze); size_t height = matrix::height(maze); for(size_t y = 1; y < height; y += 2) { for(size_t x = 1; x < width; x += 2) { if(maze[y][x] == WALL_VALUE) { auto found = neighborsAB(maze, {x, y}); for(auto point : found) { if(maze[point.y][point.x] == 0) { return {{x, y}, point}; } } } } } matrix::dump("BAD MAZE", maze); dbc::sentinel("failed to find coord?"); } void maze::randomize_rooms(std::vector& rooms_out, std::vector& maybe_here) { dbc::check(maybe_here.size() >= 2, "must have at least two possible points to place rooms"); while(rooms_out.size() < 2) { // use those dead ends to randomly place rooms for(auto at : maybe_here) { if(Random::uniform(0,1)) { size_t offset = Random::uniform(0,1); Room cur{at.x+offset, at.y+offset, 1, 1}; rooms_out.push_back(cur); } } } } void maze::init(Matrix& maze) { matrix::assign(maze, WALL_VALUE); } void maze::divide(Matrix& maze, Point start, Point end) { for(matrix::line it{start, end}; it.next();) { maze[it.y][it.x] = 0; maze[it.y+1][it.x] = 0; } } void maze::hunt_and_kill(Matrix& maze, std::vector& rooms, std::vector& dead_ends) { for(auto& room : rooms) { for(matrix::box it{maze, room.x, room.y, room.width}; it.next();) { maze[it.y][it.x] = 0; } } Point on{1,1}; while(!complete(maze)) { auto n = neighbors(maze, on); if(n.size() == 0) { dead_ends.push_back(on); auto t = find_coord(maze); on = t.first; maze[on.y][on.x] = 0; size_t row = (on.y + t.second.y) / 2; size_t col = (on.x + t.second.x) / 2; maze[row][col] = 0; } else { auto nb = n[rand(size_t(0), n.size() - 1)]; maze[nb.y][nb.x] = 0; size_t row = (nb.y + on.y) / 2; size_t col = (nb.x + on.x) / 2; maze[row][col] = 0; on = nb; } } for(auto at : dead_ends) { for(auto& room : rooms) { Point room_ul{room.x - room.width - 1, room.y - room.height - 1}; Point room_lr{room.x + room.width - 1, room.y + room.height - 1}; if(at.x >= room_ul.x && at.y >= room_ul.y && at.x <= room_lr.x && at.y <= room_lr.y) { for(matrix::compass it{maze, at.x, at.y}; it.next();) { if(maze[it.y][it.x] == 1) { maze[it.y][it.x] = 0; break; } } } } } } void maze::inner_donut(Matrix& maze, float outer_rad, float inner_rad) { size_t x = matrix::width(maze) / 2; size_t y = matrix::height(maze) / 2; for(matrix::circle it{maze, {x, y}, outer_rad}; it.next();) { for(int x = it.left; x < it.right; x++) { maze[it.y][x] = 0; } } for(matrix::circle it{maze, {x, y}, inner_rad}; it.next();) { for(int x = it.left; x < it.right; x++) { maze[it.y][x] = 1; } } } void maze::inner_box(Matrix& maze, size_t outer_size, size_t inner_size) { size_t x = matrix::width(maze) / 2; size_t y = matrix::height(maze) / 2; for(matrix::box it{maze, x, y, outer_size}; it.next();) { maze[it.y][it.x] = 0; } for(matrix::box it{maze, x, y, inner_size}; it.next();) { maze[it.y][it.x] = 1; } } void maze::remove_dead_ends(Matrix& maze, std::vector& dead_ends) { for(auto at : dead_ends) { for(matrix::compass it{maze, at.x, at.y}; it.next();) { if(maze[it.y][it.x] == 0) { int diff_x = at.x - it.x; int diff_y = at.y - it.y; maze[at.y + diff_y][at.x + diff_x] = 0; } } } }