#include #include #include #include "algos/matrix.hpp" #include "algos/rand.hpp" #include "constants.hpp" #include "algos/maze.hpp" #include "algos/stats.hpp" #define DUMP 0 using std::string; using matrix::Matrix; using namespace fuc2; namespace mazes_tests { void test_hunt_and_kill() { Map map(21, 21); maze::Builder maze(map); maze.hunt_and_kill(); CHECK(maze.repair() == true); if(DUMP) maze.dump("BASIC MAZE"); maze.randomize_rooms(ROOM_SIZE); maze.hunt_and_kill(); maze.place_doors(); CHECK(maze.repair() == true); if(DUMP) maze.dump("ROOM MAZE"); CHECK(map.$dead_ends.size() > 0); CHECK(map.$rooms.size() > 0); } void test_hunt_and_kill_box() { for(int i = 25; i < 65; i += 2) { Map map(i, i); maze::Builder maze(map); maze.hunt_and_kill(); maze.clear(); maze.inner_box(6, 4); maze.randomize_rooms(ROOM_SIZE); maze.hunt_and_kill(); maze.open_box(6); maze.place_doors(); auto valid = maze.repair(); if(i == 41 && DUMP) { maze.dump(valid ? "INNER BOX" : "FAILED BOX"); } } } void test_hunt_and_kill_ring() { Map map(21, 21); maze::Builder maze(map); maze.inner_donut(5.5, 3.5); maze.hunt_and_kill(); CHECK(maze.repair() == true); if(DUMP) maze.dump("INNER RING"); CHECK(maze.$rooms.size() == 0); } void test_hunt_and_kill_fissure() { Map map(21, 21); maze::Builder maze(map); maze.divide({3,3}, {19,18}); maze.hunt_and_kill(); CHECK(maze.repair() == true); if(DUMP) maze.dump("FISSURE MAZE"); CHECK(maze.$rooms.size() == 0); } void test_hunt_and_kill_no_dead_ends() { Map map(21, 21); maze::Builder maze(map); maze.hunt_and_kill(); maze.remove_dead_ends(); CHECK(maze.repair() == true); if(DUMP) maze.dump("NO DEAD ENDS"); } void test_hunt_and_kill_too_much() { for(int i = 25; i < 65; i += 2) { Map map(i, i); maze::Builder maze(map); maze.hunt_and_kill(); maze.randomize_rooms(ROOM_SIZE); maze.clear(); maze.inner_donut(9, 4); maze.divide({3,3}, {15,16}); maze.hunt_and_kill(); maze.place_doors(); auto valid = maze.repair(); if(i == 41 && DUMP && valid) { maze.dump("COMBINED"); } } } void test_hunt_and_kill_validator() { bool valid = true; Stats mofm; for(int i = 0; i < 10; i++) { Stats door_prob; do { Map map(33, 33); maze::Builder maze(map); maze.hunt_and_kill(); maze.clear(); maze.inner_box(6, 4); maze.randomize_rooms(ROOM_SIZE); maze.hunt_and_kill(); maze.open_box(6); maze.place_doors(); valid = maze.repair(); if(i == 9 && DUMP) { maze.dump(valid ? "VALIDATED" : "FAILED!"); } door_prob.sample(valid); } while(!valid); if(DUMP) door_prob.dump(); mofm.sample(door_prob.mean()); } if(DUMP) { fmt::println("FINAL m-of-m"); mofm.dump(); } CHECK(mofm.mean() > 0.20); } void test_hunt_and_kill_scripting() { using namespace nlohmann::literals; // go up by 2 to keep odd for(int i = 0; i < 20; i+=2) { auto script = R"( [ {"action": "hunt_and_kill"}, {"action": "clear"}, {"action": "inner_box", "data": [6, 4]}, {"action": "randomize_rooms", "data": [4]}, {"action": "divide", "data": [3, 3, 10, 10]}, {"action": "inner_donut", "data": [5.5,4.5]}, {"action": "hunt_and_kill"}, {"action": "open_box", "data": [6]}, {"action": "remove_dead_ends"}, {"action": "place_doors"} ] )"_json; Map map(23+i, 23+i); auto [maze, valid] = maze::script(map, script); if(valid) { CHECK(maze.validate() == true); CHECK(map.INVARIANT() == true); } if(DUMP) maze.dump(valid ? "SCRIPTED" : "SCRIPTED FAIL!"); } } fuc2::Set TESTS{ .name="mazes", .tests={ TEST(test_hunt_and_kill_scripting), TEST(test_hunt_and_kill_validator), TEST(test_hunt_and_kill_too_much), TEST(test_hunt_and_kill_no_dead_ends), TEST(test_hunt_and_kill_fissure), TEST(test_hunt_and_kill_ring), TEST(test_hunt_and_kill_box), TEST(test_hunt_and_kill), } }; }