Better map generation and a test that re-rolls maps that aren't valid to figure out the stats of map gen failures.

This commit is contained in:
Zed A. Shaw 2026-03-11 12:31:51 -04:00
parent 8090251a71
commit f85ae8a6c6
12 changed files with 131 additions and 76 deletions

View file

@ -6,6 +6,7 @@ namespace matrix {
using Matrix = shiterator::Base<int>;
using viewport = shiterator::viewport_t<Matrix>;
using perimeter = shiterator::perimeter_t<Matrix>;
using each_cell = shiterator::each_cell_t<Matrix>;
@ -17,7 +18,7 @@ namespace matrix {
using rando_rect = shiterator::rando_rect_t<Matrix>;
using rando_rect = shiterator::rando_rect_t<Matrix>;
using rando_box = shiterator::rando_box_t<Matrix>;
using line = shiterator::line;
using line = shiterator::line_t;
void dump(const std::string &msg, Matrix &map, int show_x=-1, int show_y=-1);

View file

@ -254,32 +254,15 @@ namespace maze {
matrix::dump(msg, wall_copy);
}
void Builder::perimeter(size_t x, size_t y, size_t width, size_t height, std::function<void(size_t x, size_t y)> cb) {
// BUG: is the -1 on these an off-by-one
std::array<Point, 4> starts{{
{x,y}, {x + width-1, y}, {x + width-1, y + height-1}, {x, y + height-1}
}};
std::array<Point, 4> ends{{
{x + width-1, y}, {x + width-1, y + height-1}, {x, y + height-1}, {x,y},
}};
for(size_t i = 0; i < starts.size(); i++) {
for(matrix::line it{starts[i], ends[i]}; it.next();) {
cb(it.x, it.y);
}
}
}
void Builder::enclose() {
size_t width = matrix::width($walls);
size_t height = matrix::height($walls);
perimeter(0, 0, width, height, [&](size_t x, size_t y) {
$walls[y][x] = WALL_VALUE;
Point at{x,y};
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);
});
}
}
void Builder::open_box(size_t outer_size) {
@ -296,46 +279,66 @@ namespace maze {
size_t width = (outer_size * 2) + 1;
size_t height = (outer_size * 2) + 1;
perimeter(x, y, width, height, [&](size_t x, size_t y) {
for(matrix::compass it{$walls, x, y}; it.next();) {
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::make_doors() {
for(auto room : $rooms) {
// walk the outer wall looking for an emergent door
int has_door = 0;
int possible_doors = 0;
Point last_door{0,0};
perimeter(room.x - 1, room.y - 1, room.width + 2, room.height + 2, [&](size_t x, size_t y) {
if($walls[y][x] == SPACE_VALUE) {
last_door = {x, y};
has_door++;
}
});
matrix::perimeter it{room.x - 1, room.y - 1, room.width + 2, room.height + 2};
// if only found one then make that an actual door
if(has_door == 1) {
$doors.insert_or_assign(last_door, true);
while(it.next()) {
if($walls[it.y][it.x] == SPACE_VALUE) {
last_door = {it.x, it.y};
possible_doors++;
}
}
// no emergent door found, punch out one
perimeter(room.x, room.y, room.width, room.height, [&](size_t x, size_t y) {
if($ends_map.contains({x, y})) {
for(matrix::compass door_at{$walls, x, y}; door_at.next();) {
if($walls[door_at.y][door_at.x] == WALL_VALUE) {
$walls[door_at.y][door_at.x] = SPACE_VALUE;
$doors.insert_or_assign({size_t(door_at.x), size_t(door_at.y)}, true);
// if only found one then make that an actual door
if(possible_doors == 1) {
$doors.insert_or_assign(last_door, true);
continue;
}
// no natural door found, need to make one
it = {room.x - 1, room.y - 1, room.width + 2, room.height + 2};
last_door = {0,0};
bool found_door = false;
while(it.next()) {
for(matrix::compass near_door{$walls, it.x, it.y}; near_door.next();) {
// skip the wall parts
if($walls[near_door.y][near_door.x] == WALL_VALUE) continue;
if($ends_map.contains({near_door.x, near_door.y})) {
if(room.contains({near_door.x, near_door.y})) {
// last ditch effort is use the internal dead end
last_door = {it.x, it.y};
} else {
$walls[it.y][it.x] = SPACE_VALUE;
$doors.insert_or_assign({it.x, it.y}, true);
found_door = true;
break;
}
}
}
});
}
if(!found_door && last_door.x != 0) {
// didn't find an external door so punch one at the dead end
$walls[last_door.y][last_door.x] = SPACE_VALUE;
$doors.insert_or_assign({last_door.x, last_door.y}, true);
}
}
}
@ -376,14 +379,10 @@ namespace maze {
if(!room_should_exist(room)) return false;
}
bool bad_perimeter = false;
// BUG: this is dogshit, get rid of perimeter, I can't even spell it right it's so bad
perimeter(0, 0, width, height, [&](size_t x, size_t y) {
if($ends_map.contains({x, y})) bad_perimeter = true;
if($doors.contains({x, y})) bad_perimeter = true;
});
if(bad_perimeter) return false;
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($rooms.size() == 1) return true;
@ -393,7 +392,7 @@ namespace maze {
Point test{test_room.x, test_room.y};
$paths.set_target(test);
$paths.compute_paths($walls);
$paths.dump("AFTER COMPUTE");
// $paths.dump("AFTER COMPUTE");
$paths.clear_target(test);
for(matrix::each_cell it{$walls}; it.next();) {

View file

@ -35,7 +35,6 @@ namespace maze {
void divide(Point start, Point end);
void remove_dead_ends();
void dump(const std::string& msg);
void perimeter(size_t x, size_t y, size_t width, size_t height, std::function<void(size_t x, size_t y)> cb);
void open_box(size_t outer_size);
void add_dead_end(Point at);
bool room_should_exist(Room& room, bool allow_dupes=false);

View file

@ -396,7 +396,7 @@ namespace shiterator {
* within your Matrix, as it's assumed _you_ did that
* already.
*/
struct line {
struct line_t {
int x;
int y;
int x1;
@ -407,7 +407,7 @@ namespace shiterator {
int dy;
int error;
line(Point start, Point end) :
line_t(Point start, Point end) :
x(start.x), y(start.y),
x1(end.x), y1(end.y)
{
@ -594,6 +594,46 @@ namespace shiterator {
}
};
template<typename MAT>
struct perimeter_t {
size_t x;
size_t y;
size_t width;
size_t height;
size_t i = 0;
std::array<Point, 4> starts{{
{x,y}, {x + width-1, y}, {x + width-1, y + height-1}, {x, y + height-1}
}};
std::array<Point, 4> ends{{
{x + width-1, y}, {x + width-1, y + height-1}, {x, y + height-1}, {x,y},
}};
line_t it{starts[i], ends[i]};
bool next() {
if(i >= starts.size()) return false;
if(it.next()) {
x = it.x;
y = it.y;
return true;
} else {
x = 0;
y = 0;
i++;
if(i < starts.size()) {
it = {starts[i], ends[i]};
return true;
} else {
return false;
}
}
}
};
/*
* BROKEN: I'm actually not sure what I'm trying to
* do here yet.