Add some benchmarks, add Mask and some minor refactoring

This commit is contained in:
Zachary D. Rowitsch 2023-05-03 23:52:33 -04:00
parent abe851bfa4
commit 48917f4770
24 changed files with 624 additions and 100 deletions

View File

@ -26,4 +26,12 @@ add_executable(colored_maze ColoredMaze.cpp)
target_link_libraries(colored_maze PRIVATE sfml-window sfml-graphics maze)
add_executable(dead_ends DeadEnds.cpp)
target_link_libraries(dead_ends maze)
target_link_libraries(dead_ends maze)
add_executable(simple_mask SimpleMask.cpp)
target_link_libraries(simple_mask maze)
add_executable(mask_from_file MaskFromFile.cpp)
target_link_libraries(mask_from_file PRIVATE sfml-window sfml-graphics maze)
file(COPY data DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

View File

@ -1,5 +1,4 @@
// Simple demo to color a maze based on distance from middle of the maze
#include "sfLine.h"
#include "Grid.h"
#include "Sidewinder.h"
@ -8,61 +7,11 @@
#include "Wilsons.h"
#include "HuntAndKill.h"
#include "RecursiveBacktracker.h"
#include "SfmlUtils.h"
#include <SFML/Graphics.hpp>
sf::Color interpolateColors(const sf::Color& c1, const sf::Color& c2, float t) {
auto r = static_cast<sf::Uint8>((1 - t) * c1.r + t * c2.r);
auto g = static_cast<sf::Uint8>((1 - t) * c1.g + t * c2.g);
auto b = static_cast<sf::Uint8>((1 - t) * c1.b + t * c2.b);
return {r, g, b};
}
void lineTo(sf::RenderTexture& render_texture, int x1, int y1, int x2, int y2, const sf::Color& color) {
sfLine line(sf::Vector2f(x1, y1), sf::Vector2f(x2, y2), color);
render_texture.draw(line);
}
void drawGridToTexture(sf::RenderTexture& render_texture, const DistanceGrid& grid, int cell_size) {
auto width = grid.getCols() * cell_size;
auto height = grid.getRows() * cell_size;
if (!render_texture.create(width, height)) {
throw std::runtime_error("Failed to create render texture");
}
auto background = sf::Color::White;
auto wall = sf::Color::Black;
auto start = sf::Color::Blue;
auto goal = sf::Color::Red;
render_texture.clear(background);
for (auto& cell : grid) {
auto x1 = cell.getCol() * cell_size;
auto y1 = cell.getRow() * cell_size;
auto x2 = (cell.getCol() + 1) * cell_size;
auto y2 = (cell.getRow() + 1) * cell_size;
sf::Color color = interpolateColors(start, goal, grid.intensityAt(cell));
sf::RectangleShape rectangle(sf::Vector2f(cell_size, cell_size));
rectangle.setPosition(x1, y1);
rectangle.setFillColor(color);
render_texture.draw(rectangle);
if (!cell.hasNorth()) {
lineTo(render_texture, x1, y1, x2, y1, wall);
}
if (!cell.hasWest()) {
lineTo(render_texture, x1, y1, x1, y2, wall);
}
if (!cell.isLinked(cell.east)) {
lineTo(render_texture, x2, y1, x2, y2, wall);
}
if (!cell.isLinked(cell.south)) {
lineTo(render_texture, x1, y2, x2, y2, wall);
}
}
render_texture.display();
}
const int CELL_SIZE = 50;
const int WIDTH = 30;
@ -137,7 +86,7 @@ int main() {
window.clear(sf::Color::Blue);
sf::RenderTexture render_texture;
drawGridToTexture(render_texture, grid, CELL_SIZE);
utils::sfml::drawGridToTexture(render_texture, grid, CELL_SIZE);
const sf::Texture& texture = render_texture.getTexture();
sf::Sprite sprite(texture);
window.draw(sprite);

38
demos/MaskFromFile.cpp Normal file
View File

@ -0,0 +1,38 @@
#include "Mask.h"
#include "Grid.h"
#include "RecursiveBacktracker.h"
#include "SfmlUtils.h"
#include <iostream>
#include <SFML/Graphics.hpp>
const int CELL_SIZE = 50;
int main() {
Mask mask = Mask::fromFile("data/mask.txt");
MaskedGrid grid(mask);
RecursiveBacktracker::on(grid);
sf::RenderWindow window(sf::VideoMode(grid.getCols() * CELL_SIZE, grid.getRows() * CELL_SIZE),
"s: Sidewinder b: BinaryTree a: AldousBroder w: Wilsons h: HuntAndKill, r: RecursiveBacktracker");
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)) {
window.close();
}
window.clear(sf::Color::Blue);
sf::RenderTexture render_texture;
utils::sfml::drawGridToTexture(render_texture, grid, CELL_SIZE);
const sf::Texture &texture = render_texture.getTexture();
sf::Sprite sprite(texture);
window.draw(sprite);
window.display();
}
}

