This commit is contained in:
Connor
2026-02-20 18:24:27 +09:00
parent fc3192cd1e
commit cd745f0eda
2 changed files with 215 additions and 0 deletions

48
include/Types/Grid8x8.h Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include <stdint.h>
// 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);

167
tests/Util/test_Grid8x8.cpp Normal file
View File

@@ -0,0 +1,167 @@
#include <doctest/doctest.h>
#include <vector>
#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<Grid8x8::Cell> 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<Grid8x8::Cell> 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);
}
}