diff --git a/snakes/TemplateSnake.py b/snakes/TemplateSnake.py index 70cc70e..811a841 100644 --- a/snakes/TemplateSnake.py +++ b/snakes/TemplateSnake.py @@ -1,3 +1,5 @@ +import random + class TemplateSnake: def __init__(self): self.history = [] @@ -11,5 +13,133 @@ class TemplateSnake: def get_history(self): return self.history + def add_calculations(self, calculations:dict): + self.calculations.append(calculations) + def choose_move(self, game_data:dict): - pass + self.calculations = [] + self.eat_the_snake_overwrite = False + + self.board_width = game_data['board']['width'] + self.board_height = game_data['board']['height'] + + self.my_snake = game_data['you'] + self.other_snakes = [ x for x in game_data['board']['snakes'] if x["id"] != self.my_snake["id"] ] + + self.my_head = self.my_snake['head'] + self.my_body = self.my_snake["body"] + self.game_type = game_data['game']["ruleset"]["name"] + + self.find_safe_positions() + moves = list(self.safe_positions.keys()) + if len(moves) > 0: + move = random.choice(moves) + else: + print("No safe positions left - Going to Die") + move = None + + self.add_to_history({"turn": game_data["turn"], "data": self.calculations}) + return move if move else "up" + + def get_possible_moves(self, snake_head): + return { + "up": { + "x": snake_head["x"], + "y": snake_head["y"] + 1 + }, + "down": { + "x": snake_head["x"], + "y": snake_head["y"] - 1 + }, + "left": { + "x": snake_head["x"] - 1, + "y": snake_head["y"] + }, + "right": { + "x": snake_head["x"] + 1, + "y": snake_head["y"] + } + } + + def get_snake_body_without_snake_tail(self, snake:list[dict]): + if len(set((pos["x"], pos["y"]) for pos in snake)) < 3: + return snake + + snake.pop() + return snake + + def avoid_my_body(self) -> 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} ] + possible_moves: List of strings. Moves to pick from. + e.g. ["up", "down", "left", "right"] + + return: The list of remaining possible_moves, with the 'neck' direction removed + """ + remove = [] + for direction, location in self.safe_positions.items(): + if location in self.my_body: + remove.append(direction) + + for direction in remove: + del self.safe_positions[direction] + + self.add_calculations({"function": "avoid_my_body", "my_body": self.my_body, "safe_positions": self.safe_positions}) + + def avoid_walls(self): + remove = [] + for direction, location in list(self.safe_positions.items()): + x_out_range = (location["x"] < 0 or location["x"] == self.board_width) + y_out_range = (location["y"] < 0 or location["y"] == self.board_height) + if x_out_range or y_out_range: + remove.append(direction) + + for direction in remove: + del self.safe_positions[direction] + + self.add_calculations({"function": "avoid_walls", "board_width": self.board_width, "board_height": self.board_height, "safe_positions": self.safe_positions}) + + def avoid_snakes(self): + remove = [] + for snake in self.other_snakes: + for direction, location in self.safe_positions.items(): + #if self.game_type == "constrictor": + if location in snake["body"]: + remove.append(direction) + #else: + # if location in self.get_snake_body_without_snake_tail(snake["body"]): + # remove.append(direction) + + remove = set(remove) + for direction in remove: + del self.safe_positions[direction] + + self.add_calculations({"function": "avoid_snakes", "other_snakes": self.other_snakes, "safe_positions": self.safe_positions}) + + def avoid_get_eaten_by_other_snakes(self): + remove = [] + for snake in self.other_snakes: + for direction, location in self.safe_positions.items(): + if len(self.safe_positions) > 1: + if snake["length"] < self.my_snake["length"] and location in [{"x": v["x"], "y": v["y"]} for k, v in self.get_possible_moves(snake["head"]).items()]: + self.eat_the_snake_overwrite = True + return direction + #TODO: Check if snake on the way to the bood here and only remove this pos + elif location in [{"x": v["x"], "y": v["y"]} for k, v in self.get_possible_moves(snake["head"]).items()]: + remove.append(direction) + + remove = set(remove) + for direction in remove: + del self.safe_positions[direction] + + self.add_calculations({"function": "avoid_get_eaten_by_other_snakes", "other_snakes": self.other_snakes, "safe_positions": self.safe_positions}) + + def find_safe_positions(self): + self.safe_positions = self.get_possible_moves(self.my_head) + self.add_calculations({"function": "get_possible_moves", "safe_positions": self.safe_positions}) + + self.avoid_my_body() + self.avoid_walls() + self.avoid_snakes() + self.avoid_get_eaten_by_other_snakes()