Now the rooms are better at having good doors.
This commit is contained in:
parent
908f5bfb3e
commit
8e2a691337
8 changed files with 69 additions and 70 deletions
|
|
@ -267,7 +267,7 @@ namespace maze {
|
||||||
if(path_too) {
|
if(path_too) {
|
||||||
for(matrix::each_cell it{wall_copy}; it.next();) {
|
for(matrix::each_cell it{wall_copy}; it.next();) {
|
||||||
if(wall_copy[it.y][it.x] == SPACE_VALUE) {
|
if(wall_copy[it.y][it.x] == SPACE_VALUE) {
|
||||||
wall_copy[it.y][it.x] = $paths.$paths[it.y][it.x];
|
wall_copy[it.y][it.x] = $pathing.$paths[it.y][it.x];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -309,57 +309,47 @@ namespace maze {
|
||||||
|
|
||||||
void Builder::place_doors() {
|
void Builder::place_doors() {
|
||||||
for(auto room : $rooms) {
|
for(auto room : $rooms) {
|
||||||
// walk the outer wall looking for an emergent door
|
int best_longest = 0;
|
||||||
int possible_doors = 0;
|
Point best_door{0,0};
|
||||||
Point last_door{0,0};
|
bool door_found = false;
|
||||||
|
|
||||||
|
// do an initial pathing check and if it's good then done
|
||||||
|
int longest = compute_paths(room.x, room.y);
|
||||||
|
if(longest < WALL_PATH_LIMIT && longest > int($width * 2)) continue;
|
||||||
|
|
||||||
|
// can't path out of the room, so now punch a hole until it can
|
||||||
matrix::perimeter it{room.x - 1, room.y - 1, room.width + 2, room.height + 2};
|
matrix::perimeter it{room.x - 1, room.y - 1, room.width + 2, room.height + 2};
|
||||||
|
|
||||||
while(it.next()) {
|
while(it.next()) {
|
||||||
if(space_available(it.x, it.y)) {
|
// don't use corners
|
||||||
// IN_BOUNDS CHECK, or maybe a can_place or is_available?
|
if((it.x == room.x - 1 && it.y == room.y - 1) ||
|
||||||
last_door = {it.x, it.y};
|
(it.x == room.x - 1 && it.y == room.y + 2) ||
|
||||||
possible_doors++;
|
(it.x == room.width + 2 && it.y == room.y + 2) ||
|
||||||
}
|
(it.x == room.width + 2 && it.y == room.y - 1)) {
|
||||||
}
|
|
||||||
|
|
||||||
// if only found one then make that an actual door
|
|
||||||
if(possible_doors == 1) {
|
|
||||||
$doors.insert_or_assign(last_door, true);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// no natural door found, need to make one
|
if($walls[it.y][it.x] == WALL_VALUE) {
|
||||||
it = {room.x - 1, room.y - 1, room.width + 2, room.height + 2};
|
|
||||||
last_door = {0,0};
|
|
||||||
bool found_door = false;
|
|
||||||
|
|
||||||
while(it.next()) {
|
|
||||||
for(matrix::compass near_door{$walls, it.x, it.y}; near_door.next();) {
|
|
||||||
// skip the wall parts
|
|
||||||
if(!space_available(near_door.x, near_door.y)) continue;
|
|
||||||
|
|
||||||
if($ends_map.contains({near_door.x, near_door.y})) {
|
|
||||||
if(room.contains({near_door.x, near_door.y})) {
|
|
||||||
// last ditch effort is use the internal dead end
|
|
||||||
// IN_BOUNDS_HERE
|
|
||||||
last_door = {it.x, it.y};
|
|
||||||
} else {
|
|
||||||
$walls[it.y][it.x] = SPACE_VALUE;
|
$walls[it.y][it.x] = SPACE_VALUE;
|
||||||
$doors.insert_or_assign({it.x, it.y}, true);
|
longest = compute_paths(room.x, room.y);
|
||||||
found_door = true;
|
|
||||||
break;
|
// keep track of the best door so far, which is the one with the longest path
|
||||||
}
|
if(longest != WALL_PATH_LIMIT && longest > best_longest) {
|
||||||
}
|
best_longest = longest;
|
||||||
|
best_door = {it.x, it.y};
|
||||||
}
|
}
|
||||||
|
$walls[it.y][it.x] = WALL_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!found_door && last_door.x != 0) {
|
dbc::check(best_longest < WALL_PATH_LIMIT, "bad best_longest!");
|
||||||
// didn't find an external door so punch one at the dead end
|
if(best_longest > int($width * 2)) break;
|
||||||
$walls[last_door.y][last_door.x] = SPACE_VALUE;
|
|
||||||
$doors.insert_or_assign({last_door.x, last_door.y}, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// should now have a door with the longest path
|
||||||
|
$walls[best_door.y][best_door.x] = SPACE_VALUE;
|
||||||
|
$doors.insert_or_assign(best_door, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt::println("map valid after doors? {}", validate());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Builder::inner_box(size_t outer_size, size_t inner_size) {
|
void Builder::inner_box(size_t outer_size, size_t inner_size) {
|
||||||
|
|
@ -390,6 +380,15 @@ namespace maze {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Builder::compute_paths(size_t x, size_t y) {
|
||||||
|
Point test{x, y};
|
||||||
|
$pathing.set_target(test);
|
||||||
|
int longest = $pathing.compute_paths($walls);
|
||||||
|
// $pathing.dump("AFTER COMPUTE");
|
||||||
|
$pathing.clear_target(test);
|
||||||
|
return longest;
|
||||||
|
}
|
||||||
|
|
||||||
bool Builder::validate() {
|
bool Builder::validate() {
|
||||||
size_t width = matrix::width($walls);
|
size_t width = matrix::width($walls);
|
||||||
size_t height = matrix::height($walls);
|
size_t height = matrix::height($walls);
|
||||||
|
|
@ -410,15 +409,11 @@ namespace maze {
|
||||||
// initial path test can just use one room then look for
|
// initial path test can just use one room then look for
|
||||||
// any cells that are empty in the walls map but unpathed in the paths
|
// any cells that are empty in the walls map but unpathed in the paths
|
||||||
Room test_room = $rooms.at(rand(0, $rooms.size() - 1));
|
Room test_room = $rooms.at(rand(0, $rooms.size() - 1));
|
||||||
Point test{test_room.x, test_room.y};
|
compute_paths(test_room.x, test_room.y);
|
||||||
$paths.set_target(test);
|
|
||||||
$paths.compute_paths($walls);
|
|
||||||
// $paths.dump("AFTER COMPUTE");
|
|
||||||
$paths.clear_target(test);
|
|
||||||
|
|
||||||
for(matrix::each_cell it{$walls}; it.next();) {
|
for(matrix::each_cell it{$walls}; it.next();) {
|
||||||
if($walls[it.y][it.x] == SPACE_VALUE &&
|
if($walls[it.y][it.x] == SPACE_VALUE &&
|
||||||
$paths.$paths[it.y][it.x] == WALL_PATH_LIMIT) {
|
$pathing.$paths[it.y][it.x] == WALL_PATH_LIMIT) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -430,8 +425,6 @@ namespace maze {
|
||||||
bool in_bounds = matrix::inbounds($walls, x, y);
|
bool in_bounds = matrix::inbounds($walls, x, y);
|
||||||
bool is_space = $walls[y][x] == SPACE_VALUE;
|
bool is_space = $walls[y][x] == SPACE_VALUE;
|
||||||
bool not_perimeter = x > 0 && y > 0 && x < $width - 2 && y < $height - 2;
|
bool not_perimeter = x > 0 && y > 0 && x < $width - 2 && y < $height - 2;
|
||||||
dbc::check(x != $width, "bad x");
|
|
||||||
dbc::check(y != $height, "bad y");
|
|
||||||
return in_bounds && is_space && not_perimeter;
|
return in_bounds && is_space && not_perimeter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,11 @@ namespace maze {
|
||||||
Room $no_rooms_region{0,0,0,0};
|
Room $no_rooms_region{0,0,0,0};
|
||||||
// BUG: instead of bool map it to the room?
|
// BUG: instead of bool map it to the room?
|
||||||
std::unordered_map<Point, bool> $doors;
|
std::unordered_map<Point, bool> $doors;
|
||||||
Pathing $paths;
|
Pathing $pathing;
|
||||||
|
|
||||||
Builder(Map& map) :
|
Builder(Map& map) :
|
||||||
$width(map.$width), $height(map.$height), $walls(map.$walls),
|
$width(map.$width), $height(map.$height), $walls(map.$walls),
|
||||||
$rooms(map.$rooms), $dead_ends(map.$dead_ends), $paths{$width, $height}
|
$rooms(map.$rooms), $dead_ends(map.$dead_ends), $pathing{$width, $height}
|
||||||
{
|
{
|
||||||
dbc::check($width % 2 == 1, "map width not an ODD number (perimter dead ends bug)");
|
dbc::check($width % 2 == 1, "map width not an ODD number (perimter dead ends bug)");
|
||||||
dbc::check($height % 2 == 1, "map height not an ODD number (perimter dead ends bug)");
|
dbc::check($height % 2 == 1, "map height not an ODD number (perimter dead ends bug)");
|
||||||
|
|
@ -46,6 +46,7 @@ namespace maze {
|
||||||
bool repair();
|
bool repair();
|
||||||
void punch_dead_end(size_t at_x, size_t at_y, size_t x, size_t y);
|
void punch_dead_end(size_t at_x, size_t at_y, size_t x, size_t y);
|
||||||
bool space_available(size_t x, size_t y);
|
bool space_available(size_t x, size_t y);
|
||||||
|
int compute_paths(size_t x, size_t y);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::pair<Builder, bool> script(Map& map, nlohmann::json& config);
|
std::pair<Builder, bool> script(Map& map, nlohmann::json& config);
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ inline void add_neighbors(PointList &neighbors, Matrix &closed, size_t y, size_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pathing::compute_paths(Matrix &walls) {
|
int Pathing::compute_paths(Matrix &walls) {
|
||||||
INVARIANT();
|
INVARIANT();
|
||||||
dbc::check(walls[0].size() == $width,
|
dbc::check(walls[0].size() == $width,
|
||||||
$F("Pathing::compute_paths called with walls.width={} but paths $width={}", walls[0].size(), $width));
|
$F("Pathing::compute_paths called with walls.width={} but paths $width={}", walls[0].size(), $width));
|
||||||
|
|
@ -22,44 +22,46 @@ void Pathing::compute_paths(Matrix &walls) {
|
||||||
dbc::check(walls.size() == $height,
|
dbc::check(walls.size() == $height,
|
||||||
$F("Pathing::compute_paths called with walls.height={} but paths $height={}", walls[0].size(), $height));
|
$F("Pathing::compute_paths called with walls.height={} but paths $height={}", walls[0].size(), $height));
|
||||||
|
|
||||||
// Initialize the new array with every pixel at limit distance
|
// Initialize the new array with every cell at limit distance
|
||||||
matrix::assign($paths, WALL_PATH_LIMIT);
|
matrix::assign($paths, WALL_PATH_LIMIT);
|
||||||
|
|
||||||
Matrix closed = walls;
|
Matrix closed = walls;
|
||||||
PointList starting_pixels;
|
PointList starting_cells;
|
||||||
PointList open_pixels;
|
PointList open_cells;
|
||||||
|
|
||||||
// First pass: Add starting pixels and put them in closed
|
// First pass: Add starting cells and put them in closed
|
||||||
for(size_t counter = 0; counter < $height * $width; counter++) {
|
for(size_t counter = 0; counter < $height * $width; counter++) {
|
||||||
size_t x = counter % $width;
|
size_t x = counter % $width;
|
||||||
size_t y = counter / $width;
|
size_t y = counter / $width;
|
||||||
if($input[y][x] == 0) {
|
if($input[y][x] == 0) {
|
||||||
$paths[y][x] = 0;
|
$paths[y][x] = 0;
|
||||||
closed[y][x] = 1;
|
closed[y][x] = 1;
|
||||||
starting_pixels.emplace_back(x,y);
|
starting_cells.emplace_back(x,y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second pass: Add border to open
|
// Second pass: Add border to open
|
||||||
for(auto sp : starting_pixels) {
|
for(auto sp : starting_cells) {
|
||||||
add_neighbors(open_pixels, closed, sp.y, sp.x);
|
add_neighbors(open_cells, closed, sp.y, sp.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Third pass: Iterate filling in the open list
|
// Third pass: Iterate filling in the open list
|
||||||
int counter = 1; // leave this here so it's available below
|
int counter = 1; // leave this here so it's available below
|
||||||
for(; counter < WALL_PATH_LIMIT && !open_pixels.empty(); ++counter) {
|
for(; counter < WALL_PATH_LIMIT && !open_cells.empty(); ++counter) {
|
||||||
PointList next_open;
|
PointList next_open;
|
||||||
for(auto sp : open_pixels) {
|
for(auto sp : open_cells) {
|
||||||
$paths[sp.y][sp.x] = counter;
|
$paths[sp.y][sp.x] = counter;
|
||||||
add_neighbors(next_open, closed, sp.y, sp.x);
|
add_neighbors(next_open, closed, sp.y, sp.x);
|
||||||
}
|
}
|
||||||
open_pixels = next_open;
|
open_cells = next_open;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last pass: flood last pixels
|
// Last pass: flood last cells
|
||||||
for(auto sp : open_pixels) {
|
for(auto sp : open_cells) {
|
||||||
$paths[sp.y][sp.x] = counter;
|
$paths[sp.y][sp.x] = counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pathing::set_target(const Point &at, int value) {
|
void Pathing::set_target(const Point &at, int value) {
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ public:
|
||||||
$input(height, matrix::Row(width, 1))
|
$input(height, matrix::Row(width, 1))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void compute_paths(Matrix &walls);
|
int compute_paths(Matrix &walls);
|
||||||
void set_target(const Point &at, int value=0);
|
void set_target(const Point &at, int value=0);
|
||||||
void clear_target(const Point &at);
|
void clear_target(const Point &at);
|
||||||
Matrix &paths() { return $paths; }
|
Matrix &paths() { return $paths; }
|
||||||
|
|
|
||||||
|
|
@ -71,8 +71,8 @@ constexpr int COMBAT_UI_Y = RAY_VIEW_HEIGHT;
|
||||||
constexpr int COMBAT_UI_WIDTH = RAY_VIEW_WIDTH ;
|
constexpr int COMBAT_UI_WIDTH = RAY_VIEW_WIDTH ;
|
||||||
constexpr int COMBAT_UI_HEIGHT = SCREEN_HEIGHT - RAY_VIEW_HEIGHT;
|
constexpr int COMBAT_UI_HEIGHT = SCREEN_HEIGHT - RAY_VIEW_HEIGHT;
|
||||||
|
|
||||||
constexpr int INITIAL_MAP_W = 25;
|
constexpr int INITIAL_MAP_W = 21;
|
||||||
constexpr int INITIAL_MAP_H = 25;
|
constexpr int INITIAL_MAP_H = 21;
|
||||||
|
|
||||||
constexpr float DEFAULT_ROTATE=0.25f;
|
constexpr float DEFAULT_ROTATE=0.25f;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ TEST_CASE("camera control", "[map]") {
|
||||||
TEST_CASE("map placement test", "[map-fail]") {
|
TEST_CASE("map placement test", "[map-fail]") {
|
||||||
GameDB::init();
|
GameDB::init();
|
||||||
|
|
||||||
for(int i = 0; i < 20; i++) {
|
for(int i = 0; i < 10; i++) {
|
||||||
auto& level = GameDB::create_level();
|
auto& level = GameDB::create_level();
|
||||||
|
|
||||||
for(size_t rnum = 0; rnum < level.map->room_count(); rnum++) {
|
for(size_t rnum = 0; rnum < level.map->room_count(); rnum++) {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
#include "algos/maze.hpp"
|
#include "algos/maze.hpp"
|
||||||
#include "algos/stats.hpp"
|
#include "algos/stats.hpp"
|
||||||
|
|
||||||
#define DUMP 0
|
#define DUMP 1
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using matrix::Matrix;
|
using matrix::Matrix;
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include "algos/rand.hpp"
|
#include "algos/rand.hpp"
|
||||||
#include "game/systems.hpp"
|
#include "game/systems.hpp"
|
||||||
|
#include "constants.hpp"
|
||||||
|
|
||||||
using namespace fmt;
|
using namespace fmt;
|
||||||
using namespace nlohmann;
|
using namespace nlohmann;
|
||||||
|
|
@ -63,7 +64,9 @@ TEST_CASE("dijkstra algo test", "[pathing-old]") {
|
||||||
pathing.$input = test["input"];
|
pathing.$input = test["input"];
|
||||||
|
|
||||||
REQUIRE(pathing.INVARIANT());
|
REQUIRE(pathing.INVARIANT());
|
||||||
pathing.compute_paths(walls);
|
int longest = pathing.compute_paths(walls);
|
||||||
|
REQUIRE(longest > 0);
|
||||||
|
REQUIRE(longest < WALL_PATH_LIMIT);
|
||||||
|
|
||||||
REQUIRE(pathing.INVARIANT());
|
REQUIRE(pathing.INVARIANT());
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue