From cd745f0eda327be65399c3194bdccde621aa936b Mon Sep 17 00:00:00 2001 From: Connor Date: Fri, 20 Feb 2026 18:24:27 +0900 Subject: [PATCH] 8x8 grid --- include/Types/Grid8x8.h | 48 +++++++++++ tests/Util/test_Grid8x8.cpp | 167 ++++++++++++++++++++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 include/Types/Grid8x8.h create mode 100644 tests/Util/test_Grid8x8.cpp diff --git a/include/Types/Grid8x8.h b/include/Types/Grid8x8.h new file mode 100644 index 0000000..edd6857 --- /dev/null +++ b/include/Types/Grid8x8.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +// 8x8 bitset grid. Each bit represents a cell: bit index = (y << 3) | x. +struct Grid8x8 final +{ + struct Cell + { + int X, Y; + }; + + class Iterator + { + uint64_t Remaining; + + public: + explicit Iterator(uint64_t bits) : Remaining(bits) {} + + bool operator!=(const Iterator& other) const { return Remaining != other.Remaining; } + + Cell operator*() const + { + int pos = __builtin_ctzll(Remaining); + return { pos & 7, pos >> 3 }; + } + + Iterator& operator++() + { + Remaining &= Remaining - 1; // clear lowest set bit + return *this; + } + }; + + Iterator begin() const { return Iterator(Bits); } + Iterator end() const { return Iterator(0); } + + // Number of filled cells. + int Count() const { return __builtin_popcountll(Bits); } + + bool Get(int x, int y) const { return (Bits >> ((y << 3) | x)) & 1; } + void Set(int x, int y) { Bits |= uint64_t(1) << ((y << 3) | x); } + void Clear(int x, int y) { Bits &= ~(uint64_t(1) << ((y << 3) | x)); } + + uint64_t Bits = 0; +}; + +static_assert(sizeof(Grid8x8) == 8); diff --git a/tests/Util/test_Grid8x8.cpp b/tests/Util/test_Grid8x8.cpp new file mode 100644 index 0000000..8d09e9d --- /dev/null +++ b/tests/Util/test_Grid8x8.cpp @@ -0,0 +1,167 @@ +#include +#include +#include "Types/Grid8x8.h" + +TEST_SUITE("Grid8x8") +{ + TEST_CASE("default construction - all cells empty") + { + Grid8x8 g; + CHECK(g.Bits == 0); + CHECK(g.Count() == 0); + } + + TEST_CASE("Set and Get single cell") + { + Grid8x8 g; + g.Set(3, 5); + CHECK(g.Get(3, 5)); + CHECK_FALSE(g.Get(0, 0)); + CHECK_FALSE(g.Get(3, 4)); + CHECK_FALSE(g.Get(4, 5)); + } + + TEST_CASE("Clear cell") + { + Grid8x8 g; + g.Set(2, 2); + CHECK(g.Get(2, 2)); + g.Clear(2, 2); + CHECK_FALSE(g.Get(2, 2)); + CHECK(g.Count() == 0); + } + + TEST_CASE("Set does not affect other cells") + { + Grid8x8 g; + g.Set(0, 0); + g.Set(7, 7); + CHECK(g.Get(0, 0)); + CHECK(g.Get(7, 7)); + CHECK_FALSE(g.Get(0, 7)); + CHECK_FALSE(g.Get(7, 0)); + CHECK(g.Count() == 2); + } + + TEST_CASE("Count matches number of set cells") + { + Grid8x8 g; + g.Set(0, 0); + g.Set(1, 0); + g.Set(0, 1); + CHECK(g.Count() == 3); + g.Clear(1, 0); + CHECK(g.Count() == 2); + } + + TEST_CASE("Set same cell twice does not increase Count") + { + Grid8x8 g; + g.Set(4, 4); + g.Set(4, 4); + CHECK(g.Count() == 1); + } + + TEST_CASE("iterator - empty grid yields no cells") + { + Grid8x8 g; + int count = 0; + for (auto cell : g) + ++count; + CHECK(count == 0); + } + + TEST_CASE("iterator - single cell") + { + Grid8x8 g; + g.Set(3, 6); + + std::vector cells; + for (auto cell : g) + cells.push_back(cell); + + REQUIRE(cells.size() == 1); + CHECK(cells[0].X == 3); + CHECK(cells[0].Y == 6); + } + + TEST_CASE("iterator - corners") + { + Grid8x8 g; + g.Set(0, 0); + g.Set(7, 0); + g.Set(0, 7); + g.Set(7, 7); + + std::vector cells; + for (auto cell : g) + cells.push_back(cell); + + REQUIRE(cells.size() == 4); + + // Iterator visits in bit-index order (row-major: left-to-right, top-to-bottom) + CHECK(cells[0].X == 0); CHECK(cells[0].Y == 0); + CHECK(cells[1].X == 7); CHECK(cells[1].Y == 0); + CHECK(cells[2].X == 0); CHECK(cells[2].Y == 7); + CHECK(cells[3].X == 7); CHECK(cells[3].Y == 7); + } + + TEST_CASE("iterator - full grid visits all 64 cells") + { + Grid8x8 g; + g.Bits = ~uint64_t(0); + + int count = 0; + bool seen[8][8] = {}; + for (auto cell : g) + { + CHECK(cell.X >= 0); CHECK(cell.X < 8); + CHECK(cell.Y >= 0); CHECK(cell.Y < 8); + CHECK_FALSE(seen[cell.Y][cell.X]); + seen[cell.Y][cell.X] = true; + ++count; + } + CHECK(count == 64); + } + + TEST_CASE("iterator count matches Count()") + { + Grid8x8 g; + g.Set(1, 2); + g.Set(3, 4); + g.Set(5, 6); + g.Set(7, 1); + + int iterated = 0; + for (auto cell : g) + ++iterated; + + CHECK(iterated == g.Count()); + } + + TEST_CASE("iterator - coordinates decode correctly for every cell") + { + for (int y = 0; y < 8; ++y) + { + for (int x = 0; x < 8; ++x) + { + Grid8x8 g; + g.Set(x, y); + + int count = 0; + for (auto cell : g) + { + CHECK(cell.X == x); + CHECK(cell.Y == y); + ++count; + } + CHECK(count == 1); + } + } + } + + TEST_CASE("size is 8 bytes") + { + static_assert(sizeof(Grid8x8) == 8); + } +}