update UltimateBattleSnake to 4.5.0

This commit is contained in:
2026-04-05 15:19:53 +02:00
parent 3cb3517892
commit efe15dd8e7
2 changed files with 256 additions and 9 deletions
+191 -3
View File
@@ -745,9 +745,9 @@ class TestVersion(unittest.TestCase):
def test_version_matches_registry(self):
from snakes import SNAKE_REGISTRY, get_snake_version
self.assertEqual(UltimateBattleSnake.VERSION, "4.4.0")
self.assertEqual(get_snake_version("UltimateBattleSnake"), "4.4.0")
self.assertEqual(SNAKE_REGISTRY["UltimateBattleSnake"], "4.4.0")
self.assertEqual(UltimateBattleSnake.VERSION, "4.5.0")
self.assertEqual(get_snake_version("UltimateBattleSnake"), "4.5.0")
self.assertEqual(SNAKE_REGISTRY["UltimateBattleSnake"], "4.5.0")
def test_instance_version_matches_class(self):
snake = UltimateBattleSnake()
@@ -1022,5 +1022,193 @@ class TestModeDetection(unittest.TestCase):
self.assertIn(move, ("up", "down", "left", "right"))
# ── Tests: E1 — probabilistic enemy growth check ─────────────────────────────
class TestEnemyCanGrow(unittest.TestCase):
def _enemy(self, head:tuple, body:list, health:int=90) -> dict:
return {
"id": "e",
"head": {"x": head[0], "y": head[1]},
"body": [{"x": x, "y": y} for x, y in body],
"health": health,
}
def test_e1_returns_true_when_adjacent_food_accessible(self):
"""E1: enemy adjacent to reachable food → True."""
snake = UltimateBattleSnake()
enemy = self._enemy((5, 5), [(5, 5), (5, 4), (5, 3)])
food_set = {(6, 5)}
self.assertTrue(snake._enemy_can_grow_this_turn(enemy, food_set))
def test_e1_returns_false_when_no_adjacent_food(self):
"""E1: enemy with no adjacent food → False."""
snake = UltimateBattleSnake()
enemy = self._enemy((5, 5), [(5, 5), (5, 4), (5, 3)])
food_set = {(9, 9)}
self.assertFalse(snake._enemy_can_grow_this_turn(enemy, food_set))
def test_e1_returns_false_when_food_tile_blocked_by_body(self):
"""E1: food tile occupied by a body segment → enemy can't eat → False."""
snake = UltimateBattleSnake()
enemy = self._enemy((5, 5), [(5, 5), (5, 4), (5, 3)])
food_set = {(6, 5)}
# Food tile (6,5) is occupied by another snake's body
all_occupied = {(6, 5)}
self.assertFalse(
snake._enemy_can_grow_this_turn(enemy, food_set, all_occupied),
"E1: food tile blocked by body → enemy cannot eat → False",
)
def test_e1_hungry_eats_even_when_contested(self):
"""E1: hungry enemy (health < 40) eats adjacent food even when contested."""
snake = UltimateBattleSnake()
enemy = self._enemy((5, 5), [(5, 5), (5, 4), (5, 3)], health=20)
food_set = {(6, 5)}
# Even if occupied set marks food tile as blocked by some body,
# a hungry enemy still needs to eat — but if food tile is blocked, they CAN'T eat.
# E1 skips blocked tiles regardless of health.
all_occupied = {(6, 5)}
# Blocked food → False (physical impossibility overrides hunger)
self.assertFalse(snake._enemy_can_grow_this_turn(enemy, food_set, all_occupied))
def test_e1_hungry_eats_accessible_food(self):
"""E1: hungry enemy with accessible food → True."""
snake = UltimateBattleSnake()
enemy = self._enemy((5, 5), [(5, 5), (5, 4), (5, 3)], health=15)
food_set = {(6, 5)}
self.assertTrue(snake._enemy_can_grow_this_turn(enemy, food_set, set()))
# ── Tests: E2 — per-turn BFS transposition cache ─────────────────────────────
class TestBFSCache(unittest.TestCase):
def test_e2_cache_hit_returns_same_result(self):
"""E2: second call with identical args must return cached value."""
snake = UltimateBattleSnake()
snake._bfs_cache = {}
snake._bfs_cache_turn = 0
blocked = frozenset([(1, 0), (0, 1)])
result1 = snake._flood_fill_count((0, 0), set(blocked), 11, 11)
result2 = snake._flood_fill_count((0, 0), set(blocked), 11, 11)
self.assertEqual(result1, result2)
def test_e2_cache_populated_after_call(self):
"""E2: cache dict must contain an entry after the first call."""
snake = UltimateBattleSnake()
snake._bfs_cache = {}
snake._bfs_cache_turn = 0
snake._flood_fill_count((5, 5), set(), 11, 11)
self.assertGreater(len(snake._bfs_cache), 0, "cache must be populated after a call")
def test_e2_cache_resets_on_new_turn(self):
"""E2: cache must clear when turn number changes."""
snake = UltimateBattleSnake()
snake._bfs_cache = {"stale": 42}
snake._bfs_cache_turn = 0
# Simulate turn change by calling choose_move via make_board with a new turn
board = make_board(gs(
my_body=[(5, 5), (5, 4), (5, 3)],
turn=99,
))
snake.choose_move(board)
self.assertEqual(snake._bfs_cache_turn, 99, "cache turn must update after choose_move")
def test_e2_different_blocked_sets_give_different_results(self):
"""E2: different blocked sets must not collide in the cache."""
snake = UltimateBattleSnake()
snake._bfs_cache = {}
r_open = snake._flood_fill_count((5, 5), set(), 11, 11)
r_blocked = snake._flood_fill_count((5, 5), {(5, 6), (6, 5), (4, 5), (5, 4)}, 11, 11)
self.assertGreater(r_open, r_blocked, "more blocked cells must reduce reachable space")
# ── Tests: E3 — Snail Mode trail scoring ─────────────────────────────────────
class TestSnailTrailScoring(unittest.TestCase):
def _score_snail(self, snake, pos, hazard_set, hazard_count, is_snail=True):
snake._enemy_dmaps = []
snake._enemy_heads = []
snake._base_blocked = set()
snake._is_snail = is_snail
body = [{"x": 5, "y": 5}, {"x": 5, "y": 4}, {"x": 5, "y": 3}]
sc, _ = snake._score_move(
move="right", pos=pos,
my_body=body, my_len=3, my_health=90,
other_snakes=[], food_set=set(),
hazard_set=hazard_set, hazard_damage=14, hazard_count=hazard_count,
previous_hazard_set=hazard_set,
is_constrictor=False, enemy_attack_map={},
enemy_can_grow={}, total_occupancy=0.05,
width=11, height=11, deadline=None,
)
return sc
def test_e3_snail_penalises_high_adjacent_hazard_density(self):
"""E3: move into area surrounded by hazard scores worse in snail mode."""
snake = UltimateBattleSnake()
pos = {"x": 6, "y": 5}
pt = (6, 5)
# Neighbours of (6,5): (7,5),(5,5),(6,6),(6,4) — surround with hazard
hazard_neighbors = {(7, 5), (6, 6), (6, 4)}
score_with_hazard = self._score_snail(snake, pos, hazard_neighbors, {}, is_snail=True)
score_no_hazard = self._score_snail(snake, pos, set(), {}, is_snail=True)
self.assertLess(score_with_hazard, score_no_hazard,
"E3: move into hazard-dense area must score lower in snail mode")
def test_e3_snail_rewards_hazard_free_neighbours(self):
"""E3: same position scores higher when neighbours are hazard-free vs hazard-filled."""
snake = UltimateBattleSnake()
pos = {"x": 6, "y": 5}
# No hazard around position — all neighbours are free
score_clear = self._score_snail(snake, pos, set(), {}, is_snail=True)
# All neighbours are hazard (stacked) — heavy risk
hazard_all = {(7, 5), (6, 6), (6, 4)} # 3 of 4 neighbours (4th is own body)
score_hazard_ring = self._score_snail(snake, pos, hazard_all, {n: 2 for n in hazard_all}, is_snail=True)
self.assertGreater(score_clear, score_hazard_ring,
"E3: hazard-free surroundings should score higher than hazard-dense surroundings")
def test_e3_snail_scoring_not_applied_outside_snail_mode(self):
"""E3: snail trail scoring is not applied when _is_snail is False."""
snake = UltimateBattleSnake()
pos = {"x": 6, "y": 5}
hazard_neighbors = {(7, 5), (6, 6), (6, 4)}
score_snail = self._score_snail(snake, pos, hazard_neighbors, {}, is_snail=True)
score_normal = self._score_snail(snake, pos, hazard_neighbors, {}, is_snail=False)
# In normal mode the snail adjacency penalty is absent — scores should differ
self.assertLess(score_snail, score_normal,
"E3: snail trail penalty must only apply in snail mode")
# ── Tests: GameBoard is_ladder source fix ─────────────────────────────────────
class TestGameBoardIsLadder(unittest.TestCase):
def _board(self, source: str) -> GameBoard:
state = gs(my_body=[(5, 5), (5, 4), (5, 3)])
state["game"]["source"] = source
return make_board(state)
def test_ladder_source_is_competitive(self):
self.assertTrue(self._board("ladder").is_ladder)
def test_league_source_is_competitive(self):
self.assertTrue(self._board("league").is_ladder)
def test_arena_source_is_competitive(self):
self.assertTrue(self._board("arena").is_ladder)
def test_custom_source_is_not_competitive(self):
self.assertFalse(self._board("custom").is_ladder)
def test_challenge_source_is_not_competitive(self):
self.assertFalse(self._board("challenge").is_ladder)
def test_tournament_source_is_not_competitive(self):
self.assertFalse(self._board("tournament").is_ladder)
if __name__ == "__main__":
unittest.main()