Improvements from the Go version of the mazes.
This commit is contained in:
parent
fee925505f
commit
b62cb8fe92
2 changed files with 173 additions and 160 deletions
|
|
@ -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<Point> neighborsAB(Matrix& maze, Point on) {
|
||||
inline std::vector<Point> neighbors(Matrix& maze, Point on) {
|
||||
std::vector<Point> result;
|
||||
|
||||
std::array<Point, 4> points{{
|
||||
|
|
@ -38,44 +38,74 @@ namespace maze {
|
|||
return result;
|
||||
}
|
||||
|
||||
std::vector<Point> neighbors(Matrix& maze, Point on) {
|
||||
inline std::vector<Point> neighbor_walls(Matrix& maze, Point on) {
|
||||
auto near_me = neighbors(maze, on);
|
||||
std::vector<Point> result;
|
||||
|
||||
std::array<Point, 4> 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<Point, Point> 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;
|
||||
}
|
||||
|
||||
for(auto point : found) {
|
||||
if(maze[point.y][point.x] == 0) {
|
||||
return {{x, y}, point};
|
||||
}
|
||||
auto near_me = neighbors($walls, {x, y});
|
||||
|
||||
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<Builder, bool> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue