Initial fix for large center rooms not having entry exit.
This commit is contained in:
parent
da7f24c126
commit
47c0d4a5f0
8 changed files with 130 additions and 85 deletions
2
Makefile
2
Makefile
|
|
@ -37,7 +37,7 @@ tracy_build:
|
||||||
meson compile -j 10 -C builddir
|
meson compile -j 10 -C builddir
|
||||||
|
|
||||||
test: build
|
test: build
|
||||||
./builddir/runtests -d yes
|
./builddir/runtests -d yes "[mazes]"
|
||||||
|
|
||||||
run: build test
|
run: build test
|
||||||
ifeq '$(OS)' 'Windows_NT'
|
ifeq '$(OS)' 'Windows_NT'
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
// #define DEBUG_CYCLES 1
|
// #define DEBUG_CYCLES 1
|
||||||
|
|
||||||
namespace ai {
|
namespace ai {
|
||||||
|
|
||||||
using namespace nlohmann;
|
using namespace nlohmann;
|
||||||
using namespace dbc;
|
using namespace dbc;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
using std::string;
|
using std::string;
|
||||||
using matrix::Matrix;
|
using matrix::Matrix;
|
||||||
|
|
||||||
|
constexpr const int ROOM_SIZE=1;
|
||||||
|
|
||||||
namespace maze {
|
namespace maze {
|
||||||
inline size_t rand(size_t i, size_t j) {
|
inline size_t rand(size_t i, size_t j) {
|
||||||
if(i < j) {
|
if(i < j) {
|
||||||
|
|
@ -18,7 +20,6 @@ namespace maze {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool complete(Matrix& maze) {
|
inline bool complete(Matrix& maze) {
|
||||||
size_t width = matrix::width(maze);
|
size_t width = matrix::width(maze);
|
||||||
size_t height = matrix::height(maze);
|
size_t height = matrix::height(maze);
|
||||||
|
|
@ -97,9 +98,19 @@ namespace maze {
|
||||||
void Builder::randomize_rooms() {
|
void Builder::randomize_rooms() {
|
||||||
// use those dead ends to randomly place rooms
|
// use those dead ends to randomly place rooms
|
||||||
for(auto at : $dead_ends) {
|
for(auto at : $dead_ends) {
|
||||||
if(Random::uniform(0,1)) {
|
// moving by +1 can randomly surround rooms with walls or not
|
||||||
size_t offset = Random::uniform(0,1);
|
size_t offset = Random::uniform(0,1);
|
||||||
Room cur{at.x+offset, at.y+offset, 1, 1};
|
Room cur{at.x+offset, at.y+offset, ROOM_SIZE, ROOM_SIZE};
|
||||||
|
|
||||||
|
// 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) {
|
||||||
$rooms.push_back(cur);
|
$rooms.push_back(cur);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -117,15 +128,13 @@ namespace maze {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Builder::hunt_and_kill(Point on) {
|
void Builder::hunt_and_kill(Point on) {
|
||||||
for(auto& room : $rooms) {
|
if($rooms.size() > 0) place_rooms();
|
||||||
for(matrix::box it{$walls, room.x, room.y, room.width}; it.next();) {
|
|
||||||
$walls[it.y][it.x] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while(!complete($walls)) {
|
while(!complete($walls)) {
|
||||||
auto n = neighbors($walls, on);
|
auto n = neighbors($walls, on);
|
||||||
|
|
||||||
if(n.size() == 0) {
|
if(n.size() == 0) {
|
||||||
|
// no neighbors, must be dead end
|
||||||
$dead_ends.push_back(on);
|
$dead_ends.push_back(on);
|
||||||
auto t = find_coord($walls);
|
auto t = find_coord($walls);
|
||||||
on = t.first;
|
on = t.first;
|
||||||
|
|
@ -134,6 +143,7 @@ namespace maze {
|
||||||
size_t col = (on.x + t.second.x) / 2;
|
size_t col = (on.x + t.second.x) / 2;
|
||||||
$walls[row][col] = 0;
|
$walls[row][col] = 0;
|
||||||
} else {
|
} else {
|
||||||
|
// found neighbors, pick random one
|
||||||
auto nb = n[rand(size_t(0), n.size() - 1)];
|
auto nb = n[rand(size_t(0), n.size() - 1)];
|
||||||
$walls[nb.y][nb.x] = 0;
|
$walls[nb.y][nb.x] = 0;
|
||||||
|
|
||||||
|
|
@ -144,23 +154,22 @@ namespace maze {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto at : $dead_ends) {
|
if($rooms.size() > 0) ensure_doors();
|
||||||
for(auto& room : $rooms) {
|
enclose();
|
||||||
Point room_ul{room.x - room.width - 1, room.y - room.height - 1};
|
}
|
||||||
Point room_lr{room.x + room.width - 1, room.y + room.height - 1};
|
|
||||||
|
|
||||||
if(at.x >= room_ul.x && at.y >= room_ul.y &&
|
void Builder::ensure_doors() {
|
||||||
at.x <= room_lr.x && at.y <= room_lr.y)
|
// NEED TO REWRITE
|
||||||
{
|
}
|
||||||
for(matrix::compass it{$walls, at.x, at.y}; it.next();) {
|
|
||||||
if($walls[it.y][it.x] == 1) {
|
void Builder::place_rooms() {
|
||||||
$walls[it.y][it.x] = 0;
|
for(auto& room : $rooms) {
|
||||||
break;
|
for(matrix::box it{$walls, room.x, room.y, room.width}; it.next();) {
|
||||||
}
|
$walls[it.y][it.x] = 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enclose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Builder::inner_donut(float outer_rad, float inner_rad) {
|
void Builder::inner_donut(float outer_rad, float inner_rad) {
|
||||||
|
|
@ -184,24 +193,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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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");
|
||||||
for(auto at : $dead_ends) {
|
for(auto at : $dead_ends) {
|
||||||
|
|
@ -216,6 +207,90 @@ namespace maze {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Builder::dump(const std::string& msg) {
|
void Builder::dump(const std::string& msg) {
|
||||||
matrix::dump(msg, $walls);
|
auto wall_copy = $walls;
|
||||||
|
|
||||||
|
// mark the rooms too
|
||||||
|
for(auto& room : $rooms) {
|
||||||
|
for(matrix::box it{wall_copy, room.x, room.y, room.width};
|
||||||
|
it.next();)
|
||||||
|
{
|
||||||
|
if(wall_copy[it.y][it.x] == 0) {
|
||||||
|
wall_copy[it.y][it.x] = WALL_PATH_LIMIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark dead ends
|
||||||
|
for(auto at : $dead_ends) {
|
||||||
|
wall_copy[at.y][at.x]=32;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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] = 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::open_box(size_t outer_size, size_t inner_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;
|
||||||
|
|
||||||
|
std::unordered_map<Point, bool> ends;
|
||||||
|
for(auto& at : $dead_ends) {
|
||||||
|
ends.insert_or_assign(at, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
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})) {
|
||||||
|
$walls[y][x] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "algos/matrix.hpp"
|
#include "algos/matrix.hpp"
|
||||||
#include "game/map.hpp"
|
#include "game/map.hpp"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace maze {
|
namespace maze {
|
||||||
|
|
||||||
|
|
||||||
struct Builder {
|
struct Builder {
|
||||||
Matrix& $walls;
|
Matrix& $walls;
|
||||||
std::vector<Room>& $rooms;
|
std::vector<Room>& $rooms;
|
||||||
|
|
@ -16,14 +16,18 @@ namespace maze {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void hunt_and_kill(Point on={1,1});
|
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
void hunt_and_kill(Point on={1,1});
|
||||||
|
void place_rooms();
|
||||||
|
void ensure_doors();
|
||||||
|
void enclose();
|
||||||
void randomize_rooms();
|
void randomize_rooms();
|
||||||
void inner_donut(float outer_rad, float inner_rad);
|
void inner_donut(float outer_rad, float inner_rad);
|
||||||
void inner_box(size_t outer_size, size_t inner_size);
|
void inner_box(size_t outer_size, size_t inner_size);
|
||||||
void divide(Point start, Point end);
|
void divide(Point start, Point end);
|
||||||
void remove_dead_ends();
|
void remove_dead_ends();
|
||||||
void dump(const std::string& msg);
|
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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,22 +120,6 @@ void Map::init_tiles() {
|
||||||
$tiles = $walls;
|
$tiles = $walls;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Map::enclose() {
|
|
||||||
// wraps the outside edge with solid walls
|
|
||||||
std::array<Point, 4> starts{{
|
|
||||||
{0,0}, {$width-1, 0}, {$width-1, $height-1}, {0, $height-1}
|
|
||||||
}};
|
|
||||||
|
|
||||||
std::array<Point, 4> ends{{
|
|
||||||
{$width-1, 0}, {$width-1, $height-1}, {0, $height-1}, {0,0},
|
|
||||||
}};
|
|
||||||
|
|
||||||
for(size_t i = 0; i < starts.size(); i++) {
|
|
||||||
for(matrix::line it{starts[i], ends[i]}; it.next();) {
|
|
||||||
$walls[it.y][it.x] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Map::add_room(Room &room) {
|
void Map::add_room(Room &room) {
|
||||||
$rooms.push_back(room);
|
$rooms.push_back(room);
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,6 @@ public:
|
||||||
Point map_to_camera(const Point &loc, const Point &cam_orig);
|
Point map_to_camera(const Point &loc, const Point &cam_orig);
|
||||||
Point center_camera(const Point &around, size_t view_x, size_t view_y);
|
Point center_camera(const Point &around, size_t view_x, size_t view_y);
|
||||||
|
|
||||||
void enclose();
|
|
||||||
|
|
||||||
void dump(int show_x=-1, int show_y=-1);
|
void dump(int show_x=-1, int show_y=-1);
|
||||||
bool INVARIANT();
|
bool INVARIANT();
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,6 @@ void WorldBuilder::generate_map() {
|
||||||
|
|
||||||
maze.hunt_and_kill();
|
maze.hunt_and_kill();
|
||||||
|
|
||||||
$map.enclose();
|
|
||||||
$map.init_tiles();
|
$map.init_tiles();
|
||||||
|
|
||||||
stylize_rooms();
|
stylize_rooms();
|
||||||
|
|
|
||||||
|
|
@ -18,30 +18,21 @@ TEST_CASE("hunt-and-kill", "[mazes]") {
|
||||||
|
|
||||||
maze.randomize_rooms();
|
maze.randomize_rooms();
|
||||||
maze.hunt_and_kill();
|
maze.hunt_and_kill();
|
||||||
|
maze.dump("ROOM MAZE");
|
||||||
|
|
||||||
REQUIRE(map.$dead_ends.size() > 0);
|
REQUIRE(map.$dead_ends.size() > 0);
|
||||||
REQUIRE(map.$rooms.size() > 0);
|
REQUIRE(map.$rooms.size() > 0);
|
||||||
|
|
||||||
for(auto& room : maze.$rooms) {
|
|
||||||
for(matrix::box it{maze.$walls, room.x, room.y, room.width};
|
|
||||||
it.next();)
|
|
||||||
{
|
|
||||||
maze.$walls[it.y][it.x] = WALL_PATH_LIMIT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("hunt-and-kill box", "[mazes]") {
|
TEST_CASE("hunt-and-kill box", "[mazes]") {
|
||||||
Map map(21, 21);
|
Map map(21, 21);
|
||||||
maze::Builder maze(map);
|
maze::Builder maze(map);
|
||||||
|
|
||||||
maze.inner_box(5, 3);
|
maze.inner_box(5, 4);
|
||||||
maze.hunt_and_kill();
|
maze.hunt_and_kill();
|
||||||
|
maze.open_box(5, 4);
|
||||||
|
|
||||||
for(auto at : maze.$dead_ends) {
|
maze.dump("INNER BOX");
|
||||||
maze.$walls[at.y][at.x]=32;
|
|
||||||
}
|
|
||||||
// maze.dump("INNER BOX");
|
|
||||||
|
|
||||||
REQUIRE(maze.$rooms.size() == 0);
|
REQUIRE(maze.$rooms.size() == 0);
|
||||||
}
|
}
|
||||||
|
|
@ -53,9 +44,6 @@ TEST_CASE("hunt-and-kill ring", "[mazes]") {
|
||||||
maze.inner_donut(5.5, 3.5);
|
maze.inner_donut(5.5, 3.5);
|
||||||
maze.hunt_and_kill();
|
maze.hunt_and_kill();
|
||||||
|
|
||||||
for(auto at : maze.$dead_ends) {
|
|
||||||
maze.$walls[at.y][at.x]=32;
|
|
||||||
}
|
|
||||||
// maze.dump("INNER RING");
|
// maze.dump("INNER RING");
|
||||||
|
|
||||||
REQUIRE(maze.$rooms.size() == 0);
|
REQUIRE(maze.$rooms.size() == 0);
|
||||||
|
|
@ -68,9 +56,6 @@ TEST_CASE("hunt-and-kill fissure", "[mazes]") {
|
||||||
maze.divide({3,3}, {19,18});
|
maze.divide({3,3}, {19,18});
|
||||||
maze.hunt_and_kill();
|
maze.hunt_and_kill();
|
||||||
|
|
||||||
for(auto at : maze.$dead_ends) {
|
|
||||||
maze.$walls[at.y][at.x]=32;
|
|
||||||
}
|
|
||||||
// maze.dump("FISSURE MAZE");
|
// maze.dump("FISSURE MAZE");
|
||||||
|
|
||||||
REQUIRE(maze.$rooms.size() == 0);
|
REQUIRE(maze.$rooms.size() == 0);
|
||||||
|
|
@ -83,8 +68,8 @@ TEST_CASE("hunt-and-kill no-dead-ends", "[mazes]") {
|
||||||
maze::Builder maze(map);
|
maze::Builder maze(map);
|
||||||
|
|
||||||
maze.hunt_and_kill();
|
maze.hunt_and_kill();
|
||||||
|
|
||||||
maze.remove_dead_ends();
|
maze.remove_dead_ends();
|
||||||
|
maze.enclose();
|
||||||
|
|
||||||
// maze.dump("NO DEAD ENDS");
|
// maze.dump("NO DEAD ENDS");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue