diff --git a/src/algos/maze.cpp b/src/algos/maze.cpp index 923e517..48e7337 100644 --- a/src/algos/maze.cpp +++ b/src/algos/maze.cpp @@ -3,6 +3,7 @@ #include "algos/rand.hpp" #include "constants.hpp" #include "algos/maze.hpp" +#include using std::string; using matrix::Matrix; @@ -18,10 +19,7 @@ namespace maze { } } - inline bool complete(Matrix& maze) { - size_t width = matrix::width(maze); - size_t height = matrix::height(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; @@ -71,10 +69,7 @@ namespace maze { return result; } - inline std::pair find_coord(Matrix& maze) { - size_t width = matrix::width(maze); - size_t height = matrix::height(maze); - + 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) { @@ -161,13 +156,13 @@ namespace maze { void Builder::hunt_and_kill(Point on) { if($rooms.size() > 0) place_rooms(); - while(!complete($walls)) { + 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); + 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; @@ -195,8 +190,8 @@ namespace maze { } void Builder::inner_donut(float outer_rad, float inner_rad) { - size_t x = matrix::width($walls) / 2; - size_t y = matrix::height($walls) / 2; + size_t x = $width / 2; + size_t y = $height / 2; for(matrix::circle it{$walls, {x, y}, outer_rad}; it.next();) { @@ -254,10 +249,7 @@ namespace maze { } void Builder::enclose() { - size_t width = matrix::width($walls); - size_t height = matrix::height($walls); - - for(matrix::perimeter it{0, 0, width, height}; it.next();) { + for(matrix::perimeter it{0, 0, $width, $height}; it.next();) { $walls[it.y][it.x] = WALL_VALUE; Point at{it.x,it.y}; if($doors.contains(at)) $doors.erase(at); @@ -265,8 +257,8 @@ namespace maze { } void Builder::open_box(size_t outer_size) { - size_t center_x = matrix::width($walls) / 2; - size_t center_y = matrix::height($walls) / 2; + size_t center_x = $width / 2; + size_t center_y = $height / 2; // compensate for the box's border now outer_size++; @@ -381,9 +373,10 @@ namespace maze { for(matrix::perimeter it{0, 0, width, height}; it.next();) { if($ends_map.contains({it.x, it.y})) return false; if($doors.contains({it.x, it.y})) return false; + if($walls[it.y][it.x] != WALL_VALUE) return false; } - if($rooms.size() == 1) return true; + if($rooms.size() <= 1) return true; // initial path test can just use one room then look for // any cells that are empty in the walls map but unpathed in the paths @@ -404,6 +397,12 @@ namespace maze { return true; } + void Builder::in_bounds(size_t x, size_t y) { + if(x > $width) { + + } + } + void Builder::punch_dead_end(size_t at_x, size_t at_y, size_t x, size_t y) { int diff_x = at_x - x; int diff_y = at_y - y; @@ -411,11 +410,10 @@ namespace maze { } bool Builder::repair() { - // possible fixes - // go through each dead end and remove until it works - // go through each room and add a door until it works - // only find rooms that are unpathable and fix them - // walk the path deleting dead ends at the end of the path + enclose(); + // if it's already valid then done + if(validate()) return true; + std::vector removed_ends; bool now_valid = false; @@ -432,9 +430,6 @@ namespace maze { // if that validates it then done if(validate()) { now_valid = true; - fmt::println("MID dead_end removed={}, total={}, %={}", - removed_ends.size(), $dead_ends.size(), - float(removed_ends.size()) / float($dead_ends.size())); break; } } @@ -454,10 +449,6 @@ namespace maze { enclose(); now_valid = validate(); - fmt::println("FINAL now_valid={} added_back={} removed={}, total={}, %={}", - now_valid, added_back, removed_ends.size() - added_back, $dead_ends.size(), - float(removed_ends.size() - added_back) / float($dead_ends.size())); - // didn't find a way to make it valid return now_valid; } diff --git a/src/algos/maze.hpp b/src/algos/maze.hpp index 717fad8..9d96182 100644 --- a/src/algos/maze.hpp +++ b/src/algos/maze.hpp @@ -6,6 +6,8 @@ namespace maze { struct Builder { + size_t $width = 0; + size_t $height = 0; Matrix& $walls; std::vector& $rooms; std::vector& $dead_ends; @@ -16,11 +18,11 @@ namespace maze { Pathing $paths; Builder(Map& map) : - $walls(map.$walls), $rooms(map.$rooms), $dead_ends(map.$dead_ends), - $paths{matrix::width(map.$walls), matrix::height(map.$walls)} + $width(map.$width), $height(map.$height), $walls(map.$walls), + $rooms(map.$rooms), $dead_ends(map.$dead_ends), $paths{$width, $height} { - dbc::check(map.$width % 2 == 1, "map width not an ODD number (perimter dead ends bug)"); - dbc::check(map.$height % 2 == 1, "map height not an ODD number (perimter dead ends bug)"); + dbc::check($width % 2 == 1, "map width not an ODD number (perimter dead ends bug)"); + dbc::check($height % 2 == 1, "map height not an ODD number (perimter dead ends bug)"); clear(); } @@ -42,5 +44,6 @@ namespace maze { bool validate(); bool repair(); void punch_dead_end(size_t at_x, size_t at_y, size_t x, size_t y); + void in_bounds(size_t x, size_t y); }; } diff --git a/tests/mazes.cpp b/tests/mazes.cpp index 57b1943..476f059 100644 --- a/tests/mazes.cpp +++ b/tests/mazes.cpp @@ -7,7 +7,7 @@ #include "algos/maze.hpp" #include "algos/stats.hpp" -#define DUMP 0 +#define DUMP 1 using std::string; using matrix::Matrix; @@ -17,11 +17,15 @@ TEST_CASE("hunt-and-kill", "[mazes]") { maze::Builder maze(map); maze.hunt_and_kill(); + REQUIRE(maze.repair() == true); + if(DUMP) maze.dump("BASIC MAZE"); maze.randomize_rooms(ROOM_SIZE); maze.hunt_and_kill(); maze.make_doors(); + REQUIRE(maze.repair() == true); + if(DUMP) maze.dump("ROOM MAZE"); REQUIRE(map.$dead_ends.size() > 0); @@ -36,14 +40,15 @@ TEST_CASE("hunt-and-kill box", "[mazes]") { maze.hunt_and_kill(); maze.clear(); maze.inner_box(6, 4); - maze.randomize_rooms(ROOM_SIZE+2); + maze.randomize_rooms(ROOM_SIZE); maze.hunt_and_kill(); maze.open_box(6); maze.make_doors(); + auto valid = maze.repair(); if(i == 41 && DUMP) { - maze.dump("INNER BOX"); + maze.dump(valid ? "INNER BOX" : "FAILED BOX"); } } } @@ -54,6 +59,7 @@ TEST_CASE("hunt-and-kill ring", "[mazes]") { maze.inner_donut(5.5, 3.5); maze.hunt_and_kill(); + REQUIRE(maze.repair() == true); if(DUMP) maze.dump("INNER RING"); @@ -66,6 +72,7 @@ TEST_CASE("hunt-and-kill fissure", "[mazes]") { maze.divide({3,3}, {19,18}); maze.hunt_and_kill(); + REQUIRE(maze.repair() == true); if(DUMP) maze.dump("FISSURE MAZE"); @@ -78,7 +85,7 @@ TEST_CASE("hunt-and-kill no-dead-ends", "[mazes]") { maze.hunt_and_kill(); maze.remove_dead_ends(); - maze.enclose(); + REQUIRE(maze.repair() == true); if(DUMP) maze.dump("NO DEAD ENDS"); } @@ -95,8 +102,9 @@ TEST_CASE("hunt-and-kill too much", "[mazes]") { maze.divide({3,3}, {15,16}); maze.hunt_and_kill(); maze.make_doors(); + auto valid = maze.repair(); - if(i == 41 && DUMP) { + if(i == 41 && DUMP && valid) { maze.dump("COMBINED"); } } @@ -120,10 +128,7 @@ TEST_CASE("hunt-and-kill validator", "[mazes]") { maze.hunt_and_kill(); maze.open_box(6); maze.make_doors(); - maze.enclose(); - - valid = maze.validate(); - if(!valid) valid = maze.repair(); + valid = maze.repair(); if(i == 9) { if(valid) { @@ -132,7 +137,6 @@ TEST_CASE("hunt-and-kill validator", "[mazes]") { matrix::dump("PATHING", maze.$paths.$paths); } } - door_prob.sample(valid); } while(!valid);