#pragma once #include #include #include #include "game_rng.h" namespace roro::games { // Minesweeper (issue #101's follow-up): a field of 20 x 9 with 28 mines, cells big enough to read // on this screen. The mines are laid at // the first dig, never under it or next to it, so the first dig always opens something. Digging // an open number whose flags are all placed opens what is left around it. The clock runs from the // first dig to the end. class Mines { public: static constexpr int kCols = 20, kRows = 9, kMines = 28; // the classic "intermediate" share: one cell in six or seven enum Event : uint8_t { Opened = 1, Flagged = 2, Boom = 4, Won = 8 }; enum class Phase : uint8_t { Fresh, Running, Lost, Won }; explicit Mines(uint32_t seed) : rng_(seed) { start(); } void start(); uint8_t dig(int col, int row); uint8_t flag(int col, int row); // put on a closed cell, or taken off void tick(uint32_t ms); // the clock, while the game runs Phase phase() const { return phase_; } bool open(int col, int row) const { return cells_[at(col, row)] & kOpen; } bool flagged(int col, int row) const { return cells_[at(col, row)] & kFlag; } bool mine(int col, int row) const { return cells_[at(col, row)] & kMine; } int around(int col, int row) const { return cells_[at(col, row)] & kCount; } // mines next to it int minesLeft() const { return kMines - flags_; } // by the flags placed, right or wrong uint32_t seconds() const { return elapsedMs_ / 1000; } // For the tests: the mines put where one wants them, before any dig. void lay(const uint16_t* where, int count); private: static constexpr uint8_t kCount = 0x0F, kMine = 0x10, kOpen = 0x20, kFlag = 0x40; static size_t at(int col, int row) { return static_cast(row * kCols + col); } static bool inside(int col, int row) { return col >= 0 && col < kCols && row >= 0 && row < kRows; } void layAwayFrom(int col, int row); void count(); uint8_t reveal(int col, int row); uint8_t settle(uint8_t events); Rng rng_; std::array cells_{}; Phase phase_ = Phase::Fresh; bool laid_ = false; int flags_ = 0, opened_ = 0; uint32_t elapsedMs_ = 0; }; } // namespace roro::games