fix: resolve duel roots and recover legacy snake data

- Resolve selected moves and enemy replies on the same simulated turn.
- Add an Apex candidate hook and bump the Prism snake to version 1.1.0.
- Rebuild benchmark states from normalized turn data when snapshots are empty.
- Synthesize missing game snake identities during legacy database migration.
- Add regression coverage for duel timing and partial legacy schemas.
This commit is contained in:
2026-08-01 19:11:32 +02:00
parent c646392b84
commit 6643eb35af
8 changed files with 329 additions and 39 deletions
+47 -11
View File
@@ -31,7 +31,8 @@ def load_states(db_path: str, samples: int, stride: int) -> list[tuple[dict, dic
states: list[tuple[dict, dict]] = []
next_id = max(1, max_id - (samples - 1) * stride)
query = """
SELECT t.board_state_json, t.you_json, g.your_snake_id,
SELECT t.id, t.board_state_json, t.you_json, t.food_json, t.hazards_json,
g.your_snake_id, g.your_snake_name, g.width, g.height,
g.game_id, g.source, g.map_name,
g.ruleset_name, g.ruleset_version, t.turn
FROM turns AS t
@@ -40,30 +41,65 @@ def load_states(db_path: str, samples: int, stride: int) -> list[tuple[dict, dic
ORDER BY t.id
LIMIT 1
"""
snake_query = """
SELECT st.snake_id, COALESCE(gs.snake_name, st.snake_name),
st.health, st.length, st.head_x, st.head_y, st.body_json,
COALESCE(gs.customizations_json, '{}')
FROM snake_turns AS st
LEFT JOIN game_snakes AS gs
ON gs.game_id = st.game_id AND gs.snake_id = st.snake_id
WHERE st.game_id = ? AND st.turn = ?
ORDER BY st.id
"""
while len(states) < samples and next_id <= max_id:
row = connection.execute(query, (next_id,)).fetchone()
if row is None:
break
board = json.loads(row[0])
you = json.loads(row[1])
board = json.loads(row[1])
you = json.loads(row[2])
if not board.get("snakes"):
snakes = []
for snake_row in connection.execute(snake_query, (row[9], row[14])):
snake_id = snake_row[0]
snake_name = snake_row[1] or (row[6] if snake_id == row[5] else snake_id)
body = json.loads(snake_row[6])
snakes.append({
"id": snake_id,
"name": snake_name,
"health": snake_row[2],
"length": snake_row[3],
"head": {"x": snake_row[4], "y": snake_row[5]},
"body": body,
"customizations": json.loads(snake_row[7]),
})
board = {
"width": row[7],
"height": row[8],
"food": json.loads(row[3]),
"hazards": json.loads(row[4]),
"snakes": snakes,
}
if not you:
you = next(
(snake for snake in board.get("snakes", []) if snake.get("id") == row[2]),
(snake for snake in board.get("snakes", []) if snake.get("id") == row[5]),
{},
)
if not you or not board.get("snakes"):
next_id = int(row[0]) + stride
continue
metadata = {
"game_id": row[3],
"source": row[4] or "custom",
"map": row[5] or "standard",
"game_id": row[9],
"source": row[10] or "custom",
"map": row[11] or "standard",
"ruleset": {
"name": row[6] or "standard",
"version": row[7] or "v1.0.0",
"name": row[12] or "standard",
"version": row[13] or "v1.0.0",
"settings": {},
},
"turn": int(row[8]),
"turn": int(row[14]),
}
states.append((board, {"you": you, **metadata}))
next_id += stride
next_id = int(row[0]) + stride
connection.close()
return states