Files
daniel156161 39a7a53873
Build and Push Docker Container / build-and-push (push) Successful in 3m52s
feat(database): store gameplay replays in compact form
- Add compact storage package: codec, records, hydrate, replay blobs, cache.
- Running games write game_turns and snake_states keyed by integer ids.
- Encode moves as 1-byte codes and bodies as head plus step codes.
- Store food and hazards only when they changed since the previous turn.
- Strip reasoning fields that duplicate stored values and restore on read.
- Pack finished games into one zstd blob per game in game_replays.
- Merge late turns into packed blobs during the stale-game sweep.
- Extract SQLite and PostgreSQL schemas into dedicated modules.
- Add read/import game bundle and list_game_ids to the backend template.
- Add legacy readers and converter for the old turns/snake_turns layout.
- Add migrate_gameplay_to_compact.py with in-place or cross-DB migration.
- Verify every migrated game and keep legacy rows on failure.
- Support --since, --minimum-quality and guarded --drop-legacy.
- Add just recipe migrate-gameplay-postgres.
- Rewrite merge script to copy game by game via backend bundles.
- Move benchmark state loading into benchmark_states for both backends.
- Update PostgreSQL cleanup script to the new replay tables.
- Drop sqlite-zstd build stages from the Dockerfile.
- Remove old migration scripts, normalized_turn and their tests.
- Add tests for compact storage, schema, legacy migration and PostgreSQL.
- Document the compact layout and migration workflow in the README.
2026-11-05 00:02:56 +01:00

29 lines
1.0 KiB
Python

"""Column list of the ``games`` table shared by backends and migrations."""
GAME_COLUMNS = (
"game_id", "started_at", "ended_at", "width", "height", "source", "map_name",
"ruleset_name", "ruleset_version", "your_snake_id", "your_snake_name",
"your_snake_type", "your_snake_version", "game_type", "winner_name",
"winner_you", "final_turn", "status", "has_replay", "quality_status",
"quality_score", "quality_tier", "quality_reasons",
)
GAME_DEFAULTS = {
"winner_you": False,
"final_turn": 0,
"status": "running",
"has_replay": True,
"quality_status": "retained",
}
def normalize_game(row:dict) -> dict:
"""Return a dict with every games column, filling legacy gaps with defaults."""
game = {column: row.get(column, GAME_DEFAULTS.get(column)) for column in GAME_COLUMNS}
for column, default in GAME_DEFAULTS.items():
if game[column] is None:
game[column] = default
game["winner_you"] = bool(game["winner_you"])
game["has_replay"] = bool(game["has_replay"])
game["final_turn"] = int(game["final_turn"] or 0)
return game