Compare commits
2 Commits
f0d62a6049
...
f6e19e18e6
| Author | SHA1 | Date | |
|---|---|---|---|
|
f6e19e18e6
|
|||
|
f479541c04
|
@@ -151,6 +151,11 @@ class GameBoard:
|
||||
|
||||
return {"name": self.type, "is_ladder": self.is_ladder}
|
||||
|
||||
def __getstate__(self):
|
||||
state = self.__dict__.copy()
|
||||
state['turns'] = [] # strip turn history — grows linearly, not needed for move computation
|
||||
return state
|
||||
|
||||
async def save(self, store_class, **kwargs):
|
||||
store = store_class(**kwargs)
|
||||
await store.save(self)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from typing import TYPE_CHECKING, cast
|
||||
import json, time, os
|
||||
import asyncio, json, time, os
|
||||
|
||||
from quart import Blueprint, request, jsonify
|
||||
|
||||
@@ -61,9 +61,27 @@ def create_battlesnake_blueprint(server:'Server') -> Blueprint:
|
||||
server.metrics_collector.record_http_request('move')
|
||||
game_state = await request.get_json()
|
||||
move_started = time.perf_counter()
|
||||
game_board = cast(GameBoard, await server.game_runtime.get_game_board(game_state))
|
||||
next_move = game_board.snake_neat_make_a_move()
|
||||
await server.game_runtime.persist_game_board(game_state['game']['id'], game_board)
|
||||
|
||||
game_id = game_state['game']['id']
|
||||
timeout_ms = int(game_state.get('game', {}).get('timeout', 500))
|
||||
budget_sec = max(0.05, (timeout_ms - 50) / 1000.0)
|
||||
|
||||
next_move = None
|
||||
move_completed = False
|
||||
game_board = None
|
||||
|
||||
try:
|
||||
async with asyncio.timeout(budget_sec):
|
||||
game_board = cast(GameBoard, await server.game_runtime.get_game_board(game_state))
|
||||
loop = asyncio.get_running_loop()
|
||||
next_move = await loop.run_in_executor(None, game_board.snake_neat_make_a_move)
|
||||
move_completed = True
|
||||
except TimeoutError:
|
||||
await await_log(server.logger.warning(f'MOVE TIMEOUT: turn={game_state.get("turn")}, game={game_id}, returning fallback {next_move!r}'))
|
||||
|
||||
if move_completed:
|
||||
await server.game_runtime.persist_game_board(game_id, game_board)
|
||||
|
||||
await server.gameplay_tracking.record_gameplay_turn(game_state, next_move, game_board)
|
||||
elapsed_ms = (time.perf_counter() - move_started) * 1000.0
|
||||
await server.metrics_collector.record_move(next_move, elapsed_ms)
|
||||
|
||||
@@ -121,7 +121,7 @@ class GameplayDatabase:
|
||||
self._enable_zstd_compression(connection)
|
||||
connection.execute("PRAGMA optimize")
|
||||
|
||||
def _create_indexes_if_tables(self, connection: sqlite3.Connection) -> None:
|
||||
def _create_indexes_if_tables(self, connection:sqlite3.Connection) -> None:
|
||||
real_tables = {
|
||||
row[0] for row in connection.execute(
|
||||
"SELECT name FROM sqlite_master WHERE type='table'"
|
||||
@@ -159,7 +159,7 @@ class GameplayDatabase:
|
||||
|
||||
connection.execute(f"ALTER TABLE {actual_table} ADD COLUMN {column_name} {column_type}")
|
||||
|
||||
def _enable_zstd_compression(self, connection: sqlite3.Connection) -> None:
|
||||
def _enable_zstd_compression(self, connection:sqlite3.Connection) -> None:
|
||||
compressed_columns = [
|
||||
("turns", "board_state_json"),
|
||||
("turns", "snakes_json"),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from typing import TYPE_CHECKING
|
||||
import inspect, pickle
|
||||
import inspect, pickle, zlib
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from server.GameBoard import GameBoard
|
||||
@@ -28,7 +28,7 @@ class RedisGameBoardStore:
|
||||
|
||||
async def save(self, game_id:str, game_board:'GameBoard') -> None:
|
||||
redis = await self._get_redis()
|
||||
payload = pickle.dumps(game_board, protocol=pickle.HIGHEST_PROTOCOL)
|
||||
payload = zlib.compress(pickle.dumps(game_board, protocol=pickle.HIGHEST_PROTOCOL), level=1)
|
||||
await redis.set(self._key(game_id), payload, ex=self.ttl_seconds)
|
||||
|
||||
async def load(self, game_id:str):
|
||||
@@ -36,7 +36,10 @@ class RedisGameBoardStore:
|
||||
payload = await redis.get(self._key(game_id))
|
||||
if payload is None:
|
||||
return None
|
||||
return pickle.loads(payload)
|
||||
try:
|
||||
return pickle.loads(zlib.decompress(payload))
|
||||
except zlib.error:
|
||||
return pickle.loads(payload)
|
||||
|
||||
async def delete(self, game_id:str) -> None:
|
||||
redis = await self._get_redis()
|
||||
|
||||
@@ -79,6 +79,11 @@ class ApexBattleSnake(TemplateSnake):
|
||||
# RL bootstrap dataset recorder
|
||||
self.rl_bootstrap = RLBootstrapDataset()
|
||||
|
||||
def __getstate__(self):
|
||||
state = super().__getstate__()
|
||||
state['_game_phase'] = 0.0 # A3: per-turn scalar, recomputed in choose_move
|
||||
return state
|
||||
|
||||
# ── Env helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
def _get_timeout_buffer_ms(self) -> int:
|
||||
|
||||
@@ -202,3 +202,12 @@ class TemplateSnake:
|
||||
def set_target_food(self, target_food:dict):
|
||||
self.target_food = target_food
|
||||
return True
|
||||
|
||||
def __getstate__(self):
|
||||
state = self.__dict__.copy()
|
||||
state['history'] = [] # strip history — grows per turn, not needed for moves
|
||||
state.pop('game_board', None) # re-set at top of every choose_move; circular ref
|
||||
state.pop('calculations', None) # re-initialised at top of every choose_move
|
||||
state.pop('eat_the_snake_overwrite', None) # re-initialised at top of every choose_move
|
||||
state.pop('kill_the_snake', None) # per-call transient
|
||||
return state
|
||||
|
||||
@@ -100,6 +100,17 @@ class UltimateBattleSnake(TemplateSnake):
|
||||
# RL bootstrap dataset recorder
|
||||
self.rl_bootstrap = RLBootstrapDataset()
|
||||
|
||||
def __getstate__(self):
|
||||
state = super().__getstate__()
|
||||
# strip per-turn precomputed state — all re-assigned at the top of choose_move
|
||||
state['_enemy_dmaps'] = []
|
||||
state['_enemy_heads'] = []
|
||||
state['_base_blocked'] = set()
|
||||
state['_is_snail'] = False
|
||||
state['_bfs_cache'] = {}
|
||||
state['_bfs_cache_turn'] = -1
|
||||
return state
|
||||
|
||||
# ── Env helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
def _get_timeout_buffer_ms(self) -> int:
|
||||
|
||||
Reference in New Issue
Block a user