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) {
|
||||
for(matrix::each_cell it{wall_copy}; it.next();) {
|
||||
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() {
|
||||
for(auto room : $rooms) {
|
||||
// walk the outer wall looking for an emergent door
|
||||
int possible_doors = 0;
|
||||
Point last_door{0,0};
|
||||
int best_longest = 0;
|
||||
Point best_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};
|
||||
|
||||
while(it.next()) {
|
||||
if(space_available(it.x, it.y)) {
|
||||
// IN_BOUNDS CHECK, or maybe a can_place or is_available?
|
||||
last_door = {it.x, it.y};
|
||||
possible_doors++;
|
||||
// don't use corners
|
||||
if((it.x == room.x - 1 && it.y == room.y - 1) ||
|
||||
(it.x == room.x - 1 && it.y == room.y + 2) ||
|
||||
(it.x == room.width + 2 && it.y == room.y + 2) ||
|
||||
(it.x == room.width + 2 && it.y == room.y - 1)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// if only found one then make that an actual door
|
||||
if(possible_doors == 1) {
|
||||
$doors.insert_or_assign(last_door, true);
|
||||
continue;
|
||||
}
|
||||
if($walls[it.y][it.x] == WALL_VALUE) {
|
||||
$walls[it.y][it.x] = SPACE_VALUE;
|
||||
longest = compute_paths(room.x, room.y);
|
||||
|
||||
// no natural door found, need to make one
|
||||
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;
|
||||
$doors.insert_or_assign({it.x, it.y}, true);
|
||||
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;
|
||||
}
|
||||
|
||||
dbc::check(best_longest < WALL_PATH_LIMIT, "bad best_longest!");
|
||||
if(best_longest > int($width * 2)) break;
|
||||
}
|
||||
|
||||
if(!found_door && last_door.x != 0) {
|
||||
// didn't find an external door so punch one at the dead end
|
||||
$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) {
|
||||
|
|
@ -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() {
|
||||
size_t width = matrix::width($walls);
|
||||
size_t height = matrix::height($walls);
|
||||
|
|
@ -410,15 +409,11 @@ namespace maze {
|
|||
// 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
|
||||
Room test_room = $rooms.at(rand(0, $rooms.size() - 1));
|
||||
Point test{test_room.x, test_room.y};
|
||||
$paths.set_target(test);
|
||||
$paths.compute_paths($walls);
|
||||
// $paths.dump("AFTER COMPUTE");
|
||||
$paths.clear_target(test);
|
||||
compute_paths(test_room.x, test_room.y);
|
||||
|
||||
for(matrix::each_cell it{$walls}; it.next();) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -430,8 +425,6 @@ namespace maze {
|
|||
bool in_bounds = matrix::inbounds($walls, x, y);
|
||||
bool is_space = $walls[y][x] == SPACE_VALUE;
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ namespace maze {
|
|||
Room $no_rooms_region{0,0,0,0};
|
||||
// BUG: instead of bool map it to the room?
|
||||
std::unordered_map<Point, bool> $doors;
|
||||
Pathing $paths;
|
||||
Pathing $pathing;
|
||||
|
||||
Builder(Map& map) :
|
||||
$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($height % 2 == 1, "map height not an ODD number (perimter dead ends bug)");
|
||||
|
|
@ -46,6 +46,7 @@ namespace maze {
|
|||
bool repair();
|
||||
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);
|
||||
int compute_paths(size_t x, size_t y);
|
||||
};
|
||||
|
||||
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();
|
||||
dbc::check(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,
|
||||
$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 closed = walls;
|
||||
PointList starting_pixels;
|
||||
PointList open_pixels;
|
||||
PointList starting_cells;
|
||||
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++) {
|
||||
size_t x = counter % $width;
|
||||
size_t y = counter / $width;
|
||||
if($input[y][x] == 0) {
|
||||
$paths[y][x] = 0;
|
||||
closed[y][x] = 1;
|
||||
starting_pixels.emplace_back(x,y);
|
||||
starting_cells.emplace_back(x,y);
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: Add border to open
|
||||
for(auto sp : starting_pixels) {
|
||||
add_neighbors(open_pixels, closed, sp.y, sp.x);
|
||||
for(auto sp : starting_cells) {
|
||||
add_neighbors(open_cells, closed, sp.y, sp.x);
|
||||
}
|
||||
|
||||
// Third pass: Iterate filling in the open list
|
||||
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;
|
||||
for(auto sp : open_pixels) {
|
||||
for(auto sp : open_cells) {
|
||||
$paths[sp.y][sp.x] = counter;
|
||||
add_neighbors(next_open, closed, sp.y, sp.x);
|
||||
}
|
||||
open_pixels = next_open;
|
||||
open_cells = next_open;
|
||||
}
|
||||
|
||||
// Last pass: flood last pixels
|
||||
for(auto sp : open_pixels) {
|
||||
// Last pass: flood last cells
|
||||
for(auto sp : open_cells) {
|
||||
$paths[sp.y][sp.x] = counter;
|
||||
}
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
void Pathing::set_target(const Point &at, int value) {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public:
|
|||
$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 clear_target(const Point &at);
|
||||
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_HEIGHT = SCREEN_HEIGHT - RAY_VIEW_HEIGHT;
|
||||
|
||||
constexpr int INITIAL_MAP_W = 25;
|
||||
constexpr int INITIAL_MAP_H = 25;
|
||||
constexpr int INITIAL_MAP_W = 21;
|
||||
constexpr int INITIAL_MAP_H = 21;
|
||||
|
||||
constexpr float DEFAULT_ROTATE=0.25f;
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ TEST_CASE("camera control", "[map]") {
|
|||
TEST_CASE("map placement test", "[map-fail]") {
|
||||
GameDB::init();
|
||||
|
||||
for(int i = 0; i < 20; i++) {
|
||||
for(int i = 0; i < 10; i++) {
|
||||
auto& level = GameDB::create_level();
|
||||
|
||||
for(size_t rnum = 0; rnum < level.map->room_count(); rnum++) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#include "algos/maze.hpp"
|
||||
#include "algos/stats.hpp"
|
||||
|
||||
#define DUMP 0
|
||||
#define DUMP 1
|
||||
|
||||
using std::string;
|
||||
using matrix::Matrix;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <thread>
|
||||
#include "algos/rand.hpp"
|
||||
#include "game/systems.hpp"
|
||||
#include "constants.hpp"
|
||||
|
||||
using namespace fmt;
|
||||
using namespace nlohmann;
|
||||
|
|
@ -63,7 +64,9 @@ TEST_CASE("dijkstra algo test", "[pathing-old]") {
|
|||
pathing.$input = test["input"];
|
||||
|
||||
REQUIRE(pathing.INVARIANT());
|
||||
pathing.compute_paths(walls);
|
||||
int longest = pathing.compute_paths(walls);
|
||||
REQUIRE(longest > 0);
|
||||
REQUIRE(longest < WALL_PATH_LIMIT);
|
||||
|
||||
REQUIRE(pathing.INVARIANT());
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue