from snakes.TemplateSnake import TemplateSnake class MasterSnake(TemplateSnake): VERSION = "1.2.0" def __init__(self): super().__init__() self.name = "MasterSnake" self.version = self.VERSION self.disabled_find_near_by_food = True def is_food_nearby(self, head, food_positions): for food in food_positions: if abs(head['x'] - food['x']) <= 1 and abs(head['y'] - food['y']) <= 1: return True return False 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) # Finde die nächstgelegene Nahrungsquelle, wenn Nahrung vorhanden ist try: 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 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) # Überprüfe zukünftige Bewegungen, um Sackgassen zu vermeiden move = self.avoid_dead_ends(my_head, move, safe_positions, snakes) self.add_to_history({"my_head": my_head, "move": move}) return move 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 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(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 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 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 avoid_self_collision(self, future_head, body_positions): # Ü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): 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} body_positions = set((part['x'], part['y']) for part in snakes[0]['body']) if not self.is_future_move_safe(future_head, safe_positions, snakes) or not self.avoid_self_collision(future_head, body_positions): 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, snakes) and self.avoid_self_collision(alternative_future_head, body_positions): return alternative_move return move 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, 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 fill_bool = len(reachable_positions) > len(safe_positions_set) * 0.25 if fill_bool: return fill_bool return len(safe_positions_set) >= len(snakes[0]['body']) def flood_fill(self, start, safe_positions): stack = [start] visited = set() max_area = 0 max_area_start = None while stack: position = stack.pop() if isinstance(position, dict): position = tuple(position.values()) else: position = tuple(position) 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