diff --git a/matrix.cpp b/matrix.cpp index 08c1a14..e3f9ed9 100644 --- a/matrix.cpp +++ b/matrix.cpp @@ -9,72 +9,6 @@ using namespace fmt; using std::min, std::max; namespace matrix { - - flood::flood(Matrix &mat, Point start, int old_val, int new_val) : - mat(mat), start(start), old_val(old_val), new_val(new_val), - x(start.x), y(start.y), dirs{mat, start.x, start.y} - { - dbc::check(old_val != new_val, "what you doing?"); - current_loc = start; - q.push(start); - } - - bool flood::next() { - if(!q.empty()) { - if(!dirs.next()) { - // box is done reset it - auto current_loc = q.front(); - q.pop(); - - dirs = matrix::compass{mat, current_loc.x, current_loc.y}; - dirs.next(); - } - - // get the next thing - if(mat[dirs.y][dirs.x] <= old_val) { - mat[dirs.y][dirs.x] = new_val; - x = dirs.x; - y = dirs.y; - - q.push({.x=dirs.x, .y=dirs.y}); - } - - return true; - } else { - return false; - } - } - - line::line(Point start, Point end) : - x(start.x), y(start.y), - x1(end.x), y1(end.y) - { - dx = std::abs(x1 - x); - sx = x < x1 ? 1 : -1; - dy = std::abs(y1 - y) * -1; - sy = y < y1 ? 1 : -1; - error = dx + dy; - } - - bool line::next() { - if(x != x1 || y != y1) { - int e2 = 2 * error; - - if(e2 >= dy) { - error = error + dy; - x = x + sx; - } - - if(e2 <= dx) { - error = error + dx; - y = y + sy; - } - return true; - } else { - return false; - } - } - void dump(const std::string &msg, Matrix &map, int show_x, int show_y) { println("----------------- {}", msg); diff --git a/matrix.hpp b/matrix.hpp index 9d8a4bb..3974043 100644 --- a/matrix.hpp +++ b/matrix.hpp @@ -9,385 +9,43 @@ #include "point.hpp" #include "rand.hpp" #include "dbc.hpp" +#include "shiterator.hpp" namespace matrix { - using std::vector, std::queue, std::array; - using std::min, std::max, std::floor; + using Row = shiterator::BaseRow; + using Matrix = shiterator::Base; - template - using BaseRow = vector; + using viewport = shiterator::viewport_t; - template - using Base = vector>; + using each_cell = shiterator::each_cell_t; - using Row = vector; - using Matrix = vector; + using each_row = shiterator::each_row_t; + using box = shiterator::box_t; + using compass = shiterator::compass_t; + using circle = shiterator::circle_t; + using rectangle = shiterator::rectangle_t; + using rando_rect = shiterator::rando_rect_t; + using line = shiterator::line; - - /* - * Just a quick thing to reset a matrix to a value. - */ - template - inline void assign(MAT &out, VAL new_value) { - for(auto &row : out) { - row.assign(row.size(), new_value); - } - } - - template - inline bool inbounds(MAT &mat, size_t x, size_t y) { - // since Point.x and Point.y are size_t any negatives are massive - bool res = (y < mat.size()) && (x < mat[0].size()); - return res; - } - - template - inline size_t width(MAT &mat) { - return mat[0].size(); - } - - template - inline size_t height(MAT &mat) { - return mat.size(); - } - - template - inline Base make_base(size_t width, size_t height) { - Base result(height, BaseRow(width)); - return result; - } + void dump(const std::string &msg, Matrix &map, int show_x=-1, int show_y=-1); inline Matrix make(size_t width, size_t height) { - Matrix result(height, Row(width)); - return result; + return shiterator::make(width, height); } - inline size_t next_x(size_t x, size_t width) { - return (x + 1) * ((x + 1) < width); + inline bool inbounds(Matrix &mat, size_t x, size_t y) { + return shiterator::inbounds(mat, x, y); } - inline size_t next_y(size_t x, size_t y) { - return y + (x == 0); + inline size_t width(Matrix &mat) { + return shiterator::width(mat); } - inline bool at_end(size_t y, size_t height) { - return y < height; + inline size_t height(Matrix &mat) { + return shiterator::height(mat); } - inline bool end_row(size_t x, size_t width) { - return x == width - 1; + inline void assign(Matrix &out, int new_value) { + shiterator::assign(out, new_value); } - - void dump(const std::string &msg, Matrix &map, int show_x=-1, int show_y=-1); - - template - struct each_cell_t { - size_t x = ~0; - size_t y = ~0; - size_t width = 0; - size_t height = 0; - - each_cell_t(MAT &mat) - { - height = matrix::height(mat); - width = matrix::width(mat); - } - - bool next() { - x = next_x(x, width); - y = next_y(x, y); - return at_end(y, height); - } - }; - - template - struct viewport_t { - Point start; - // this is the point in the map - size_t x; - size_t y; - // this is the point inside the box, start at 0 - size_t view_x = ~0; - size_t view_y = ~0; - // viewport width/height - size_t width; - size_t height; - - viewport_t(MAT &mat, Point start, int max_x, int max_y) : - start(start), - x(start.x-1), - y(start.y-1) - { - width = std::min(size_t(max_x), matrix::width(mat) - start.x); - height = std::min(size_t(max_y), matrix::height(mat) - start.y); - fmt::println("viewport_t max_x, max_y {},{} vs matrix {},{}, x={}, y={}", - max_x, max_y, matrix::width(mat), matrix::height(mat), x, y); - } - - bool next() { - y = next_y(x, y); - x = next_x(x, width); - view_x = next_x(view_x, width); - view_y = next_y(view_x, view_y); - return at_end(y, height); - } - }; - - using viewport = viewport_t; - - using each_cell = each_cell_t; - - template - struct each_row_t { - size_t x = ~0; - size_t y = ~0; - size_t width = 0; - size_t height = 0; - bool row = false; - - each_row_t(MAT &mat) { - height = matrix::height(mat); - width = matrix::width(mat); - } - - bool next() { - x = next_x(x, width); - y = next_y(x, y); - row = end_row(x, width); - return at_end(y, height); - } - }; - - using each_row = each_row_t; - - template - struct box_t { - size_t from_x; - size_t from_y; - size_t x = 0; // these are set in constructor - size_t y = 0; // again, no fancy ~ trick needed - size_t left = 0; - size_t top = 0; - size_t right = 0; - size_t bottom = 0; - - box_t(MAT &mat, size_t at_x, size_t at_y, size_t size) : - box_t(mat, at_x, at_y, size, size) { - } - - box_t(MAT &mat, size_t at_x, size_t at_y, size_t width, size_t height) : - from_x(at_x), from_y(at_y) - { - size_t h = matrix::height(mat); - size_t w = matrix::width(mat); - - // keeps it from going below zero - // need extra -1 to compensate for the first next() - left = max(from_x, width) - width; - x = left - 1; // must be -1 for next() - // keeps it from going above width - right = min(from_x + width + 1, w); - - // same for these two - top = max(from_y, height) - height; - y = top - (left == 0); - bottom = min(from_y + height + 1, h); - } - - bool next() { - // calc next but allow to go to 0 for next - x = next_x(x, right); - // x will go to 0, which signals new line - y = next_y(x, y); // this must go here - // if x==0 then this moves it to min_x - x = max(x, left); - // and done - - return at_end(y, bottom); - } - - float distance() { - int dx = from_x - x; - int dy = from_y - y; - - return sqrt((dx * dx) + (dy * dy)); - } - }; - - using box = box_t; - - template - struct compass_t { - size_t x = 0; // these are set in constructor - size_t y = 0; // again, no fancy ~ trick needed - array x_dirs{0, 1, 0, -1}; - array y_dirs{-1, 0, 1, 0}; - size_t max_dirs=0; - size_t dir = ~0; - - compass_t(MAT &mat, size_t x, size_t y) : - x(x), y(y) - { - array x_in{0, 1, 0, -1}; - array y_in{-1, 0, 1, 0}; - - for(size_t i = 0; i < 4; i++) { - int nx = x + x_in[i]; - int ny = y + y_in[i]; - if(matrix::inbounds(mat, nx, ny)) { - x_dirs[max_dirs] = nx; - y_dirs[max_dirs] = ny; - max_dirs++; - } - } - } - - bool next() { - dir++; - if(dir < max_dirs) { - x = x_dirs[dir]; - y = y_dirs[dir]; - return true; - } else { - return false; - } - } - }; - - using compass = compass_t; - - struct flood { - Matrix &mat; - Point start; - int old_val; - int new_val; - queue q; - Point current_loc; - int x; - int y; - matrix::compass dirs; - - flood(Matrix &mat, Point start, int old_val, int new_val); - bool next(); - bool next_working(); - }; - - struct line { - int x; - int y; - int x1; - int y1; - int sx; - int sy; - int dx; - int dy; - int error; - - line(Point start, Point end); - bool next(); - }; - - template - struct circle_t { - float center_x; - float center_y; - float radius = 0.0f; - int y = 0; - int dx = 0; - int dy = 0; - int left = 0; - int right = 0; - int top = 0; - int bottom = 0; - int width = 0; - int height = 0; - - circle_t(MAT &mat, Point center, float radius) : - center_x(center.x), center_y(center.y), radius(radius) - { - width = matrix::width(mat); - height = matrix::height(mat); - top = max(int(floor(center_y - radius)), 0); - bottom = min(int(floor(center_y + radius)), height - 1); - - y = top; - } - - bool next() { - y++; - if(y <= bottom) { - dy = y - center_y; - dx = floor(sqrt(radius * radius - dy * dy)); - left = max(0, int(center_x) - dx); - right = min(width, int(center_x) + dx + 1); - return true; - } else { - return false; - } - } - }; - - using circle = circle_t; - - template - struct rectangle_t { - int x; - int y; - int top; - int left; - int width; - int height; - int right; - int bottom; - - rectangle_t(MAT &mat, size_t start_x, size_t start_y, size_t width, size_t height) : - top(start_y), - left(start_x), - width(width), - height(height) - { - size_t h = matrix::height(mat); - size_t w = matrix::width(mat); - y = start_y - 1; - x = left - 1; // must be -1 for next() - right = min(start_x + width, w); - - y = start_y; - bottom = min(start_y + height, h); - } - - bool next() { - x = next_x(x, right); - y = next_y(x, y); - x = max(x, left); - return at_end(y, bottom); - } - }; - - using rectangle = rectangle_t; - - template - struct rando_rect_t { - int x; - int y; - int x_offset; - int y_offset; - rectangle_t it; - - rando_rect_t(MAT &mat, size_t start_x, size_t start_y, size_t width, size_t height) : - it{mat, start_x, start_y, width, height} - { - x_offset = Random::uniform(0, int(width)); - y_offset = Random::uniform(0, int(height)); - } - - bool next() { - bool done = it.next(); - x = it.left + ((it.x + x_offset) % it.width); - y = it.top + ((it.y + y_offset) % it.height); - return done; - } - }; - - using rando_rect = rando_rect_t; } diff --git a/shiterator.hpp b/shiterator.hpp new file mode 100644 index 0000000..c2a7dbe --- /dev/null +++ b/shiterator.hpp @@ -0,0 +1,530 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include "point.hpp" +#include "rand.hpp" +#include "dbc.hpp" + +/* + * # What is This Shit? + * + * Announcing the Shape Iterators, or `shiterators` for short. You could also say these are Shaw's Iterators, but + * either way they are the _shit_. Or are they shit? You decide. Maybe they're "shite"? + * + * A shiterator is a simple generator that converts 2D shapes into a 1D stream of x/y coordinates. You give it a matrix, some parameters like start, end, etc. and each time you call `next()` you the next viable x/y coordinate to complete the shape. + * + * A shiterator tries to ensure a few things: + * + * 1. All x/y values will be within the Matrix you give it. + * 2. They try to not store anything and only calculate the math necessary to linearlize the shape. + * 3. You can store them and incrementally call next to get the next value. + * 4. You should be able to compose them together on the same Matrix or different matrices of the same dimensions. + * 5. Most of them will only require 1 for-loop, the few that require 2 only do this so you can draw the inside of a shape. `circle` is like this. + * 6. They don't assume any particular classes or require subclassing. As long as the type given enables `mat[y][x]` (row major) access then it'll work. + * 7. The matrix given to a shiterator isn't actually attached to it, so you can use one matrix to setup an iterator, then apply the x/y values to any other matrix of the same dimensions. + * 8. More importantly, shiterators _do not return any values from the matrix_. They only do the math for coordinates and leave it to you to work your matrix. + * + * These shiterators are used all over the game to do map rendering, randomization, drawing, nearly everything that involves a shape. + * + * ## Algorithms I Need + * + * I'm currently looking for a few algorithms, so if you know how to do these let me know: + * + * 1. _Flood fill_ This turns out to be really hard because most algorithms require keeping track of visited cells with a queue, recursion, etc. + * 2. _Random rectangle fill_ I have something that mostly works but it's really only random across each y-axis, then separate y-axes are randomized. + * 3. _Dijkstra Map_ I have a Dijkstra algorithm but it's not in this style yet. Look in `worldbuilder.cpp` for my current implementation. + * 4. _Viewport_ Currently working on this but I need to have a rectangle I can move around as a viewport. + * + * + * ## Usage + * + * Check the `matrix.hpp` for an example if you want to make it more conventient for your own type. + * + * ## Thanks + * + * Special thanks for Amit and hirdrac for their help with the math and for + * giving me the initial idea. hirdrac doesn't want to be held responsible for + * this travesty but he showed me that you can do iteration and _not_ use the + * weird C++ iterators. Amit did a lot to show me how to do these calculations + * without branching. Thanks to you both and everyone helping me while I + * stream my development. + */ +namespace shiterator { using std::vector, std::queue, std::array; using + std::min, std::max, std::floor; + + template + using BaseRow = vector; + + template + using Base = vector>; + + template + inline Base make(size_t width, size_t height) { + Base result(height, BaseRow(width)); + return result; + } + + /* + * Just a quick thing to reset a matrix to a value. + */ + template + inline void assign(MAT &out, VAL new_value) { + for(auto &row : out) { + row.assign(row.size(), new_value); + } + } + + + /* + * Tells you if a coordinate is in bounds of the matrix + * and therefore safe to use. + */ + template + inline bool inbounds(MAT &mat, size_t x, size_t y) { + // since Point.x and Point.y are size_t any negatives are massive + return (y < mat.size()) && (x < mat[0].size()); + } + + /* + * Gives the width of a matrix. Assumes row major (y/x) + * and vector API .size(). + */ + template + inline size_t width(MAT &mat) { + return mat[0].size(); + } + + /* + * Same as shiterator::width but just the height. + */ + template + inline size_t height(MAT &mat) { + return mat.size(); + } + + /* + * These are internal calculations that help + * with keeping track of the next x coordinate. + */ + inline size_t next_x(size_t x, size_t width) { + return (x + 1) * ((x + 1) < width); + } + + /* + * Same as next_x but updates the next y coordinate. + * It uses the fact that when x==0 you have a new + * line so increment y. + */ + inline size_t next_y(size_t x, size_t y) { + return y + (x == 0); + } + + /* + * Figures out if you're at the end of the shape, + * which is usually when y > height. + */ + inline bool at_end(size_t y, size_t height) { + return y < height; + } + + /* + * Determines if you're at the end of a row. + */ + inline bool end_row(size_t x, size_t width) { + return x == width - 1; + } + + /* + * Most basic shiterator. It just goes through + * every cell in the matrix in linear order + * with not tracking of anything else. + */ + template + struct each_cell_t { + size_t x = ~0; + size_t y = ~0; + size_t width = 0; + size_t height = 0; + + each_cell_t(MAT &mat) + { + height = shiterator::height(mat); + width = shiterator::width(mat); + } + + bool next() { + x = next_x(x, width); + y = next_y(x, y); + return at_end(y, height); + } + }; + + /* + * This is just each_cell_t but it sets + * a boolean value `bool row` so you can + * tell when you've reached the end of a + * row. This is mostly used for printing + * out a matrix and similar just drawing the + * whole thing with its boundaries. + */ + template + struct each_row_t { + size_t x = ~0; + size_t y = ~0; + size_t width = 0; + size_t height = 0; + bool row = false; + + each_row_t(MAT &mat) { + height = shiterator::height(mat); + width = shiterator::width(mat); + } + + bool next() { + x = next_x(x, width); + y = next_y(x, y); + row = end_row(x, width); + return at_end(y, height); + } + }; + + /* + * This is a CENTERED box, that will create + * a centered rectangle around a point of a + * certain dimension. This kind of needs a + * rewrite but if you want a rectangle from + * a upper corner then use rectangle_t type. + * + * Passing 1 parameter for the size will make + * a square. + */ + template + struct box_t { + size_t from_x; + size_t from_y; + size_t x = 0; // these are set in constructor + size_t y = 0; // again, no fancy ~ trick needed + size_t left = 0; + size_t top = 0; + size_t right = 0; + size_t bottom = 0; + + box_t(MAT &mat, size_t at_x, size_t at_y, size_t size) : + box_t(mat, at_x, at_y, size, size) { + } + + box_t(MAT &mat, size_t at_x, size_t at_y, size_t width, size_t height) : + from_x(at_x), from_y(at_y) + { + size_t h = shiterator::height(mat); + size_t w = shiterator::width(mat); + + // keeps it from going below zero + // need extra -1 to compensate for the first next() + left = max(from_x, width) - width; + x = left - 1; // must be -1 for next() + // keeps it from going above width + right = min(from_x + width + 1, w); + + // same for these two + top = max(from_y, height) - height; + y = top - (left == 0); + bottom = min(from_y + height + 1, h); + } + + bool next() { + // calc next but allow to go to 0 for next + x = next_x(x, right); + // x will go to 0, which signals new line + y = next_y(x, y); // this must go here + // if x==0 then this moves it to min_x + x = max(x, left); + // and done + + return at_end(y, bottom); + } + + /* + * This was useful for doing quick lighting + * calculations, and I might need to implement + * it in other shiterators. It gives the distance + * to the center from the current x/y. + */ + float distance() { + int dx = from_x - x; + int dy = from_y - y; + + return sqrt((dx * dx) + (dy * dy)); + } + }; + + /* + * Stupid simple compass shape North/South/East/West. + * This comes up a _ton_ when doing searching, flood + * algorithms, collision, etc. Probably not the + * fastest way to do it but good enough. + */ + template + struct compass_t { + size_t x = 0; // these are set in constructor + size_t y = 0; // again, no fancy ~ trick needed + array x_dirs{0, 1, 0, -1}; + array y_dirs{-1, 0, 1, 0}; + size_t max_dirs=0; + size_t dir = ~0; + + compass_t(MAT &mat, size_t x, size_t y) : + x(x), y(y) + { + array x_in{0, 1, 0, -1}; + array y_in{-1, 0, 1, 0}; + + for(size_t i = 0; i < 4; i++) { + int nx = x + x_in[i]; + int ny = y + y_in[i]; + if(shiterator::inbounds(mat, nx, ny)) { + x_dirs[max_dirs] = nx; + y_dirs[max_dirs] = ny; + max_dirs++; + } + } + } + + bool next() { + dir++; + if(dir < max_dirs) { + x = x_dirs[dir]; + y = y_dirs[dir]; + return true; + } else { + return false; + } + } + }; + + /* + * Draws a line from start to end using a algorithm from + * https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm + * No idea if the one I picked is best but it's the one + * that works in the shiterator requirements and produced + * good results. + * + * _WARNING_: This one doesn't check if the start/end are + * within your Matrix, as it's assumed _you_ did that + * already. + */ + struct line { + int x; + int y; + int x1; + int y1; + int sx; + int sy; + int dx; + int dy; + int error; + + line(Point start, Point end) : + x(start.x), y(start.y), + x1(end.x), y1(end.y) + { + dx = std::abs(x1 - x); + sx = x < x1 ? 1 : -1; + dy = std::abs(y1 - y) * -1; + sy = y < y1 ? 1 : -1; + error = dx + dy; + } + + bool next() { + if(x != x1 || y != y1) { + int e2 = 2 * error; + + if(e2 >= dy) { + error = error + dy; + x = x + sx; + } + + if(e2 <= dx) { + error = error + dx; + y = y + sy; + } + return true; + } else { + return false; + } + } + }; + + /* + * Draws a simple circle using a fairly naive algorithm + * but one that actually worked. So, so, so, so many + * circle drawing algorithms described online don't work + * or are flat wrong. Even the very best I could find + * did overdrawing of multiple lines or simply got the + * math wrong. Keep in mind, _I_ am bad at this trig math + * so if I'm finding errors in your circle drawing then + * you got problems. + * + * This one is real simple, and works. If you got better + * then take the challenge but be ready to get it wrong. + */ + template + struct circle_t { + float center_x; + float center_y; + float radius = 0.0f; + int y = 0; + int dx = 0; + int dy = 0; + int left = 0; + int right = 0; + int top = 0; + int bottom = 0; + int width = 0; + int height = 0; + + circle_t(MAT &mat, Point center, float radius) : + center_x(center.x), center_y(center.y), radius(radius) + { + width = shiterator::width(mat); + height = shiterator::height(mat); + top = max(int(floor(center_y - radius)), 0); + bottom = min(int(floor(center_y + radius)), height - 1); + + y = top; + } + + bool next() { + y++; + if(y <= bottom) { + dy = y - center_y; + dx = floor(sqrt(radius * radius - dy * dy)); + left = max(0, int(center_x) - dx); + right = min(width, int(center_x) + dx + 1); + return true; + } else { + return false; + } + } + }; + + /* + * Basic rectangle shiterator, and like box and rando_rect_t you can + * pass only 1 parameter for size to do a square. + */ + template + struct rectangle_t { + int x; + int y; + int top; + int left; + int width; + int height; + int right; + int bottom; + + rectangle_t(MAT &mat, size_t start_x, size_t start_y, size_t size) : + rectangle_t(mat, start_x, start_y, size, size) { + } + + rectangle_t(MAT &mat, size_t start_x, size_t start_y, size_t width, size_t height) : + top(start_y), + left(start_x), + width(width), + height(height) + { + size_t h = shiterator::height(mat); + size_t w = shiterator::width(mat); + y = start_y - 1; + x = left - 1; // must be -1 for next() + right = min(start_x + width, w); + + y = start_y; + bottom = min(start_y + height, h); + } + + bool next() { + x = next_x(x, right); + y = next_y(x, y); + x = max(x, left); + return at_end(y, bottom); + } + }; + + /* + * WIP: This one is used to place entities randomly but + * could be used for effects like random destruction of floors. + * It simply "wraps" the rectangle_t but randomizes the x/y values + * using a random starting point. This makes it random across the + * x-axis but only partially random across the y. + */ + template + struct rando_rect_t { + int x; + int y; + int x_offset; + int y_offset; + rectangle_t it; + + rando_rect_t(MAT &mat, size_t start_x, size_t start_y, size_t size) : + rando_rect_t(mat, start_x, start_y, size, size) { + } + + rando_rect_t(MAT &mat, size_t start_x, size_t start_y, size_t width, size_t height) : + it{mat, start_x, start_y, width, height} + { + x_offset = Random::uniform(0, int(width)); + y_offset = Random::uniform(0, int(height)); + } + + bool next() { + bool done = it.next(); + x = it.left + ((it.x + x_offset) % it.width); + y = it.top + ((it.y + y_offset) % it.height); + return done; + } + }; + + /* + * BROKEN: I'm actually not sure what I'm trying to + * do here yet. + */ + template + struct viewport_t { + Point start; + // this is the point in the map + size_t x; + size_t y; + // this is the point inside the box, start at 0 + size_t view_x = ~0; + size_t view_y = ~0; + // viewport width/height + size_t width; + size_t height; + + viewport_t(MAT &mat, Point start, int max_x, int max_y) : + start(start), + x(start.x-1), + y(start.y-1) + { + width = std::min(size_t(max_x), shiterator::width(mat) - start.x); + height = std::min(size_t(max_y), shiterator::height(mat) - start.y); + fmt::println("viewport_t max_x, max_y {},{} vs matrix {},{}, x={}, y={}", + max_x, max_y, shiterator::width(mat), shiterator::height(mat), x, y); + } + + bool next() { + y = next_y(x, y); + x = next_x(x, width); + view_x = next_x(view_x, width); + view_y = next_y(view_x, view_y); + return at_end(y, height); + } + }; + +} diff --git a/tests/matrix.cpp b/tests/matrix.cpp index 490e50f..5dd3fb9 100644 --- a/tests/matrix.cpp +++ b/tests/matrix.cpp @@ -183,34 +183,6 @@ TEST_CASE("thrash compass iterators", "[matrix:compass]") { } } -TEST_CASE("prototype flood algorithm", "[matrix:flood]") { - for(int count = 0; count < 20; count++) { - size_t width = Random::uniform(10, 25); - size_t height = Random::uniform(10, 33); - - Map map(width,height); - WorldBuilder builder(map); - builder.generate_map(); - - if(map.room_count() < 2) continue; - - Point start; - REQUIRE(map.place_entity(map.room_count() / 2, start)); - map.set_target(start); - map.make_paths(); - Matrix result = map.paths(); - - // matrix::dump("WALLS BEFORE FLOOD", result, start.x, start.y); - - for(matrix::flood it{result, start, 3, 15}; it.next();) { - REQUIRE(matrix::inbounds(result, it.x, it.y)); - result[it.y][it.x] = 15; - } - - // matrix::dump("WALLS AFTER FLOOD", result, start.x, start.y); - } -} - TEST_CASE("prototype line algorithm", "[matrix:line]") { size_t width = Random::uniform(10, 12); size_t height = Random::uniform(10, 15);