From b1f20338809f8dc99eefda69a990ade3a1e29cd0 Mon Sep 17 00:00:00 2001 From: Gregor Klevze Date: Thu, 25 Dec 2025 10:15:23 +0100 Subject: [PATCH] Scaffold the pure game model - Added a pure, SDL-free Board model implementing grid access and clearFullLines(). - Added a small standalone test at test_board.cpp (simple assert-based; not yet wired into CMake). --- src/logic/Board.cpp | 59 ++++++++++++++++++++++++++++++++++++++++++++ src/logic/Board.h | 32 ++++++++++++++++++++++++ tests/test_board.cpp | 31 +++++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 src/logic/Board.cpp create mode 100644 src/logic/Board.h create mode 100644 tests/test_board.cpp diff --git a/src/logic/Board.cpp b/src/logic/Board.cpp new file mode 100644 index 0000000..ca6ef59 --- /dev/null +++ b/src/logic/Board.cpp @@ -0,0 +1,59 @@ +#include "Board.h" +#include + +namespace logic { + +Board::Board() + : grid_(Width * Height, Cell::Empty) +{ +} + +void Board::clear() +{ + std::fill(grid_.begin(), grid_.end(), Cell::Empty); +} + +bool Board::inBounds(int x, int y) const +{ + return x >= 0 && x < Width && y >= 0 && y < Height; +} + +Board::Cell Board::at(int x, int y) const +{ + if (!inBounds(x, y)) return Cell::Empty; + return grid_[y * Width + x]; +} + +void Board::set(int x, int y, Cell c) +{ + if (!inBounds(x, y)) return; + grid_[y * Width + x] = c; +} + +int Board::clearFullLines() +{ + int cleared = 0; + // scan from bottom to top + for (int y = Height - 1; y >= 0; --y) { + bool full = true; + for (int x = 0; x < Width; ++x) { + if (at(x, y) == Cell::Empty) { full = false; break; } + } + if (full) { + // remove row y: move all rows above down by one + for (int yy = y; yy > 0; --yy) { + for (int x = 0; x < Width; ++x) { + grid_[yy * Width + x] = grid_[(yy - 1) * Width + x]; + } + } + // clear top row + for (int x = 0; x < Width; ++x) grid_[x] = Cell::Empty; + ++cleared; + // stay on same y to re-check the row that fell into place + ++y; // because next iteration decrements y + } + } + return cleared; +} + +} // namespace logic diff --git a/src/logic/Board.h b/src/logic/Board.h new file mode 100644 index 0000000..6ed5a27 --- /dev/null +++ b/src/logic/Board.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +namespace logic { + +class Board { +public: + static constexpr int Width = 10; + static constexpr int Height = 20; + + enum class Cell : uint8_t { Empty = 0, Filled = 1 }; + + Board(); + + void clear(); + + Cell at(int x, int y) const; + void set(int x, int y, Cell c); + bool inBounds(int x, int y) const; + + // Remove and return number of full lines cleared. Rows above fall down. + int clearFullLines(); + + const std::vector& data() const { return grid_; } + +private: + std::vector grid_; // row-major: y*Width + x +}; + +} // namespace logic diff --git a/tests/test_board.cpp b/tests/test_board.cpp new file mode 100644 index 0000000..babc08a --- /dev/null +++ b/tests/test_board.cpp @@ -0,0 +1,31 @@ +#include "../src/logic/Board.h" +#include +#include + +int main() +{ + using logic::Board; + Board b; + // board starts empty + for (int y = 0; y < Board::Height; ++y) + for (int x = 0; x < Board::Width; ++x) + assert(b.at(x,y) == Board::Cell::Empty); + + // fill a single full row at bottom + int y = Board::Height - 1; + for (int x = 0; x < Board::Width; ++x) b.set(x, y, Board::Cell::Filled); + int cleared = b.clearFullLines(); + assert(cleared == 1); + // bottom row should be empty after clear + for (int x = 0; x < Board::Width; ++x) assert(b.at(x, Board::Height - 1) == Board::Cell::Empty); + + // fill two non-adjacent rows and verify both clear + int y1 = Board::Height - 1; + int y2 = Board::Height - 3; + for (int x = 0; x < Board::Width; ++x) { b.set(x, y1, Board::Cell::Filled); b.set(x, y2, Board::Cell::Filled); } + cleared = b.clearFullLines(); + assert(cleared == 2); + + std::cout << "tests/test_board: all assertions passed\n"; + return 0; +}