From e6fcbd8dcf89c3dfb76ac42f9c06e8dbfc588199 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Thu, 12 Mar 2026 00:54:34 -0400 Subject: [PATCH] Maze gen can now more efficiently produce an interesting map that is fully pathable. --- src/algos/maze.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++--- src/algos/maze.hpp | 2 ++ tests/mazes.cpp | 16 +++++++++--- 3 files changed, 75 insertions(+), 8 deletions(-) diff --git a/src/algos/maze.cpp b/src/algos/maze.cpp index 143e862..923e517 100644 --- a/src/algos/maze.cpp +++ b/src/algos/maze.cpp @@ -215,12 +215,11 @@ namespace maze { void Builder::remove_dead_ends() { dbc::check($dead_ends.size() > 0, "you have to run an algo first, no dead_ends to remove"); + for(auto at : $dead_ends) { for(matrix::compass it{$walls, at.x, at.y}; it.next();) { - if($walls[it.y][it.x] == 0) { - int diff_x = at.x - it.x; - int diff_y = at.y - it.y; - $walls[at.y + diff_y][at.x + diff_x] = 0; + if($walls[it.y][it.x] == SPACE_VALUE) { + punch_dead_end(at.x, at.y, it.x, it.y); break; } } @@ -404,4 +403,62 @@ namespace maze { return true; } + + 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; + $walls[at_y + diff_y][at_x + diff_x] = SPACE_VALUE; + } + + 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 + std::vector removed_ends; + bool now_valid = false; + + for(auto& at : $dead_ends) { + // punch a hole for this dead end + for(matrix::compass it{$walls, at.x, at.y}; it.next();) { + if($walls[it.y][it.x] == SPACE_VALUE) { + punch_dead_end(at.x, at.y, it.x, it.y); + removed_ends.push_back({it.x, it.y}); + break; + } + } + + // 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; + } + } + + // now go back and see if we can add any back + int added_back = 0; + for(auto& at : removed_ends) { + $walls[at.y][at.x] = WALL_VALUE; + + if(!validate()) { + $walls[at.y][at.x] = SPACE_VALUE; + } else { + added_back++; + } + } + + 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 a2e6259..717fad8 100644 --- a/src/algos/maze.hpp +++ b/src/algos/maze.hpp @@ -40,5 +40,7 @@ namespace maze { bool room_should_exist(Room& room, bool allow_dupes=false); void make_doors(); bool validate(); + bool repair(); + void punch_dead_end(size_t at_x, size_t at_y, size_t x, size_t y); }; } diff --git a/tests/mazes.cpp b/tests/mazes.cpp index 85304f6..57b1943 100644 --- a/tests/mazes.cpp +++ b/tests/mazes.cpp @@ -7,7 +7,7 @@ #include "algos/maze.hpp" #include "algos/stats.hpp" -#define DUMP 1 +#define DUMP 0 using std::string; using matrix::Matrix; @@ -106,7 +106,7 @@ TEST_CASE("hunt-and-kill validator", "[mazes]") { bool valid = true; Stats mofm; - for(int i = 0; i < 100; i++) { + for(int i = 0; i < 10; i++) { Stats door_prob; do { @@ -123,16 +123,24 @@ TEST_CASE("hunt-and-kill validator", "[mazes]") { maze.enclose(); valid = maze.validate(); - if(valid && i == 99) { - maze.dump("VALIDATED"); + if(!valid) valid = maze.repair(); + + if(i == 9) { + if(valid) { + maze.dump("VALIDATED"); + } else { + matrix::dump("PATHING", maze.$paths.$paths); + } } door_prob.sample(valid); } while(!valid); + door_prob.dump(); mofm.sample(door_prob.mean()); } + fmt::println("FINAL m-of-m"); mofm.dump(); REQUIRE(mofm.mean() > 0.20); }