With some help from chat I sorted out how to randomize rooms without overlap.

This commit is contained in:
Zed A. Shaw 2026-03-08 03:28:49 -04:00
parent 47c0d4a5f0
commit fb41c153c1
6 changed files with 126 additions and 52 deletions

View file

@ -7,7 +7,6 @@
using std::string;
using matrix::Matrix;
constexpr const int ROOM_SIZE=1;
namespace maze {
inline size_t rand(size_t i, size_t j) {
@ -95,28 +94,37 @@ namespace maze {
dbc::sentinel("failed to find coord?");
}
void Builder::randomize_rooms() {
bool Builder::room_should_exist(Room& room) {
if(!matrix::inbounds($walls, room.x, room.y) ||
!matrix::inbounds($walls, room.x + room.width, room.y + room.height))
{
return false;
}
for(auto& other : $rooms) {
if(room.overlaps(other)) return false;
}
// it's in the map and doesn't collide with another room
return true;
}
void Builder::randomize_rooms(size_t room_size) {
// use those dead ends to randomly place rooms
for(auto at : $dead_ends) {
// moving by +1 can randomly surround rooms with walls or not
size_t offset = Random::uniform(0,1);
Room cur{at.x+offset, at.y+offset, ROOM_SIZE, ROOM_SIZE};
Room cur{at.x+offset, at.y+offset, room_size, room_size};
bool selected = Random::uniform(0,1) == 0;
// if it's out of bounds skip it
if(!matrix::inbounds($walls, cur.x, cur.y)
|| !matrix::inbounds($walls, cur.x + cur.width, cur.y + cur.height))
{
continue;
}
// randomly select 2/3rd
if(Random::uniform(0,2) > 0) {
if(selected && room_should_exist(cur)) {
$rooms.push_back(cur);
}
}
}
void Builder::init() {
void Builder::clear() {
matrix::assign($walls, WALL_VALUE);
}
@ -135,7 +143,7 @@ namespace maze {
if(n.size() == 0) {
// no neighbors, must be dead end
$dead_ends.push_back(on);
add_dead_end(on);
auto t = find_coord($walls);
on = t.first;
$walls[on.y][on.x] = 0;
@ -164,7 +172,7 @@ namespace maze {
void Builder::place_rooms() {
for(auto& room : $rooms) {
for(matrix::box it{$walls, room.x, room.y, room.width}; it.next();) {
for(matrix::rectangle it{$walls, room.x, room.y, room.width, room.height}; it.next();) {
$walls[it.y][it.x] = 0;
}
}
@ -201,6 +209,7 @@ namespace maze {
int diff_x = at.x - it.x;
int diff_y = at.y - it.y;
$walls[at.y + diff_y][at.x + diff_x] = 0;
break;
}
}
}
@ -211,10 +220,10 @@ namespace maze {
// mark the rooms too
for(auto& room : $rooms) {
for(matrix::box it{wall_copy, room.x, room.y, room.width};
for(matrix::rectangle it{wall_copy, room.x, room.y, room.width, room.height};
it.next();)
{
if(wall_copy[it.y][it.x] == 0) {
if(wall_copy[it.y][it.x] == 0 && wall_copy[it.y][it.x] != 3) {
wall_copy[it.y][it.x] = WALL_PATH_LIMIT;
}
}
@ -222,6 +231,7 @@ namespace maze {
// 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]=32;
}
@ -229,6 +239,7 @@ namespace maze {
}
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}
}};
@ -253,24 +264,23 @@ namespace maze {
});
}
void Builder::open_box(size_t outer_size, size_t inner_size) {
void Builder::open_box(size_t outer_size) {
size_t center_x = matrix::width($walls) / 2;
size_t center_y = matrix::height($walls) / 2;
// this can't be right but for now it's working
size_t x = center_x - outer_size - 1;
size_t y = center_y - outer_size - 1;
size_t width = (outer_size + 1) * 2 + 1;
size_t height = (outer_size + 1) * 2 + 1;
// compensate for the box's border now
outer_size++;
std::unordered_map<Point, bool> ends;
for(auto& at : $dead_ends) {
ends.insert_or_assign(at, true);
}
// 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;
perimeter(x, y, width, height, [&](size_t x, size_t y) {
for(matrix::compass it{$walls, x, y}; it.next();) {
if(ends.contains({it.x, it.y})) {
if($ends_map.contains({it.x, it.y})) {
$walls[y][x] = 0;
break;
}
@ -282,7 +292,6 @@ namespace maze {
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;
@ -293,4 +302,12 @@ namespace maze {
$walls[it.y][it.x] = 1;
}
}
void Builder::add_dead_end(Point at) {
auto [_, inserted] = $ends_map.insert_or_assign(at, true);
if(inserted) {
$dead_ends.push_back(at);
}
}
}

View file

@ -9,25 +9,28 @@ namespace maze {
Matrix& $walls;
std::vector<Room>& $rooms;
std::vector<Point>& $dead_ends;
std::unordered_map<Point, bool> $ends_map;
Builder(Map& map) :
$walls(map.$walls), $rooms(map.$rooms), $dead_ends(map.$dead_ends)
{
init();
clear();
}
void init();
void clear();
void hunt_and_kill(Point on={1,1});
void place_rooms();
void ensure_doors();
void enclose();
void randomize_rooms();
void randomize_rooms(size_t room_size);
void inner_donut(float outer_rad, float inner_rad);
void inner_box(size_t outer_size, size_t inner_size);
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, size_t inner_size);
void open_box(size_t outer_size);
void add_dead_end(Point at);
bool room_should_exist(Room& room);
};
}