Files
snake-python/snakes/engine/duel.py
T
daniel156161 3a9af3f54d feat(snake): modularize engine and add tournament tools
- Split active strategies, reusable engine code, core classes, and legacy snakes.
- Replace implicit snake imports with explicit module registrations.
- Extract Prism duel, spatial, and survival behavior into focused mixins.
- Improve duel scoring with food races, pressure, caches, and depth metrics.
- Add deterministic arena scenarios and paired seeded engine tournaments.
- Expand benchmark telemetry and bump Prism to version 1.3.0.
- Update documentation and tests for the new package layout and tooling.
2026-08-01 20:25:07 +02:00

90 lines
3.5 KiB
Python

"""Reusable compact duel-search integration for Apex-style snakes."""
from __future__ import annotations
from snakes.engine.duel_search import BitboardDuelSearch
class BitboardDuelMixin:
def _new_duel_search(
self, food_set: set, hazard_set: set, hazard_count: dict,
hazard_damage: int, width: int, height: int, deadline: float | None,
) -> BitboardDuelSearch:
if self._duel_search_context is None:
self._duel_search_context = BitboardDuelSearch(
board=self._get_bb(width, height),
food=food_set,
hazards=hazard_set,
hazard_count=hazard_count,
hazard_damage=hazard_damage,
deadline=deadline,
)
return self._duel_search_context
def _minimax_candidate_id(
self, my_body: list, enemy_body: list, my_target: tuple[int, int],
food_set: set, hazard_set: set,
my_health: int, enemy_health: int, hazard_damage: int, hazard_count: dict,
width: int, height: int, max_depth: int, alpha: float, beta: float,
deadline: float | None, previous_hazard_set: set | None = None,
) -> tuple[float, int]:
"""Resolve our selected move and every enemy reply simultaneously."""
search = self._new_duel_search(
food_set, hazard_set, hazard_count, hazard_damage,
width, height, deadline,
)
adaptive_depth = max_depth
remaining = self._remaining_ms(deadline)
if remaining > 250:
adaptive_depth = min(7, max_depth + 1)
elif remaining < 120:
adaptive_depth = min(max_depth, 2)
return search.search_candidate(
my_body=my_body,
enemy_body=enemy_body,
my_target=my_target,
my_health=my_health,
enemy_health=enemy_health,
max_depth=adaptive_depth,
previous_hazards=previous_hazard_set if previous_hazard_set is not None else hazard_set,
)
def _minimax_sim_id(
self, my_body: list, enemy_body: list, food_set: set, hazard_set: set,
my_health: int, enemy_health: int, hazard_damage: int, hazard_count: dict,
width: int, height: int, max_depth: int, alpha: float, beta: float,
deadline: float | None, previous_hazard_set: set | None = None,
) -> tuple[float, int]:
"""Run iterative deepening with one reusable compact search context."""
search = self._new_duel_search(
food_set, hazard_set, hazard_count, hazard_damage,
width, height, deadline,
)
return search.search(
my_body=my_body,
enemy_body=enemy_body,
my_health=my_health,
enemy_health=enemy_health,
max_depth=max_depth,
previous_hazards=previous_hazard_set if previous_hazard_set is not None else hazard_set,
)
def _minimax_sim(
self, my_body: list, enemy_body: list, food_set: set, hazard_set: set,
my_health: int, enemy_health: int, hazard_damage: int, hazard_count: dict,
width: int, height: int, depth: int, alpha: float, beta: float,
deadline: float | None, previous_hazard_set: set | None = None,
) -> float:
"""Compatibility entry point for tests and callers requesting one depth."""
search = self._new_duel_search(
food_set, hazard_set, hazard_count, hazard_damage,
width, height, deadline,
)
return search.search_depth(
my_body=my_body,
enemy_body=enemy_body,
my_health=my_health,
enemy_health=enemy_health,
depth=depth,
previous_hazards=previous_hazard_set if previous_hazard_set is not None else hazard_set,
)