diff --git a/snakes/MasterSnake_FoodBySpace.py b/snakes/MasterSnake_FoodBySpace.py deleted file mode 100644 index 49df991..0000000 --- a/snakes/MasterSnake_FoodBySpace.py +++ /dev/null @@ -1,256 +0,0 @@ -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 diff --git a/snakes/TemplateSnake.py b/snakes/TemplateSnake.py index 48b9144..eee42bc 100644 --- a/snakes/TemplateSnake.py +++ b/snakes/TemplateSnake.py @@ -119,6 +119,7 @@ class TemplateSnake: def avoid_get_eaten_by_other_snakes(self): remove = [] + no_way_out = {} for snake in self.other_snakes: for direction, location in self.safe_positions.items(): if len(self.safe_positions) > 1: @@ -128,12 +129,16 @@ class TemplateSnake: self.kill_the_snake = direction #TODO: Check if snake on the way to the food here and only remove this pos elif location in [{"x": v["x"], "y": v["y"]} for k, v in self.get_possible_moves(snake["head"]).items()]: + 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):