Finally have a name for my little matrix shape iterator library. I present to you, the SHITErators. Are they THE shit? Shit? Or just Shite?

main
Zed A. Shaw 11 months ago
parent ffdea41faa
commit 73b85e8057
  1. 66
      matrix.cpp
  2. 386
      matrix.hpp
  3. 530
      shiterator.hpp
  4. 28
      tests/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);

@ -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<int>;
using Matrix = shiterator::Base<int>;
template<typename T>
using BaseRow = vector<T>;
using viewport = shiterator::viewport_t<Matrix>;
template<typename T>
using Base = vector<BaseRow<T>>;
using each_cell = shiterator::each_cell_t<Matrix>;
using Row = vector<int>;
using Matrix = vector<Row>;
using each_row = shiterator::each_row_t<Matrix>;
using box = shiterator::box_t<Matrix>;
using compass = shiterator::compass_t<Matrix>;
using circle = shiterator::circle_t<Matrix>;
using rectangle = shiterator::rectangle_t<Matrix>;
using rando_rect = shiterator::rando_rect_t<Matrix>;
using line = shiterator::line;
/*
* Just a quick thing to reset a matrix to a value.
*/
template<typename MAT, typename VAL>
inline void assign(MAT &out, VAL new_value) {
for(auto &row : out) {
row.assign(row.size(), new_value);
}
}
template<typename MAT>
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<typename MAT>
inline size_t width(MAT &mat) {
return mat[0].size();
}
template<typename MAT>
inline size_t height(MAT &mat) {
return mat.size();
}
template<typename T>
inline Base<T> make_base(size_t width, size_t height) {
Base<T> result(height, BaseRow<T>(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<int>(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<typename MAT>
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<typename MAT>
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<Matrix>;
using each_cell = each_cell_t<Matrix>;
template<typename MAT>
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<Matrix>;
template<typename MAT>
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<Matrix>;
template<typename MAT>
struct compass_t {
size_t x = 0; // these are set in constructor
size_t y = 0; // again, no fancy ~ trick needed
array<int, 4> x_dirs{0, 1, 0, -1};
array<int, 4> 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<int, 4> x_in{0, 1, 0, -1};
array<int, 4> 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<Matrix>;
struct flood {
Matrix &mat;
Point start;
int old_val;
int new_val;
queue<Point> 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<typename MAT>
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<Matrix>;
template<typename MAT>
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<Matrix>;
template<typename MAT>
struct rando_rect_t {
int x;
int y;
int x_offset;
int y_offset;
rectangle_t<MAT> 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<Matrix>;
}

@ -0,0 +1,530 @@
#pragma once
#include <vector>
#include <queue>
#include <string>
#include <array>
#include <numeric>
#include <algorithm>
#include <fmt/core.h>
#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<typename T>
using BaseRow = vector<T>;
template<typename T>
using Base = vector<BaseRow<T>>;
template<typename T>
inline Base<T> make(size_t width, size_t height) {
Base<T> result(height, BaseRow<T>(width));
return result;
}
/*
* Just a quick thing to reset a matrix to a value.
*/
template<typename MAT, typename VAL>
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<typename MAT>
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<typename MAT>
inline size_t width(MAT &mat) {
return mat[0].size();
}
/*
* Same as shiterator::width but just the height.
*/
template<typename MAT>
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<typename MAT>
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<typename MAT>
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<typename MAT>
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<typename MAT>
struct compass_t {
size_t x = 0; // these are set in constructor
size_t y = 0; // again, no fancy ~ trick needed
array<int, 4> x_dirs{0, 1, 0, -1};
array<int, 4> 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<int, 4> x_in{0, 1, 0, -1};
array<int, 4> 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<typename MAT>
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<typename MAT>
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<typename MAT>
struct rando_rect_t {
int x;
int y;
int x_offset;
int y_offset;
rectangle_t<MAT> 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<typename MAT>
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);
}
};
}

@ -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<size_t>(10, 25);
size_t height = Random::uniform<size_t>(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<size_t>(10, 12);
size_t height = Random::uniform<size_t>(10, 15);