diff --git a/constants.py b/constants.py index e652659..e3623c0 100644 --- a/constants.py +++ b/constants.py @@ -6,7 +6,7 @@ class Constants: def __init__(self): self.BLACK = (0, 0, 0) self.RED = (255, 0, 0) - self.GRID_SIZE = 75 + self.GRID_SIZE = 65 self.GRID_WIDTH = 30 self.GRID_HEIGHT = 15 self.WINDOW_SIZE = (self.GRID_WIDTH * self.GRID_SIZE, self.GRID_HEIGHT * self.GRID_SIZE) diff --git a/genetics.py b/genetics.py new file mode 100644 index 0000000..22dea52 --- /dev/null +++ b/genetics.py @@ -0,0 +1,148 @@ +from state_space_search import graphsearch, generate_cost_map +import random + +# Parametry algorytmu genetycznego +POPULATION_SIZE = 700 +MUTATION_RATE = 0.01 +NUM_GENERATIONS = 600 + +# Generowanie początkowej populacji +def generate_individual(animals): + return random.sample(animals, len(animals)) + +def generate_population(animals, size): + return [generate_individual(animals) for _ in range(size)] + +# Obliczanie odległości między zwierzetami +def calculate_distance(animal1, animal2): + x1, y1 = animal1 + x2, y2 = animal2 + return abs(x1 - x2) + abs(y1 - y2) # Odległość Manhattana + +def calculate_total_distance(animals): + total_distance = 0 + for i in range(len(animals) - 1): + total_distance += calculate_distance(animals[i], animals[i+1]) + total_distance += calculate_distance(animals[-1], animals[0]) # Zamknięcie cyklu + return total_distance + +# Selekcja rodziców za pomocą metody ruletki +def select_parents(population, num_parents): + fitness_scores = [1 / calculate_total_distance(individual) for individual in population] + total_fitness = sum(fitness_scores) + selection_probs = [fitness / total_fitness for fitness in fitness_scores] + + parents = random.choices(population, weights=selection_probs, k=num_parents) + return parents + +# Krzyżowanie rodziców (OX,Davis) +def crossover(parent1, parent2): + child1 = [None] * len(parent1) + child2 = [None] * len(parent1) + start_index = random.randint(0, len(parent1) - 1) + end_index = random.randint(start_index, len(parent1) - 1) + child1[start_index:end_index+1] = parent1[start_index:end_index+1] + child2[start_index:end_index+1] = parent2[start_index:end_index+1] + + # Uzupełnienie brakujących zwierząt z drugiego rodzica + for i in range(len(parent1)): + if parent2[i] not in child1: + for j in range(len(parent2)): + if child1[j] is None: + child1[j] = parent2[i] + break + + for i in range(len(parent1)): + if parent1[i] not in child2: + for j in range(len(parent1)): + if child2[j] is None: + child2[j] = parent1[i] + break + + return child1, child2 + +# Mutacja: zamiana dwóch losowych zwierząt z prawdopodobieństwem MUTATION_RATE +def mutate(individual): + if random.random() < MUTATION_RATE: + index1, index2 = random.sample(range(len(individual)), 2) + individual[index1], individual[index2] = individual[index2], individual[index1] + +# Algorytm genetyczny +def genetic_algorithm(animals): + population = generate_population(animals, POPULATION_SIZE) + + for generation in range(NUM_GENERATIONS): + # Selekcja rodziców + parents = select_parents(population, POPULATION_SIZE // 2) + + # Krzyżowanie i tworzenie nowej populacji + next_generation = [] + for i in range(0, len(parents), 2): + parent1 = parents[i] + if i + 1 < len(parents): + parent2 = parents[i + 1] + else: + parent2 = parents[0] + child1, child2 = crossover(parent1, parent2) + next_generation.extend([child1, child2]) + + # Mutacja nowej populacji + for individual in next_generation: + mutate(individual) + + # Zastąpienie starej populacji nową + population = next_generation + + # Znalezienie najlepszego osobnika + best_individual = min(population, key=calculate_total_distance) + + return best_individual + +# def calculate_distance(start, goal, max_x, max_y, obstacles, cost_map): +# istate = (start[0], start[1], 'N') # Zakładamy, że zaczynamy od kierunku północnego +# actions, cost = graphsearch(istate, goal, max_x, max_y, obstacles, cost_map) +# return cost + +# def calculate_total_distance(animals, max_x, max_y, obstacles, cost_map): +# total_distance = 0 +# for i in range(len(animals) - 1): +# total_distance += calculate_distance(animals[i], animals[i+1], max_x, max_y, obstacles, cost_map) +# total_distance += calculate_distance(animals[-1], animals[0], max_x, max_y, obstacles, cost_map) # Zamknięcie cyklu +# return total_distance + +# # Selekcja rodziców za pomocą metody ruletki +# def select_parents(population, num_parents, max_x, max_y, obstacles, cost_map): +# fitness_scores = [1 / calculate_total_distance(individual, max_x, max_y, obstacles, cost_map) for individual in population] +# total_fitness = sum(fitness_scores) +# selection_probs = [fitness / total_fitness for fitness in fitness_scores] + +# parents = random.choices(population, weights=selection_probs, k=num_parents) +# return parents + + +# def genetic_algorithm(animals, max_x, max_y, obstacles, cost_map): +# population = generate_population(animals, POPULATION_SIZE) + +# for generation in range(NUM_GENERATIONS): +# # Selekcja rodziców +# parents = select_parents(population, POPULATION_SIZE // 2, max_x, max_y, obstacles, cost_map) + +# # Krzyżowanie i tworzenie nowej populacji +# next_generation = [] +# for i in range(0, len(parents), 2): +# parent1 = parents[i] +# parent2 = parents[i + 1] +# child1, child2 = crossover(parent1, parent2) +# next_generation.extend([child1, child2]) + +# # Mutacja nowej populacji +# for individual in next_generation: +# mutate(individual) + +# # Zastąpienie starej populacji nową +# population = next_generation + +# # Znalezienie najlepszego osobnika +# best_individual = min(population, key=lambda individual: calculate_total_distance(individual, max_x, max_y, obstacles, cost_map)) + +# return best_individual \ No newline at end of file diff --git a/main.py b/main.py index 2e6692e..738317f 100644 --- a/main.py +++ b/main.py @@ -13,6 +13,7 @@ from constants import Constants, init_pygame from draw import draw_goal, draw_grid, draw_house from season import draw_background from night import change_time +from genetics import genetic_algorithm const = Constants() init_pygame(const) @@ -77,12 +78,13 @@ def main(): actions = [] clock = pygame.time.Clock() spawned = False + route = False - # Lista zawierająca klatki do odwiedzenia - enclosures_to_visit = Enclosures.copy() - current_enclosure_index = -1 # Indeks bieżącej klatki - actions_to_compare_list = [] # Lista zawierająca ścieżki do porównania - goals_to_compare_list = list() # Lista zawierająca cele do porównania + # # Lista zawierająca klatki do odwiedzenia + # enclosures_to_visit = Enclosures.copy() + # current_enclosure_index = -1 # Indeks bieżącej klatki + # actions_to_compare_list = [] # Lista zawierająca ścieżki do porównania + # goals_to_compare_list = list() # Lista zawierająca cele do porównania while True: for event in pygame.event.get(): @@ -105,6 +107,11 @@ def main(): # animal._feed = 0 animal._feed = random.randint(0, 10) spawned = True + + if not route: + animals = [(animal.x, animal.y) for animal in Animals] + best_route = genetic_algorithm(animals) + route = True draw_Animals(Animals, const) draw_Terrain_Obstacles(Terrain_Obstacles, const) @@ -118,31 +125,34 @@ def main(): pygame.time.wait(200) else: if agent._dryfood > 1 and agent._wetfood > 1 : - if not goals_to_compare_list: - current_enclosure_index = (current_enclosure_index + 1) % len(enclosures_to_visit) - current_enclosure = enclosures_to_visit[current_enclosure_index] + # if not goals_to_compare_list: + # current_enclosure_index = (current_enclosure_index + 1) % len(enclosures_to_visit) + # current_enclosure = enclosures_to_visit[current_enclosure_index] - for animal in current_enclosure.animals: - goal = (animal.x, animal.y) - goals_to_compare_list.append(goal) + # for animal in current_enclosure.animals: + # goal = (animal.x, animal.y) + # goals_to_compare_list.append(goal) - actions_to_compare = graphsearch(agent.istate, goal, const.GRID_WIDTH, const.GRID_HEIGHT, obstacles, cost_map) - actions_to_compare_list.append((actions_to_compare, goal)) + # actions_to_compare = graphsearch(agent.istate, goal, const.GRID_WIDTH, const.GRID_HEIGHT, obstacles, cost_map) + # actions_to_compare_list.append((actions_to_compare, goal)) - chosen_path_and_goal = min(actions_to_compare_list, key=lambda x: len(x[0])) - goal = chosen_path_and_goal[1] + # chosen_path_and_goal = min(actions_to_compare_list, key=lambda x: len(x[0])) + # goal = chosen_path_and_goal[1] + # draw_goal(const, goal) + + # # Usuń wybrany element z listy + # actions_to_compare_list.remove(chosen_path_and_goal) + # goals_to_compare_list.remove(goal) + goal = best_route.pop(0) + best_route.append(goal) draw_goal(const, goal) - # Usuń wybrany element z listy - actions_to_compare_list.remove(chosen_path_and_goal) - goals_to_compare_list.remove(goal) - - actions = graphsearch(agent.istate, goal, const.GRID_WIDTH, const.GRID_HEIGHT, obstacles, cost_map) + actions, cost = graphsearch(agent.istate, goal, const.GRID_WIDTH, const.GRID_HEIGHT, obstacles, cost_map) else: goal = (3,1) draw_goal(const, goal) - actions = graphsearch(agent.istate, goal, const.GRID_WIDTH, const.GRID_HEIGHT, obstacles, cost_map) + actions, cost = graphsearch(agent.istate, goal, const.GRID_WIDTH, const.GRID_HEIGHT, obstacles, cost_map) if __name__ == "__main__": main() \ No newline at end of file diff --git a/state_space_search.py b/state_space_search.py index b9d99e9..0f1ea91 100644 --- a/state_space_search.py +++ b/state_space_search.py @@ -40,7 +40,7 @@ def graphsearch(istate, goal, max_x, max_y, obstacles, cost_map): state, _, _ = node if goaltest(state, goal): - return build_action_sequence(node) + return build_action_sequence(node), current_cost(node, cost_map) explored.add(state) @@ -61,7 +61,7 @@ def graphsearch(istate, goal, max_x, max_y, obstacles, cost_map): else: break - return False + return False, float('inf') def is_state_in_queue(state, queue): for _, (s, _, _) in queue.queue: @@ -124,4 +124,5 @@ def generate_cost_map(Animals, Terrain_Obstacles, cost_map={}): else: cost_map[(terrain_obstacle.x , terrain_obstacle.y )] = bush_cost - return cost_map \ No newline at end of file + return cost_map + diff --git a/tree.png b/tree.png index 0c2c985..e87cc57 100644 Binary files a/tree.png and b/tree.png differ