add better enemy constrictor projection
This commit is contained in:
@@ -583,6 +583,12 @@ class BestBattleSnake(TemplateSnake):
|
|||||||
width=width,
|
width=width,
|
||||||
height=height,
|
height=height,
|
||||||
)
|
)
|
||||||
|
enemy_best_space, enemy_total_options = self._enemy_constrictor_projection(
|
||||||
|
other_snakes=other_snakes,
|
||||||
|
blocked=blocked,
|
||||||
|
width=width,
|
||||||
|
height=height,
|
||||||
|
)
|
||||||
|
|
||||||
enemy_len = enemy_attack_map.get(point)
|
enemy_len = enemy_attack_map.get(point)
|
||||||
is_losing_head_to_head = enemy_len is not None and enemy_len >= my_len
|
is_losing_head_to_head = enemy_len is not None and enemy_len >= my_len
|
||||||
@@ -596,6 +602,13 @@ class BestBattleSnake(TemplateSnake):
|
|||||||
score += liberties * 24.0
|
score += liberties * 24.0
|
||||||
score += next_options * 16.0
|
score += next_options * 16.0
|
||||||
score += territory * 0.65
|
score += territory * 0.65
|
||||||
|
score += (reachable_space - enemy_best_space) * 3.2
|
||||||
|
score += (max(0, 8 - enemy_total_options)) * 18.0
|
||||||
|
|
||||||
|
if enemy_total_options <= 2:
|
||||||
|
score += 110.0
|
||||||
|
if enemy_best_space > int(reachable_space * 1.2):
|
||||||
|
score -= 320.0
|
||||||
|
|
||||||
if is_dead_end:
|
if is_dead_end:
|
||||||
score -= 2600.0
|
score -= 2600.0
|
||||||
@@ -992,6 +1005,33 @@ class BestBattleSnake(TemplateSnake):
|
|||||||
)
|
)
|
||||||
return enemy_space, enemy_options
|
return enemy_space, enemy_options
|
||||||
|
|
||||||
|
def _enemy_constrictor_projection(self, other_snakes:list[SnakeState], blocked:set[Point], width:int, height: int) -> tuple[int, int]:
|
||||||
|
"""Estimate enemy best-space and total options after our candidate move."""
|
||||||
|
best_enemy_space = 0
|
||||||
|
total_enemy_options = 0
|
||||||
|
|
||||||
|
for enemy in other_snakes:
|
||||||
|
enemy_head = (enemy["head"]["x"], enemy["head"]["y"])
|
||||||
|
enemy_best_for_snake = 0
|
||||||
|
|
||||||
|
for neighbor in self._neighbors(enemy_head):
|
||||||
|
if not self._in_bounds(neighbor, width, height):
|
||||||
|
continue
|
||||||
|
if neighbor in blocked:
|
||||||
|
continue
|
||||||
|
|
||||||
|
total_enemy_options += 1
|
||||||
|
enemy_blocked = set(blocked)
|
||||||
|
enemy_blocked.add(neighbor)
|
||||||
|
enemy_space = self._flood_fill_count(
|
||||||
|
neighbor, enemy_blocked, width, height
|
||||||
|
)
|
||||||
|
enemy_best_for_snake = max(enemy_best_for_snake, enemy_space)
|
||||||
|
|
||||||
|
best_enemy_space = max(best_enemy_space, enemy_best_for_snake)
|
||||||
|
|
||||||
|
return best_enemy_space, total_enemy_options
|
||||||
|
|
||||||
def _neighbors(self, point:Point) -> Iterator[Point]:
|
def _neighbors(self, point:Point) -> Iterator[Point]:
|
||||||
"""Yield orthogonal neighbor coordinates for a point."""
|
"""Yield orthogonal neighbor coordinates for a point."""
|
||||||
for dx, dy in self.DIRECTIONS.values():
|
for dx, dy in self.DIRECTIONS.values():
|
||||||
|
|||||||
@@ -681,5 +681,75 @@ class TestBestBattleSnake(unittest.TestCase):
|
|||||||
move = make_board(game_state).snake_neat_make_a_move()
|
move = make_board(game_state).snake_neat_make_a_move()
|
||||||
self.assertEqual(move, "right")
|
self.assertEqual(move, "right")
|
||||||
|
|
||||||
|
def test_constrictor_prefers_choke_when_safe(self):
|
||||||
|
game_state = {
|
||||||
|
"game": {
|
||||||
|
"id": "test-constrictor-choke",
|
||||||
|
"ruleset": {"name": "constrictor", "version": "v1.0.0"},
|
||||||
|
"source": "custom",
|
||||||
|
"map": "standard",
|
||||||
|
},
|
||||||
|
"turn": 25,
|
||||||
|
"board": {
|
||||||
|
"height": 7,
|
||||||
|
"width": 7,
|
||||||
|
"food": [],
|
||||||
|
"hazards": [],
|
||||||
|
"snakes": [
|
||||||
|
{
|
||||||
|
"id": "me",
|
||||||
|
"name": "me",
|
||||||
|
"health": 100,
|
||||||
|
"length": 7,
|
||||||
|
"head": {"x": 2, "y": 3},
|
||||||
|
"body": [
|
||||||
|
{"x": 2, "y": 3},
|
||||||
|
{"x": 2, "y": 2},
|
||||||
|
{"x": 1, "y": 2},
|
||||||
|
{"x": 1, "y": 3},
|
||||||
|
{"x": 1, "y": 4},
|
||||||
|
{"x": 2, "y": 4},
|
||||||
|
{"x": 2, "y": 5},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "enemy",
|
||||||
|
"name": "enemy",
|
||||||
|
"health": 100,
|
||||||
|
"length": 7,
|
||||||
|
"head": {"x": 4, "y": 3},
|
||||||
|
"body": [
|
||||||
|
{"x": 4, "y": 3},
|
||||||
|
{"x": 5, "y": 3},
|
||||||
|
{"x": 5, "y": 2},
|
||||||
|
{"x": 4, "y": 2},
|
||||||
|
{"x": 4, "y": 1},
|
||||||
|
{"x": 5, "y": 1},
|
||||||
|
{"x": 6, "y": 1},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"you": {
|
||||||
|
"id": "me",
|
||||||
|
"name": "me",
|
||||||
|
"health": 100,
|
||||||
|
"length": 7,
|
||||||
|
"head": {"x": 2, "y": 3},
|
||||||
|
"body": [
|
||||||
|
{"x": 2, "y": 3},
|
||||||
|
{"x": 2, "y": 2},
|
||||||
|
{"x": 1, "y": 2},
|
||||||
|
{"x": 1, "y": 3},
|
||||||
|
{"x": 1, "y": 4},
|
||||||
|
{"x": 2, "y": 4},
|
||||||
|
{"x": 2, "y": 5},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
move = make_board(game_state).snake_neat_make_a_move()
|
||||||
|
self.assertEqual(move, "right")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user