152
demos/SfmlUtils.h Normal file
View File

@ -0,0 +1,152 @@
//Utils to make easy SFML based demos with Maze library
#pragma once
#include "Grid.h"
#include <SFML/Graphics.hpp>
#include <cmath>
namespace utils {
namespace sfml {
//From: https://github.com/SFML/SFML/wiki/Source:-Line-segment-with-thickness
class sfLine : public sf::Drawable
{
public:
sfLine(const sf::Vector2f& point1, const sf::Vector2f& point2, const sf::Color& color = sf::Color::Yellow):
color(color), thickness(5.f)
{
sf::Vector2f direction = point2 - point1;
sf::Vector2f unitDirection = direction/std::sqrt(direction.x*direction.x+direction.y*direction.y);
sf::Vector2f unitPerpendicular(-unitDirection.y,unitDirection.x);
sf::Vector2f offset = (thickness/2.f)*unitPerpendicular;
vertices[0].position = point1 + offset;
vertices[1].position = point2 + offset;
vertices[2].position = point2 - offset;
vertices[3].position = point1 - offset;
for (int i=0; i<4; ++i)
vertices[i].color = color;
}
void draw(sf::RenderTarget &target, sf::RenderStates states) const
{
target.draw(vertices,4,sf::Quads);
}
private:
sf::Vertex vertices[4];
float thickness;
sf::Color color;
};
sf::Color interpolateColors(const sf::Color& c1, const sf::Color& c2, float t) {
auto r = static_cast<sf::Uint8>((1 - t) * c1.r + t * c2.r);
auto g = static_cast<sf::Uint8>((1 - t) * c1.g + t * c2.g);
auto b = static_cast<sf::Uint8>((1 - t) * c1.b + t * c2.b);
return {r, g, b};
}
void lineTo(sf::RenderTexture& render_texture, int x1, int y1, int x2, int y2, const sf::Color& color) {
sfLine line(sf::Vector2f(x1, y1), sf::Vector2f(x2, y2), color);
render_texture.draw(line);
}
void drawGridLinesToTexture(sf::RenderTexture& render_texture, const Grid& grid, int cell_size) {
const auto wall = sf::Color::Black;
for (auto& cell : grid) {
auto x1 = cell.getCol() * cell_size;
auto y1 = cell.getRow() * cell_size;
auto x2 = (cell.getCol() + 1) * cell_size;
auto y2 = (cell.getRow() + 1) * cell_size;
if (!cell.hasNorth()) {
lineTo(render_texture, x1, y1, x2, y1, wall);
}
if (!cell.hasWest()) {
lineTo(render_texture, x1, y1, x1, y2, wall);
}
if (!cell.isLinked(cell.east)) {
lineTo(render_texture, x2, y1, x2, y2, wall);
}
if (!cell.isLinked(cell.south)) {
lineTo(render_texture, x1, y2, x2, y2, wall);
}
}
}
void drawDistanceGridBackgroundsToTexture(sf::RenderTexture& render_texture, const DistanceGrid& grid, int cell_size) {
const auto start = sf::Color::Blue;
const auto goal = sf::Color::Red;
for (auto& cell : grid) {
auto x1 = cell.getCol() * cell_size;
auto y1 = cell.getRow() * cell_size;
auto x2 = (cell.getCol() + 1) * cell_size;
auto y2 = (cell.getRow() + 1) * cell_size;
sf::Color color = interpolateColors(start, goal, grid.intensityAt(cell));
sf::RectangleShape rectangle(sf::Vector2f(cell_size, cell_size));
rectangle.setPosition(x1, y1);
rectangle.setFillColor(color);
render_texture.draw(rectangle);
}
}
void drawMaskedGridBackgroundsToTexture(sf::RenderTexture& render_texture, const MaskedGrid& grid, int cell_size) {
const auto disabled = sf::Color::Black;
for (auto& cell : grid) {
if (!grid.isCellEnabled(cell)) {
auto x1 = cell.getCol() * cell_size;
auto y1 = cell.getRow() * cell_size;
sf::RectangleShape rectangle(sf::Vector2f(cell_size, cell_size));
rectangle.setPosition(x1, y1);
rectangle.setFillColor(disabled);
render_texture.draw(rectangle);
}
}
}
void drawGridToTexture(sf::RenderTexture& render_texture, const MaskedGrid& grid, int cell_size) {
const auto width = grid.getCols() * cell_size;
const auto height = grid.getRows() * cell_size;
if (!render_texture.create(width, height)) {
throw std::runtime_error("Failed to create render texture");
}
auto background = sf::Color::White;
render_texture.clear(background);
drawGridLinesToTexture(render_texture, grid, cell_size);
drawMaskedGridBackgroundsToTexture(render_texture, grid, cell_size);
render_texture.display();
}
void drawGridToTexture(sf::RenderTexture& render_texture, const DistanceGrid& grid, int cell_size) {
const auto width = grid.getCols() * cell_size;
const auto height = grid.getRows() * cell_size;
if (!render_texture.create(width, height)) {
throw std::runtime_error("Failed to create render texture");
}
auto background = sf::Color::White;
render_texture.clear(background);
drawDistanceGridBackgroundsToTexture(render_texture, grid, cell_size);
drawGridLinesToTexture(render_texture, grid, cell_size);
render_texture.display();
}
void drawGridToTexture(sf::RenderTexture& render_texture, const Grid& grid, int cell_size) {
const auto width = grid.getCols() * cell_size;
const auto height = grid.getRows() * cell_size;
if (!render_texture.create(width, height)) {
throw std::runtime_error("Failed to create render texture");
}
auto background = sf::Color::White;
render_texture.clear(background);
drawGridLinesToTexture(render_texture, grid, cell_size);
render_texture.display();
}
}
}

