Games: Pong, Snake and Tetris as seeded state machines, and a tune

The rules of the three Games (issue #101), with no screen and no
keyboard in them: time and keys go in, the new state and what happened
come out, and a seed decides the dice. With a player for a tune a note
at a time, and Korobeiniki's notes. Host-tested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
This commit is contained in:
2026-10-13 22:59:05 +02:00
co-authored by Claude Opus 5.5
parent 5c660d866d
commit 68d50b320f
9 changed files with 1417 additions and 0 deletions
+60
View File
@@ -0,0 +1,60 @@
#pragma once
#include <array>
#include <cstddef>
#include <cstdint>
#include <deque>
#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<Cell>& 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<size_t>(c.row * kCols + c.col)]; }
void mark(Cell c, bool on) { taken_[static_cast<size_t>(c.row * kCols + c.col)] = on; }
void placeFood();
uint8_t move();
Rng rng_;
std::deque<Cell> body_;
std::array<bool, kCols * kRows> 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