176 lines
6.2 KiB
Python
176 lines
6.2 KiB
Python
from server.GameBoard import GameBoard
|
|
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:GameBoard):
|
|
self.game_board = game_data
|
|
self.calculations = []
|
|
self.eat_the_snake_overwrite = False
|
|
|
|
self.safe_positions = self.find_safe_positions(add_to_calculations=True)
|
|
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.get_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, my_body:list[dict], safe_positions:dict[str, dict], add_to_calculations:bool=False) -> 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 safe_positions.items():
|
|
if location in my_body:
|
|
remove.append(direction)
|
|
|
|
for direction in remove:
|
|
del safe_positions[direction]
|
|
|
|
if add_to_calculations:
|
|
self.add_calculations({"function": "avoid_my_body", "my_body": my_body, "safe_positions": safe_positions})
|
|
|
|
return safe_positions
|
|
|
|
def avoid_walls(self, safe_positions:dict[str, dict], add_to_calculations:bool=False):
|
|
remove = []
|
|
for direction, location in list(safe_positions.items()):
|
|
x_out_range = (location["x"] < 0 or location["x"] == self.game_board.get_width())
|
|
y_out_range = (location["y"] < 0 or location["y"] == self.game_board.get_height())
|
|
if x_out_range or y_out_range:
|
|
remove.append(direction)
|
|
|
|
for direction in remove:
|
|
del safe_positions[direction]
|
|
|
|
if add_to_calculations:
|
|
self.add_calculations({"function": "avoid_walls", "board_width": self.game_board.get_width(), "board_height": self.game_board.get_height(), "safe_positions": safe_positions})
|
|
|
|
return safe_positions
|
|
|
|
def avoid_snakes(self, other_snakes:list[dict], safe_positions:dict[str, dict], add_to_calculations:bool=False):
|
|
remove = []
|
|
for snake in other_snakes:
|
|
for direction, location in 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 safe_positions[direction]
|
|
|
|
if add_to_calculations:
|
|
self.add_calculations({"function": "avoid_snakes", "other_snakes": other_snakes, "safe_positions": safe_positions})
|
|
|
|
return safe_positions
|
|
|
|
def avoid_get_eaten_by_other_snakes(self, other_snakes:list[dict], safe_positions:dict[str, dict], add_to_calculations:bool=False):
|
|
remove = []
|
|
no_way_out = {}
|
|
self.other_snake_posible_moves = []
|
|
|
|
for snake in other_snakes:
|
|
for direction, location in safe_positions.items():
|
|
if len(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.game_board.get_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.game_board.get_food()]:
|
|
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 safe_positions[direction]
|
|
|
|
if len(safe_positions) == 0:
|
|
safe_positions = no_way_out
|
|
|
|
if add_to_calculations:
|
|
self.add_calculations({"function": "avoid_get_eaten_by_other_snakes", "other_snakes": other_snakes, "safe_positions": safe_positions})
|
|
|
|
return safe_positions
|
|
|
|
def find_safe_positions(self, add_to_calculations:bool=False):
|
|
safe_positions = self.get_possible_moves(self.game_board.get_my_snake_head())
|
|
if add_to_calculations:
|
|
self.add_calculations({"function": "get_possible_moves", "safe_positions": safe_positions})
|
|
|
|
safe_positions = self.avoid_my_body(self.game_board.get_my_snake_body(), safe_positions, add_to_calculations)
|
|
safe_positions = self.avoid_walls(safe_positions, add_to_calculations)
|
|
safe_positions = self.avoid_snakes(self.game_board.get_other_snakes(), safe_positions, add_to_calculations)
|
|
safe_positions = self.avoid_get_eaten_by_other_snakes(self.game_board.get_other_snakes(), safe_positions, add_to_calculations)
|
|
return safe_positions
|
|
|
|
def calculate_new_body_position(self, move:str=None, with_tail:bool=False):
|
|
if move:
|
|
head = self.get_possible_moves(self.game_board.get_my_snake_head())[move]
|
|
|
|
body = [head]
|
|
body.extend(self.game_board.get_my_snake_body())
|
|
body.pop()
|
|
|
|
if not with_tail:
|
|
body.pop()
|
|
|
|
return body
|
|
return move
|