import random class TemplateSnake: def __init__(self): self.history = [] def clear_history(self): self.history = [] def add_to_history(self, data:dict): self.history.append(data) def get_history(self): return self.history def add_calculations(self, calculations:dict): self.calculations.append(calculations) def choose_move(self, game_data:dict): 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.food_positions = game_data['board']['food'] 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 = [] no_way_out = {} self.other_snake_posible_moves = [] for snake in self.other_snakes: for direction, location in self.safe_positions.items(): if len(self.safe_positions) > 1: self.other_snake_posible_moves = [{"x": v["x"], "y": v["y"]} for k, v in self.get_possible_moves(snake["head"]).items()] if snake["length"] < self.my_snake["length"] and location in self.other_snake_posible_moves: self.eat_the_snake_overwrite = True self.kill_the_snake = direction #TODO: Testing - Check if snake on the way to the food here and only remove this pos elif location in self.other_snake_posible_moves and location in [{"x": food["x"], "y": food["y"]} for food in self.food_positions]: remove.append(direction) elif location in self.other_snake_posible_moves: no_way_out[direction] = location remove.append(direction) remove = set(remove) for direction in remove: del self.safe_positions[direction] if len(self.safe_positions) == 0: self.safe_positions = no_way_out 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()