add versions to snakes and read it in server class
This commit is contained in:
@@ -17,6 +17,7 @@ class Server:
|
|||||||
'color': '#888888',
|
'color': '#888888',
|
||||||
'head': 'default',
|
'head': 'default',
|
||||||
'tail': 'default',
|
'tail': 'default',
|
||||||
|
'version': '1.0.0',
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, data_path:str, snake_type:str, storage_type:str, debug:bool=False, check_tls_security:bool=False):
|
def __init__(self, data_path:str, snake_type:str, storage_type:str, debug:bool=False, check_tls_security:bool=False):
|
||||||
@@ -42,6 +43,7 @@ class Server:
|
|||||||
'max_turn': 0,
|
'max_turn': 0,
|
||||||
}
|
}
|
||||||
self.logger = build_logger('Battlesnake', debug_env_var='DEBUG_SERVER')
|
self.logger = build_logger('Battlesnake', debug_env_var='DEBUG_SERVER')
|
||||||
|
self.snake_version = self._get_snake_version()
|
||||||
|
|
||||||
self.app = Quart('Battlesnake')
|
self.app = Quart('Battlesnake')
|
||||||
|
|
||||||
@@ -134,12 +136,36 @@ class Server:
|
|||||||
return await self._override_snake_config_with_environment_variables(snake_config)
|
return await self._override_snake_config_with_environment_variables(snake_config)
|
||||||
|
|
||||||
async def _override_snake_config_with_environment_variables(self, config:dict[str, str]) -> dict[str, str]:
|
async def _override_snake_config_with_environment_variables(self, config:dict[str, str]) -> dict[str, str]:
|
||||||
|
config['version'] = self.snake_version
|
||||||
|
|
||||||
for key in ('author', 'color', 'head', 'tail'):
|
for key in ('author', 'color', 'head', 'tail'):
|
||||||
value = os.environ.get(f'SNAKE_{key.upper()}')
|
value = os.environ.get(f'SNAKE_{key.upper()}')
|
||||||
if value is not None:
|
if value is not None:
|
||||||
config[key] = value
|
config[key] = value
|
||||||
|
|
||||||
|
version_override = os.environ.get('SNAKE_VERSION')
|
||||||
|
if version_override is not None:
|
||||||
|
config['version'] = version_override
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
def _get_snake_version(self) -> str:
|
||||||
|
configured_version = SnakeBuilder.get_version(self.snake_type)
|
||||||
|
if configured_version:
|
||||||
|
return configured_version
|
||||||
|
|
||||||
|
try:
|
||||||
|
snake = SnakeBuilder.build(self.snake_type)
|
||||||
|
except Exception:
|
||||||
|
return self.default_snake_config['version']
|
||||||
|
|
||||||
|
version = getattr(snake, 'version', None)
|
||||||
|
if version is None:
|
||||||
|
version = getattr(snake, 'VERSION', None)
|
||||||
|
if not version:
|
||||||
|
return self.default_snake_config['version']
|
||||||
|
return str(version)
|
||||||
|
|
||||||
async def _create_game_board(self, game_state:dict):
|
async def _create_game_board(self, game_state:dict):
|
||||||
game_id = game_state['game']['id']
|
game_id = game_state['game']['id']
|
||||||
new_game_board = GameBoard(
|
new_game_board = GameBoard(
|
||||||
|
|||||||
+14
-2
@@ -1,7 +1,19 @@
|
|||||||
|
|
||||||
class SnakeBuilder:
|
class SnakeBuilder:
|
||||||
|
SNAKE_VERSIONS = {
|
||||||
|
"TemplateSnake": "1.0.0",
|
||||||
|
"DummSnake": "1.0.0",
|
||||||
|
"LogicSnake": "1.1.0",
|
||||||
|
"MasterSnake": "1.2.0",
|
||||||
|
"BetterMasterSnake": "1.3.0",
|
||||||
|
"BestBattleSnake": "2.5.0",
|
||||||
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def build(self, selected_snake:str):
|
def build(self, selected_snake:str):
|
||||||
snake_module = __import__(f'snakes.{selected_snake}', fromlist=[selected_snake])
|
snake_module = __import__(f"snakes.{selected_snake}", fromlist=[selected_snake])
|
||||||
snake_class = getattr(snake_module, selected_snake)
|
snake_class = getattr(snake_module, selected_snake)
|
||||||
return snake_class()
|
return snake_class()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_version(self, selected_snake:str) -> str | None:
|
||||||
|
return self.SNAKE_VERSIONS.get(selected_snake)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import os
|
|||||||
from snakes.TemplateSnake import TemplateSnake
|
from snakes.TemplateSnake import TemplateSnake
|
||||||
|
|
||||||
class BestBattleSnake(TemplateSnake):
|
class BestBattleSnake(TemplateSnake):
|
||||||
|
VERSION = "2.5.0"
|
||||||
|
|
||||||
DIRECTIONS = {
|
DIRECTIONS = {
|
||||||
"up": (0, 1),
|
"up": (0, 1),
|
||||||
"down": (0, -1),
|
"down": (0, -1),
|
||||||
@@ -23,6 +25,7 @@ class BestBattleSnake(TemplateSnake):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.name = "BestBattleSnake"
|
self.name = "BestBattleSnake"
|
||||||
|
self.version = self.VERSION
|
||||||
self.recent_heads = deque(maxlen=14)
|
self.recent_heads = deque(maxlen=14)
|
||||||
self.last_move = None
|
self.last_move = None
|
||||||
self.last_game_id = None
|
self.last_game_id = None
|
||||||
|
|||||||
+201
-198
@@ -3,219 +3,222 @@ from server.GameBoard import GameBoard
|
|||||||
from collections import deque
|
from collections import deque
|
||||||
|
|
||||||
class BetterMasterSnake(TemplateSnake):
|
class BetterMasterSnake(TemplateSnake):
|
||||||
def __init__(self):
|
VERSION = "1.3.0"
|
||||||
super().__init__()
|
|
||||||
self.name = "BetterMasterSnake"
|
|
||||||
# Definiere die möglichen Bewegungsrichtungen
|
|
||||||
self.min_safe_area = 2
|
|
||||||
|
|
||||||
def choose_move(self, game_data:GameBoard):
|
def __init__(self):
|
||||||
self.game_board = game_data
|
super().__init__()
|
||||||
self.calculations = []
|
self.name = "BetterMasterSnake"
|
||||||
self.eat_the_snake_overwrite = False
|
self.version = self.VERSION
|
||||||
|
# Definiere die möglichen Bewegungsrichtungen
|
||||||
|
self.min_safe_area = 2
|
||||||
|
|
||||||
self.safe_positions = self.find_safe_positions(add_to_calculations=True)
|
def choose_move(self, game_data:GameBoard):
|
||||||
if self.eat_the_snake_overwrite:
|
self.game_board = game_data
|
||||||
return self.overwrite_eat_the_other_snake(game_data.get_turn())
|
self.calculations = []
|
||||||
|
self.eat_the_snake_overwrite = False
|
||||||
|
|
||||||
if game_data.get_type() == "constrictor":
|
self.safe_positions = self.find_safe_positions(add_to_calculations=True)
|
||||||
move = self.selected_move_constrictor()
|
if self.eat_the_snake_overwrite:
|
||||||
|
return self.overwrite_eat_the_other_snake(game_data.get_turn())
|
||||||
|
|
||||||
|
if game_data.get_type() == "constrictor":
|
||||||
|
move = self.selected_move_constrictor()
|
||||||
|
else:
|
||||||
|
move = self.selected_move_standard()
|
||||||
|
|
||||||
|
self.add_to_history({"turn": game_data.get_turn(), "data": self.calculations})
|
||||||
|
return move if move else "up"
|
||||||
|
|
||||||
|
def overwrite_eat_the_other_snake(self, turn:int):
|
||||||
|
self.add_calculations({"function": "eat_the_snake_overwrite", "my_head": self.game_board.get_my_snake_head(), "move": self.kill_the_snake, "safe_positions": self.safe_positions})
|
||||||
|
self.add_to_history({"turn": turn, "data": self.calculations})
|
||||||
|
return self.kill_the_snake
|
||||||
|
|
||||||
|
#TODO: How to Fill the Gameboard best?
|
||||||
|
def selected_move_constrictor(self):
|
||||||
|
move = self.move_close_to_body()
|
||||||
|
self.add_calculations({"function": "move_close_to_body", "my_head": self.game_board.get_my_snake_head(), "move": move})
|
||||||
|
move = self.ensure_escape_route(move)
|
||||||
|
self.add_calculations({"function": "ensure_escape_route", "my_head": self.game_board.get_my_snake_head(), "move": move, "safe_positions": self.safe_positions})
|
||||||
|
return move
|
||||||
|
|
||||||
|
def selected_move_standard(self, move=None):
|
||||||
|
# Finde den besten Weg zur Nahrung
|
||||||
|
path_to_food = self.find_path_to_food()
|
||||||
|
if path_to_food:
|
||||||
|
move = self.move_towards(path_to_food[0])
|
||||||
|
self.add_calculations({"function": "move_towards", "my_head": self.game_board.get_my_snake_head(), "path_to_food": path_to_food, "move": move})
|
||||||
|
|
||||||
|
if not move or self.would_eating_the_food_kill_the_snake(move):
|
||||||
|
move = self.move_close_to_body(move_close_to_tail=True)
|
||||||
|
self.add_calculations({"function": "move_close_to_body", "my_head": self.game_board.get_my_snake_head(), "move": move})
|
||||||
|
|
||||||
|
# Überprfe, ob der Zug einen Ausweg lässt
|
||||||
|
move = self.ensure_escape_route(move)
|
||||||
|
self.add_calculations({"function": "ensure_escape_route", "my_head": self.game_board.get_my_snake_head(), "move": move, "safe_positions": self.safe_positions})
|
||||||
|
return move
|
||||||
|
|
||||||
|
def find_path_to_food(self):
|
||||||
|
# Exclude own snake's body from obstacles
|
||||||
|
obstacles = set((part['x'], part['y']) for part in self.game_board.get_my_snake_body())
|
||||||
|
|
||||||
|
for snake in self.game_board.get_other_snakes():
|
||||||
|
for part in snake['body']:
|
||||||
|
obstacles.add((part['x'], part['y']))
|
||||||
|
|
||||||
|
other_snakes_other_snake_posible_moves_set = {(d['x'], d['y']) for d in self.other_snake_posible_moves}
|
||||||
|
removed_elements_set = set([(elem['x'], elem['y']) for elem in self.game_board.get_food() if (elem['x'], elem['y']) in other_snakes_other_snake_posible_moves_set])
|
||||||
|
obstacles |= removed_elements_set
|
||||||
|
|
||||||
|
self.food_positions = [elem for elem in self.game_board.get_food() if (elem['x'], elem['y']) not in other_snakes_other_snake_posible_moves_set]
|
||||||
|
|
||||||
|
if len(self.food_positions) > 0:
|
||||||
|
# Choose the closest food source based on the heuristic
|
||||||
|
closest_food = min(self.food_positions, key=lambda food: abs(food['x'] - self.game_board.get_my_snake_head()['x']) + abs(food['y'] - self.game_board.get_my_snake_head()['y']))
|
||||||
|
self.set_target_food(closest_food)
|
||||||
|
|
||||||
|
# Use A* to search for a safe path
|
||||||
|
return self.a_star_search(self.game_board.get_my_snake_head(), closest_food, obstacles)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def find_path_to_tail(self):
|
||||||
|
# Exclude other snake's body from obstacles
|
||||||
|
obstacles = set((part['x'], part['y']) for part in self.game_board.get_my_snake_body())
|
||||||
|
for snake in self.game_board.get_other_snakes():
|
||||||
|
for part in snake['body']:
|
||||||
|
obstacles.add((part['x'], part['y']))
|
||||||
|
|
||||||
|
my_snake_tail = {"x": self.game_board.get_my_snake_tail()['x'], "y": self.game_board.get_my_snake_tail()['y']}
|
||||||
|
|
||||||
|
# Use A* to search for a safe path
|
||||||
|
path = self.a_star_search(self.game_board.get_my_snake_head(), my_snake_tail, obstacles)
|
||||||
|
return path
|
||||||
|
|
||||||
|
def move_towards(self, target):
|
||||||
|
best_direction = None
|
||||||
|
min_distance = float('inf')
|
||||||
|
for direction, coords in self.safe_positions.items():
|
||||||
|
distance = abs(target['x'] - coords['x']) + abs(target['y'] - coords['y'])
|
||||||
|
if distance < min_distance:
|
||||||
|
min_distance = distance
|
||||||
|
best_direction = direction
|
||||||
|
|
||||||
|
return best_direction if best_direction else "up"
|
||||||
|
|
||||||
|
def move_close_to_body(self, move_close_to_tail=False):
|
||||||
|
# Heuristik, um Positionen nahe dem eigenen Körper zu bevorzugen
|
||||||
|
body_positions = set((part['x'], part['y']) for part in self.game_board.get_my_snake_body())
|
||||||
|
tail_position = (self.game_board.get_my_snake_tail()['x'], self.game_board.get_my_snake_tail()['y'])
|
||||||
|
|
||||||
|
best_move = None
|
||||||
|
max_distance = -1 # Initialize maximum distance
|
||||||
|
for direction, pos in self.safe_positions.items():
|
||||||
|
next_position = (pos['x'], pos['y'])
|
||||||
|
if next_position in self.safe_positions:
|
||||||
|
# Berechne die Distanz zum eigenen Körper
|
||||||
|
distance_to_body = min(abs(next_position[0] - part[0]) + abs(next_position[1] - part[1]) for part in body_positions)
|
||||||
|
# Berechne die Distanz zum eigenen Schwanz
|
||||||
|
distance_to_tail = abs(next_position[0] - tail_position[0]) + abs(next_position[1] - tail_position[1])
|
||||||
|
# Wähle die maximale Distanz (Körper oder Schwanz)
|
||||||
|
if move_close_to_tail:
|
||||||
|
distance = min(next_position, distance_to_tail)
|
||||||
else:
|
else:
|
||||||
move = self.selected_move_standard()
|
distance = max(next_position, distance_to_body)
|
||||||
|
# Update max_distance if a larger distance is found
|
||||||
|
if distance > max_distance:
|
||||||
|
max_distance = distance
|
||||||
|
best_move = direction
|
||||||
|
return best_move if best_move else "up" # Standardbewegung, falls keine bessere gefunden wird
|
||||||
|
|
||||||
self.add_to_history({"turn": game_data.get_turn(), "data": self.calculations})
|
#TODO: Neat to Implement Function to check if eating the food would kill the snake?
|
||||||
return move if move else "up"
|
def would_eating_the_food_kill_the_snake(self, move:str):
|
||||||
|
return False
|
||||||
|
|
||||||
def overwrite_eat_the_other_snake(self, turn:int):
|
def ensure_escape_route(self, move:str):
|
||||||
self.add_calculations({"function": "eat_the_snake_overwrite", "my_head": self.game_board.get_my_snake_head(), "move": self.kill_the_snake, "safe_positions": self.safe_positions})
|
try:
|
||||||
self.add_to_history({"turn": turn, "data": self.calculations})
|
future_position = self.safe_positions[move]
|
||||||
return self.kill_the_snake
|
except KeyError:
|
||||||
|
for move, pos in self.safe_positions.items():
|
||||||
|
if self.is_near_tail(pos, (self.game_board.get_my_snake_tail()['x'], self.game_board.get_my_snake_tail()['y'])):
|
||||||
|
self.add_calculations({"function": "ensure_escape_route", "move": move, "is_near_tail": True})
|
||||||
|
move = self.move_towards(pos)
|
||||||
|
return move
|
||||||
|
else:
|
||||||
|
path_to_tail = self.find_path_to_tail()
|
||||||
|
if path_to_tail:
|
||||||
|
self.add_calculations({"function": "move_towards", "my_head": self.game_board.get_my_snake_head(), "path_to_tail": path_to_tail, "move": move})
|
||||||
|
move = self.move_towards(path_to_tail[0])
|
||||||
|
|
||||||
#TODO: How to Fill the Gameboard best?
|
self.add_calculations({"function": "ensure_escape_route", "move": move, "KeyError": "Snake Coild itself up"})
|
||||||
def selected_move_constrictor(self):
|
#return move
|
||||||
move = self.move_close_to_body()
|
|
||||||
self.add_calculations({"function": "move_close_to_body", "my_head": self.game_board.get_my_snake_head(), "move": move})
|
|
||||||
move = self.ensure_escape_route(move)
|
|
||||||
self.add_calculations({"function": "ensure_escape_route", "my_head": self.game_board.get_my_snake_head(), "move": move, "safe_positions": self.safe_positions})
|
|
||||||
return move
|
|
||||||
|
|
||||||
def selected_move_standard(self, move=None):
|
# TODO: Fix - Snake Neat to find the best way - Close to the Tail and maybe fill most free cells as posible
|
||||||
# Finde den besten Weg zur Nahrung
|
return move
|
||||||
path_to_food = self.find_path_to_food()
|
|
||||||
if path_to_food:
|
|
||||||
move = self.move_towards(path_to_food[0])
|
|
||||||
self.add_calculations({"function": "move_towards", "my_head": self.game_board.get_my_snake_head(), "path_to_food": path_to_food, "move": move})
|
|
||||||
|
|
||||||
if not move or self.would_eating_the_food_kill_the_snake(move):
|
def is_near_tail(self, position, tail):
|
||||||
move = self.move_close_to_body(move_close_to_tail=True)
|
return abs(position["x"] - tail[0]) + abs(position["y"] - tail[1]) <= 2
|
||||||
self.add_calculations({"function": "move_close_to_body", "my_head": self.game_board.get_my_snake_head(), "move": move})
|
|
||||||
|
|
||||||
# Überprfe, ob der Zug einen Ausweg lässt
|
def a_star_search(self, start, goal, obstacles):
|
||||||
move = self.ensure_escape_route(move)
|
# Helper functions
|
||||||
self.add_calculations({"function": "ensure_escape_route", "my_head": self.game_board.get_my_snake_head(), "move": move, "safe_positions": self.safe_positions})
|
def is_position_safe(position):
|
||||||
return move
|
return 0 <= position['x'] < self.game_board.get_width() and 0 <= position['y'] < self.game_board.get_height() and (position['x'], position['y']) not in obstacles
|
||||||
|
|
||||||
def find_path_to_food(self):
|
def get_neighbors(position):
|
||||||
# Exclude own snake's body from obstacles
|
neighbors = []
|
||||||
obstacles = set((part['x'], part['y']) for part in self.game_board.get_my_snake_body())
|
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: # links, rechts, oben, unten
|
||||||
|
neighbor = {'x': position['x'] + dx, 'y': position['y'] + dy}
|
||||||
|
if is_position_safe(neighbor):
|
||||||
|
neighbors.append(neighbor)
|
||||||
|
return neighbors
|
||||||
|
|
||||||
for snake in self.game_board.get_other_snakes():
|
def heuristic(position, goal):
|
||||||
for part in snake['body']:
|
# Verwenden Sie eine Heuristik, die immer positiv ist, selbst wenn das Ziel in der Nähe ist
|
||||||
obstacles.add((part['x'], part['y']))
|
return max(abs(position['x'] - goal['x']), abs(position['y'] - goal['y']))
|
||||||
|
|
||||||
other_snakes_other_snake_posible_moves_set = {(d['x'], d['y']) for d in self.other_snake_posible_moves}
|
# Überprüfen, ob das Ziel direkt neben dem Startpunkt liegt
|
||||||
removed_elements_set = set([(elem['x'], elem['y']) for elem in self.game_board.get_food() if (elem['x'], elem['y']) in other_snakes_other_snake_posible_moves_set])
|
if start == goal or (abs(start['x'] - goal['x']) <= 1 and abs(start['y'] - goal['y']) <= 1):
|
||||||
obstacles |= removed_elements_set
|
# Wenn das Ziel neben dem Startpunkt liegt, ist der Pfad das Ziel selbst
|
||||||
|
return [goal]
|
||||||
|
|
||||||
self.food_positions = [elem for elem in self.game_board.get_food() if (elem['x'], elem['y']) not in other_snakes_other_snake_posible_moves_set]
|
# Initialize the open and closed list
|
||||||
|
open_set = set([(start['x'], start['y'])])
|
||||||
|
came_from = {}
|
||||||
|
g_score = {(start['x'], start['y']): 0}
|
||||||
|
f_score = {(start['x'], start['y']): heuristic(start, goal)}
|
||||||
|
|
||||||
if len(self.food_positions) > 0:
|
while open_set:
|
||||||
# Choose the closest food source based on the heuristic
|
current = min(open_set, key=lambda pos: f_score.get(pos, float('inf')))
|
||||||
closest_food = min(self.food_positions, key=lambda food: abs(food['x'] - self.game_board.get_my_snake_head()['x']) + abs(food['y'] - self.game_board.get_my_snake_head()['y']))
|
current_dict = {'x': current[0], 'y': current[1]}
|
||||||
self.set_target_food(closest_food)
|
if current_dict == goal:
|
||||||
|
# Reconstruct the path
|
||||||
|
path = []
|
||||||
|
while current in came_from:
|
||||||
|
current = came_from[current]
|
||||||
|
path.append({'x': current[0], 'y': current[1]})
|
||||||
|
path.reverse()
|
||||||
|
if path and path[0] == start:
|
||||||
|
path.pop(0) # Entferne das erste Element, wenn es dem Start entspricht
|
||||||
|
return path # Return the path as a list of dicts
|
||||||
|
|
||||||
# Use A* to search for a safe path
|
open_set.remove(current)
|
||||||
return self.a_star_search(self.game_board.get_my_snake_head(), closest_food, obstacles)
|
for neighbor in get_neighbors(current_dict):
|
||||||
return None
|
neighbor_tuple = (neighbor['x'], neighbor['y'])
|
||||||
|
tentative_g_score = g_score[current] + 1 # Distance between neighbors is always 1
|
||||||
|
if tentative_g_score < g_score.get(neighbor_tuple, float('inf')):
|
||||||
|
came_from[neighbor_tuple] = current
|
||||||
|
g_score[neighbor_tuple] = tentative_g_score
|
||||||
|
f_score[neighbor_tuple] = g_score[neighbor_tuple] + heuristic(neighbor, goal)
|
||||||
|
if neighbor_tuple not in open_set:
|
||||||
|
open_set.add(neighbor_tuple)
|
||||||
|
|
||||||
def find_path_to_tail(self):
|
return None # Kein Pfad gefunden
|
||||||
# Exclude other snake's body from obstacles
|
|
||||||
obstacles = set((part['x'], part['y']) for part in self.game_board.get_my_snake_body())
|
|
||||||
for snake in self.game_board.get_other_snakes():
|
|
||||||
for part in snake['body']:
|
|
||||||
obstacles.add((part['x'], part['y']))
|
|
||||||
|
|
||||||
my_snake_tail = {"x": self.game_board.get_my_snake_tail()['x'], "y": self.game_board.get_my_snake_tail()['y']}
|
def find_direction(self):
|
||||||
|
# Beispielhafte Logik zur Auswahl einer Bewegungsrichtung
|
||||||
# Use A* to search for a safe path
|
for direction, pos in self.safe_positions.items():
|
||||||
path = self.a_star_search(self.game_board.get_my_snake_head(), my_snake_tail, obstacles)
|
next_position = (pos['x'], pos['y'])
|
||||||
return path
|
# Konvertiere safe_positions in eine Liste von Tupeln für den Vergleich
|
||||||
|
safe_positions_tuples = [(pos['x'], pos['y']) for pos in self.safe_positions.values()]
|
||||||
def move_towards(self, target):
|
if next_position in safe_positions_tuples:
|
||||||
best_direction = None
|
return direction
|
||||||
min_distance = float('inf')
|
return "up" # Standardbewegung, falls keine sichere Position gefunden wird
|
||||||
for direction, coords in self.safe_positions.items():
|
|
||||||
distance = abs(target['x'] - coords['x']) + abs(target['y'] - coords['y'])
|
|
||||||
if distance < min_distance:
|
|
||||||
min_distance = distance
|
|
||||||
best_direction = direction
|
|
||||||
|
|
||||||
return best_direction if best_direction else "up"
|
|
||||||
|
|
||||||
def move_close_to_body(self, move_close_to_tail=False):
|
|
||||||
# Heuristik, um Positionen nahe dem eigenen Körper zu bevorzugen
|
|
||||||
body_positions = set((part['x'], part['y']) for part in self.game_board.get_my_snake_body())
|
|
||||||
tail_position = (self.game_board.get_my_snake_tail()['x'], self.game_board.get_my_snake_tail()['y'])
|
|
||||||
|
|
||||||
best_move = None
|
|
||||||
max_distance = -1 # Initialize maximum distance
|
|
||||||
for direction, pos in self.safe_positions.items():
|
|
||||||
next_position = (pos['x'], pos['y'])
|
|
||||||
if next_position in self.safe_positions:
|
|
||||||
# Berechne die Distanz zum eigenen Körper
|
|
||||||
distance_to_body = min(abs(next_position[0] - part[0]) + abs(next_position[1] - part[1]) for part in body_positions)
|
|
||||||
# Berechne die Distanz zum eigenen Schwanz
|
|
||||||
distance_to_tail = abs(next_position[0] - tail_position[0]) + abs(next_position[1] - tail_position[1])
|
|
||||||
# Wähle die maximale Distanz (Körper oder Schwanz)
|
|
||||||
if move_close_to_tail:
|
|
||||||
distance = min(next_position, distance_to_tail)
|
|
||||||
else:
|
|
||||||
distance = max(next_position, distance_to_body)
|
|
||||||
# Update max_distance if a larger distance is found
|
|
||||||
if distance > max_distance:
|
|
||||||
max_distance = distance
|
|
||||||
best_move = direction
|
|
||||||
return best_move if best_move else "up" # Standardbewegung, falls keine bessere gefunden wird
|
|
||||||
|
|
||||||
#TODO: Neat to Implement Function to check if eating the food would kill the snake?
|
|
||||||
def would_eating_the_food_kill_the_snake(self, move:str):
|
|
||||||
return False
|
|
||||||
|
|
||||||
def ensure_escape_route(self, move:str):
|
|
||||||
try:
|
|
||||||
future_position = self.safe_positions[move]
|
|
||||||
except KeyError:
|
|
||||||
for move, pos in self.safe_positions.items():
|
|
||||||
if self.is_near_tail(pos, (self.game_board.get_my_snake_tail()['x'], self.game_board.get_my_snake_tail()['y'])):
|
|
||||||
self.add_calculations({"function": "ensure_escape_route", "move": move, "is_near_tail": True})
|
|
||||||
move = self.move_towards(pos)
|
|
||||||
return move
|
|
||||||
else:
|
|
||||||
path_to_tail = self.find_path_to_tail()
|
|
||||||
if path_to_tail:
|
|
||||||
self.add_calculations({"function": "move_towards", "my_head": self.game_board.get_my_snake_head(), "path_to_tail": path_to_tail, "move": move})
|
|
||||||
move = self.move_towards(path_to_tail[0])
|
|
||||||
|
|
||||||
self.add_calculations({"function": "ensure_escape_route", "move": move, "KeyError": "Snake Coild itself up"})
|
|
||||||
#return move
|
|
||||||
|
|
||||||
# TODO: Fix - Snake Neat to find the best way - Close to the Tail and maybe fill most free cells as posible
|
|
||||||
return move
|
|
||||||
|
|
||||||
def is_near_tail(self, position, tail):
|
|
||||||
return abs(position["x"] - tail[0]) + abs(position["y"] - tail[1]) <= 2
|
|
||||||
|
|
||||||
def a_star_search(self, start, goal, obstacles):
|
|
||||||
# Helper functions
|
|
||||||
def is_position_safe(position):
|
|
||||||
return 0 <= position['x'] < self.game_board.get_width() and 0 <= position['y'] < self.game_board.get_height() and (position['x'], position['y']) not in obstacles
|
|
||||||
|
|
||||||
def get_neighbors(position):
|
|
||||||
neighbors = []
|
|
||||||
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: # links, rechts, oben, unten
|
|
||||||
neighbor = {'x': position['x'] + dx, 'y': position['y'] + dy}
|
|
||||||
if is_position_safe(neighbor):
|
|
||||||
neighbors.append(neighbor)
|
|
||||||
return neighbors
|
|
||||||
|
|
||||||
def heuristic(position, goal):
|
|
||||||
# Verwenden Sie eine Heuristik, die immer positiv ist, selbst wenn das Ziel in der Nähe ist
|
|
||||||
return max(abs(position['x'] - goal['x']), abs(position['y'] - goal['y']))
|
|
||||||
|
|
||||||
# Überprüfen, ob das Ziel direkt neben dem Startpunkt liegt
|
|
||||||
if start == goal or (abs(start['x'] - goal['x']) <= 1 and abs(start['y'] - goal['y']) <= 1):
|
|
||||||
# Wenn das Ziel neben dem Startpunkt liegt, ist der Pfad das Ziel selbst
|
|
||||||
return [goal]
|
|
||||||
|
|
||||||
# Initialize the open and closed list
|
|
||||||
open_set = set([(start['x'], start['y'])])
|
|
||||||
came_from = {}
|
|
||||||
g_score = {(start['x'], start['y']): 0}
|
|
||||||
f_score = {(start['x'], start['y']): heuristic(start, goal)}
|
|
||||||
|
|
||||||
while open_set:
|
|
||||||
current = min(open_set, key=lambda pos: f_score.get(pos, float('inf')))
|
|
||||||
current_dict = {'x': current[0], 'y': current[1]}
|
|
||||||
if current_dict == goal:
|
|
||||||
# Reconstruct the path
|
|
||||||
path = []
|
|
||||||
while current in came_from:
|
|
||||||
current = came_from[current]
|
|
||||||
path.append({'x': current[0], 'y': current[1]})
|
|
||||||
path.reverse()
|
|
||||||
if path and path[0] == start:
|
|
||||||
path.pop(0) # Entferne das erste Element, wenn es dem Start entspricht
|
|
||||||
return path # Return the path as a list of dicts
|
|
||||||
|
|
||||||
open_set.remove(current)
|
|
||||||
for neighbor in get_neighbors(current_dict):
|
|
||||||
neighbor_tuple = (neighbor['x'], neighbor['y'])
|
|
||||||
tentative_g_score = g_score[current] + 1 # Distance between neighbors is always 1
|
|
||||||
if tentative_g_score < g_score.get(neighbor_tuple, float('inf')):
|
|
||||||
came_from[neighbor_tuple] = current
|
|
||||||
g_score[neighbor_tuple] = tentative_g_score
|
|
||||||
f_score[neighbor_tuple] = g_score[neighbor_tuple] + heuristic(neighbor, goal)
|
|
||||||
if neighbor_tuple not in open_set:
|
|
||||||
open_set.add(neighbor_tuple)
|
|
||||||
|
|
||||||
return None # Kein Pfad gefunden
|
|
||||||
|
|
||||||
def find_direction(self):
|
|
||||||
# Beispielhafte Logik zur Auswahl einer Bewegungsrichtung
|
|
||||||
for direction, pos in self.safe_positions.items():
|
|
||||||
next_position = (pos['x'], pos['y'])
|
|
||||||
# Konvertiere safe_positions in eine Liste von Tupeln für den Vergleich
|
|
||||||
safe_positions_tuples = [(pos['x'], pos['y']) for pos in self.safe_positions.values()]
|
|
||||||
if next_position in safe_positions_tuples:
|
|
||||||
return direction
|
|
||||||
return "up" # Standardbewegung, falls keine sichere Position gefunden wird
|
|
||||||
|
|||||||
+38
-36
@@ -3,53 +3,55 @@ from snakes.TemplateSnake import TemplateSnake
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
class DummSnake(TemplateSnake):
|
class DummSnake(TemplateSnake):
|
||||||
def choose_move(self, data: dict) -> str:
|
VERSION = "1.0.0"
|
||||||
is_move_safe = {"up": True, "down": True, "left": True, "right": True}
|
|
||||||
|
|
||||||
# We've included code to prevent your Battlesnake from moving backwards
|
def choose_move(self, data: dict) -> str:
|
||||||
my_head = data["you"]["body"][0] # Coordinates of your head
|
is_move_safe = {"up": True, "down": True, "left": True, "right": True}
|
||||||
my_neck = data["you"]["body"][1] # Coordinates of your "neck"
|
|
||||||
|
|
||||||
if my_neck["x"] < my_head["x"]: # Neck is left of head, don't move left
|
# We've included code to prevent your Battlesnake from moving backwards
|
||||||
is_move_safe["left"] = False
|
my_head = data["you"]["body"][0] # Coordinates of your head
|
||||||
|
my_neck = data["you"]["body"][1] # Coordinates of your "neck"
|
||||||
|
|
||||||
elif my_neck["x"] > my_head["x"]: # Neck is right of head, don't move right
|
if my_neck["x"] < my_head["x"]: # Neck is left of head, don't move left
|
||||||
is_move_safe["right"] = False
|
is_move_safe["left"] = False
|
||||||
|
|
||||||
elif my_neck["y"] < my_head["y"]: # Neck is below head, don't move down
|
elif my_neck["x"] > my_head["x"]: # Neck is right of head, don't move right
|
||||||
is_move_safe["down"] = False
|
is_move_safe["right"] = False
|
||||||
|
|
||||||
elif my_neck["y"] > my_head["y"]: # Neck is above head, don't move up
|
elif my_neck["y"] < my_head["y"]: # Neck is below head, don't move down
|
||||||
is_move_safe["up"] = False
|
is_move_safe["down"] = False
|
||||||
|
|
||||||
# TODO: Step 1 - Prevent your Battlesnake from moving out of bounds
|
elif my_neck["y"] > my_head["y"]: # Neck is above head, don't move up
|
||||||
# board_width = game_state['board']['width']
|
is_move_safe["up"] = False
|
||||||
# board_height = game_state['board']['height']
|
|
||||||
|
|
||||||
# TODO: Step 2 - Prevent your Battlesnake from colliding with itself
|
# TODO: Step 1 - Prevent your Battlesnake from moving out of bounds
|
||||||
# my_body = game_state['you']['body']
|
# board_width = game_state['board']['width']
|
||||||
|
# board_height = game_state['board']['height']
|
||||||
|
|
||||||
# TODO: Step 3 - Prevent your Battlesnake from colliding with other Battlesnakes
|
# TODO: Step 2 - Prevent your Battlesnake from colliding with itself
|
||||||
# opponents = game_state['board']['snakes']
|
# my_body = game_state['you']['body']
|
||||||
|
|
||||||
# Are there any safe moves left?
|
# TODO: Step 3 - Prevent your Battlesnake from colliding with other Battlesnakes
|
||||||
safe_moves = []
|
# opponents = game_state['board']['snakes']
|
||||||
for move, isSafe in is_move_safe.items():
|
|
||||||
if isSafe:
|
|
||||||
safe_moves.append(move)
|
|
||||||
|
|
||||||
if len(safe_moves) == 0:
|
# Are there any safe moves left?
|
||||||
print(f"MOVE {data['turn']}: No safe moves detected! Moving down")
|
safe_moves = []
|
||||||
self.add_to_history({"my_head": my_head, "my_neck": my_neck, "move": move, "safe_moves": safe_moves, "is_move_safe": is_move_safe})
|
for move, isSafe in is_move_safe.items():
|
||||||
return {"move": "down"}
|
if isSafe:
|
||||||
|
safe_moves.append(move)
|
||||||
|
|
||||||
# Choose a random move from the safe ones
|
if len(safe_moves) == 0:
|
||||||
move = random.choice(safe_moves)
|
print(f"MOVE {data['turn']}: No safe moves detected! Moving down")
|
||||||
|
self.add_to_history({"my_head": my_head, "my_neck": my_neck, "move": move, "safe_moves": safe_moves, "is_move_safe": is_move_safe})
|
||||||
|
return {"move": "down"}
|
||||||
|
|
||||||
# TODO: Step 4 - Move towards food instead of random, to regain health and survive longer
|
# Choose a random move from the safe ones
|
||||||
# food = game_state['board']['food']
|
move = random.choice(safe_moves)
|
||||||
|
|
||||||
self.add_to_history({"my_head": my_head, "my_neck": my_neck, "move": move, "safe_moves": safe_moves, "is_move_safe": is_move_safe})
|
# TODO: Step 4 - Move towards food instead of random, to regain health and survive longer
|
||||||
print(f"{data['game']['id']} MOVE {data['turn']}: {move} picked from all valid options in {is_move_safe}")
|
# food = game_state['board']['food']
|
||||||
|
|
||||||
return move
|
self.add_to_history({"my_head": my_head, "my_neck": my_neck, "move": move, "safe_moves": safe_moves, "is_move_safe": is_move_safe})
|
||||||
|
print(f"{data['game']['id']} MOVE {data['turn']}: {move} picked from all valid options in {is_move_safe}")
|
||||||
|
|
||||||
|
return move
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import random
|
|||||||
from scipy import spatial
|
from scipy import spatial
|
||||||
|
|
||||||
class LogicSnake(TemplateSnake):
|
class LogicSnake(TemplateSnake):
|
||||||
|
VERSION = "1.1.0"
|
||||||
|
|
||||||
def avoid_my_body(self, my_body, possible_moves: dict) -> list:
|
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.
|
my_body: List of dictionaries of x/y coordinates for every segment of a Battlesnake.
|
||||||
|
|||||||
+211
-208
@@ -1,246 +1,249 @@
|
|||||||
from snakes.TemplateSnake import TemplateSnake
|
from snakes.TemplateSnake import TemplateSnake
|
||||||
|
|
||||||
class MasterSnake(TemplateSnake):
|
class MasterSnake(TemplateSnake):
|
||||||
def __init__(self):
|
VERSION = "1.2.0"
|
||||||
super().__init__()
|
|
||||||
self.name = "MasterSnake"
|
|
||||||
self.disabled_find_near_by_food = True
|
|
||||||
|
|
||||||
def is_food_nearby(self, head, food_positions):
|
def __init__(self):
|
||||||
for food in food_positions:
|
super().__init__()
|
||||||
if abs(head['x'] - food['x']) <= 1 and abs(head['y'] - food['y']) <= 1:
|
self.name = "MasterSnake"
|
||||||
return True
|
self.version = self.VERSION
|
||||||
return False
|
self.disabled_find_near_by_food = True
|
||||||
|
|
||||||
def avoid_snake_body(self, snakes, board_width, board_height):
|
def is_food_nearby(self, head, food_positions):
|
||||||
# Konvertiere die Körperpositionen der Schlangen in ein Set von Tupeln für schnellen Zugriff
|
for food in food_positions:
|
||||||
body_positions = set()
|
if abs(head['x'] - food['x']) <= 1 and abs(head['y'] - food['y']) <= 1:
|
||||||
for snake in snakes:
|
return True
|
||||||
for part in snake['body']:
|
return False
|
||||||
body_positions.add((part['x'], part['y']))
|
|
||||||
|
|
||||||
# Implementiere die Logik, um Positionen zu finden, die nicht von Schlangenkörpern belegt sind
|
def avoid_snake_body(self, snakes, board_width, board_height):
|
||||||
safe_positions = self.find_safe_positions(body_positions, board_width, board_height)
|
# Konvertiere die Körperpositionen der Schlangen in ein Set von Tupeln für schnellen Zugriff
|
||||||
return safe_positions
|
body_positions = set()
|
||||||
|
for snake in snakes:
|
||||||
|
for part in snake['body']:
|
||||||
|
body_positions.add((part['x'], part['y']))
|
||||||
|
|
||||||
def find_safe_positions(self, body_positions, board_width, board_height):
|
# Implementiere die Logik, um Positionen zu finden, die nicht von Schlangenkörpern belegt sind
|
||||||
# Finde sichere Positionen basierend auf den Körperpositionen und der Größe des Spielbretts
|
safe_positions = self.find_safe_positions(body_positions, board_width, board_height)
|
||||||
safe_positions = []
|
return safe_positions
|
||||||
for x in range(board_width): # Nutze die tatsächliche Breite des Spielbretts
|
|
||||||
for y in range(board_height): # Nutze die tatsächliche Höhe des Spielbretts
|
|
||||||
if (x, y) not in body_positions:
|
|
||||||
safe_positions.append({'x': x, 'y': y})
|
|
||||||
return safe_positions
|
|
||||||
|
|
||||||
def choose_move(self, game_data):
|
def find_safe_positions(self, body_positions, board_width, board_height):
|
||||||
board_width = game_data['board']['width']
|
# Finde sichere Positionen basierend auf den Körperpositionen und der Größe des Spielbretts
|
||||||
board_height = game_data['board']['height']
|
safe_positions = []
|
||||||
snakes = game_data['board']['snakes']
|
for x in range(board_width): # Nutze die tatsächliche Breite des Spielbretts
|
||||||
my_snake = game_data['you']
|
for y in range(board_height): # Nutze die tatsächliche Höhe des Spielbretts
|
||||||
my_head = my_snake['head']
|
if (x, y) not in body_positions:
|
||||||
|
safe_positions.append({'x': x, 'y': y})
|
||||||
|
return safe_positions
|
||||||
|
|
||||||
# Vermeide Schlangenkörper
|
def choose_move(self, game_data):
|
||||||
safe_positions = self.avoid_snake_body(snakes, board_width, board_height)
|
board_width = game_data['board']['width']
|
||||||
|
board_height = game_data['board']['height']
|
||||||
|
snakes = game_data['board']['snakes']
|
||||||
|
my_snake = game_data['you']
|
||||||
|
my_head = my_snake['head']
|
||||||
|
|
||||||
# Finde die nächstgelegene Nahrungsquelle, wenn Nahrung vorhanden ist
|
# Vermeide Schlangenkörper
|
||||||
try:
|
safe_positions = self.avoid_snake_body(snakes, board_width, board_height)
|
||||||
if self.is_food_nearby(my_head, game_data['board']['food']) or self.disabled_find_near_by_food:
|
|
||||||
path_to_food = self.find_path_to_food(game_data)
|
|
||||||
if path_to_food:
|
|
||||||
# Implementiere Logik, um in Richtung der Nahrungsquelle zu bewegen, falls sicher
|
|
||||||
move = self.move_towards(my_head, path_to_food[0], safe_positions)
|
|
||||||
self.add_to_history({"my_head": my_head, "path_to_food": path_to_food, "move": move})
|
|
||||||
else:
|
|
||||||
# Einfache Logik, um eine Bewegungsrichtung zu wählen, wenn keine Nahrung vorhanden ist
|
|
||||||
move = self.find_direction(my_head, safe_positions)
|
|
||||||
self.add_to_history({"my_head": my_head, "move": move})
|
|
||||||
else:
|
|
||||||
# Wenn keine Nahrung in der Nähe ist, bewege dich in eine Richtung, die dich nahe an deinem eigenen Körper hält
|
|
||||||
move = self.find_direction(my_head, safe_positions)
|
|
||||||
self.add_to_history({"my_head": my_head, "move": move})
|
|
||||||
except ValueError:
|
|
||||||
move = self.find_direction(my_head, safe_positions)
|
|
||||||
self.add_to_history({"my_head": my_head, "move": move})
|
|
||||||
|
|
||||||
# Finde den größten sicheren Bereich
|
# Finde die nächstgelegene Nahrungsquelle, wenn Nahrung vorhanden ist
|
||||||
max_area_start, max_area = self.flood_fill(my_head, safe_positions)
|
try:
|
||||||
# Wenn der Schwanz der Schlange im größten sicheren Bereich liegt, bewege dich in Richtung des Schwanzes
|
if self.is_food_nearby(my_head, game_data['board']['food']) or self.disabled_find_near_by_food:
|
||||||
my_tail = (my_snake['body'][-1]['x'], my_snake['body'][-1]['y']) # Convert to tuple
|
path_to_food = self.find_path_to_food(game_data)
|
||||||
if my_tail in max_area:
|
if path_to_food:
|
||||||
move = self.move_towards(my_head, my_tail, safe_positions)
|
# Implementiere Logik, um in Richtung der Nahrungsquelle zu bewegen, falls sicher
|
||||||
|
move = self.move_towards(my_head, path_to_food[0], safe_positions)
|
||||||
# Überprüfe zukünftige Bewegungen, um Sackgassen zu vermeiden
|
self.add_to_history({"my_head": my_head, "path_to_food": path_to_food, "move": move})
|
||||||
move = self.avoid_dead_ends(my_head, move, safe_positions, snakes)
|
else:
|
||||||
|
# Einfache Logik, um eine Bewegungsrichtung zu wählen, wenn keine Nahrung vorhanden ist
|
||||||
|
move = self.find_direction(my_head, safe_positions)
|
||||||
|
self.add_to_history({"my_head": my_head, "move": move})
|
||||||
|
else:
|
||||||
|
# Wenn keine Nahrung in der Nähe ist, bewege dich in eine Richtung, die dich nahe an deinem eigenen Körper hält
|
||||||
|
move = self.find_direction(my_head, safe_positions)
|
||||||
self.add_to_history({"my_head": my_head, "move": move})
|
self.add_to_history({"my_head": my_head, "move": move})
|
||||||
|
except ValueError:
|
||||||
|
move = self.find_direction(my_head, safe_positions)
|
||||||
|
self.add_to_history({"my_head": my_head, "move": move})
|
||||||
|
|
||||||
return move
|
# Finde den größten sicheren Bereich
|
||||||
|
max_area_start, max_area = self.flood_fill(my_head, safe_positions)
|
||||||
|
# Wenn der Schwanz der Schlange im größten sicheren Bereich liegt, bewege dich in Richtung des Schwanzes
|
||||||
|
my_tail = (my_snake['body'][-1]['x'], my_snake['body'][-1]['y']) # Convert to tuple
|
||||||
|
if my_tail in max_area:
|
||||||
|
move = self.move_towards(my_head, my_tail, safe_positions)
|
||||||
|
|
||||||
def move_towards(self, head, target, safe_positions):
|
# Überprüfe zukünftige Bewegungen, um Sackgassen zu vermeiden
|
||||||
directions = {'up': (0, 1), 'down': (0, -1), 'left': (-1, 0), 'right': (1, 0)}
|
move = self.avoid_dead_ends(my_head, move, safe_positions, snakes)
|
||||||
best_direction = None
|
self.add_to_history({"my_head": my_head, "move": move})
|
||||||
min_distance = float('inf')
|
|
||||||
min_distance_to_body = float('inf')
|
|
||||||
body_positions = set((pos['x'], pos['y']) for pos in safe_positions[:-1]) # Exclude the head from body positions
|
|
||||||
|
|
||||||
for direction, (dx, dy) in directions.items():
|
return move
|
||||||
next_position = {'x': head['x'] + dx, 'y': head['y'] + dy}
|
|
||||||
if next_position in safe_positions:
|
|
||||||
distance = abs(target[0] - next_position['x']) + abs(target[1] - next_position['y'])
|
|
||||||
distance_to_body = sum(abs(part[0] - next_position['x']) + abs(part[1] - next_position['y']) for part in body_positions)
|
|
||||||
if distance < min_distance or (distance == min_distance and distance_to_body < min_distance_to_body):
|
|
||||||
best_direction = direction
|
|
||||||
min_distance = distance
|
|
||||||
min_distance_to_body = distance_to_body
|
|
||||||
|
|
||||||
return best_direction if best_direction else "up" # Default to moving up if no safe direction found
|
def move_towards(self, head, target, safe_positions):
|
||||||
|
directions = {'up': (0, 1), 'down': (0, -1), 'left': (-1, 0), 'right': (1, 0)}
|
||||||
|
best_direction = None
|
||||||
|
min_distance = float('inf')
|
||||||
|
min_distance_to_body = float('inf')
|
||||||
|
body_positions = set((pos['x'], pos['y']) for pos in safe_positions[:-1]) # Exclude the head from body positions
|
||||||
|
|
||||||
def find_path_to_food(self, game_data):
|
for direction, (dx, dy) in directions.items():
|
||||||
my_head = game_data['you']['head']
|
next_position = {'x': head['x'] + dx, 'y': head['y'] + dy}
|
||||||
food_positions = game_data['board']['food']
|
if next_position in safe_positions:
|
||||||
snakes = game_data['board']['snakes']
|
distance = abs(target[0] - next_position['x']) + abs(target[1] - next_position['y'])
|
||||||
board_width = game_data['board']['width']
|
distance_to_body = sum(abs(part[0] - next_position['x']) + abs(part[1] - next_position['y']) for part in body_positions)
|
||||||
board_height = game_data['board']['height']
|
if distance < min_distance or (distance == min_distance and distance_to_body < min_distance_to_body):
|
||||||
|
best_direction = direction
|
||||||
# Exclude own snake's body from obstacles
|
min_distance = distance
|
||||||
own_snake_body = game_data['you']['body']
|
min_distance_to_body = distance_to_body
|
||||||
obstacles = set((part['x'], part['y']) for part in own_snake_body)
|
|
||||||
|
|
||||||
for snake in snakes:
|
|
||||||
if snake['id'] != game_data['you']['id']:
|
|
||||||
for part in snake['body']:
|
|
||||||
obstacles.add((part['x'], part['y']))
|
|
||||||
|
|
||||||
# Choose the closest food source based on the heuristic
|
|
||||||
closest_food = min(food_positions, key=lambda food: abs(food['x'] - my_head['x']) + abs(food['y'] - my_head['y']))
|
|
||||||
|
|
||||||
# Use A* to search for a safe path
|
|
||||||
path = self.a_star_search(my_head, closest_food, obstacles, board_width, board_height)
|
|
||||||
return path
|
|
||||||
|
|
||||||
def a_star_search(self, start, goal, obstacles, board_width, board_height):
|
return best_direction if best_direction else "up" # Default to moving up if no safe direction found
|
||||||
# Convert snake positions into a set of obstacles
|
|
||||||
# Helper functions
|
|
||||||
def is_position_safe(position):
|
|
||||||
x, y = position
|
|
||||||
return 0 <= x < board_width and 0 <= y < board_height and position not in obstacles
|
|
||||||
|
|
||||||
def get_neighbors(position):
|
def find_path_to_food(self, game_data):
|
||||||
x, y = position
|
my_head = game_data['you']['head']
|
||||||
return [(nx, ny) for nx, ny in [(x-1, y), (x+1, y), (x, y-1), (x, y+1)] if is_position_safe((nx, ny))]
|
food_positions = game_data['board']['food']
|
||||||
|
snakes = game_data['board']['snakes']
|
||||||
|
board_width = game_data['board']['width']
|
||||||
|
board_height = game_data['board']['height']
|
||||||
|
|
||||||
|
# Exclude own snake's body from obstacles
|
||||||
|
own_snake_body = game_data['you']['body']
|
||||||
|
obstacles = set((part['x'], part['y']) for part in own_snake_body)
|
||||||
|
|
||||||
|
for snake in snakes:
|
||||||
|
if snake['id'] != game_data['you']['id']:
|
||||||
|
for part in snake['body']:
|
||||||
|
obstacles.add((part['x'], part['y']))
|
||||||
|
|
||||||
|
# Choose the closest food source based on the heuristic
|
||||||
|
closest_food = min(food_positions, key=lambda food: abs(food['x'] - my_head['x']) + abs(food['y'] - my_head['y']))
|
||||||
|
|
||||||
|
# Use A* to search for a safe path
|
||||||
|
path = self.a_star_search(my_head, closest_food, obstacles, board_width, board_height)
|
||||||
|
return path
|
||||||
|
|
||||||
def heuristic(position, goal):
|
def a_star_search(self, start, goal, obstacles, board_width, board_height):
|
||||||
return abs(position[0] - goal[0]) + abs(position[1] - goal[1])
|
# Convert snake positions into a set of obstacles
|
||||||
|
# Helper functions
|
||||||
|
def is_position_safe(position):
|
||||||
|
x, y = position
|
||||||
|
return 0 <= x < board_width and 0 <= y < board_height and position not in obstacles
|
||||||
|
|
||||||
# Initialize start and goal positions
|
def get_neighbors(position):
|
||||||
start = (start['x'], start['y'])
|
x, y = position
|
||||||
goal = (goal['x'], goal['y'])
|
return [(nx, ny) for nx, ny in [(x-1, y), (x+1, y), (x, y-1), (x, y+1)] if is_position_safe((nx, ny))]
|
||||||
|
|
||||||
# Initialize the open and closed list
|
def heuristic(position, goal):
|
||||||
open_set = set([start])
|
return abs(position[0] - goal[0]) + abs(position[1] - goal[1])
|
||||||
came_from = {}
|
|
||||||
g_score = {start: 0}
|
|
||||||
f_score = {start: heuristic(start, goal)}
|
|
||||||
|
|
||||||
while open_set:
|
# Initialize start and goal positions
|
||||||
current = min(open_set, key=lambda pos: f_score.get(pos, float('inf')))
|
start = (start['x'], start['y'])
|
||||||
if current == goal:
|
goal = (goal['x'], goal['y'])
|
||||||
# Reconstruct the path
|
|
||||||
path = []
|
|
||||||
while current in came_from:
|
|
||||||
path.append(current)
|
|
||||||
current = came_from[current]
|
|
||||||
path.reverse()
|
|
||||||
return path # Return the path as a list of tuples
|
|
||||||
|
|
||||||
open_set.remove(current)
|
# Initialize the open and closed list
|
||||||
for neighbor in get_neighbors(current):
|
open_set = set([start])
|
||||||
tentative_g_score = g_score[current] + 1 # Distance between neighbors is always 1
|
came_from = {}
|
||||||
if tentative_g_score < g_score.get(neighbor, float('inf')):
|
g_score = {start: 0}
|
||||||
came_from[neighbor] = current
|
f_score = {start: heuristic(start, goal)}
|
||||||
g_score[neighbor] = tentative_g_score
|
|
||||||
f_score[neighbor] = g_score[neighbor] + heuristic(neighbor, goal)
|
|
||||||
if neighbor not in open_set:
|
|
||||||
open_set.add(neighbor)
|
|
||||||
|
|
||||||
return None # Kein Pfad gefunden
|
while open_set:
|
||||||
|
current = min(open_set, key=lambda pos: f_score.get(pos, float('inf')))
|
||||||
|
if current == goal:
|
||||||
|
# Reconstruct the path
|
||||||
|
path = []
|
||||||
|
while current in came_from:
|
||||||
|
path.append(current)
|
||||||
|
current = came_from[current]
|
||||||
|
path.reverse()
|
||||||
|
return path # Return the path as a list of tuples
|
||||||
|
|
||||||
def find_direction(self, head, safe_positions):
|
open_set.remove(current)
|
||||||
# Beispielhafte Logik zur Auswahl einer Bewegungsrichtung
|
for neighbor in get_neighbors(current):
|
||||||
directions = {'up': (0, 1), 'down': (0, -1), 'left': (-1, 0), 'right': (1, 0)}
|
tentative_g_score = g_score[current] + 1 # Distance between neighbors is always 1
|
||||||
for direction, (dx, dy) in directions.items():
|
if tentative_g_score < g_score.get(neighbor, float('inf')):
|
||||||
next_position = {'x': head['x'] + dx, 'y': head['y'] + dy}
|
came_from[neighbor] = current
|
||||||
if next_position in safe_positions:
|
g_score[neighbor] = tentative_g_score
|
||||||
return direction
|
f_score[neighbor] = g_score[neighbor] + heuristic(neighbor, goal)
|
||||||
return "up" # Standardbewegung, falls keine sichere Position gefunden wird
|
if neighbor not in open_set:
|
||||||
|
open_set.add(neighbor)
|
||||||
|
|
||||||
def avoid_self_collision(self, future_head, body_positions):
|
return None # Kein Pfad gefunden
|
||||||
# Überprüft, ob die zukünftige Kopfposition im Körper der Schlange liegt
|
|
||||||
return (future_head['x'], future_head['y']) not in body_positions
|
|
||||||
|
|
||||||
def avoid_dead_ends(self, head, move, safe_positions, snakes):
|
def find_direction(self, head, safe_positions):
|
||||||
directions = {'up': (0, 1), 'down': (0, -1), 'left': (-1, 0), 'right': (1, 0)}
|
# Beispielhafte Logik zur Auswahl einer Bewegungsrichtung
|
||||||
dx, dy = directions[move]
|
directions = {'up': (0, 1), 'down': (0, -1), 'left': (-1, 0), 'right': (1, 0)}
|
||||||
future_head = {'x': head['x'] + dx, 'y': head['y'] + dy}
|
for direction, (dx, dy) in directions.items():
|
||||||
body_positions = set((part['x'], part['y']) for part in snakes[0]['body'])
|
next_position = {'x': head['x'] + dx, 'y': head['y'] + dy}
|
||||||
|
if next_position in safe_positions:
|
||||||
|
return direction
|
||||||
|
return "up" # Standardbewegung, falls keine sichere Position gefunden wird
|
||||||
|
|
||||||
if not self.is_future_move_safe(future_head, safe_positions, snakes) or not self.avoid_self_collision(future_head, body_positions):
|
def avoid_self_collision(self, future_head, body_positions):
|
||||||
for alternative_move in directions.keys():
|
# Überprüft, ob die zukünftige Kopfposition im Körper der Schlange liegt
|
||||||
dx, dy = directions[alternative_move]
|
return (future_head['x'], future_head['y']) not in body_positions
|
||||||
alternative_future_head = {'x': head['x'] + dx, 'y': head['y'] + dy}
|
|
||||||
if self.is_future_move_safe(alternative_future_head, safe_positions, snakes) and self.avoid_self_collision(alternative_future_head, body_positions):
|
|
||||||
return alternative_move
|
|
||||||
return move
|
|
||||||
|
|
||||||
def simulate_snake_movement(self, snakes):
|
def avoid_dead_ends(self, head, move, safe_positions, snakes):
|
||||||
future_body_positions = set()
|
directions = {'up': (0, 1), 'down': (0, -1), 'left': (-1, 0), 'right': (1, 0)}
|
||||||
for snake in snakes:
|
dx, dy = directions[move]
|
||||||
# Beachte, dass dies nur ein Beispiel ist und angepasst werden muss, um deine spezifische Spiellogik zu berücksichtigen
|
future_head = {'x': head['x'] + dx, 'y': head['y'] + dy}
|
||||||
for part in snake['body'][:-1]: # Ignoriere den letzten Teil des Körpers, da er sich bewegt
|
body_positions = set((part['x'], part['y']) for part in snakes[0]['body'])
|
||||||
future_body_positions.add((part['x'], part['y']))
|
|
||||||
return future_body_positions
|
|
||||||
|
|
||||||
def is_future_move_safe(self, future_head, safe_positions, snakes):
|
if not self.is_future_move_safe(future_head, safe_positions, snakes) or not self.avoid_self_collision(future_head, body_positions):
|
||||||
# Simuliere die Bewegung der Schlange und aktualisiere die Positionen des eigenen Körpers
|
for alternative_move in directions.keys():
|
||||||
future_body_positions = self.simulate_snake_movement(snakes)
|
dx, dy = directions[alternative_move]
|
||||||
# Konvertiere safe_positions in ein Set von Tupeln für den Flood Fill Algorithmus
|
alternative_future_head = {'x': head['x'] + dx, 'y': head['y'] + dy}
|
||||||
safe_positions_set = set((pos['x'], pos['y']) for pos in safe_positions)
|
if self.is_future_move_safe(alternative_future_head, safe_positions, snakes) and self.avoid_self_collision(alternative_future_head, body_positions):
|
||||||
# Entferne die zukünftigen Körperpositionen aus den sicheren Positionen
|
return alternative_move
|
||||||
safe_positions_set = safe_positions_set - future_body_positions
|
return move
|
||||||
# Füge die zukünftige Kopfposition hinzu, um sie als Startpunkt zu verwenden
|
|
||||||
safe_positions_set.add((future_head['x'], future_head['y']))
|
|
||||||
# Berechne die Anzahl der erreichbaren sicheren Positionen von der zukünftigen Kopfposition aus
|
|
||||||
reachable_positions = self.flood_fill((future_head['x'], future_head['y']), safe_positions_set)
|
|
||||||
# Entscheide, ob die Bewegung sicher ist, basierend auf der Anzahl der erreichbaren Positionen
|
|
||||||
|
|
||||||
fill_bool = len(reachable_positions) > len(safe_positions_set) * 0.25
|
def simulate_snake_movement(self, snakes):
|
||||||
if fill_bool:
|
future_body_positions = set()
|
||||||
return fill_bool
|
for snake in snakes:
|
||||||
|
# Beachte, dass dies nur ein Beispiel ist und angepasst werden muss, um deine spezifische Spiellogik zu berücksichtigen
|
||||||
|
for part in snake['body'][:-1]: # Ignoriere den letzten Teil des Körpers, da er sich bewegt
|
||||||
|
future_body_positions.add((part['x'], part['y']))
|
||||||
|
return future_body_positions
|
||||||
|
|
||||||
return len(safe_positions_set) >= len(snakes[0]['body'])
|
def is_future_move_safe(self, future_head, safe_positions, snakes):
|
||||||
|
# Simuliere die Bewegung der Schlange und aktualisiere die Positionen des eigenen Körpers
|
||||||
|
future_body_positions = self.simulate_snake_movement(snakes)
|
||||||
|
# Konvertiere safe_positions in ein Set von Tupeln für den Flood Fill Algorithmus
|
||||||
|
safe_positions_set = set((pos['x'], pos['y']) for pos in safe_positions)
|
||||||
|
# Entferne die zukünftigen Körperpositionen aus den sicheren Positionen
|
||||||
|
safe_positions_set = safe_positions_set - future_body_positions
|
||||||
|
# Füge die zukünftige Kopfposition hinzu, um sie als Startpunkt zu verwenden
|
||||||
|
safe_positions_set.add((future_head['x'], future_head['y']))
|
||||||
|
# Berechne die Anzahl der erreichbaren sicheren Positionen von der zukünftigen Kopfposition aus
|
||||||
|
reachable_positions = self.flood_fill((future_head['x'], future_head['y']), safe_positions_set)
|
||||||
|
# Entscheide, ob die Bewegung sicher ist, basierend auf der Anzahl der erreichbaren Positionen
|
||||||
|
|
||||||
def flood_fill(self, start, safe_positions):
|
fill_bool = len(reachable_positions) > len(safe_positions_set) * 0.25
|
||||||
stack = [start]
|
if fill_bool:
|
||||||
visited = set()
|
return fill_bool
|
||||||
max_area = 0
|
|
||||||
max_area_start = None
|
|
||||||
|
|
||||||
while stack:
|
return len(safe_positions_set) >= len(snakes[0]['body'])
|
||||||
position = stack.pop()
|
|
||||||
if isinstance(position, dict):
|
|
||||||
position = tuple(position.values())
|
|
||||||
else:
|
|
||||||
position = tuple(position)
|
|
||||||
|
|
||||||
if position not in visited:
|
def flood_fill(self, start, safe_positions):
|
||||||
visited.add(position)
|
stack = [start]
|
||||||
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: # links, rechts, oben, unten
|
visited = set()
|
||||||
next_position = tuple([position[0] + dx, position[1] + dy])
|
max_area = 0
|
||||||
if next_position in safe_positions:
|
max_area_start = None
|
||||||
stack.append(next_position)
|
|
||||||
|
|
||||||
# Überprüfe, ob der aktuelle Bereich größer ist als der bisher größte Bereich
|
while stack:
|
||||||
if len(visited) > max_area:
|
position = stack.pop()
|
||||||
max_area = len(visited)
|
if isinstance(position, dict):
|
||||||
max_area_start = position
|
position = tuple(position.values())
|
||||||
|
else:
|
||||||
|
position = tuple(position)
|
||||||
|
|
||||||
return max_area_start, visited
|
if position not in visited:
|
||||||
|
visited.add(position)
|
||||||
|
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: # links, rechts, oben, unten
|
||||||
|
next_position = tuple([position[0] + dx, position[1] + dy])
|
||||||
|
if next_position in safe_positions:
|
||||||
|
stack.append(next_position)
|
||||||
|
|
||||||
|
# Überprüfe, ob der aktuelle Bereich größer ist als der bisher größte Bereich
|
||||||
|
if len(visited) > max_area:
|
||||||
|
max_area = len(visited)
|
||||||
|
max_area_start = position
|
||||||
|
|
||||||
|
return max_area_start, visited
|
||||||
|
|||||||
@@ -2,9 +2,13 @@ from server.GameBoard import GameBoard
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
class TemplateSnake:
|
class TemplateSnake:
|
||||||
|
VERSION = "1.0.0"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.history = []
|
self.history = []
|
||||||
self.target_food = None
|
self.target_food = None
|
||||||
|
self.name = self.__class__.__name__
|
||||||
|
self.version = getattr(self, "VERSION", "1.0.0")
|
||||||
|
|
||||||
def clear_history(self):
|
def clear_history(self):
|
||||||
self.history = []
|
self.history = []
|
||||||
|
|||||||
Reference in New Issue
Block a user