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
+29
View File
@@ -0,0 +1,29 @@
"""Health-cost routes to food or non-hazard refuge, with current bodies fixed."""
import heapq
def health_route_cost(start, food, hazards, stacks, damage, blocked, width, height, *, refuge=False):
"""Cost until food resets health, or until leaving hazards for refuge.
Food is consumed before elimination, so its final step needs no remaining
health. Other steps must leave health positive. Bodies remain obstacles,
including food-covered body cells; the caller removes the starting head.
"""
queue = [(0, start)]
best = {start: 0}
while queue:
cost, point = heapq.heappop(queue)
if cost != best[point]:
continue
if point in food or (refuge and point not in hazards):
return cost
x, y = point
for target in ((x, y + 1), (x, y - 1), (x - 1, y), (x + 1, y)):
nx, ny = target
if not (0 <= nx < width and 0 <= ny < height) or target in blocked:
continue
step = 0 if target in food else 1 + (damage * stacks.get(target, 1) if target in hazards else 0)
value = cost + step
if value < best.get(target, float('inf')):
best[target] = value
heapq.heappush(queue, (value, target))
return None
+1 -1
View File
@@ -235,7 +235,7 @@ class CompactSurvivalSearch:
blocked = self._occupied(mine, enemies) & ~(1 << mine[0])
space = self.board.flood_count(mine[0], blocked)
liberties = self.board.open_neighbor_count(mine[0], blocked)
if liberties == 0 or space < len(mine):
if liberties == 0 or (not self.is_constrictor and space < len(mine)):
return self.DEATH
enemy_pressure = 0.0
for enemy in enemies: