add enemy cut off trap check

This commit is contained in:
2026-04-04 09:55:58 +02:00
parent b0d484dbab
commit 316870ef7a
2 changed files with 186 additions and 1 deletions
+108 -1
View File
@@ -295,6 +295,15 @@ class BestBattleSnake(TemplateSnake):
next_options = self._next_turn_option_count(
future_body, blocked, width, height
)
enemy_safe_options = self._safe_next_turn_option_count(
future_body=future_body,
other_snakes=other_snakes,
food_set=food_set,
is_constrictor=is_constrictor,
enemy_can_grow_cache=enemy_can_grow_cache,
width=width,
height=height,
)
territory = self._territory_control_score(
my_start=point,
enemy_starts=enemy_heads,
@@ -320,12 +329,17 @@ class BestBattleSnake(TemplateSnake):
score += reachable_space * 2.6
score += liberties * 18.0
score += next_options * 10.0
score += enemy_safe_options * 24.0
score += territory * 0.35
if reachable_space < required_space:
score -= 1200.0
if liberties == 0:
score -= 900.0
if enemy_safe_options == 0:
score -= 1700.0
elif enemy_safe_options == 1:
score -= 420.0
enemy_len = enemy_attack_map.get(point)
if enemy_len is not None:
@@ -382,9 +396,11 @@ class BestBattleSnake(TemplateSnake):
move_safety[move] = {
"is_survivable": (not is_dead_end)
and (not is_losing_head_to_head)
and enemy_safe_options > 0
and health_after_move > 0,
"reachable_space": reachable_space,
"next_options": next_options,
"enemy_safe_options": enemy_safe_options,
"tail_escape": has_tail_escape,
}
@@ -474,6 +490,15 @@ class BestBattleSnake(TemplateSnake):
next_options = self._next_turn_option_count(
future_body, blocked, width, height
)
enemy_safe_options = self._safe_next_turn_option_count(
future_body=future_body,
other_snakes=other_snakes,
food_set=food_set,
is_constrictor=False,
enemy_can_grow_cache=enemy_can_grow_cache,
width=width,
height=height,
)
nearest_food_dist = self._nearest_food_distance(point, food_set, blocked, width, height)
future_tail = future_body[-1]
@@ -507,6 +532,7 @@ class BestBattleSnake(TemplateSnake):
score += reachable_space * 2.8
score += liberties * 18.0
score += next_options * 10.0
score += enemy_safe_options * 24.0
score += territory * 0.50
if likely_dead_end:
@@ -514,6 +540,10 @@ class BestBattleSnake(TemplateSnake):
if losing_head_to_head:
score -= 1500.0
if enemy_safe_options == 0:
score -= 1800.0
elif enemy_safe_options == 1:
score -= 450.0
is_safe_tightening_move = not likely_dead_end and not losing_head_to_head
if is_safe_tightening_move and enemy_space <= encase_target_space:
@@ -578,8 +608,10 @@ class BestBattleSnake(TemplateSnake):
move_safety[move] = {
"is_survivable": (not likely_dead_end)
and (not losing_head_to_head)
and enemy_safe_options > 0
and health_after_move > 0,
"reachable_space": reachable_space,
"enemy_safe_options": enemy_safe_options,
"tail_escape": has_tail_escape,
}
scores[move] = round(score, 5)
@@ -644,6 +676,15 @@ class BestBattleSnake(TemplateSnake):
next_options = self._next_turn_option_count(
future_body, blocked, width, height
)
enemy_safe_options = self._safe_next_turn_option_count(
future_body=future_body,
other_snakes=other_snakes,
food_set=food_set,
is_constrictor=True,
enemy_can_grow_cache=enemy_can_grow_cache,
width=width,
height=height,
)
territory = self._territory_control_score(
my_start=point,
enemy_starts=enemy_heads,
@@ -669,6 +710,7 @@ class BestBattleSnake(TemplateSnake):
score += reachable_space * 3.8
score += liberties * 24.0
score += next_options * 16.0
score += enemy_safe_options * 26.0
score += territory * 0.65
score += (reachable_space - enemy_best_space) * 3.2
score += (max(0, 8 - enemy_total_options)) * 18.0
@@ -685,6 +727,10 @@ class BestBattleSnake(TemplateSnake):
score -= 2400.0
elif enemy_len is not None:
score += 90.0
if enemy_safe_options == 0:
score -= 2200.0
elif enemy_safe_options == 1:
score -= 520.0
score -= self._revisit_penalty(point)
@@ -698,8 +744,11 @@ class BestBattleSnake(TemplateSnake):
score -= 35.0
move_safety[move] = {
"is_survivable": (not is_dead_end) and (not is_losing_head_to_head),
"is_survivable": (not is_dead_end)
and (not is_losing_head_to_head)
and enemy_safe_options > 0,
"reachable_space": reachable_space,
"enemy_safe_options": enemy_safe_options,
}
scores[move] = round(score, 5)
@@ -997,6 +1046,64 @@ class BestBattleSnake(TemplateSnake):
count += 1
return count
def _safe_next_turn_option_count(self, future_body:list[Coord], other_snakes:list[SnakeState], food_set:set[Point], is_constrictor:bool, enemy_can_grow_cache:dict[Any, bool]|None, width:int, height:int) -> int:
"""Count next-turn moves that stay safe from enemy head contests."""
if not future_body:
return 0
my_len = len(future_body)
future_snake = {
"head": future_body[0],
"body": future_body,
"length": my_len,
}
enemy_attack_map = self._build_enemy_attack_map(
my_snake=future_snake,
other_snakes=other_snakes,
food_set=food_set,
is_constrictor=is_constrictor,
width=width,
height=height,
enemy_can_grow_cache=enemy_can_grow_cache,
)
blocked = self._simulation_blocked(
future_body=future_body,
other_snakes=other_snakes,
food_set=food_set,
is_constrictor=is_constrictor,
enemy_can_grow_cache=enemy_can_grow_cache,
)
next_head = future_body[0]
own_tail = (future_body[-1]["x"], future_body[-1]["y"])
own_tail_stacked = self._is_tail_stacked(future_body)
safe_count = 0
for dx, dy in self.DIRECTIONS.values():
point = (next_head["x"] + dx, next_head["y"] + dy)
if not self._in_bounds(point, width, height):
continue
ate_food = point in food_set
can_step_on_tail = self._can_step_on_own_tail(
point=point,
own_tail=own_tail,
own_tail_is_stacked=own_tail_stacked,
ate_food=ate_food,
is_constrictor=is_constrictor,
)
if point in blocked and not can_step_on_tail:
continue
enemy_len = enemy_attack_map.get(point)
if enemy_len is not None and enemy_len >= my_len:
continue
safe_count += 1
return safe_count
def _revisit_penalty(self, point:Point) -> float:
"""Return penalty for revisiting recent head positions."""
if not self.recent_heads:
+78
View File
@@ -468,6 +468,84 @@ class TestBestBattleSnake(unittest.TestCase):
move = make_board(game_state).snake_neat_make_a_move()
self.assertNotEqual(move, "up")
def test_avoids_enemy_block_in_trap(self):
game_state = {
"game": {
"id": "test-enemy-block-in-trap",
"ruleset": {"name": "standard", "version": "v1.0.0"},
"source": "custom",
"map": "standard",
},
"turn": 24,
"board": {
"height": 7,
"width": 7,
"food": [{"x": 3, "y": 3}],
"hazards": [],
"snakes": [
{
"id": "me",
"name": "me",
"health": 72,
"length": 4,
"head": {"x": 3, "y": 2},
"body": [
{"x": 3, "y": 2},
{"x": 3, "y": 1},
{"x": 2, "y": 1},
{"x": 2, "y": 2},
],
},
{
"id": "enemy-a",
"name": "enemy-a",
"health": 90,
"length": 6,
"head": {"x": 4, "y": 4},
"body": [
{"x": 4, "y": 4},
{"x": 4, "y": 3},
{"x": 4, "y": 2},
{"x": 5, "y": 2},
{"x": 5, "y": 3},
{"x": 5, "y": 4},
],
},
{
"id": "enemy-b",
"name": "enemy-b",
"health": 90,
"length": 6,
"head": {"x": 1, "y": 3},
"body": [
{"x": 1, "y": 3},
{"x": 1, "y": 4},
{"x": 0, "y": 4},
{"x": 0, "y": 3},
{"x": 0, "y": 2},
{"x": 1, "y": 2},
],
},
],
},
"you": {
"id": "me",
"name": "me",
"health": 72,
"length": 4,
"head": {"x": 3, "y": 2},
"body": [
{"x": 3, "y": 2},
{"x": 3, "y": 1},
{"x": 2, "y": 1},
{"x": 2, "y": 2},
],
},
}
move = make_board(game_state).snake_neat_make_a_move()
self.assertEqual(move, "left")
def test_royale_uses_ruleset_hazard_damage_setting(self):
game_state = {
"game": {