Refactored the maze functions to be a builder that can do different things to the maze. Also when I hit p in the game it'll save the map to a file. This was extremely hard for no reason.

master
Zed A. Shaw 1 week ago
parent 20f03731e5
commit 5f1a453fb4
  1. 2
      gui/fsm.cpp
  2. 14
      gui/map_view.cpp
  3. 1
      gui/map_view.hpp
  4. 3
      map.cpp
  5. 311
      maze.cpp
  6. 26
      maze.hpp
  7. 116
      tests/mazes.cpp
  8. 17
      worldbuilder.cpp

@ -320,6 +320,8 @@ namespace gui {
sound::mute(false);
$debug_ui.debug();
shaders::reload();
dbc::log("save map!");
$map_ui.save_map("map.txt", $main_ui.$compass_dir);
break;
case KEY::O:
autowalking = true;

@ -10,6 +10,7 @@
#include <codecvt>
#include <iostream>
#include <fmt/xchar.h>
#include <fstream>
namespace gui {
using namespace components;
@ -58,6 +59,19 @@ namespace gui {
// $gui.debug_layout(window);
}
void MapViewUI::save_map(const std::string& outfile, int compass_dir) {
std::wstring map_out = System::draw_map(
$level, $level.map->width(), $level.map->height(), compass_dir);
dbc::check(map_out.size() > 0, "WHAT? printed map has nothing in it.");
std::wofstream out(outfile, std::ios::binary);
std::locale loc(std::locale::classic(), new std::codecvt_utf8<wchar_t>);
out.imbue(loc);
out << map_out;
dbc::check(out.good(), "failed to write map file");
}
void MapViewUI::update() {
if($gui.has<Textual>($log_to)) {
auto& text = $gui.get<Textual>($log_to);

@ -20,5 +20,6 @@ namespace gui {
void update_level(GameLevel &level);
void log(std::wstring msg);
void update();
void save_map(const std::string& outfile, int compass_dir);
};
}

@ -45,12 +45,11 @@ void Map::clear_target(const Point &at) {
}
bool Map::place_entity(size_t room_index, Point &out) {
dbc::check($dead_ends.size() != 0, "no dead ends?!");
if($rooms.size() == 0) {
dbc::log("fucking dead end?");
out = $dead_ends.at(room_index % $dead_ends.size());
return true;
} else {
dbc::log("fucking fuckng fuck fuck");
dbc::check(room_index < $rooms.size(), "room_index is out of bounds, not enough rooms");
Room &start = $rooms.at(room_index);

@ -7,216 +7,221 @@
using std::string;
using matrix::Matrix;
inline size_t rand(size_t i, size_t j) {
if(i < j) {
return Random::uniform(i, j);
} else if(j < i) {
return Random::uniform(j, i);
} else {
return i;
namespace maze {
inline size_t rand(size_t i, size_t j) {
if(i < j) {
return Random::uniform(i, j);
} else if(j < i) {
return Random::uniform(j, i);
} else {
return i;
}
}
}
inline bool complete(Matrix& maze) {
size_t width = matrix::width(maze);
size_t height = matrix::height(maze);
inline bool complete(Matrix& maze) {
size_t width = matrix::width(maze);
size_t height = matrix::height(maze);
for(size_t row = 1; row < height; row += 2) {
for(size_t col = 1; col < width; col += 2) {
if(maze[row][col] != 0) return false;
for(size_t row = 1; row < height; row += 2) {
for(size_t col = 1; col < width; col += 2) {
if(maze[row][col] != 0) return false;
}
}
}
return true;
}
return true;
}
std::vector<Point> neighborsAB(Matrix& maze, Point on) {
std::vector<Point> result;
std::vector<Point> neighborsAB(Matrix& maze, Point on) {
std::vector<Point> result;
std::array<Point, 4> points{{
{on.x, on.y - 2},
{on.x, on.y + 2},
{on.x - 2, on.y},
{on.x + 2, on.y}
}};
std::array<Point, 4> points{{
{on.x, on.y - 2},
{on.x, on.y + 2},
{on.x - 2, on.y},
{on.x + 2, on.y}
}};
for(auto point : points) {
if(matrix::inbounds(maze, point.x, point.y)) {
result.push_back(point);
for(auto point : points) {
if(matrix::inbounds(maze, point.x, point.y)) {
result.push_back(point);
}
}
}
return result;
}
return result;
}
std::vector<Point> neighbors(Matrix& maze, Point on) {
std::vector<Point> result;
std::vector<Point> neighbors(Matrix& maze, Point on) {
std::vector<Point> result;
std::array<Point, 4> points{{
{on.x, on.y - 2},
{on.x, on.y + 2},
{on.x - 2, on.y},
{on.x + 2, on.y}
}};
std::array<Point, 4> points{{
{on.x, on.y - 2},
{on.x, on.y + 2},
{on.x - 2, on.y},
{on.x + 2, on.y}
}};
for(auto point : points) {
if(matrix::inbounds(maze, point.x, point.y)) {
if(maze[point.y][point.x] == WALL_VALUE) {
result.push_back(point);
for(auto point : points) {
if(matrix::inbounds(maze, point.x, point.y)) {
if(maze[point.y][point.x] == WALL_VALUE) {
result.push_back(point);
}
}
}
}
return result;
}
return result;
}
inline std::pair<Point, Point> find_coord(Matrix& maze) {
size_t width = matrix::width(maze);
size_t height = matrix::height(maze);
inline std::pair<Point, Point> find_coord(Matrix& maze) {
size_t width = matrix::width(maze);
size_t height = matrix::height(maze);
for(size_t y = 1; y < height; y += 2) {
for(size_t x = 1; x < width; x += 2) {
if(maze[y][x] == WALL_VALUE) {
auto found = neighborsAB(maze, {x, y});
for(size_t y = 1; y < height; y += 2) {
for(size_t x = 1; x < width; x += 2) {
if(maze[y][x] == WALL_VALUE) {
auto found = neighborsAB(maze, {x, y});
for(auto point : found) {
if(maze[point.y][point.x] == 0) {
return {{x, y}, point};
for(auto point : found) {
if(maze[point.y][point.x] == 0) {
return {{x, y}, point};
}
}
}
}
}
}
matrix::dump("BAD MAZE", maze);
dbc::sentinel("failed to find coord?");
}
matrix::dump("BAD MAZE", maze);
dbc::sentinel("failed to find coord?");
}
void maze::randomize_rooms(std::vector<Room>& rooms_out, std::vector<Point>& maybe_here) {
dbc::check(maybe_here.size() >= 2, "must have at least two possible points to place rooms");
void Builder::randomize_rooms() {
dbc::check($dead_ends.size() >= 2, "must have at least two possible points to place rooms");
while(rooms_out.size() < 2) {
// use those dead ends to randomly place rooms
for(auto at : maybe_here) {
if(Random::uniform(0,1)) {
size_t offset = Random::uniform(0,1);
Room cur{at.x+offset, at.y+offset, 1, 1};
rooms_out.push_back(cur);
while($rooms.size() < 2) {
// 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};
$rooms.push_back(cur);
}
}
}
}
}
void maze::init(Matrix& maze) {
matrix::assign(maze, WALL_VALUE);
}
void maze::divide(Matrix& maze, Point start, Point end) {
for(matrix::line it{start, end}; it.next();) {
maze[it.y][it.x] = 0;
maze[it.y+1][it.x] = 0;
void Builder::init() {
matrix::assign($walls, WALL_VALUE);
}
}
void maze::hunt_and_kill(Matrix& maze, std::vector<Room>& rooms, std::vector<Point>& dead_ends) {
for(auto& room : rooms) {
for(matrix::box it{maze, room.x, room.y, room.width}; it.next();) {
maze[it.y][it.x] = 0;
void Builder::divide(Point start, Point end) {
for(matrix::line it{start, end}; it.next();) {
$walls[it.y][it.x] = 0;
$walls[it.y+1][it.x] = 0;
}
}
Point on{1,1};
while(!complete(maze)) {
auto n = neighbors(maze, on);
if(n.size() == 0) {
dead_ends.push_back(on);
auto t = find_coord(maze);
on = t.first;
maze[on.y][on.x] = 0;
size_t row = (on.y + t.second.y) / 2;
size_t col = (on.x + t.second.x) / 2;
maze[row][col] = 0;
} else {
auto nb = n[rand(size_t(0), n.size() - 1)];
maze[nb.y][nb.x] = 0;
void Builder::hunt_and_kill() {
for(auto& room : $rooms) {
for(matrix::box it{$walls, room.x, room.y, room.width}; it.next();) {
$walls[it.y][it.x] = 0;
}
}
size_t row = (nb.y + on.y) / 2;
size_t col = (nb.x + on.x) / 2;
maze[row][col] = 0;
on = nb;
Point on{1,1};
while(!complete($walls)) {
auto n = neighbors($walls, on);
if(n.size() == 0) {
$dead_ends.push_back(on);
auto t = find_coord($walls);
on = t.first;
$walls[on.y][on.x] = 0;
size_t row = (on.y + t.second.y) / 2;
size_t col = (on.x + t.second.x) / 2;
$walls[row][col] = 0;
} else {
auto nb = n[rand(size_t(0), n.size() - 1)];
$walls[nb.y][nb.x] = 0;
size_t row = (nb.y + on.y) / 2;
size_t col = (nb.x + on.x) / 2;
$walls[row][col] = 0;
on = nb;
}
}
}
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(at.x >= room_ul.x && at.y >= room_ul.y &&
at.x <= room_lr.x && at.y <= room_lr.y)
{
for(matrix::compass it{maze, at.x, at.y}; it.next();) {
if(maze[it.y][it.x] == 1) {
maze[it.y][it.x] = 0;
break;
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(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 maze::inner_donut(Matrix& maze, float outer_rad, float inner_rad) {
size_t x = matrix::width(maze) / 2;
size_t y = matrix::height(maze) / 2;
void Builder::inner_donut(float outer_rad, float inner_rad) {
size_t x = matrix::width($walls) / 2;
size_t y = matrix::height($walls) / 2;
for(matrix::circle it{maze, {x, y}, outer_rad};
it.next();)
{
for(int x = it.left; x < it.right; x++) {
maze[it.y][x] = 0;
for(matrix::circle it{$walls, {x, y}, outer_rad};
it.next();)
{
for(int x = it.left; x < it.right; x++) {
$walls[it.y][x] = 0;
}
}
}
for(matrix::circle it{maze, {x, y}, inner_rad};
it.next();)
{
for(int x = it.left; x < it.right; x++) {
maze[it.y][x] = 1;
for(matrix::circle it{$walls, {x, y}, inner_rad};
it.next();)
{
for(int x = it.left; x < it.right; x++) {
$walls[it.y][x] = 1;
}
}
}
}
void maze::inner_box(Matrix& maze, size_t outer_size, size_t inner_size) {
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;
size_t x = matrix::width(maze) / 2;
size_t y = matrix::height(maze) / 2;
for(matrix::box it{maze, x, y, outer_size};
it.next();)
{
maze[it.y][it.x] = 0;
}
for(matrix::box it{$walls, x, y, outer_size};
it.next();)
{
$walls[it.y][it.x] = 0;
}
for(matrix::box it{maze, x, y, inner_size};
it.next();)
{
maze[it.y][it.x] = 1;
for(matrix::box it{$walls, x, y, inner_size};
it.next();)
{
$walls[it.y][it.x] = 1;
}
}
}
void maze::remove_dead_ends(Matrix& maze, std::vector<Point>& dead_ends) {
for(auto at : dead_ends) {
for(matrix::compass it{maze, at.x, at.y}; it.next();) {
if(maze[it.y][it.x] == 0) {
int diff_x = at.x - it.x;
int diff_y = at.y - it.y;
maze[at.y + diff_y][at.x + diff_x] = 0;
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) {
for(matrix::compass it{$walls, at.x, at.y}; it.next();) {
if($walls[it.y][it.x] == 0) {
int diff_x = at.x - it.x;
int diff_y = at.y - it.y;
$walls[at.y + diff_y][at.x + diff_x] = 0;
}
}
}
}
void Builder::dump(const std::string& msg) {
matrix::dump(msg, $walls);
}
}

@ -4,16 +4,26 @@
namespace maze {
void init(Matrix& maze);
void hunt_and_kill(Matrix& maze, std::vector<Room>& rooms, std::vector<Point>& dead_ends);
struct Builder {
Matrix& $walls;
std::vector<Room>& $rooms;
std::vector<Point>& $dead_ends;
void randomize_rooms(std::vector<Room>& rooms_out, std::vector<Point>& maybe_here);
Builder(Map& map) :
$walls(map.$walls), $rooms(map.$rooms), $dead_ends(map.$dead_ends)
{
init();
}
void inner_donut(Matrix& maze, float outer_rad, float inner_rad);
void inner_box(Matrix& map, size_t outer_size, size_t inner_size);
void hunt_and_kill();
void divide(Matrix& maze, Point start, Point end);
void remove_dead_ends(Matrix& maze, std::vector<Point>& dead_ends);
void init();
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);
};
}

@ -9,112 +9,98 @@
using std::string;
using matrix::Matrix;
TEST_CASE("hunt-and-kill", "[mazes]") {
auto map = matrix::make(21, 21);
std::vector<Room> rooms;
std::vector<Point> dead_ends;
Map map(21, 21);
maze::Builder maze(map);
maze.hunt_and_kill();
maze.dump("BASIC MAZE");
maze::init(map);
maze::hunt_and_kill(map, rooms, dead_ends);
matrix::dump("BASIC MAZE", map);
maze.randomize_rooms();
maze.hunt_and_kill();
maze::randomize_rooms(rooms, dead_ends);
maze::hunt_and_kill(map, rooms, dead_ends);
REQUIRE(map.$dead_ends.size() > 0);
REQUIRE(map.$rooms.size() > 0);
for(auto& room : rooms) {
for(matrix::box it{map, room.x, room.y, room.width};
for(auto& room : maze.$rooms) {
for(matrix::box it{maze.$walls, room.x, room.y, room.width};
it.next();)
{
map[it.y][it.x] = WALL_PATH_LIMIT;
maze.$walls[it.y][it.x] = WALL_PATH_LIMIT;
}
}
matrix::dump("MAZE WITH ROOMS", map);
maze.dump("MAZE WITH ROOMS");
}
TEST_CASE("hunt-and-kill box", "[mazes]") {
auto map = matrix::make(21, 21);
std::vector<Room> rooms;
std::vector<Point> dead_ends;
Map map(21, 21);
maze::Builder maze(map);
maze::init(map);
maze::inner_box(map, 5, 3);
maze::hunt_and_kill(map, rooms, dead_ends);
maze.inner_box(5, 3);
maze.hunt_and_kill();
for(auto at : dead_ends) {
map[at.y][at.x]=32;
for(auto at : maze.$dead_ends) {
maze.$walls[at.y][at.x]=32;
}
matrix::dump("INNER BOX", map);
maze.dump("INNER BOX");
REQUIRE(rooms.size() == 0);
REQUIRE(maze.$rooms.size() == 0);
}
TEST_CASE("hunt-and-kill ring", "[mazes]") {
auto map = matrix::make(21, 21);
std::vector<Room> rooms;
std::vector<Point> dead_ends;
Map map(21, 21);
maze::Builder maze(map);
maze::init(map);
maze::inner_donut(map, 5.5, 3.5);
maze::hunt_and_kill(map, rooms, dead_ends);
maze.inner_donut(5.5, 3.5);
maze.hunt_and_kill();
for(auto at : dead_ends) {
map[at.y][at.x]=32;
for(auto at : maze.$dead_ends) {
maze.$walls[at.y][at.x]=32;
}
matrix::dump("INNER RING", map);
maze.dump("INNER RING");
REQUIRE(rooms.size() == 0);
REQUIRE(maze.$rooms.size() == 0);
}
TEST_CASE("hunt-and-kill fissure", "[mazes]") {
auto map = matrix::make(21, 21);
std::vector<Room> rooms;
std::vector<Point> dead_ends;
Map map(21, 21);
maze::Builder maze(map);
maze::init(map);
maze::divide(map, {3,3}, {19,18});
maze::hunt_and_kill(map, rooms, dead_ends);
maze.divide({3,3}, {19,18});
maze.hunt_and_kill();
for(auto at : dead_ends) {
map[at.y][at.x]=32;
for(auto at : maze.$dead_ends) {
maze.$walls[at.y][at.x]=32;
}
matrix::dump("FISSURE MAZE", map);
maze.dump("FISSURE MAZE");
REQUIRE(rooms.size() == 0);
REQUIRE(maze.$rooms.size() == 0);
}
TEST_CASE("hunt-and-kill no-dead-ends", "[mazes]") {
auto map = matrix::make(21, 21);
std::vector<Room> rooms;
std::vector<Point> dead_ends;
maze::init(map);
Map map(21, 21);
maze::Builder maze(map);
maze::hunt_and_kill(map, rooms, dead_ends);
maze.hunt_and_kill();
maze::remove_dead_ends(map, dead_ends);
maze.remove_dead_ends();
matrix::dump("NO DEAD ENDS", map);
maze.dump("NO DEAD ENDS");
}
TEST_CASE("hunt-and-kill too much", "[mazes]") {
auto map = matrix::make(21, 21);
std::vector<Room> rooms;
std::vector<Point> dead_ends;
maze::init(map);
maze::inner_donut(map, 4, 2);
maze::divide(map, {3,3}, {19,18});
auto copy = map;
maze::hunt_and_kill(copy, rooms, dead_ends);
Map map(21, 21);
maze::Builder maze(map);
map = copy;
maze::randomize_rooms(rooms, dead_ends);
maze::hunt_and_kill(map, rooms, dead_ends);
maze.hunt_and_kill();
maze.randomize_rooms();
maze.init();
maze.inner_donut(4, 2);
maze.divide({3,3}, {19,18});
maze.hunt_and_kill();
matrix::dump("COMBINED", map);
maze.dump("COMBINED");
}

@ -11,17 +11,20 @@ using namespace fmt;
using namespace components;
void WorldBuilder::generate_map() {
maze::init($map.$walls);
maze::hunt_and_kill($map.$walls, $map.$rooms, $map.$dead_ends);
maze::randomize_rooms($map.$rooms, $map.$dead_ends);
maze::hunt_and_kill($map.$walls, $map.$rooms, $map.$dead_ends);
maze::Builder maze($map);
size_t x_diff = $map.width() / 4;
size_t y_diff = $map.height() / 4;
maze.divide({x_diff, y_diff}, {$map.width() - x_diff, $map.height() - y_diff});
maze.hunt_and_kill();
dbc::check($map.$dead_ends.size() > 0, "world builder/maze builder made a map with no dead ends.");
$map.expand();
$map.load_tiles();
}
DinkyECS::Entity WorldBuilder::configure_entity_in_map(DinkyECS::World &world, json &entity_data, Point pos_out) {
dbc::log(">>>>>>>>>>> ENTER");
auto item = world.entity();
world.set<Position>(item, {pos_out.x+1, pos_out.y+1});
@ -33,17 +36,14 @@ DinkyECS::Entity WorldBuilder::configure_entity_in_map(DinkyECS::World &world, j
components::configure_entity($components, world, item, entity_data["components"]);
}
dbc::log("<<<<<<<<<<<<< EXIT");
return item;
}
DinkyECS::Entity WorldBuilder::configure_entity_in_room(DinkyECS::World &world, json &entity_data, int in_room) {
Point pos_out;
dbc::log("is it configure_entity_in_map's fault?");
bool placed = $map.place_entity(in_room, pos_out);
dbc::check(placed, "failed to randomly place item in room");
auto entity = configure_entity_in_map(world, entity_data, pos_out);
dbc::log("<<<<<<<<<<<<<<<<<<<<<<<<<<< leaving configure_entity_in_room");
return entity;
}
@ -118,7 +118,6 @@ void WorldBuilder::place_entities(DinkyECS::World &world) {
if(world.has_the<Player>()) {
auto& player = world.get_the<Player>();
Point pos_out;
dbc::log("or is it in place_entities placing the player?");
bool placed = $map.place_entity(0, pos_out);
dbc::check(placed, "failed to randomly place item in room");
world.set<Position>(player.entity, {pos_out.x+1, pos_out.y+1});

Loading…
Cancel
Save