feat: add Zenith strategy and move latency tracing
Build and Push Docker Container / build-and-push (push) Successful in 2m18s

- Register Zenith 1.1 with balanced duel verdicts and forced-outcome overrides.
- Prioritize permanent-trail space in Constrictor and health-aware hazard routes.
- Deduct executor queue delays and return fallback directions on move timeouts.
- Correlate response headers, replay timings, and sampled structured trace logs.
- Move shared metrics publication off the response path without losing local counts.
- Allow configurable tournament opponents and add a psql helper recipe.
- Preserve empty latency values through compact replay encoding and migration.
- Update Arena game links and use default assets for missing customizations.
- Cover strategy, HTTP lifecycle, tracing, replay codecs, and dashboard behavior.
- Validate with 260 passing Python tests, 4 skips, and 2 passing JavaScript tests.
This commit is contained in:
2026-11-05 01:11:31 +01:00
parent 39a7a53873
commit a2414f0b3d
26 changed files with 758 additions and 45 deletions
+52 -1
View File
@@ -100,6 +100,28 @@ Run it with:
SNAKE=PrismBattleSnake_GPT_5_6_Sol python main.py
```
## ZenithBattleSnake_ClaudeOpus5_5
`ZenithBattleSnake_ClaudeOpus5_5` builds on Prism and keeps its engine. Prism
only runs the exact duel search on near-tied heuristic candidates. Zenith adds a
root verdict pass in duels: every safe move is resolved against all
simultaneous enemy replies using the shared transposition table. A move whose
search proves a forced loss is replaced by the best surviving move. A move whose
search proves a forced win is played even if the heuristic preferred another
move. Its public Battlesnake API name is `ZenithBattleSnake`.
Version 1.1.0 adds permanent-trail space scoring for Constrictor (free capacity
is not compared against the already occupied body length), balanced root-search
budgets, and health-cost routes for hazard escape and food. It reserves at least
180 ms for non-search work. The HTTP move path also deducts executor queue wait
from the strategy budget and returns a legal-direction fallback instead of null
when its request budget expires. This is best-effort deadline handling, not a
hard real-time guarantee under CPU saturation.
Run it with:
```sh
SNAKE=ZenithBattleSnake_ClaudeOpus5_5 python main.py
```
Benchmark Apex and Prism against sampled positions from a gameplay database:
```sh
python scripts/benchmark_snakes_from_db.py \
@@ -129,12 +151,41 @@ Run paired seeded games through the official local Battlesnake rules engine:
just bench-snake-tournament games=20 gametype=standard map=standard
```
Each seed is played twice with Apex and Prism swapping initial engine slots. The
Pick other opponents with `snake_a=` and `snake_b=`, for example
`just bench-snake-tournament snake_a=PrismBattleSnake_GPT_5_6_Sol snake_b=ZenithBattleSnake_ClaudeOpus5_5`.
Each seed is played twice with both snakes swapping initial engine slots. The
report includes wins, draws, win rates, and average game length. Save all
per-game results with `output=data/tournament-report.json`. The tournament starts
both snake servers with gameplay persistence disabled, adds the engine identity
header required by the API, and shuts them down when finished.
## Move tracing and latency diagnosis
Every `/move` response includes `X-Move-Trace-ID`. Replay thinking contains a
small `move_trace` object: JSON parse, runtime lookup, executor queue wait,
strategy execution, handler/response encoding, bookkeeping queue wait, and
metrics publication timings. No board payloads are copied into traces.
Structured application log lines start with `move_trace` and include the same
ID, game ID and turn, plus persistence and dashboard-publication duration once
background work finishes. These last two timings cannot be included in the
record being written itself. Timed-out workers may finish later; missing
`strategy_ms`/`worker_finished` means they had not finished at that snapshot.
- `MOVE_TRACE_SAMPLE_EVERY=100`: approximately 1% of normal requests logged;
use `1` for all, `0` to disable normal sampling.
- `MOVE_TRACE_SLOW_MS=300`: always log a trace if any measured stage exceeds
this threshold. Timeouts and metrics publication failures are always logged.
- Metrics counters update locally before responding; shared metrics backend
publication now runs in ordered background bookkeeping, not on `/move`'s
response path.
These are lightweight local timings, not exported OpenTelemetry spans. Handler
time starts inside the route and excludes middleware, socket delivery and
reverse-proxy/network delay. Compare with client-observed latency. Background
stage times can include event-loop scheduling delays; production improvements
must be verified after deployment under representative concurrency.
## Compact gameplay database
Gameplay storage keeps game results in `games` and snake identity (name,
customizations) once per game in `game_snakes`. Replay data is stored compactly: