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.
This commit is contained in:
@@ -2,11 +2,11 @@ import unittest
|
||||
from time import perf_counter
|
||||
|
||||
from snakes import SnakeBuilder, get_snake_version
|
||||
from snakes.ApexBattleSnake import ApexBattleSnake
|
||||
from snakes.bitboard import BitBoard
|
||||
from snakes.bitboard_duel_search import BitboardDuelSearch
|
||||
from snakes.compact_survival_search import CompactSurvivalSearch
|
||||
from snakes.PrismBattleSnake_GPT_5_6_Sol import PrismBattleSnake_GPT_5_6_Sol
|
||||
from snakes.engine.bitboard import BitBoard
|
||||
from snakes.engine.duel_search import BitboardDuelSearch
|
||||
from snakes.engine.survival_search import CompactSurvivalSearch
|
||||
from snakes.strategies.apex import ApexBattleSnake
|
||||
from snakes.strategies.prism import PrismBattleSnake_GPT_5_6_Sol
|
||||
|
||||
class TestBitBoard(unittest.TestCase):
|
||||
|
||||
@@ -52,8 +52,9 @@ class TestPrismBattleSnake_GPT_5_6_Sol(unittest.TestCase):
|
||||
snake = PrismBattleSnake_GPT_5_6_Sol()
|
||||
|
||||
self.assertEqual(snake.name, "PrismBattleSnake")
|
||||
self.assertEqual(snake.version, "1.2.0")
|
||||
self.assertEqual(get_snake_version("PrismBattleSnake_GPT_5_6_Sol"), "1.2.0")
|
||||
self.assertEqual(snake.version, "1.3.0")
|
||||
self.assertEqual(get_snake_version("PrismBattleSnake_GPT_5_6_Sol"), "1.3.0")
|
||||
self.assertGreaterEqual(snake._planning_depth, 4)
|
||||
self.assertIsInstance(SnakeBuilder.build("PrismBattleSnake_GPT_5_6_Sol"), PrismBattleSnake_GPT_5_6_Sol)
|
||||
|
||||
def test_bitboard_primitives_match_apex(self):
|
||||
@@ -175,6 +176,21 @@ class TestPrismBattleSnake_GPT_5_6_Sol(unittest.TestCase):
|
||||
|
||||
self.assertIs(snake._duel_search_context, first_context)
|
||||
self.assertGreater(first_context.nodes, 0)
|
||||
self.assertGreater(first_context.completed_depth, 0)
|
||||
|
||||
def test_duel_evaluation_prioritizes_reachable_food_when_starving(self):
|
||||
board = BitBoard(5, 3)
|
||||
search = BitboardDuelSearch(
|
||||
board=board, food={(2, 1)}, hazards=set(), hazard_count={},
|
||||
hazard_damage=15, deadline=None,
|
||||
)
|
||||
my_body = [{"x": 0, "y": 1}, {"x": 0, "y": 0}]
|
||||
enemy_body = [{"x": 4, "y": 1}, {"x": 4, "y": 0}]
|
||||
|
||||
hungry = search.search_depth(my_body, enemy_body, 15, 100, 0, set())
|
||||
healthy = search.search_depth(my_body, enemy_body, 100, 100, 0, set())
|
||||
|
||||
self.assertLess(hungry, healthy)
|
||||
|
||||
def test_compact_rollout_models_lethal_enemy_head_response(self):
|
||||
board = BitBoard(3, 3)
|
||||
@@ -205,6 +221,7 @@ class TestPrismBattleSnake_GPT_5_6_Sol(unittest.TestCase):
|
||||
search.search_selected(mine, enemies, (2, 1), depth=3)
|
||||
|
||||
self.assertGreater(search.cache_hits, hits_before)
|
||||
self.assertGreaterEqual(search.completed_depth, 3)
|
||||
|
||||
def test_bitboard_duel_search_reuses_transpositions(self):
|
||||
board = BitBoard(5, 5)
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import unittest
|
||||
|
||||
from snakes import SNAKE_REGISTRATIONS, SnakeBuilder
|
||||
from snakes.engine import (
|
||||
BitBoard,
|
||||
BitboardDuelMixin,
|
||||
BitboardSpatialMixin,
|
||||
BitboardSurvivalMixin,
|
||||
)
|
||||
from snakes.strategies.prism import PrismBattleSnake_GPT_5_6_Sol
|
||||
|
||||
class TestSnakePackageLayout(unittest.TestCase):
|
||||
|
||||
def test_registry_uses_explicit_package_modules(self):
|
||||
for name, registration in SNAKE_REGISTRATIONS.items():
|
||||
with self.subTest(name=name):
|
||||
self.assertTrue(registration.module.startswith("snakes."))
|
||||
self.assertNotEqual(registration.module, f"snakes.{name}")
|
||||
|
||||
def test_registry_builds_active_strategies_after_package_move(self):
|
||||
for name in ("ApexBattleSnake", "PrismBattleSnake_GPT_5_6_Sol"):
|
||||
with self.subTest(name=name):
|
||||
snake = SnakeBuilder.build(name)
|
||||
self.assertEqual(snake.__class__.__name__, name)
|
||||
|
||||
def test_prism_composes_reusable_engine_mixins(self):
|
||||
snake = PrismBattleSnake_GPT_5_6_Sol()
|
||||
|
||||
self.assertIsInstance(snake, BitboardDuelMixin)
|
||||
self.assertIsInstance(snake, BitboardSpatialMixin)
|
||||
self.assertIsInstance(snake, BitboardSurvivalMixin)
|
||||
self.assertIsInstance(snake._get_bb(11, 11), BitBoard)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -5,8 +5,8 @@ and that the bitboard engine itself is sound.
|
||||
"""
|
||||
import unittest
|
||||
|
||||
from snakes.SupremeBattleSnake_ClaudeOpus4_6 import SupremeBattleSnake_ClaudeOpus4_6 as SupremeBattleSnake
|
||||
from snakes.bitboard import BitBoard
|
||||
from snakes.legacy.SupremeBattleSnake_ClaudeOpus4_6 import SupremeBattleSnake_ClaudeOpus4_6 as SupremeBattleSnake
|
||||
from snakes.engine.bitboard import BitBoard
|
||||
from server.GameBoard import GameBoard
|
||||
|
||||
# ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
@@ -348,7 +348,7 @@ class TestParityWithApex(unittest.TestCase):
|
||||
|
||||
def test_trapped_corner(self):
|
||||
"""Both snakes should survive a forced single-exit scenario."""
|
||||
from snakes.ApexBattleSnake import ApexBattleSnake
|
||||
from snakes.strategies.apex import ApexBattleSnake
|
||||
|
||||
state = gs(my_body=[(1, 1), (1, 2), (2, 2), (2, 1)],
|
||||
other_bodies=[], foods=[(5, 5)], width=7, height=7)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import unittest
|
||||
|
||||
from snakes.UltimateBattleSnake import UltimateBattleSnake
|
||||
from snakes.legacy.UltimateBattleSnake import UltimateBattleSnake
|
||||
from server.GameBoard import GameBoard
|
||||
|
||||
# ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user