add enemy cut off trap check
This commit is contained in:
+108
-1
@@ -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:
|
||||
|
||||
@@ -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": {
|
||||
|
||||
Reference in New Issue
Block a user