15
demos/SimpleMask.cpp Normal file
View File

@ -0,0 +1,15 @@
#include "Mask.h"
#include "Grid.h"
#include "RecursiveBacktracker.h"
#include <iostream>
int main() {
Mask mask(10, 10);
mask.disable(0, 0);
mask.disable(0, 1);
mask.disable(1, 0);
MaskedGrid grid(mask);
RecursiveBacktracker::on(grid);
std::cout << grid.toString() << std::endl;
}

10
demos/data/mask.txt Normal file
View File

@ -0,0 +1,10 @@
X........X
....XX....
...XXXX...
....XX....
X........X
X........X
....XX....
...XXXX...
....XX....
X........X

View File

@ -1,38 +0,0 @@
#pragma once
#include <SFML/Graphics.hpp>
#include <cmath>
//From: https://github.com/SFML/SFML/wiki/Source:-Line-segment-with-thickness
class sfLine : public sf::Drawable
{
public:
sfLine(const sf::Vector2f& point1, const sf::Vector2f& point2, const sf::Color& color = sf::Color::Yellow):
color(color), thickness(5.f)
{
sf::Vector2f direction = point2 - point1;
sf::Vector2f unitDirection = direction/std::sqrt(direction.x*direction.x+direction.y*direction.y);
sf::Vector2f unitPerpendicular(-unitDirection.y,unitDirection.x);
sf::Vector2f offset = (thickness/2.f)*unitPerpendicular;
vertices[0].position = point1 + offset;
vertices[1].position = point2 + offset;
vertices[2].position = point2 - offset;
vertices[3].position = point1 - offset;
for (int i=0; i<4; ++i)
vertices[i].color = color;
}
void draw(sf::RenderTarget &target, sf::RenderStates states) const
{
target.draw(vertices,4,sf::Quads);
}
private:
sf::Vertex vertices[4];
float thickness;
sf::Color color;
};

