diff --git a/config.py b/config.py index 37ccfb6..6da5a4f 100644 --- a/config.py +++ b/config.py @@ -1,3 +1,3 @@ -from snakes.MySnake import MySnake +from snakes.LogicSnake import LogicSnake -SNAKE = MySnake() +SNAKE = LogicSnake() diff --git a/server_logic.py b/snakes/LogicSnake.py similarity index 59% rename from server_logic.py rename to snakes/LogicSnake.py index 9a6f1d0..ae5f8cc 100644 --- a/server_logic.py +++ b/snakes/LogicSnake.py @@ -1,15 +1,8 @@ import random from scipy import spatial -""" -This file can be a nice home for your move logic, and to write helper functions. - -We have started this for you, with a function to help remove the 'neck' direction -from the list of possible moves! -""" - - -def avoid_my_body(my_body, possible_moves: dict) -> list: +class LogicSnake: + def avoid_my_body(self, my_body, possible_moves: dict) -> list: """ my_body: List of dictionaries of x/y coordinates for every segment of a Battlesnake. e.g. [ {"x": 0, "y": 0}, {"x": 1, "y": 0}, {"x": 2, "y": 0} ] @@ -28,61 +21,60 @@ def avoid_my_body(my_body, possible_moves: dict) -> list: return possible_moves - -def avoid_walls(board_width: int, board_height: int, possible_moves: dict): - remove = [] - for direction, location in possible_moves.items(): - x_out_range = (location["x"] < 0 or location["x"] == board_width) - y_out_range = (location["y"] < 0 or location["y"] == board_height) - if x_out_range or y_out_range: - remove.append(direction) - - for direction in remove: - del possible_moves[direction] - - return possible_moves - -def avoid_snakes(snakes: list, possible_moves: dict): - remove = [] - for snake in snakes: + def avoid_walls(self, board_width: int, board_height: int, possible_moves: dict): + remove = [] for direction, location in possible_moves.items(): - if location in snake["body"]: + x_out_range = (location["x"] < 0 or location["x"] == board_width) + y_out_range = (location["y"] < 0 or location["y"] == board_height) + if x_out_range or y_out_range: remove.append(direction) - remove = set(remove) - for direction in remove: - del possible_moves[direction] + for direction in remove: + del possible_moves[direction] - return possible_moves + return possible_moves -def get_rarget_close(foods: list, my_head: dict): - coordinates = [] + def avoid_snakes(self, snakes: list, possible_moves: dict): + remove = [] + for snake in snakes: + for direction, location in possible_moves.items(): + if location in snake["body"]: + remove.append(direction) - if len(foods) == 0: - return None + remove = set(remove) + for direction in remove: + del possible_moves[direction] - for food in foods: - coordinates.append((food["x"], food["y"])) + return possible_moves - tree = spatial.KDTree(coordinates) - results = tree.query([(my_head["x"], my_head["y"])])[1] + def get_rarget_close(self, foods: list, my_head: dict): + coordinates = [] - return foods[results[0]] + if len(foods) == 0: + return None -def move_target(possible_moves: list, my_head: dict, target:dict): - distance_x = abs(my_head["x"] - target["x"]) - distance_y = abs(my_head["y"] - target["y"]) + for food in foods: + coordinates.append((food["x"], food["y"])) - for direction, location in possible_moves.items(): - new_distance_x = abs(location["x"] - target["x"]) - new_distance_y = abs(location["y"] - target["y"]) + tree = spatial.KDTree(coordinates) + results = tree.query([(my_head["x"], my_head["y"])])[1] - if new_distance_x < distance_x or new_distance_y < distance_y: - return direction + return foods[results[0]] - return list(possible_moves.keys())[0] + def move_target(self, possible_moves: list, my_head: dict, target:dict): + distance_x = abs(my_head["x"] - target["x"]) + distance_y = abs(my_head["y"] - target["y"]) -def choose_move(data: dict) -> str: + for direction, location in possible_moves.items(): + new_distance_x = abs(location["x"] - target["x"]) + new_distance_y = abs(location["y"] - target["y"]) + + if new_distance_x < distance_x or new_distance_y < distance_y: + return direction + + return list(possible_moves.keys())[0] + + def choose_move(self, data: dict) -> str: """ data: Dictionary of all Game Board data as received from the Battlesnake Engine. For a full example of 'data', see https://docs.battlesnake.com/references/api/sample-move-request @@ -129,16 +121,16 @@ def choose_move(data: dict) -> str: } # Don't allow your Battlesnake to move back in on it's own neck - possible_moves = avoid_my_body(my_body, possible_moves) - possible_moves = avoid_walls(board_width, board_height, possible_moves) - possible_moves = avoid_snakes(snakes, possible_moves) + possible_moves = self.avoid_my_body(my_body, possible_moves) + possible_moves = self.avoid_walls(board_width, board_height, possible_moves) + possible_moves = self.avoid_snakes(snakes, possible_moves) - target = get_rarget_close(foods, my_head) + target = self.get_rarget_close(foods, my_head) # TODO: Explore new strategies for picking a move that are better than random if len(possible_moves) > 0: if target is not None: - move = move_target(possible_moves, my_head, target) + move = self.move_target(possible_moves, my_head, target) else: possible_moves = list(possible_moves.keys()) move = random.choice(possible_moves) diff --git a/tests/tests.py b/tests/tests.py index 9fb109e..f926a85 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -12,7 +12,7 @@ in the folder where this file exists: """ import unittest -from snakes.MySnake import avoid_my_neck +from snakes.LogicSnake import avoid_my_neck class AvoidNeckTest(unittest.TestCase):