feat(database): store gameplay replays in compact form
Build and Push Docker Container / build-and-push (push) Successful in 3m52s
Build and Push Docker Container / build-and-push (push) Successful in 3m52s
- 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.
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
"""Rate a game's replay quality from its records."""
|
||||
|
||||
from server.database.game_quality import GameQuality, GameQualityInput, rate_game_quality
|
||||
|
||||
from .codec import MOVES
|
||||
from .records import GameBundle
|
||||
|
||||
def rate_bundle(bundle:GameBundle) -> GameQuality:
|
||||
turns = bundle.turns
|
||||
numbers = [record.turn for record in turns]
|
||||
moves = [record.move for record in turns]
|
||||
return rate_game_quality(GameQualityInput(
|
||||
status=bundle.game.get("status") or "running",
|
||||
final_turn=int(bundle.game.get("final_turn") or 0),
|
||||
turn_rows=len(turns),
|
||||
min_turn=min(numbers) if numbers else None,
|
||||
max_turn=max(numbers) if numbers else None,
|
||||
valid_moves=sum(1 for move in moves if move in MOVES),
|
||||
thinking_rows=sum(1 for record in turns if record.thinking not in (None, {})),
|
||||
distinct_moves=len({move for move in moves if move is not None}),
|
||||
snake_turn_rows=len(bundle.states),
|
||||
winner_name=bundle.game.get("winner_name"),
|
||||
))
|
||||
Reference in New Issue
Block a user