feat: add gameplay stats and restore Zenith rollout depth
Build and Push Docker Container / build-and-push (push) Successful in 3m6s

- Add read-only gameplay statistics with filters and opponent comparisons.
- Report move-trace latency percentiles and external timeout counts.
- Pair engine latency with the preceding turn to separate timing stages.
- Adjust Zenith rollout deepening for its larger timeout reserve.
- Bump Zenith to 1.2.0 and document the fix and snake-stats command.
- Cover statistics, trace aggregation, rollout depth, and verdict budgets.
This commit is contained in:
2026-11-05 08:07:15 +01:00
parent a2414f0b3d
commit 5e121c20cf
10 changed files with 498 additions and 7 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ SNAKE_REGISTRATIONS = {
"snakes.strategies.prism", "1.5.0"
),
"ZenithBattleSnake_ClaudeOpus5_5": SnakeRegistration(
"snakes.strategies.zenith", "1.1.0"
"snakes.strategies.zenith", "1.2.0"
),
"DummSnake": SnakeRegistration("snakes.legacy.DummSnake", "1.0.0"),
"LogicSnake": SnakeRegistration("snakes.legacy.LogicSnake", "1.1.0"),
+5 -1
View File
@@ -4,6 +4,10 @@ from time import perf_counter
from snakes.engine.survival_search import CompactSurvivalSearch
class BitboardSurvivalMixin:
# Remaining budget above which rollouts against at most two enemies may look
# one turn deeper. Snakes with a larger timeout reserve lower this threshold.
_rollout_deepen_ms = 250.0
def _future_rollout_bonus(
self, move: str, safe_moves: dict, my_body: list, other_snakes: list,
food_set: set, is_constrictor: bool, width: int, height: int,
@@ -36,7 +40,7 @@ class BitboardSurvivalMixin:
depth = min(self._planning_depth, 2 if len(other_snakes) > 1 else 3)
if remaining < 90:
depth = min(depth, 2)
elif remaining > 250 and len(other_snakes) <= 2:
elif remaining > self._rollout_deepen_ms and len(other_snakes) <= 2:
depth = min(4, depth + 1)
raw = self._survival_search_context.search_selected(
my_body=my_body,
+12 -3
View File
@@ -1,4 +1,4 @@
"""ZenithBattleSnake_ClaudeOpus5_5 v1.1.0
"""ZenithBattleSnake_ClaudeOpus5_5 v1.2.0
Built on PrismBattleSnake_GPT_5_6_Sol v1.5.0. Prism's bitboard engine, duel
search, and adversarial rollouts are inherited unchanged.
@@ -15,6 +15,11 @@ Z2: Proven-loss veto — a candidate whose completed search proves a loss is
replaced by the best non-losing candidate.
Z3: Proven-win override — a candidate whose completed search proves a win is
played even if the heuristic preferred another move.
Z4: Elapsed-time rollout gate — Zenith keeps a larger timeout reserve than
Prism. The inherited multiplayer "deepen when > 250 ms remain" gate is
lowered by that extra reserve, so 3-snake rollouts deepen after the same
elapsed time as Prism instead of never reaching depth 3. The duel gate is
left alone: the verdict pass already spends the duel budget.
"""
from time import perf_counter
@@ -27,18 +32,22 @@ _PROVEN_WIN = BitboardDuelSearch.WIN / 2
_PROVEN_LOSS = BitboardDuelSearch.LOSS / 2
# Reserve kept for Apex's history bookkeeping after the verdict pass.
_VERDICT_RESERVE_MS = 25.0
_ZENITH_TIMEOUT_BUFFER_MS = 180
class ZenithBattleSnake_ClaudeOpus5_5(ZenithSurvivalMixin, PrismBattleSnake_GPT_5_6_Sol):
VERSION = "1.1.0"
VERSION = "1.2.0"
def __init__(self) -> None:
super().__init__()
self.name = "ZenithBattleSnake"
self.version = self.VERSION
# Z4: deepen after the same elapsed time as the inherited Prism gates.
extra_reserve = self._get_timeout_buffer_ms() - super()._get_timeout_buffer_ms()
self._rollout_deepen_ms = max(120.0, self._rollout_deepen_ms - extra_reserve)
def _get_timeout_buffer_ms(self):
# Leave room for executor scheduling, history, JSON and network delivery.
return max(180, super()._get_timeout_buffer_ms())
return max(_ZENITH_TIMEOUT_BUFFER_MS, super()._get_timeout_buffer_ms())
def _choose_duel_move(
self, safe_moves, my_body, my_len, my_health, other_snakes, food_set,