View File

@ -1,3 +1,3 @@
add_library(maze STATIC Cell.cpp Grid.cpp BinaryTree.cpp Sidewinder.cpp Distances.cpp AldousBroder.cpp Wilsons.cpp Utils.cpp HuntAndKill.cpp RecursiveBacktracker.cpp)
add_library(maze STATIC Cell.cpp Grid.cpp BinaryTree.cpp Sidewinder.cpp Distances.cpp AldousBroder.cpp Wilsons.cpp Utils.cpp HuntAndKill.cpp RecursiveBacktracker.cpp Mask.cpp)
target_include_directories(maze PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -127,6 +127,8 @@ std::vector<std::reference_wrapper<Cell>> Grid::deadEnds() {
return dead_ends;
}
DistanceGrid::DistanceGrid(int rows, int cols) : Grid(rows, cols), distances(getCellRef(0, 0).distances()), max_distance(0) {};
std::string DistanceGrid::contentsOf(const Cell &cell) const {
if (distances.contains(cell)) {
std::string str = std::to_string(distances.getDistance(cell));
@ -155,3 +157,37 @@ float DistanceGrid::intensityAt(const Cell &cell) const {
}
return intensity;
}
//TODO: Is there a better way to do this than to do it twice? (base class is already running configure cells)
// I guess we could pull Grid into a BaseGrid with some pure virtual methods and make each call configureCells.
MaskedGrid::MaskedGrid(Mask mask) : Grid(mask.getRows(), mask.getCols()), mask(mask) {
configureCells();
};
Cell &MaskedGrid::randomCell() {
auto [row, col] = mask.randomLocation();
return getCellRef(col, row);
}
int MaskedGrid::size() const {
return mask.size();
}
Cell* MaskedGrid::operator()(int col, int row) {
if (row < 0 || row >= rows) {
return nullptr;
}
if (col < 0 || col >= cols) {
return nullptr;
}
if (!mask(row, col)) {
return nullptr;
}
return &grid[row * cols + col];
}
bool MaskedGrid::isCellEnabled(const Cell &cell) const {
return mask(cell);
}

View File

@ -2,6 +2,7 @@
#include "Cell.h"
#include "Distances.h"
#include "Mask.h"
#include <vector>
#include <iostream>
@ -11,10 +12,10 @@ public:
Grid(int rows, int cols);
[[nodiscard]] int getRows() const;
[[nodiscard]] int getCols() const;
[[nodiscard]] int size() const;
[[nodiscard]] Cell& randomCell();
[[nodiscard]] virtual int size() const;
[[nodiscard]] virtual Cell& randomCell();
[[nodiscard]] Cell* operator()(int col, int row);
[[nodiscard]] virtual Cell* operator()(int col, int row);
friend std::ostream& operator<< (std::ostream &os, const Grid &grid);
[[nodiscard]] Cell& getCellRef(int col, int row);
[[nodiscard]] const Cell& getCellRef(int col, int row) const;
@ -154,7 +155,7 @@ public:
return {*this};
}
private:
protected:
int rows;
int cols;
std::vector<Cell> grid;
@ -165,7 +166,7 @@ private:
class DistanceGrid : public Grid {
public:
DistanceGrid(int rows, int cols) : Grid(rows, cols), distances(getCellRef(0, 0).distances()), max_distance(0) {}
DistanceGrid(int rows, int cols);
[[nodiscard]] std::string contentsOf(const Cell& cell) const override;
[[nodiscard]] float intensityAt(const Cell& cell) const;
void setStart(const Cell& cell);
@ -173,3 +174,15 @@ public:
private:
int max_distance;
};
class MaskedGrid : public Grid {
public:
MaskedGrid(Mask mask);
[[nodiscard]] Cell& randomCell() override;
[[nodiscard]] int size() const override;
[[nodiscard]] Cell* operator()(int col, int row) override;
[[nodiscard]] bool isCellEnabled(const Cell& cell) const;
private:
Mask mask;
};

109
src/Mask.cpp Normal file
View File

@ -0,0 +1,109 @@
#include "Mask.h"
#include "Cell.h"
#include "Utils.h"
#include <random>
#include <stdexcept>
#include <fstream>
#include <string>
static std::random_device rd;
static std::mt19937 gen(rd());
Mask::Mask(int rows, int columns): rows(rows), columns(columns), enabled_cells(rows * columns) {
mask = std::vector<bool>(rows * columns, true);
}
void Mask::enable(int row, int column) {
if (!mask[row * columns + column]) {
mask[row * columns + column] = true;
enabled_cells++;
}
}
void Mask::disable(int row, int column) {
if (mask[row * columns + column]) {
mask[row * columns + column] = false;
enabled_cells--;
}
}
bool Mask::operator()(int row, int column) const {
return mask[row * columns + column];
}
int Mask::count() const {
return enabled_cells;
}
bool Mask::operator()(const Cell &cell) const {
return (*this)(cell.getRow(), cell.getCol());
}
void Mask::enable(const Cell &cell) {
(*this).enable(cell.getRow(), cell.getCol());
}
void Mask::disable(const Cell &cell) {
(*this).disable(cell.getRow(), cell.getCol());
}
std::tuple<int, int> Mask::randomLocation() const {
if (enabled_cells == 0) {
throw std::runtime_error("No enabled cells in mask");
}
std::uniform_int_distribution<std::vector<Cell>::size_type> rows_dist(0, rows - 1);
std::uniform_int_distribution<std::vector<Cell>::size_type> cols_dist(0, columns - 1);
int row = rows_dist(gen);
int col = cols_dist(gen);
while (!(*this)(row, col)) {
row = rows_dist(gen);
col = cols_dist(gen);
}
return std::make_tuple(row, col);
}
Mask Mask::fromFile(const std::string &filename) {
std::ifstream file(filename);
if (!file.is_open()) {
throw std::runtime_error("Could not open file " + filename);
}
std::string line;
std::vector<std::string> lines;
int line_length = 0;
while (std::getline(file, line)) {
if (line.empty()) {
continue;
}
if (line[0] == '#') {
continue;
}
if (line_length == 0) {
line_length = line.length();
} else if (line_length != line.length()) {
throw std::runtime_error("Line length mismatch in file " + filename);
}
lines.emplace_back(line);
}
int rows = lines.size();
int columns = line_length;
Mask mask(rows, columns);
for (int row = 0; row < rows; row++) {
line = lines[row];
for (int col = 0; col < columns; col++) {
if (line[col] == '.') {
mask.enable(row, col);
} else if (line[col] == 'X') {
mask.disable(row, col);
} else {
throw std::runtime_error("Invalid character in file " + filename);
}
}
}
return mask;
}

34
src/Mask.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <vector>
#include <string>
class Cell;
class Mask {
public:
static Mask fromFile(const std::string& filename);
Mask(int rows, int columns);
[[nodiscard]] int count() const;
[[nodiscard]] bool operator()(int row, int column) const;
void enable(int row, int column);
void disable(int row, int column);
[[nodiscard]] bool operator()(const Cell& cell) const;
void enable(const Cell& cell);
void disable(const Cell& cell);
[[nodiscard]] int getRows() const { return rows; }
[[nodiscard]] int getCols() const { return columns; }
[[nodiscard]] int size() const { return enabled_cells; }
[[nodiscard]] std::tuple<int, int> randomLocation() const;
private:
std::vector<bool> mask;
int rows;
int columns;
int enabled_cells;
};

View File

@ -2,6 +2,7 @@
#include "Cell.h"
#include <random>
#include <stdexcept>
static std::random_device rd;
static std::mt19937 gen(rd());

View File

@ -2,6 +2,8 @@
#include "Cell.h"
#include <algorithm>
namespace utils {
bool randomBool();
Cell& randomElement(const std::vector<std::reference_wrapper<Cell>> &v);

View File

@ -1,5 +1,5 @@
add_executable(tests_runner main_test.cpp)
target_link_libraries(tests_runner Catch2::Catch2WithMain)
target_link_libraries(tests_runner Catch2::Catch2WithMain maze)
add_executable(tests_Cell Cell.cpp)
target_link_libraries(tests_Cell Catch2::Catch2WithMain maze)
@ -10,6 +10,11 @@ target_link_libraries(tests_Grid Catch2::Catch2WithMain maze)
add_executable(tests_Distances Distances.cpp)
target_link_libraries(tests_Distances Catch2::Catch2WithMain maze)
add_executable(tests_Mask Mask.cpp)
target_link_libraries(tests_Mask Catch2::Catch2WithMain maze)
file(COPY data DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
include(CTest)
include(Catch)
catch_discover_tests(tests_runner)

View File

@ -73,4 +73,21 @@ TEST_CASE("Count dead ends", "[grid]") {
grid(1, 0)->link(grid.getCellRef(0, 0));
grid(1, 0)->link(grid.getCellRef(2, 0));
REQUIRE(grid.deadEnds().size() == 2);
}
TEST_CASE("MaskedGrid", "[grid]") {
Mask mask(2, 2);
SECTION("MaskedGrid size with all entries enabled") {
MaskedGrid grid(mask);
REQUIRE(mask.size() == 4);
REQUIRE(grid.size() == 4);
}
SECTION("MaskedGrid size with a disabled cell") {
mask.disable(0, 0);
MaskedGrid grid(mask);
REQUIRE(mask.size() == 3);
REQUIRE(grid.size() == 3);
}
}

103
tests/Mask.cpp Normal file
View File

@ -0,0 +1,103 @@
#include <catch2/catch_all.hpp>
#include "Mask.h"
#include "Grid.h"
TEST_CASE("Mask is created with all cells enabled", "[Mask]") {
Mask mask(2, 2);
REQUIRE(mask(0, 0) == true);
REQUIRE(mask(0, 1) == true);
REQUIRE(mask(1, 0) == true);
REQUIRE(mask(1, 1) == true);
}
TEST_CASE("Test Mask cell enable/disable by coordinate", "[Mask]") {
Mask mask(2, 2);
mask.disable(0, 0);
REQUIRE(mask(0, 0) == false);
mask.enable(0,0);
REQUIRE(mask(0, 0) == true);
}
TEST_CASE("Test Mask cell enable/disable by Cell ref", "[Mask]") {
Grid grid(2, 2);
Mask mask(2, 2);
mask.disable(grid.getCellRef(0, 0));
REQUIRE(mask(0, 0) == false);
mask.enable(grid.getCellRef(0, 0));
REQUIRE(mask(0, 0) == true);
}
TEST_CASE("Mask count reflects number of enabled cells", "[Mask]") {
Mask mask(2, 2);
REQUIRE(mask.count() == 4);
mask.disable(0, 0);
REQUIRE(mask.count() == 3);
mask.disable(0, 1);
REQUIRE(mask.count() == 2);
mask.disable(1, 0);
REQUIRE(mask.count() == 1);
mask.disable(1, 1);
REQUIRE(mask.count() == 0);
}
TEST_CASE("Mask count reflects number of enabled cells with repetative enables", "[Mask]") {
Mask mask(2,2);
REQUIRE(mask.count() == 4);
mask.enable(0,0);
mask.enable(0,0);
REQUIRE(mask.count() == 4);
}
TEST_CASE("Mask count reflects number of enabled cells with repetative disables", "[Mask]") {
Mask mask(2,2);
REQUIRE(mask.count() == 4);
mask.disable(0,0);
mask.disable(0,0);
REQUIRE(mask.count() == 3);
}
TEST_CASE("Mask from file 2x2_on.txt", "[Mask]") {
Mask mask = Mask::fromFile("data/2x2_on.txt");
REQUIRE(mask(0, 0) == true);
REQUIRE(mask(0, 1) == true);
REQUIRE(mask(1, 0) == true);
REQUIRE(mask(1, 1) == true);
REQUIRE(mask.count() == 4);
}
TEST_CASE("Mask from file 2x2_off.txt", "[Mask]") {
Mask mask = Mask::fromFile("data/2x2_off.txt");
REQUIRE(mask(0, 0) == false);
REQUIRE(mask(0, 1) == false);
REQUIRE(mask(1, 0) == false);
REQUIRE(mask(1, 1) == false);
REQUIRE(mask.count() == 0);
}
TEST_CASE("Mask from file 3x3_half_on.txt", "[Mask]") {
Mask mask = Mask::fromFile("data/3x3_half_on.txt");
REQUIRE(mask(0, 0) == false);
REQUIRE(mask(0, 1) == true);
REQUIRE(mask(0, 2) == true);
REQUIRE(mask(1, 0) == false);
REQUIRE(mask(1, 1) == false);
REQUIRE(mask(1, 2) == true);
REQUIRE(mask(2, 0) == false);
REQUIRE(mask(2, 1) == false);
REQUIRE(mask(2, 2) == false);
REQUIRE(mask.count() == 3);
}
TEST_CASE("Mask from file with invalid char", "[Mask]") {
REQUIRE_THROWS(Mask::fromFile("data/mask_invalid_char.txt"));
}
TEST_CASE("Mask from file with mismatched line lengths", "[Mask]") {
REQUIRE_THROWS(Mask::fromFile("data/mask_mismatched_line_lengths.txt"));
}
TEST_CASE("Mask from more complex mask file", "[Mask]") {
Mask mask = Mask::fromFile("data/mask.txt");
REQUIRE(mask.size() == 76);
}

2
tests/data/2x2_off.txt Normal file
View File

@ -0,0 +1,2 @@
XX
XX

2
tests/data/2x2_on.txt Normal file
View File

@ -0,0 +1,2 @@
..
..

View File

@ -0,0 +1,3 @@
X..
XX.
XXX

10
tests/data/mask.txt Normal file
View File

@ -0,0 +1,10 @@
X........X
....XX....
...XXXX...
....XX....
X........X
X........X
....XX....
...XXXX...
....XX....
X........X

View File

@ -0,0 +1,3 @@
...
...
a..

View File

@ -0,0 +1,3 @@
...
...
....

View File

@ -1,4 +1,13 @@
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_all.hpp>
#include "AldousBroder.h"
#include "BinaryTree.h"
#include "Sidewinder.h"
#include "HuntAndKill.h"
#include "Wilsons.h"
#include "RecursiveBacktracker.h"
#include "Grid.h"
TEST_CASE("Sample Test", "[example]") {
REQUIRE(1 + 1 == 2);
@ -7,3 +16,41 @@ TEST_CASE("Sample Test", "[example]") {
TEST_CASE("Sample Test2", "[.][example]") {
REQUIRE(1 + 2 == 2);
}
TEST_CASE("Maze Generation Benchmarks") {
BENCHMARK("AldousBroder") {
Grid grid(20, 20);
AldousBroder::on(grid);
return grid;
};
BENCHMARK("BinaryTree") {
Grid grid(20, 20);
BinaryTree::on(grid);
return grid;
};
BENCHMARK("Sidewinder") {
Grid grid(20, 20);
Sidewinder::on(grid);
return grid;
};
BENCHMARK("HuntAndKill") {
Grid grid(20, 20);
HuntAndKill::on(grid);
return grid;
};
BENCHMARK("Wilsons") {
Grid grid(20, 20);
Wilsons::on(grid);
return grid;
};
BENCHMARK("RecursiveBacktracker") {
Grid grid(20, 20);
RecursiveBacktracker::on(grid);
return grid;
};
};