diff --git a/Makefile b/Makefile index 9913705..1323a10 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ tracy_build: meson compile -j 10 -C builddir test: build - ./builddir/runtests -d yes + ./builddir/runtests -d yes "[mazes]" run: build test ifeq '$(OS)' 'Windows_NT' diff --git a/src/ai/goap.cpp b/src/ai/goap.cpp index 19f1dc4..ee2e024 100644 --- a/src/ai/goap.cpp +++ b/src/ai/goap.cpp @@ -7,7 +7,6 @@ // #define DEBUG_CYCLES 1 namespace ai { - using namespace nlohmann; using namespace dbc; diff --git a/src/algos/maze.cpp b/src/algos/maze.cpp index 5b8d6d9..b2c4744 100644 --- a/src/algos/maze.cpp +++ b/src/algos/maze.cpp @@ -7,6 +7,8 @@ using std::string; using matrix::Matrix; +constexpr const int ROOM_SIZE=1; + namespace maze { inline size_t rand(size_t i, size_t j) { if(i < j) { @@ -18,7 +20,6 @@ namespace maze { } } - inline bool complete(Matrix& maze) { size_t width = matrix::width(maze); size_t height = matrix::height(maze); @@ -97,9 +98,19 @@ namespace maze { void Builder::randomize_rooms() { // use those dead ends to randomly place rooms for(auto at : $dead_ends) { - if(Random::uniform(0,1)) { - size_t offset = Random::uniform(0,1); - Room cur{at.x+offset, at.y+offset, 1, 1}; + // 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}; + + // 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); } } @@ -117,15 +128,13 @@ namespace maze { } void Builder::hunt_and_kill(Point on) { - for(auto& room : $rooms) { - for(matrix::box it{$walls, room.x, room.y, room.width}; it.next();) { - $walls[it.y][it.x] = 0; - } - } + if($rooms.size() > 0) place_rooms(); while(!complete($walls)) { auto n = neighbors($walls, on); + if(n.size() == 0) { + // no neighbors, must be dead end $dead_ends.push_back(on); auto t = find_coord($walls); on = t.first; @@ -134,6 +143,7 @@ namespace maze { size_t col = (on.x + t.second.x) / 2; $walls[row][col] = 0; } else { + // found neighbors, pick random one auto nb = n[rand(size_t(0), n.size() - 1)]; $walls[nb.y][nb.x] = 0; @@ -144,23 +154,22 @@ namespace maze { } } - for(auto at : $dead_ends) { - for(auto& room : $rooms) { - 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($rooms.size() > 0) ensure_doors(); + enclose(); + } - if(at.x >= room_ul.x && at.y >= room_ul.y && - at.x <= room_lr.x && at.y <= room_lr.y) - { - for(matrix::compass it{$walls, at.x, at.y}; it.next();) { - if($walls[it.y][it.x] == 1) { - $walls[it.y][it.x] = 0; - break; - } - } - } + void Builder::ensure_doors() { + // NEED TO REWRITE + } + + void Builder::place_rooms() { + for(auto& room : $rooms) { + 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) { @@ -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() { dbc::check($dead_ends.size() > 0, "you have to run an algo first, no dead_ends to remove"); for(auto at : $dead_ends) { @@ -216,6 +207,90 @@ namespace maze { } 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 cb) { + std::array starts{{ + {x,y}, {x + width-1, y}, {x + width-1, y + height-1}, {x, y + height-1} + }}; + + std::array 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 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; + } } } diff --git a/src/algos/maze.hpp b/src/algos/maze.hpp index c10dd06..cad27f4 100644 --- a/src/algos/maze.hpp +++ b/src/algos/maze.hpp @@ -1,10 +1,10 @@ #pragma once #include "algos/matrix.hpp" #include "game/map.hpp" +#include namespace maze { - struct Builder { Matrix& $walls; std::vector& $rooms; @@ -16,14 +16,18 @@ namespace maze { init(); } - void hunt_and_kill(Point on={1,1}); - void init(); + void hunt_and_kill(Point on={1,1}); + void place_rooms(); + void ensure_doors(); + void enclose(); void randomize_rooms(); 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 cb); + void open_box(size_t outer_size, size_t inner_size); }; } diff --git a/src/game/map.cpp b/src/game/map.cpp index 246e599..97c500f 100644 --- a/src/game/map.cpp +++ b/src/game/map.cpp @@ -120,22 +120,6 @@ void Map::init_tiles() { $tiles = $walls; } -void Map::enclose() { - // wraps the outside edge with solid walls - std::array starts{{ - {0,0}, {$width-1, 0}, {$width-1, $height-1}, {0, $height-1} - }}; - - std::array 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) { $rooms.push_back(room); diff --git a/src/game/map.hpp b/src/game/map.hpp index 32e9ce7..b62d6ee 100644 --- a/src/game/map.hpp +++ b/src/game/map.hpp @@ -61,7 +61,6 @@ public: 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); - void enclose(); void dump(int show_x=-1, int show_y=-1); bool INVARIANT(); diff --git a/src/game/worldbuilder.cpp b/src/game/worldbuilder.cpp index ef00b93..b9912f6 100644 --- a/src/game/worldbuilder.cpp +++ b/src/game/worldbuilder.cpp @@ -55,7 +55,6 @@ void WorldBuilder::generate_map() { maze.hunt_and_kill(); - $map.enclose(); $map.init_tiles(); stylize_rooms(); diff --git a/tests/mazes.cpp b/tests/mazes.cpp index 521dab6..e7ce5cb 100644 --- a/tests/mazes.cpp +++ b/tests/mazes.cpp @@ -18,30 +18,21 @@ TEST_CASE("hunt-and-kill", "[mazes]") { maze.randomize_rooms(); maze.hunt_and_kill(); + maze.dump("ROOM MAZE"); REQUIRE(map.$dead_ends.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]") { Map map(21, 21); maze::Builder maze(map); - maze.inner_box(5, 3); + maze.inner_box(5, 4); maze.hunt_and_kill(); + maze.open_box(5, 4); - for(auto at : maze.$dead_ends) { - maze.$walls[at.y][at.x]=32; - } - // maze.dump("INNER BOX"); + maze.dump("INNER BOX"); REQUIRE(maze.$rooms.size() == 0); } @@ -53,9 +44,6 @@ TEST_CASE("hunt-and-kill ring", "[mazes]") { maze.inner_donut(5.5, 3.5); maze.hunt_and_kill(); - for(auto at : maze.$dead_ends) { - maze.$walls[at.y][at.x]=32; - } // maze.dump("INNER RING"); REQUIRE(maze.$rooms.size() == 0); @@ -68,9 +56,6 @@ TEST_CASE("hunt-and-kill fissure", "[mazes]") { maze.divide({3,3}, {19,18}); maze.hunt_and_kill(); - for(auto at : maze.$dead_ends) { - maze.$walls[at.y][at.x]=32; - } // maze.dump("FISSURE MAZE"); REQUIRE(maze.$rooms.size() == 0); @@ -83,8 +68,8 @@ TEST_CASE("hunt-and-kill no-dead-ends", "[mazes]") { maze::Builder maze(map); maze.hunt_and_kill(); - maze.remove_dead_ends(); + maze.enclose(); // maze.dump("NO DEAD ENDS"); }