#pragma once #include #include #include #include #include "game_rng.h" namespace roro::games { // Snake (issue #101): a field of 38 x 19 cells, the walls and its own body kill it, and it goes // faster as it eats. Time comes in from outside, in milliseconds. class Snake { public: static constexpr int kCols = 38, kRows = 19; static constexpr uint32_t kSlowestMs = 150, kFastestMs = 60, kFasterMs = 5; static constexpr int kFoodsAStep = 3; // it gets faster every so many enum class Dir : uint8_t { Up, Right, Down, Left }; enum Event : uint8_t { Moved = 1, Ate = 2, Died = 4 }; struct Cell { int8_t col, row; bool operator==(const Cell& o) const { return col == o.col && row == o.row; } }; explicit Snake(uint32_t seed) : rng_(seed) { start(); } void start(); // A turn asked for; it is taken at the next move. Two are remembered, so a quick corner (up, // then left) works; turning back on itself is not a turn. void turn(Dir dir); uint8_t step(uint32_t ms); bool alive() const { return alive_; } int score() const { return score_; } // foods eaten const std::deque& body() const { return body_; } // the head first Cell food() const { return food_; } Dir heading() const { return dir_; } uint32_t intervalMs() const; // between two moves, now // For the tests: the food put where one wants it. void putFood(Cell at) { food_ = at; } private: bool taken(Cell c) const { return taken_[static_cast(c.row * kCols + c.col)]; } void mark(Cell c, bool on) { taken_[static_cast(c.row * kCols + c.col)] = on; } void placeFood(); uint8_t move(); Rng rng_; std::deque body_; std::array taken_{}; Cell food_{0, 0}; Dir dir_ = Dir::Right; Dir turns_[2] = {Dir::Right, Dir::Right}; int turnCount_ = 0, score_ = 0; uint32_t sinceMs_ = 0; bool alive_ = true; }; } // namespace roro::games