from snakes.TemplateSnake import TemplateSnake class MasterSnake(TemplateSnake): def __init__(self): super().__init__() self.name = "MasterSnake" self.history_head = [] def avoid_snake_body(self, snakes, board_width, board_height): # Konvertiere die Körperpositionen der Schlangen in ein Set von Tupeln für schnellen Zugriff body_positions = set() for snake in snakes: for part in snake['body']: body_positions.add((part['x'], part['y'])) # Implementiere die Logik, um Positionen zu finden, die nicht von Schlangenkörpern belegt sind safe_positions = self.find_safe_positions(body_positions, board_width, board_height) return safe_positions def find_safe_positions(self, body_positions, board_width, board_height): # Finde sichere Positionen basierend auf den Körperpositionen und der Größe des Spielbretts 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): 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'] # Vermeide Schlangenkörper safe_positions = self.avoid_snake_body(snakes, board_width, board_height) # Wähle Nahrung basierend auf verfügbarem Platz try: chosen_food = self.choose_food_based_on_space(game_data) if chosen_food: path_to_food = self.a_star_search(my_head, chosen_food, self.get_obstacles(game_data), board_width, board_height) if path_to_food: # Implementiere Logik, um in Richtung der Nahrungsquelle zu bewegen, falls sicher move = self.move_towards_food(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 kein Pfad zur Nahrung vorhanden ist move = self.find_direction(my_head, safe_positions) self.add_to_history({"my_head": my_head, "move": move}) else: # Einfache Logik, um eine Bewegungsrichtung zu wählen, wenn keine geeignete Nahrung gefunden wird 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}) # Überprüfe zukünftige Bewegungen, um Sackgassen zu vermeiden move = self.avoid_dead_ends_and_circles(my_head, move, safe_positions, board_width, board_height, snakes) self.add_to_history({"my_head": my_head, "move": move}) self.add_to_history_head({"my_head": my_head, "move": move}) return move def move_towards_food(self, head, food, 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 for direction, (dx, dy) in directions.items(): next_position = {'x': head['x'] + dx, 'y': head['y'] + dy} if next_position in safe_positions: distance = abs(food[0] - next_position['x']) + abs(food[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 find_path_to_food(self, game_data): my_head = game_data['you']['head'] 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 choose_food_based_on_space(self, game_data): my_head = game_data['you']['head'] food_positions = game_data['board']['food'] snakes = game_data['board']['snakes'] board_width = game_data['board']['width'] board_height = game_data['board']['height'] my_length = game_data['you']['length'] # Sortiere die Nahrungsquellen basierend auf ihrer Entfernung sorted_food = sorted(food_positions, key=lambda food: abs(food['x'] - my_head['x']) + abs(food['y'] - my_head['y'])) for food in sorted_food: path = self.a_star_search(my_head, food, self.get_obstacles(game_data), board_width, board_height) if path and self.will_fit_in_space(path, my_length, board_width, board_height): return food # Diese Nahrung ist erreichbar und es gibt genug Platz # Wenn keine geeignete Nahrung gefunden wird, gib ein Standard-Nahrungsobjekt zurück oder löse eine Ausnahme aus if food_positions: return food_positions[0] # Gib das erste Nahrungsobjekt zurück else: raise ValueError("Keine Nahrung gefunden") # Oder löse eine Ausnahme aus def will_fit_in_space(self, path, snake_length, board_width, board_height): # Überprüfe, ob die Länge des Pfades größer oder gleich der Länge der Schlange ist if len(path) >= snake_length: return True # Überprüfe, ob es genügend Platz um den Endpunkt des Pfades gibt end_of_path = path[-1] space_count = self.count_space_around(end_of_path, board_width, board_height) return space_count >= snake_length def count_space_around(self, position, board_width, board_height): # Zähle die Anzahl der erreichbaren Positionen um einen Punkt herum x, y = position count = 0 for dx in [-1, 0, 1]: for dy in [-1, 0, 1]: if (dx != 0 or dy != 0) and 0 <= x + dx < board_width and 0 <= y + dy < board_height: count += 1 return count def get_obstacles(self, game_data): # Erstelle ein Set von Hindernissen für die A* Suche obstacles = set() for snake in game_data['board']['snakes']: for part in snake['body']: obstacles.add((part['x'], part['y'])) return obstacles def a_star_search(self, start, goal, obstacles, board_width, board_height): # 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): x, y = position 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))] def heuristic(position, goal): return abs(position[0] - goal[0]) + abs(position[1] - goal[1]) # Initialize start and goal positions start = (start['x'], start['y']) goal = (goal['x'], goal['y']) # Initialize the open and closed list open_set = set([start]) came_from = {} g_score = {start: 0} f_score = {start: heuristic(start, goal)} 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 open_set.remove(current) for neighbor in get_neighbors(current): tentative_g_score = g_score[current] + 1 # Distance between neighbors is always 1 if tentative_g_score < g_score.get(neighbor, float('inf')): came_from[neighbor] = current 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 def find_direction(self, head, safe_positions): # Beispielhafte Logik zur Auswahl einer Bewegungsrichtung directions = {'up': (0, 1), 'down': (0, -1), 'left': (-1, 0), 'right': (1, 0)} for direction, (dx, dy) in directions.items(): 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 def is_in_history(self, future_head): # Überprüfe, ob die zukünftige Kopfposition in den letzten N Bewegungen vorkommt return any(future_head == move_data["my_head"] for move_data in self.history_head[-10:]) def avoid_dead_ends_and_circles(self, head, move, safe_positions, board_width, board_height, snakes): directions = {'up': (0, 1), 'down': (0, -1), 'left': (-1, 0), 'right': (1, 0)} dx, dy = directions[move] future_head = {'x': head['x'] + dx, 'y': head['y'] + dy} if not self.is_future_move_safe(future_head, safe_positions, board_width, board_height, snakes) or self.is_in_history(future_head): for alternative_move in directions.keys(): dx, dy = directions[alternative_move] alternative_future_head = {'x': head['x'] + dx, 'y': head['y'] + dy} if self.is_future_move_safe(alternative_future_head, safe_positions, board_width, board_height, snakes) and not self.is_in_history(alternative_future_head): return alternative_move return move def add_to_history_head(self, move_data): # Füge die aktuelle Kopfposition zur Historie hinzu und behalte nur die letzten 10 Positionen self.history_head.append(move_data) self.history_head = self.history_head[-10:] def simulate_snake_movement(self, snakes): future_body_positions = set() 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 def is_future_move_safe(self, future_head, safe_positions, board_width, board_height, 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 # Entscheide, ob die Bewegung sicher ist, basierend auf der Anzahl der erreichbaren Positionen return safe_positions_set # oder wähle einen anderen Schwellenwert