diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/Intelegentny_Pszczelarz.iml b/.idea/Intelegentny_Pszczelarz.iml new file mode 100644 index 00000000..27487cac --- /dev/null +++ b/.idea/Intelegentny_Pszczelarz.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000..105ce2da --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..d56657ad --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..483b6227 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/GeneticAlgorithm.py b/src/GeneticAlgorithm.py new file mode 100644 index 00000000..d17d3c95 --- /dev/null +++ b/src/GeneticAlgorithm.py @@ -0,0 +1,213 @@ +import copy +import random +from Field import Field, Neighbors +import heapq as h + +FLOWER = 0 +SHORT = 1 +TALL = 80 +TREE = 100 +population_size = 50 +num_of_trees = 30 +crossover_rate = 0.8 +mutation_rate = 0.1 +tilemapSizeX = 12 +tilemapSizeY = 12 +SHORT_VALUE = 1 +TALL_VALUE = 100 +TREE_VALUE = 200 +tilemap_values = [] +class Individual: + def __init__(self, genes): + self.genes = genes + self.fitness = 0 + + def __getitem__(self, index): + return self.genes[index] + + def __setitem__(self, index, value): + self.genes[index] = value + + +class GA: + def __init__(self, population_size, generations, flower_positions, tilemap): + self.population_size = population_size + self.generations = generations + self.population = [] + self.mutation_rate = mutation_rate + self.crossover_rate = crossover_rate + self.flower_positions = flower_positions + self.tilemap = tilemap + + def initialize_population(self): + # Generate random individuals representing tree arrangements + for _ in range(self.population_size): + individual = self.generate_individual() + self.population.append(individual) + + def generate_individual(self): + # Generate a random tree arrangement + individual = [] + available_positions = [(x, y) for x in range(1, tilemapSizeX - 1) for y in range(1, tilemapSizeY - 1)] + random.shuffle(available_positions) + + for i in range(num_of_trees): + individual.append(available_positions[i]) + + return Individual(individual) + + def evaluate_fitness(self): + for individual in self.population: + individual.fitness = self.calculate_average_distance(individual.genes) + print(f"Fitness for individual {individual}: {individual.fitness}") + + def calculate_average_distance(self, individual): + tilemap_values = [] + local_tile_map = copy.deepcopy(self.tilemap) + total_distance = 0 + + # Set trees on the local tile map + for tree_x, tree_y in individual: + local_tile_map[tree_x][tree_y] = TREE + + for X in range(0, len(local_tile_map)): + tilemap_values.append([]) + for Y in range(0, len(local_tile_map[X])): + if local_tile_map[X][Y] == SHORT: + value = SHORT_VALUE + elif local_tile_map[X][Y] == TALL: + value = TALL_VALUE + elif local_tile_map[X][Y] == TREE: + value = TREE_VALUE + elif local_tile_map[X][Y] == FLOWER: + value = 1 + tilemap_values[X].append(value) + + field = Field(local_tile_map, tilemap_values) + + for flower_x, flower_y in self.flower_positions: + totalFlowerDistance = 0 + for flower2_x, flower2_y in self.flower_positions: + if flower_x == flower2_x and flower_y == flower2_y: + continue + + pathAstar = A_star(field, (flower_x, flower_y, 3), (flower2_x, flower2_y)) + if pathAstar is None: + continue + + # Check for trees in the path + has_tree = False + for x, y, _ in pathAstar: + if local_tile_map[x][y] == TREE: + has_tree = True + break + + if has_tree: + continue + + # Calculate the unique positions in the path + check_path_A = [] + for x, y, _ in pathAstar: + if (x, y) not in check_path_A: + check_path_A.append((x, y)) + totalFlowerDistance += len(check_path_A) + + total_distance += totalFlowerDistance + + average_distance = total_distance / (len(self.flower_positions)) + return average_distance + + def selection(self): + # Select individuals based on their fitness + weights = [individual.fitness for individual in self.population] + selected = random.choices(self.population, weights=weights, k=self.population_size) + # Copy the selected individuals as the new population + self.population = selected + + def crossover(self): + # Perform crossover between selected individuals to create offspring + offspring = [] + while len(offspring) < self.population_size: + parent1, parent2 = random.sample(self.population, 2) + child = self.perform_crossover(parent1, parent2) + offspring.append(child) + self.population = offspring + + def perform_crossover(self, parent1, parent2): + child_genes = [] + for i in range(len(parent1.genes)): + # Perform crossover by randomly selecting genes from parents + if random.random() < self.crossover_rate: + child_genes.append(parent1.genes[i]) + else: + child_genes.append(parent2.genes[i]) + + child = Individual(child_genes) + return child + + def mutation(self): + # Perform mutation on the offspring + for individual in self.population: + if random.random() < mutation_rate: + self.perform_mutation(individual) + + def perform_mutation(self, individual): + # Perform mutation operation on the individual + index1 = random.randint(0, num_of_trees - 1) + index2 = random.randint(0, num_of_trees - 1) + individual.genes[index1], individual.genes[index2] = individual.genes[index2], individual.genes[index1] + + def run(self): + self.initialize_population() + for _ in range(self.generations): + self.evaluate_fitness() + self.selection() + self.crossover() + self.mutation() + self.evaluate_fitness() + + best_individual = max(self.population, key=lambda x: x.fitness) + max_dist=best_individual.fitness + print(max_dist) + return best_individual + + +def Manhattan_dis(start, end): + start_x, start_y = start + end_x, end_y = end + return abs(start_x - end_x) + abs(start_y - end_y) + + +def Make_path(start, end, previos): + path = [end] + while start not in path: + path.append(previos[path[-1]]) + path.reverse() + return path + + +def A_star(field, start, end): + open = [] + previous = {} + closed = set() + checked = set() + now = (0, start) + h.heappush(open, (0, start)) + while len(open) > 0: + now = h.heappop(open) + if now[1] in closed: + continue + if now[1][0:2] == end: + path = Make_path(start, now[1], previous) + return path + closed.add(now[1]) + checked.add(now[1][0:2]) + for x in Neighbors(field, now[1]): + if x not in closed and x not in checked: + previous[x] = now[1] + if now[1][0:2] != x[0:2]: + added_cost = field[x[0], x[1]] + Manhattan_dis(x[0:2], end) + else: + added_cost = 0 + h.heappush(open, (now[0] + added_cost, x)) + checked.add(x[0:2]) diff --git a/src/main.py b/src/main.py index f3a1a8d9..a990da5c 100644 --- a/src/main.py +++ b/src/main.py @@ -3,10 +3,56 @@ import random #import Flower import Beehive import Frames +from GeneticAlgorithm import GA from Field import Field, Neighbors from queue import Queue import heapq as h + + + + +def Manhattan_dis(start, end): + start_x, start_y = start + end_x, end_y = end + return abs(start_x - end_x) + abs(start_y - end_y) + + +def Make_path(start, end, previos): + path = [end] + while start not in path: + path.append(previos[path[-1]]) + path.reverse() + return path + + +def A_star(field, start, end): + open = [] + previous = {} + closed = set() + checked = set() + now = (0, start) + h.heappush(open, (0, start)) + while len(open) > 0: + now = h.heappop(open) + if now[1] in closed: + continue + if now[1][0:2] == end: + path = Make_path(start, now[1], previous) + return path + closed.add(now[1]) + checked.add(now[1][0:2]) + for x in Neighbors(field, now[1]): + if x not in closed and x not in checked: + previous[x] = now[1] + if now[1][0:2] != x[0:2]: + added_cost = field[x[0], x[1]] + Manhattan_dis(x[0:2], end) + else: + added_cost = 0 + h.heappush(open, (now[0] + added_cost, x)) + checked.add(x) + + white = (255, 255, 255) #setting caption and icon @@ -42,13 +88,13 @@ tiles_types = { SHORT: pygame.image.load('spritesNtiles/shortGrass64.png'), TALL: pygame.image.load('spritesNtiles/tallGrass64.png'), TREE: pygame.image.load('spritesNtiles/dirtTree64.png'), - FLOWER: pygame.image.load('spritesNtiles/input.png') + FLOWER: pygame.image.load('spritesNtiles/flower64.png') } -tilemapSizeX = 25 -tilemapSizeY = 15 +tilemapSizeX = 12 +tilemapSizeY = 12 tileSize = 64 tilemap_types = [] @@ -63,17 +109,12 @@ for x in range(tilemapSizeX): #example tilemap values tilemap_values = [] -SHORT_VALUE = 5 +SHORT_VALUE = 1 TALL_VALUE = 100 -TREE_VALUE = 50 +TREE_VALUE = 200 +FLOWER_VALUE = 1 + -for X in range(0, len(tilemap_types)): - tilemap_values.append([]) - for Y in range(0, len(tilemap_types[X])): - if tilemap_types[X][Y] == SHORT: value = SHORT_VALUE - elif tilemap_types[X][Y] == TALL: value = TALL_VALUE - elif tilemap_types[X][Y] == TREE: value = TREE_VALUE - tilemap_values[X].append(value) ### @@ -90,52 +131,43 @@ dis = pygame.display.set_mode((disX, disY)) clock = pygame.time.Clock() #position of the bee and pos changings -bee_x = 2 -bee_y = 1 -bee_dir = 'west' + +# Define parameters +flower_positions = [(flower_x, flower_y) for flower_x in range(tilemapSizeX) for flower_y in range(tilemapSizeY) if tilemap_types[flower_x][flower_y] == FLOWER] +generations = 10 +# Create an instance of the GeneticAlgorithm class +ga = GA(10, generations,flower_positions, tilemap_types) + +# Run the genetic algorithm +best_individual = ga.run() +print(best_individual) + +# Retrieve the best tree arrangement from the best individual +tree_arrangement = best_individual + +# Render the tilemap with the optimized tree positions +for x, y in tree_arrangement: + tilemap_types[x][y] = TREE + print(x,y) + +for X in range(0, len(tilemap_types)): + tilemap_values.append([]) + for Y in range(0, len(tilemap_types[X])): + if tilemap_types[X][Y] == SHORT: value = SHORT_VALUE + elif tilemap_types[X][Y] == TALL: value = TALL_VALUE + elif tilemap_types[X][Y] == TREE: value = TREE_VALUE + elif tilemap_types[X][Y] == FLOWER: value = FLOWER_VALUE + tilemap_values[X].append(value) field = Field(tilemap_types, tilemap_values) -def Manhattan_dis(start, end): - start_x, start_y = start - end_x, end_y = end - return abs(start_x - end_x) + abs(start_y - end_y) - -def Make_path(start, end, previos): - path = [end] - while start not in path: - path.append(previos[path[-1]]) - path.reverse() - return path - -def A_star(field, start, end): - open = [] - previous = {} - closed = set() - checked = set() - now = (0, start) - h.heappush(open, (0, start)) - while len(open) > 0: - now = h.heappop(open) - if now[1] in closed: - continue - if now[1][0:2] == end: - - path = Make_path(start, now[1], previous) - return path - closed.add(now[1]) - checked.add(now[1][0:2]) - for x in Neighbors(field, now[1]): - if x not in closed and x not in checked: - previous[x] = now[1] - if now[1][0:2] != x[0:2]: - added_cost = field[x[0], x[1]] + Manhattan_dis(x[0:2], end) - else : added_cost = 0 - h.heappush(open, (now[0] + added_cost, x)) - checked.add(x) - - +bee_dir = 'west' +while True: + bee_x = random.randint(0, tilemapSizeX-2) + bee_y = random.randint(0, tilemapSizeY-2) + if(field.get_type([bee_x, bee_y]) != TREE): + break while True: @@ -154,6 +186,7 @@ for x in path_A_star: if x[0:2] not in check_path_A: check_path_A.append(x[0:2]) print(check_path_A) +print(len(check_path_A)) path = path_A_star @@ -198,6 +231,3 @@ while True: if(step == -1 and current_node == 0): step *= -1 current_node += step - - -## setitem, bfs \ No newline at end of file