1026 lines
24 KiB
Python
1026 lines
24 KiB
Python
import unittest
|
|
|
|
from snakes.BestBattleSnake import BestBattleSnake
|
|
from server.GameBoard import GameBoard
|
|
|
|
def make_board(game_state):
|
|
board = GameBoard(
|
|
game_id=game_state["game"]["id"],
|
|
width=game_state["board"]["width"],
|
|
height=game_state["board"]["height"],
|
|
ruleset=game_state["game"]["ruleset"],
|
|
source=game_state["game"]["source"],
|
|
map=game_state["game"]["map"],
|
|
snake_class=BestBattleSnake(),
|
|
)
|
|
board.read_game_data(game_state)
|
|
return board
|
|
|
|
class TestBestBattleSnake(unittest.TestCase):
|
|
def test_avoids_walls_and_body(self):
|
|
game_state = {
|
|
"game": {
|
|
"id": "test-wall-body",
|
|
"ruleset": {"name": "standard", "version": "v1.0.0"},
|
|
"source": "custom",
|
|
"map": "standard",
|
|
},
|
|
"turn": 20,
|
|
"board": {
|
|
"height": 7,
|
|
"width": 7,
|
|
"food": [{"x": 5, "y": 5}],
|
|
"hazards": [],
|
|
"snakes": [
|
|
{
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 90,
|
|
"length": 4,
|
|
"head": {"x": 0, "y": 0},
|
|
"body": [
|
|
{"x": 0, "y": 0},
|
|
{"x": 0, "y": 1},
|
|
{"x": 1, "y": 1},
|
|
{"x": 1, "y": 0},
|
|
],
|
|
}
|
|
],
|
|
},
|
|
"you": {
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 90,
|
|
"length": 4,
|
|
"head": {"x": 0, "y": 0},
|
|
"body": [
|
|
{"x": 0, "y": 0},
|
|
{"x": 0, "y": 1},
|
|
{"x": 1, "y": 1},
|
|
{"x": 1, "y": 0},
|
|
],
|
|
},
|
|
}
|
|
|
|
move = make_board(game_state).snake_neat_make_a_move()
|
|
self.assertEqual(move, "right")
|
|
|
|
def test_prioritizes_food_when_low_health(self):
|
|
game_state = {
|
|
"game": {
|
|
"id": "test-food-low-health",
|
|
"ruleset": {"name": "standard", "version": "v1.0.0"},
|
|
"source": "custom",
|
|
"map": "standard",
|
|
},
|
|
"turn": 32,
|
|
"board": {
|
|
"height": 11,
|
|
"width": 11,
|
|
"food": [{"x": 6, "y": 5}],
|
|
"hazards": [],
|
|
"snakes": [
|
|
{
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 10,
|
|
"length": 3,
|
|
"head": {"x": 5, "y": 5},
|
|
"body": [
|
|
{"x": 5, "y": 5},
|
|
{"x": 5, "y": 4},
|
|
{"x": 5, "y": 3},
|
|
],
|
|
}
|
|
],
|
|
},
|
|
"you": {
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 10,
|
|
"length": 3,
|
|
"head": {"x": 5, "y": 5},
|
|
"body": [
|
|
{"x": 5, "y": 5},
|
|
{"x": 5, "y": 4},
|
|
{"x": 5, "y": 3},
|
|
],
|
|
},
|
|
}
|
|
|
|
move = make_board(game_state).snake_neat_make_a_move()
|
|
self.assertEqual(move, "right")
|
|
|
|
def test_prioritizes_food_when_safe(self):
|
|
game_state = {
|
|
"game": {
|
|
"id": "test-food-safe",
|
|
"ruleset": {"name": "standard", "version": "v1.0.0"},
|
|
"source": "custom",
|
|
"map": "standard",
|
|
},
|
|
"turn": 8,
|
|
"board": {
|
|
"height": 11,
|
|
"width": 11,
|
|
"food": [{"x": 6, "y": 5}],
|
|
"hazards": [],
|
|
"snakes": [
|
|
{
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 95,
|
|
"length": 3,
|
|
"head": {"x": 5, "y": 5},
|
|
"body": [
|
|
{"x": 5, "y": 5},
|
|
{"x": 5, "y": 4},
|
|
{"x": 5, "y": 3},
|
|
],
|
|
}
|
|
],
|
|
},
|
|
"you": {
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 95,
|
|
"length": 3,
|
|
"head": {"x": 5, "y": 5},
|
|
"body": [
|
|
{"x": 5, "y": 5},
|
|
{"x": 5, "y": 4},
|
|
{"x": 5, "y": 3},
|
|
],
|
|
},
|
|
}
|
|
|
|
move = make_board(game_state).snake_neat_make_a_move()
|
|
self.assertEqual(move, "right")
|
|
|
|
def test_avoids_losing_head_to_head(self):
|
|
game_state = {
|
|
"game": {
|
|
"id": "test-head-to-head",
|
|
"ruleset": {"name": "standard", "version": "v1.0.0"},
|
|
"source": "custom",
|
|
"map": "standard",
|
|
},
|
|
"turn": 44,
|
|
"board": {
|
|
"height": 11,
|
|
"width": 11,
|
|
"food": [{"x": 1, "y": 1}],
|
|
"hazards": [],
|
|
"snakes": [
|
|
{
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 90,
|
|
"length": 3,
|
|
"head": {"x": 5, "y": 5},
|
|
"body": [
|
|
{"x": 5, "y": 5},
|
|
{"x": 5, "y": 4},
|
|
{"x": 5, "y": 3},
|
|
],
|
|
},
|
|
{
|
|
"id": "enemy",
|
|
"name": "enemy",
|
|
"health": 90,
|
|
"length": 6,
|
|
"head": {"x": 7, "y": 5},
|
|
"body": [
|
|
{"x": 7, "y": 5},
|
|
{"x": 7, "y": 4},
|
|
{"x": 7, "y": 3},
|
|
{"x": 7, "y": 2},
|
|
{"x": 7, "y": 1},
|
|
{"x": 6, "y": 1},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
"you": {
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 90,
|
|
"length": 3,
|
|
"head": {"x": 5, "y": 5},
|
|
"body": [
|
|
{"x": 5, "y": 5},
|
|
{"x": 5, "y": 4},
|
|
{"x": 5, "y": 3},
|
|
],
|
|
},
|
|
}
|
|
|
|
move = make_board(game_state).snake_neat_make_a_move()
|
|
self.assertNotEqual(move, "right")
|
|
|
|
def test_duel_small_length_lead_does_not_head_hunt(self):
|
|
game_state = {
|
|
"game": {
|
|
"id": "test-duel-small-no-head-hunt",
|
|
"ruleset": {"name": "standard", "version": "v1.0.0"},
|
|
"source": "custom",
|
|
"map": "standard",
|
|
},
|
|
"turn": 20,
|
|
"board": {
|
|
"height": 11,
|
|
"width": 11,
|
|
"food": [{"x": 1, "y": 1}],
|
|
"hazards": [],
|
|
"snakes": [
|
|
{
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 90,
|
|
"length": 7,
|
|
"head": {"x": 5, "y": 5},
|
|
"body": [
|
|
{"x": 5, "y": 5},
|
|
{"x": 5, "y": 4},
|
|
{"x": 5, "y": 3},
|
|
{"x": 4, "y": 3},
|
|
{"x": 4, "y": 4},
|
|
{"x": 4, "y": 5},
|
|
{"x": 4, "y": 6},
|
|
],
|
|
},
|
|
{
|
|
"id": "enemy",
|
|
"name": "enemy",
|
|
"health": 90,
|
|
"length": 6,
|
|
"head": {"x": 7, "y": 5},
|
|
"body": [
|
|
{"x": 7, "y": 5},
|
|
{"x": 7, "y": 4},
|
|
{"x": 7, "y": 3},
|
|
{"x": 7, "y": 2},
|
|
{"x": 7, "y": 1},
|
|
{"x": 6, "y": 1},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
"you": {
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 90,
|
|
"length": 7,
|
|
"head": {"x": 5, "y": 5},
|
|
"body": [
|
|
{"x": 5, "y": 5},
|
|
{"x": 5, "y": 4},
|
|
{"x": 5, "y": 3},
|
|
{"x": 4, "y": 3},
|
|
{"x": 4, "y": 4},
|
|
{"x": 4, "y": 5},
|
|
{"x": 4, "y": 6},
|
|
],
|
|
},
|
|
}
|
|
|
|
move = make_board(game_state).snake_neat_make_a_move()
|
|
self.assertNotEqual(move, "right")
|
|
|
|
def test_duel_big_length_lead_can_head_hunt(self):
|
|
game_state = {
|
|
"game": {
|
|
"id": "test-duel-big-head-hunt",
|
|
"ruleset": {"name": "standard", "version": "v1.0.0"},
|
|
"source": "custom",
|
|
"map": "standard",
|
|
},
|
|
"turn": 20,
|
|
"board": {
|
|
"height": 11,
|
|
"width": 11,
|
|
"food": [{"x": 1, "y": 1}],
|
|
"hazards": [],
|
|
"snakes": [
|
|
{
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 90,
|
|
"length": 8,
|
|
"head": {"x": 5, "y": 5},
|
|
"body": [
|
|
{"x": 5, "y": 5},
|
|
{"x": 5, "y": 4},
|
|
{"x": 5, "y": 3},
|
|
{"x": 4, "y": 3},
|
|
{"x": 4, "y": 4},
|
|
{"x": 4, "y": 5},
|
|
{"x": 4, "y": 6},
|
|
{"x": 5, "y": 6},
|
|
],
|
|
},
|
|
{
|
|
"id": "enemy",
|
|
"name": "enemy",
|
|
"health": 90,
|
|
"length": 6,
|
|
"head": {"x": 7, "y": 5},
|
|
"body": [
|
|
{"x": 7, "y": 5},
|
|
{"x": 7, "y": 4},
|
|
{"x": 7, "y": 3},
|
|
{"x": 7, "y": 2},
|
|
{"x": 7, "y": 1},
|
|
{"x": 6, "y": 1},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
"you": {
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 90,
|
|
"length": 8,
|
|
"head": {"x": 5, "y": 5},
|
|
"body": [
|
|
{"x": 5, "y": 5},
|
|
{"x": 5, "y": 4},
|
|
{"x": 5, "y": 3},
|
|
{"x": 4, "y": 3},
|
|
{"x": 4, "y": 4},
|
|
{"x": 4, "y": 5},
|
|
{"x": 4, "y": 6},
|
|
{"x": 5, "y": 6},
|
|
],
|
|
},
|
|
}
|
|
|
|
move = make_board(game_state).snake_neat_make_a_move()
|
|
self.assertEqual(move, "right")
|
|
|
|
def test_does_not_step_into_stacked_tail(self):
|
|
game_state = {
|
|
"game": {
|
|
"id": "test-stacked-tail",
|
|
"ruleset": {"name": "standard", "version": "v1.0.0"},
|
|
"source": "custom",
|
|
"map": "standard",
|
|
},
|
|
"turn": 15,
|
|
"board": {
|
|
"height": 11,
|
|
"width": 11,
|
|
"food": [{"x": 10, "y": 10}],
|
|
"hazards": [],
|
|
"snakes": [
|
|
{
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 90,
|
|
"length": 5,
|
|
"head": {"x": 5, "y": 5},
|
|
"body": [
|
|
{"x": 5, "y": 5},
|
|
{"x": 5, "y": 4},
|
|
{"x": 4, "y": 4},
|
|
{"x": 4, "y": 5},
|
|
{"x": 4, "y": 5},
|
|
],
|
|
}
|
|
],
|
|
},
|
|
"you": {
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 90,
|
|
"length": 5,
|
|
"head": {"x": 5, "y": 5},
|
|
"body": [
|
|
{"x": 5, "y": 5},
|
|
{"x": 5, "y": 4},
|
|
{"x": 4, "y": 4},
|
|
{"x": 4, "y": 5},
|
|
{"x": 4, "y": 5},
|
|
],
|
|
},
|
|
}
|
|
|
|
move = make_board(game_state).snake_neat_make_a_move()
|
|
self.assertNotEqual(move, "left")
|
|
|
|
def test_avoids_food_if_it_is_a_dead_end(self):
|
|
game_state = {
|
|
"game": {
|
|
"id": "test-food-dead-end",
|
|
"ruleset": {"name": "standard", "version": "v1.0.0"},
|
|
"source": "custom",
|
|
"map": "standard",
|
|
},
|
|
"turn": 30,
|
|
"board": {
|
|
"height": 7,
|
|
"width": 7,
|
|
"food": [{"x": 3, "y": 4}],
|
|
"hazards": [],
|
|
"snakes": [
|
|
{
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 70,
|
|
"length": 3,
|
|
"head": {"x": 3, "y": 3},
|
|
"body": [
|
|
{"x": 3, "y": 3},
|
|
{"x": 3, "y": 2},
|
|
{"x": 3, "y": 1},
|
|
],
|
|
},
|
|
{
|
|
"id": "enemy",
|
|
"name": "enemy",
|
|
"health": 90,
|
|
"length": 5,
|
|
"head": {"x": 2, "y": 4},
|
|
"body": [
|
|
{"x": 2, "y": 4},
|
|
{"x": 2, "y": 5},
|
|
{"x": 3, "y": 5},
|
|
{"x": 4, "y": 5},
|
|
{"x": 4, "y": 4},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
"you": {
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 70,
|
|
"length": 3,
|
|
"head": {"x": 3, "y": 3},
|
|
"body": [
|
|
{"x": 3, "y": 3},
|
|
{"x": 3, "y": 2},
|
|
{"x": 3, "y": 1},
|
|
],
|
|
},
|
|
}
|
|
|
|
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": {
|
|
"id": "test-royale-hazard-setting",
|
|
"ruleset": {
|
|
"name": "standard",
|
|
"version": "v1.0.0",
|
|
"settings": {"hazardDamagePerTurn": 22},
|
|
},
|
|
"source": "custom",
|
|
"map": "royale",
|
|
},
|
|
"turn": 5,
|
|
"board": {
|
|
"height": 11,
|
|
"width": 11,
|
|
"food": [],
|
|
"hazards": [],
|
|
"snakes": [
|
|
{
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 80,
|
|
"length": 3,
|
|
"head": {"x": 5, "y": 5},
|
|
"body": [
|
|
{"x": 5, "y": 5},
|
|
{"x": 5, "y": 4},
|
|
{"x": 5, "y": 3},
|
|
],
|
|
}
|
|
],
|
|
},
|
|
"you": {
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 80,
|
|
"length": 3,
|
|
"head": {"x": 5, "y": 5},
|
|
"body": [
|
|
{"x": 5, "y": 5},
|
|
{"x": 5, "y": 4},
|
|
{"x": 5, "y": 3},
|
|
],
|
|
},
|
|
}
|
|
|
|
board = make_board(game_state)
|
|
snake = board.snake_class
|
|
self.assertEqual(snake._hazard_damage_per_turn(board), 22)
|
|
|
|
def test_royale_new_hazard_has_spawn_grace(self):
|
|
snake = BestBattleSnake()
|
|
point = (4, 4)
|
|
hazard_set = {point}
|
|
|
|
self.assertFalse(
|
|
snake._hazard_is_active(
|
|
point, ate_food=False, hazard_set=hazard_set, previous_hazard_set=set()
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
snake._hazard_is_active(
|
|
point,
|
|
ate_food=False,
|
|
hazard_set=hazard_set,
|
|
previous_hazard_set=hazard_set,
|
|
)
|
|
)
|
|
self.assertFalse(
|
|
snake._hazard_is_active(
|
|
point,
|
|
ate_food=True,
|
|
hazard_set=hazard_set,
|
|
previous_hazard_set=hazard_set,
|
|
)
|
|
)
|
|
|
|
def test_constrictor_avoids_growth_dead_end(self):
|
|
game_state = {
|
|
"game": {
|
|
"id": "test-constrictor-dead-end",
|
|
"ruleset": {"name": "constrictor", "version": "v1.0.0"},
|
|
"source": "custom",
|
|
"map": "standard",
|
|
},
|
|
"turn": 12,
|
|
"board": {
|
|
"height": 7,
|
|
"width": 7,
|
|
"food": [],
|
|
"hazards": [],
|
|
"snakes": [
|
|
{
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 100,
|
|
"length": 4,
|
|
"head": {"x": 1, "y": 1},
|
|
"body": [
|
|
{"x": 1, "y": 1},
|
|
{"x": 1, "y": 0},
|
|
{"x": 0, "y": 0},
|
|
{"x": 0, "y": 1},
|
|
],
|
|
},
|
|
{
|
|
"id": "enemy",
|
|
"name": "enemy",
|
|
"health": 100,
|
|
"length": 8,
|
|
"head": {"x": 4, "y": 4},
|
|
"body": [
|
|
{"x": 4, "y": 4},
|
|
{"x": 3, "y": 4},
|
|
{"x": 3, "y": 3},
|
|
{"x": 2, "y": 3},
|
|
{"x": 2, "y": 2},
|
|
{"x": 2, "y": 0},
|
|
{"x": 2, "y": 2},
|
|
{"x": 3, "y": 1},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
"you": {
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 100,
|
|
"length": 4,
|
|
"head": {"x": 1, "y": 1},
|
|
"body": [
|
|
{"x": 1, "y": 1},
|
|
{"x": 1, "y": 0},
|
|
{"x": 0, "y": 0},
|
|
{"x": 0, "y": 1},
|
|
],
|
|
},
|
|
}
|
|
|
|
move = make_board(game_state).snake_neat_make_a_move()
|
|
self.assertEqual(move, "up")
|
|
|
|
def test_duel_tightens_space_when_enemy_is_encased(self):
|
|
game_state = {
|
|
"game": {
|
|
"id": "test-encase-tighten",
|
|
"ruleset": {"name": "standard", "version": "v1.0.0"},
|
|
"source": "custom",
|
|
"map": "standard",
|
|
},
|
|
"turn": 40,
|
|
"board": {
|
|
"height": 7,
|
|
"width": 7,
|
|
"food": [{"x": 0, "y": 0}],
|
|
"hazards": [],
|
|
"snakes": [
|
|
{
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 92,
|
|
"length": 8,
|
|
"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": 3, "y": 4},
|
|
{"x": 3, "y": 5},
|
|
],
|
|
},
|
|
{
|
|
"id": "enemy",
|
|
"name": "enemy",
|
|
"health": 88,
|
|
"length": 5,
|
|
"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},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
"you": {
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 92,
|
|
"length": 8,
|
|
"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": 3, "y": 4},
|
|
{"x": 3, "y": 5},
|
|
],
|
|
},
|
|
}
|
|
|
|
move = make_board(game_state).snake_neat_make_a_move()
|
|
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")
|
|
|
|
def test_enemy_attack_map_marks_own_tail_contest(self):
|
|
snake = BestBattleSnake()
|
|
|
|
my_snake = {
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 90,
|
|
"length": 4,
|
|
"head": {"x": 3, "y": 3},
|
|
"body": [
|
|
{"x": 3, "y": 3},
|
|
{"x": 3, "y": 2},
|
|
{"x": 2, "y": 2},
|
|
{"x": 2, "y": 3},
|
|
],
|
|
}
|
|
|
|
enemy = {
|
|
"id": "enemy",
|
|
"name": "enemy",
|
|
"health": 90,
|
|
"length": 6,
|
|
"head": {"x": 1, "y": 3},
|
|
"body": [
|
|
{"x": 1, "y": 3},
|
|
{"x": 1, "y": 2},
|
|
{"x": 1, "y": 1},
|
|
{"x": 0, "y": 1},
|
|
{"x": 0, "y": 2},
|
|
{"x": 0, "y": 3},
|
|
],
|
|
}
|
|
|
|
attack_map = snake._build_enemy_attack_map(
|
|
my_snake=my_snake,
|
|
other_snakes=[enemy],
|
|
food_set=set(),
|
|
is_constrictor=False,
|
|
width=7,
|
|
height=7,
|
|
enemy_can_grow_cache={"enemy": False},
|
|
)
|
|
|
|
self.assertEqual(attack_map.get((2, 3)), 6)
|
|
|
|
def test_simulation_frees_enemy_tail_even_if_enemy_can_grow(self):
|
|
snake = BestBattleSnake()
|
|
|
|
future_body = [
|
|
{"x": 4, "y": 4},
|
|
{"x": 4, "y": 3},
|
|
{"x": 3, "y": 3},
|
|
{"x": 3, "y": 4},
|
|
]
|
|
enemy = {
|
|
"id": "enemy",
|
|
"name": "enemy",
|
|
"health": 90,
|
|
"length": 4,
|
|
"head": {"x": 1, "y": 1},
|
|
"body": [
|
|
{"x": 1, "y": 1},
|
|
{"x": 1, "y": 0},
|
|
{"x": 0, "y": 0},
|
|
{"x": 0, "y": 1},
|
|
],
|
|
}
|
|
|
|
blocked = snake._simulation_blocked(
|
|
future_body=future_body,
|
|
other_snakes=[enemy],
|
|
food_set={(2, 1)},
|
|
is_constrictor=False,
|
|
enemy_can_grow_cache={"enemy": True},
|
|
)
|
|
|
|
self.assertIn((0, 1), blocked)
|
|
|
|
def test_enemy_attack_map_allows_enemy_tail_move_when_enemy_can_grow(self):
|
|
snake = BestBattleSnake()
|
|
|
|
my_snake = {
|
|
"id": "me",
|
|
"name": "me",
|
|
"health": 90,
|
|
"length": 4,
|
|
"head": {"x": 6, "y": 6},
|
|
"body": [
|
|
{"x": 6, "y": 6},
|
|
{"x": 6, "y": 5},
|
|
{"x": 5, "y": 5},
|
|
{"x": 5, "y": 6},
|
|
],
|
|
}
|
|
|
|
enemy = {
|
|
"id": "enemy",
|
|
"name": "enemy",
|
|
"health": 90,
|
|
"length": 4,
|
|
"head": {"x": 3, "y": 3},
|
|
"body": [
|
|
{"x": 3, "y": 3},
|
|
{"x": 3, "y": 2},
|
|
{"x": 2, "y": 2},
|
|
{"x": 2, "y": 3},
|
|
],
|
|
}
|
|
|
|
attack_map = snake._build_enemy_attack_map(
|
|
my_snake=my_snake,
|
|
other_snakes=[enemy],
|
|
food_set={(4, 3)},
|
|
is_constrictor=False,
|
|
width=11,
|
|
height=11,
|
|
enemy_can_grow_cache={"enemy": True},
|
|
)
|
|
|
|
self.assertIsNone(attack_map.get((2, 3)))
|
|
|
|
def test_future_planning_prefers_non_trap_path(self):
|
|
snake = BestBattleSnake()
|
|
|
|
my_body = [
|
|
{"x": 3, "y": 3},
|
|
{"x": 3, "y": 2},
|
|
{"x": 2, "y": 2},
|
|
{"x": 2, "y": 3},
|
|
]
|
|
enemy = {
|
|
"id": "enemy",
|
|
"name": "enemy",
|
|
"health": 90,
|
|
"length": 8,
|
|
"head": {"x": 0, "y": 0},
|
|
"body": [
|
|
{"x": 0, "y": 0},
|
|
{"x": 4, "y": 4},
|
|
{"x": 4, "y": 2},
|
|
{"x": 5, "y": 4},
|
|
{"x": 5, "y": 2},
|
|
{"x": 6, "y": 4},
|
|
{"x": 6, "y": 3},
|
|
{"x": 6, "y": 2},
|
|
],
|
|
}
|
|
|
|
safe_moves = snake._legal_moves(
|
|
my_head=my_body[0],
|
|
my_body=my_body,
|
|
other_snakes=[enemy],
|
|
food_set=set(),
|
|
is_constrictor=False,
|
|
width=7,
|
|
height=7,
|
|
)
|
|
|
|
self.assertIn("left", safe_moves)
|
|
self.assertIn("right", safe_moves)
|
|
|
|
right_bonus = snake._future_rollout_bonus_for_move(
|
|
move="right",
|
|
safe_moves=safe_moves,
|
|
my_body=my_body,
|
|
other_snakes=[enemy],
|
|
food_set=set(),
|
|
is_constrictor=False,
|
|
width=7,
|
|
height=7,
|
|
enemy_can_grow_cache={"enemy": False},
|
|
depth=3,
|
|
branch_limit=2,
|
|
deadline=None,
|
|
)
|
|
left_bonus = snake._future_rollout_bonus_for_move(
|
|
move="left",
|
|
safe_moves=safe_moves,
|
|
my_body=my_body,
|
|
other_snakes=[enemy],
|
|
food_set=set(),
|
|
is_constrictor=False,
|
|
width=7,
|
|
height=7,
|
|
enemy_can_grow_cache={"enemy": False},
|
|
depth=3,
|
|
branch_limit=2,
|
|
deadline=None,
|
|
)
|
|
|
|
self.assertGreater(left_bonus, right_bonus)
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|