feat: add live replays and PostgreSQL maintenance tools
Build and Push Docker Container / build-and-push (push) Successful in 7m53s
Build and Push Docker Container / build-and-push (push) Successful in 7m53s
- Stream compact live replay updates across local and clustered dashboards. - Render responsive snake bodies as SVG paths with aligned custom icons. - Add cache-busted assets, replay fallback routes, and live-follow playback. - Support PostgreSQL benchmark sampling and idempotent SQLite migration. - Add dry-run cleanup for old low-quality PostgreSQL replay payloads. - Reward safe perimeter lanes and bump Prism to version 1.5.0. - Add backend, migration, dashboard, and perimeter regression coverage.
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
"""Safety-gated board geometry scoring for perimeter-aware snakes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
def _on_edge(point: tuple[int, int], width: int, height: int) -> bool:
|
||||
x, y = point
|
||||
return x in {0, width - 1} or y in {0, height - 1}
|
||||
|
||||
def _same_edge(
|
||||
first: tuple[int, int], second: tuple[int, int], width: int, height: int,
|
||||
) -> bool:
|
||||
boundaries = ((0, 0), (0, width - 1), (1, 0), (1, height - 1))
|
||||
return any(
|
||||
first[index] == boundary and second[index] == boundary
|
||||
for index, boundary in boundaries
|
||||
)
|
||||
|
||||
def perimeter_geometry_score(
|
||||
*,
|
||||
point: tuple[int, int],
|
||||
current_head: tuple[int, int],
|
||||
width: int,
|
||||
height: int,
|
||||
occupancy: float,
|
||||
snake_length: int,
|
||||
reachable_space: int,
|
||||
required_space: int,
|
||||
liberties: int,
|
||||
next_options: int,
|
||||
safe_next_options: int,
|
||||
tail_escape: bool,
|
||||
dead_end: bool,
|
||||
losing_head_to_head: bool,
|
||||
) -> float:
|
||||
"""Reward useful perimeter lanes without overriding tactical safety.
|
||||
|
||||
The normal move scorer already values liberties heavily, which naturally
|
||||
makes wall cells less attractive. This adjustment offsets that bias only
|
||||
when the wall position has room, a tail route, and multiple safe exits.
|
||||
"""
|
||||
x, y = point
|
||||
cx, cy = (width - 1) / 2.0, (height - 1) / 2.0
|
||||
center_score = 1.0 - (abs(x - cx) + abs(y - cy)) / max(1.0, cx + cy)
|
||||
center_weight = max(2.0, 6.0 * (1.0 - min(1.0, occupancy / 0.5)))
|
||||
score = center_score * center_weight
|
||||
|
||||
if not _on_edge(point, width, height):
|
||||
return score
|
||||
|
||||
safely_usable = (
|
||||
not dead_end
|
||||
and not losing_head_to_head
|
||||
and tail_escape
|
||||
and liberties >= 2
|
||||
and next_options >= 2
|
||||
and safe_next_options >= 2
|
||||
and reachable_space >= required_space + max(4, required_space // 2)
|
||||
)
|
||||
if not safely_usable:
|
||||
return score
|
||||
|
||||
phase = min(1.0, occupancy / 0.34)
|
||||
length_factor = min(1.0, snake_length / 12.0)
|
||||
space_margin = min(
|
||||
1.0,
|
||||
max(0, reachable_space - required_space) / max(1, required_space),
|
||||
)
|
||||
score += 24.0 + phase * 12.0 + length_factor * 8.0 + space_margin * 8.0
|
||||
|
||||
# Continuing along one edge is more useful than repeatedly entering and
|
||||
# leaving it: it keeps the body ordered and leaves the interior available.
|
||||
if _same_edge(current_head, point, width, height):
|
||||
score += 10.0
|
||||
|
||||
# Corners remove two exits. They remain usable, but should not become goals.
|
||||
if x in {0, width - 1} and y in {0, height - 1}:
|
||||
score -= 18.0
|
||||
|
||||
return score
|
||||
Reference in New Issue
Block a user