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) {
|
inline bool complete(Matrix& maze, size_t width, size_t height) {
|
||||||
for(size_t row = 1; row < height; row += 2) {
|
for(size_t row = 1; row < height; row += 2) {
|
||||||
for(size_t col = 1; col < width; col += 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Point> neighborsAB(Matrix& maze, Point on) {
|
inline std::vector<Point> neighbors(Matrix& maze, Point on) {
|
||||||
std::vector<Point> result;
|
std::vector<Point> result;
|
||||||
|
|
||||||
std::array<Point, 4> points{{
|
std::array<Point, 4> points{{
|
||||||
|
|
@ -38,44 +38,74 @@ namespace maze {
|
||||||
return result;
|
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::vector<Point> result;
|
||||||
|
|
||||||
std::array<Point, 4> points{{
|
for(auto point : near_me) {
|
||||||
{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) {
|
if(maze[point.y][point.x] == WALL_VALUE) {
|
||||||
result.push_back(point);
|
result.push_back(point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::pair<Point, Point> find_coord(Matrix& maze, size_t width, size_t height) {
|
bool Builder::hunt_next(Point& on, Point& found) {
|
||||||
for(size_t y = 1; y < height; y += 2) {
|
for(size_t y = 1; y < $height; y += 2) {
|
||||||
for(size_t x = 1; x < width; x += 2) {
|
for(size_t x = 1; x < $width; x += 2) {
|
||||||
if(maze[y][x] == WALL_VALUE) {
|
if($walls[y][x] != WALL_VALUE) {
|
||||||
auto found = neighborsAB(maze, {x, y});
|
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);
|
return false;
|
||||||
dbc::sentinel("failed to find coord?");
|
}
|
||||||
|
|
||||||
|
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) {
|
bool Builder::room_should_exist(Room& room, bool allow_dupes) {
|
||||||
|
|
@ -136,7 +166,6 @@ namespace maze {
|
||||||
|
|
||||||
size_t offset = Random::uniform(0, 3);
|
size_t offset = Random::uniform(0, 3);
|
||||||
|
|
||||||
// BUG: this still accidentally merges rooms
|
|
||||||
for(size_t i = 0; i < starts.size(); i++) {
|
for(size_t i = 0; i < starts.size(); i++) {
|
||||||
size_t index = (i + offset) % starts.size();
|
size_t index = (i + offset) % starts.size();
|
||||||
Room cur{starts[index].x, starts[index].y, room_size, room_size};
|
Room cur{starts[index].x, starts[index].y, room_size, room_size};
|
||||||
|
|
@ -154,43 +183,6 @@ namespace maze {
|
||||||
matrix::assign($walls, WALL_VALUE);
|
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() {
|
void Builder::place_rooms() {
|
||||||
for(auto& room : $rooms) {
|
for(auto& room : $rooms) {
|
||||||
for(matrix::rectangle it{$walls, room.x, room.y, room.width, room.height}; it.next();) {
|
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() {
|
void Builder::remove_dead_ends() {
|
||||||
dbc::check($dead_ends.size() > 0, "you have to run an algo first, no dead_ends to remove");
|
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() {
|
void Builder::enclose() {
|
||||||
for(matrix::perimeter it{0, 0, $width, $height}; it.next();) {
|
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) {
|
bool Builder::valid_door(size_t x, size_t y) {
|
||||||
return (space_available(x, y - 1) && space_available(x, y + 1)) // north south
|
return (space_available(x, y - 1) && space_available(x, y + 1)) // north south
|
||||||
|| (space_available(x - 1, y) && space_available(x + 1, y));
|
|| (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) {
|
void Builder::add_dead_end(Point at) {
|
||||||
// doing this ensures no dupes, if it's !inserted then it already existed
|
// doing this ensures no dupes, if it's !inserted then it already existed
|
||||||
auto [_, inserted] = $ends_map.insert_or_assign(at, true);
|
auto [_, inserted] = $ends_map.insert_or_assign(at, true);
|
||||||
|
|
@ -466,6 +362,84 @@ namespace maze {
|
||||||
return validate();
|
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) {
|
std::pair<Builder, bool> script(Map& map, nlohmann::json& config) {
|
||||||
maze::Builder maze(map);
|
maze::Builder maze(map);
|
||||||
|
|
||||||
|
|
@ -506,4 +480,41 @@ namespace maze {
|
||||||
|
|
||||||
return {maze, valid};
|
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 clear();
|
||||||
void hunt_and_kill(Point on={1,1});
|
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 place_rooms();
|
||||||
void enclose();
|
void enclose();
|
||||||
void randomize_rooms(size_t room_size);
|
void randomize_rooms(size_t room_size);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue