diff --git a/src/algos/maze.cpp b/src/algos/maze.cpp index 8646748..a7ba657 100644 --- a/src/algos/maze.cpp +++ b/src/algos/maze.cpp @@ -12,14 +12,14 @@ namespace maze { inline bool complete(Matrix& maze, size_t width, size_t height) { 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; + if(maze[row][col] != SPACE_VALUE) return false; } } return true; } - std::vector neighborsAB(Matrix& maze, Point on) { + inline std::vector neighbors(Matrix& maze, Point on) { std::vector result; std::array points{{ @@ -38,44 +38,74 @@ namespace maze { return result; } - std::vector neighbors(Matrix& maze, Point on) { + inline std::vector neighbor_walls(Matrix& maze, Point on) { + auto near_me = neighbors(maze, 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); - } + for(auto point : near_me) { + if(maze[point.y][point.x] == WALL_VALUE) { + result.push_back(point); } } return result; } - inline std::pair find_coord(Matrix& maze, size_t width, size_t height) { - 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}); + bool Builder::hunt_next(Point& on, Point& found) { + for(size_t y = 1; y < $height; y += 2) { + for(size_t x = 1; x < $width; x += 2) { + if($walls[y][x] != WALL_VALUE) { + continue; + } + + auto near_me = neighbors($walls, {x, y}); - for(auto point : found) { - if(maze[point.y][point.x] == 0) { - return {{x, y}, point}; - } + for(auto point : near_me) { + if($walls[point.y][point.x] == SPACE_VALUE) { + on = {x, y}; + found = point; + return true; } } } } - matrix::dump("BAD MAZE", maze); - dbc::sentinel("failed to find coord?"); + return false; + } + + Point Builder::hak_step(Point from, Point to) { + $walls[from.y][from.x] = SPACE_VALUE; + size_t row = (from.y + to.y) / 2; + size_t col = (from.x + to.x) / 2; + $walls[row][col] = SPACE_VALUE; + + return from; + } + + void Builder::hunt_and_kill(Point on) { + if($rooms.size() > 0) place_rooms(); + Point found{1, 1}; + + while(true) { + auto n = neighbor_walls($walls, on); + + if(n.size() == 0) { + // no neighbors, must be dead end + add_dead_end(on); + + if(!hunt_next(on, found)) { + break; + } + + on = hak_step(on, found); + } else { + // found neighbors, pick random one + auto nb = n[Random::abs(size_t(0), n.size() - 1)]; + on = hak_step(nb, on); + } + } + + if($rooms.size() > 0) place_rooms(); } bool Builder::room_should_exist(Room& room, bool allow_dupes) { @@ -136,7 +166,6 @@ namespace maze { size_t offset = Random::uniform(0, 3); - // BUG: this still accidentally merges rooms for(size_t i = 0; i < starts.size(); i++) { size_t index = (i + offset) % starts.size(); Room cur{starts[index].x, starts[index].y, room_size, room_size}; @@ -154,43 +183,6 @@ namespace maze { matrix::assign($walls, WALL_VALUE); } - void Builder::divide(Point start, Point end) { - for(matrix::line it{start, end}; it.next();) { - $walls[it.y][it.x] = 0; - $walls[it.y+1][it.x] = 0; - } - } - - void Builder::hunt_and_kill(Point on) { - if($rooms.size() > 0) place_rooms(); - - while(!complete($walls, $width, $height)) { - auto n = neighbors($walls, on); - - if(n.size() == 0) { - // no neighbors, must be dead end - add_dead_end(on); - auto t = find_coord($walls, $width, $height); - on = t.first; - $walls[on.y][on.x] = 0; - size_t row = (on.y + t.second.y) / 2; - size_t col = (on.x + t.second.x) / 2; - $walls[row][col] = 0; - } else { - // found neighbors, pick random one - auto nb = n[Random::abs(size_t(0), n.size() - 1)]; - $walls[nb.y][nb.x] = 0; - - size_t row = (nb.y + on.y) / 2; - size_t col = (nb.x + on.x) / 2; - $walls[row][col] = 0; - on = nb; - } - } - - if($rooms.size() > 0) place_rooms(); - } - void Builder::place_rooms() { for(auto& room : $rooms) { for(matrix::rectangle it{$walls, room.x, room.y, room.width, room.height}; it.next();) { @@ -199,24 +191,6 @@ namespace maze { } } - void Builder::inner_donut(float outer_rad, float inner_rad) { - size_t x = $width / 2; - size_t y = $height / 2; - - for(matrix::circle it{$walls, {x, y}, outer_rad}; it.next();) - { - for(int x = it.left; x < it.right; x++) { - $walls[it.y][x] = 0; - } - } - - for(matrix::circle it{$walls, {x, y}, inner_rad}; it.next();) - { - for(int x = it.left; x < it.right; x++) { - $walls[it.y][x] = 1; - } - } - } void Builder::remove_dead_ends() { dbc::check($dead_ends.size() > 0, "you have to run an algo first, no dead_ends to remove"); @@ -231,42 +205,6 @@ namespace maze { } } - void Builder::dump(const std::string& msg, bool path_too) { - Matrix wall_copy = $walls; - - // mark the rooms too, but not if pathing - if(!path_too) { - for(auto& room : $rooms) { - for(matrix::rectangle it{wall_copy, room.x, room.y, room.width, room.height}; - it.next();) - { - if(wall_copy[it.y][it.x] == 0) { - wall_copy[it.y][it.x] = ROOM_SPACE_VALUE; - } - } - } - } - - // mark dead ends - for(auto at : $dead_ends) { - // don't mark dead ends if there's something else there - wall_copy[at.y][at.x] = DEAD_END_VALUE; - } - - for(auto [at, _] : $doors) { - wall_copy[at.y][at.x] = DOOR_VALUE; - } - - if(path_too) { - for(matrix::each_cell it{wall_copy}; it.next();) { - if(wall_copy[it.y][it.x] == SPACE_VALUE) { - wall_copy[it.y][it.x] = $pathing.$paths[it.y][it.x]; - } - } - } - - matrix::dump(msg, wall_copy); - } void Builder::enclose() { for(matrix::perimeter it{0, 0, $width, $height}; it.next();) { @@ -276,30 +214,6 @@ namespace maze { } } - void Builder::open_box(size_t outer_size) { - size_t center_x = $width / 2; - size_t center_y = $height / 2; - - // compensate for the box's border now - outer_size++; - - // this can't be right but for now it's working - size_t x = center_x - outer_size; - size_t y = center_y - outer_size; - // BUG: is the + 1 here because the bug in perimeter - size_t width = (outer_size * 2) + 1; - size_t height = (outer_size * 2) + 1; - - for(matrix::perimeter p{x, y, width, height}; p.next();) { - for(matrix::compass it{$walls, p.x, p.y}; it.next();) { - if($ends_map.contains({it.x, it.y})) { - $walls[y][x] = 0; - break; - } - } - } - } - bool Builder::valid_door(size_t x, size_t y) { return (space_available(x, y - 1) && space_available(x, y + 1)) // north south || (space_available(x - 1, y) && space_available(x + 1, y)); @@ -345,24 +259,6 @@ namespace maze { } } - void Builder::inner_box(size_t outer_size, size_t inner_size) { - size_t x = matrix::width($walls) / 2; - size_t y = matrix::height($walls) / 2; - - for(matrix::box it{$walls, x, y, outer_size}; it.next();) - { - $walls[it.y][it.x] = 0; - } - - for(matrix::box it{$walls, x, y, inner_size}; it.next();) - { - $walls[it.y][it.x] = 1; - } - - // make a fake room that blocks others - $no_rooms_region = {x - outer_size, y - outer_size, outer_size * 2 + 1, outer_size * 2 + 1}; - } - void Builder::add_dead_end(Point at) { // doing this ensures no dupes, if it's !inserted then it already existed auto [_, inserted] = $ends_map.insert_or_assign(at, true); @@ -466,6 +362,84 @@ namespace maze { return validate(); } + + + + + + + + + /// scripting and shapes + + void Builder::inner_box(size_t outer_size, size_t inner_size) { + size_t x = matrix::width($walls) / 2; + size_t y = matrix::height($walls) / 2; + + for(matrix::box it{$walls, x, y, outer_size}; it.next();) + { + $walls[it.y][it.x] = 0; + } + + for(matrix::box it{$walls, x, y, inner_size}; it.next();) + { + $walls[it.y][it.x] = 1; + } + + // make a fake room that blocks others + $no_rooms_region = {x - outer_size, y - outer_size, outer_size * 2 + 1, outer_size * 2 + 1}; + } + + void Builder::open_box(size_t outer_size) { + size_t center_x = $width / 2; + size_t center_y = $height / 2; + + // compensate for the box's border now + outer_size++; + + // this can't be right but for now it's working + size_t x = center_x - outer_size; + size_t y = center_y - outer_size; + // BUG: is the + 1 here because the bug in perimeter + size_t width = (outer_size * 2) + 1; + size_t height = (outer_size * 2) + 1; + + for(matrix::perimeter p{x, y, width, height}; p.next();) { + for(matrix::compass it{$walls, p.x, p.y}; it.next();) { + if($ends_map.contains({it.x, it.y})) { + $walls[y][x] = 0; + break; + } + } + } + } + + void Builder::divide(Point start, Point end) { + for(matrix::line it{start, end}; it.next();) { + $walls[it.y][it.x] = 0; + $walls[it.y+1][it.x] = 0; + } + } + + void Builder::inner_donut(float outer_rad, float inner_rad) { + size_t x = $width / 2; + size_t y = $height / 2; + + for(matrix::circle it{$walls, {x, y}, outer_rad}; it.next();) + { + for(int x = it.left; x < it.right; x++) { + $walls[it.y][x] = 0; + } + } + + for(matrix::circle it{$walls, {x, y}, inner_rad}; it.next();) + { + for(int x = it.left; x < it.right; x++) { + $walls[it.y][x] = 1; + } + } + } + std::pair script(Map& map, nlohmann::json& config) { maze::Builder maze(map); @@ -506,4 +480,41 @@ namespace maze { return {maze, valid}; } + + void Builder::dump(const std::string& msg, bool path_too) { + Matrix wall_copy = $walls; + + // mark the rooms too, but not if pathing + if(!path_too) { + for(auto& room : $rooms) { + for(matrix::rectangle it{wall_copy, room.x, room.y, room.width, room.height}; + it.next();) + { + if(wall_copy[it.y][it.x] == 0) { + wall_copy[it.y][it.x] = ROOM_SPACE_VALUE; + } + } + } + } + + // mark dead ends + for(auto at : $dead_ends) { + // don't mark dead ends if there's something else there + wall_copy[at.y][at.x] = DEAD_END_VALUE; + } + + for(auto [at, _] : $doors) { + wall_copy[at.y][at.x] = DOOR_VALUE; + } + + if(path_too) { + for(matrix::each_cell it{wall_copy}; it.next();) { + if(wall_copy[it.y][it.x] == SPACE_VALUE) { + wall_copy[it.y][it.x] = $pathing.$paths[it.y][it.x]; + } + } + } + + matrix::dump(msg, wall_copy); + } } diff --git a/src/algos/maze.hpp b/src/algos/maze.hpp index 39bc41f..980ee38 100644 --- a/src/algos/maze.hpp +++ b/src/algos/maze.hpp @@ -30,6 +30,8 @@ namespace maze { void clear(); void hunt_and_kill(Point on={1,1}); + bool hunt_next(Point& on, Point& found); + Point hak_step(Point from, Point to); void place_rooms(); void enclose(); void randomize_rooms(size_t room_size);