diff --git a/GeneticAlgorithm.py b/GeneticAlgorithm.py new file mode 100644 index 0000000..3352824 --- /dev/null +++ b/GeneticAlgorithm.py @@ -0,0 +1,208 @@ +import copy +import json +import random +from displayControler import NUM_X, NUM_Y + +# Definiowanie stałych dla roślin i plonów +plants = ['corn', 'potato', 'tomato', 'carrot'] +initial_yields = {'corn': 38, 'potato': 40, 'tomato': 43, 'carrot': 45} +yield_reduction = { + 'corn': {'corn': -4.5, 'potato': -3, 'tomato': -7, 'carrot': -7}, + 'potato': {'corn': -7, 'potato': -5, 'tomato': -10, 'carrot': -6}, + 'tomato': {'corn': -4, 'potato': -5, 'tomato': -7, 'carrot': -7}, + 'carrot': {'corn': -11, 'potato': -5, 'tomato': -4, 'carrot': -7} +} +yield_multiplier = {'corn': 1.25, 'potato': 1.17, 'tomato': 1.22, 'carrot': 1.13} + + +# Generowanie listy 20x12 z losowo rozmieszczonymi roślinami +def generate_garden(rows=20, cols=12): + return [[random.choice(plants) for _ in range(cols)] for _ in range(rows)] + + +# Funkcja do obliczania liczby plonów +def calculate_yields(garden): + rows = len(garden) + cols = len(garden[0]) + + total_yields = 0 + + for i in range(rows): + for j in range(cols): + plant = garden[i][j] + yield_count = initial_yields[plant] + + # Sprawdzanie sąsiadów + neighbors = [ + (i - 1, j), (i + 1, j), (i, j - 1), (i, j + 1) + ] + + for ni, nj in neighbors: + if 0 <= ni < rows and 0 <= nj < cols: + neighbor_plant = garden[ni][nj] + yield_count += yield_reduction[plant][neighbor_plant] + + yield_count *= yield_multiplier[plant] + total_yields += yield_count + + return total_yields + + +# Funkcja do generowania planszy/ogrodu i zapisywania go jako lista z liczbą plonów +def generate_garden_with_yields(rows=NUM_Y, cols=NUM_X): + garden = generate_garden(rows, cols) + total_yields = calculate_yields(garden) + return [garden, total_yields] + + +# Funkcja do generowania linii cięcia i zapisywania jej jako liczba roślin w kolumnie z pierwszej planszy/ogrodu +def line(): + path = [] + flag = False + x = random.randint(4, 8) + position = (0, x) + path.append(position) + while not flag: # wybór punktu dopóki nie wybierze się skrajnego + # prawdopodobieństwo "ruchu" -> 0.6: w prawo, 0.2: w góre, 0.2: w dół + p = [(position[0] + 1, position[1]), (position[0], position[1] + 1), (position[0], position[1] - 1)] + w = [0.6, 0.2, 0.2] + position2 = random.choices(p, w)[0] + if position2 not in path: # sprawdzenie czy dany punkt nie był już wybrany aby nie zapętlać się + path.append(position2) + position = position2 + if position[0] == NUM_X or position[1] == 0 or position[1] == NUM_Y: # sprawdzenie czy osiągnięto skrajny punkt + flag = True + info = [] # przeformatowanie sposobu zapisu na liczbę roślin w kolumnie, które będzię się dzidziczyło z pierwszej planszy/ogrodu + for i in range(len(path) - 1): + if path[i + 1][0] - path[i][0] == 1: + info.append(NUM_Y - path[i][1]) + if len(info) < NUM_X: # uzupełnienie informacji o dziedziczeniu z planszy/ogrodu + if path[-1:][0][1] == 0: + x = NUM_Y + else: + x = 0 + while len(info) < NUM_X: + info.append(x) + # return path, info + return info + + +# Funkcja do generowania potomstwa +def divide_gardens(garden1, garden2): + info = line() + new_garden1 = [[] for _ in range(NUM_Y)] + new_garden2 = [[] for _ in range(NUM_Y)] + for i in range(NUM_X): + for j in range(NUM_Y): + # do utworzonych kolumn w nowych planszach/ogrodach dodajemy dziedziczone rośliny + if j < info[i]: + new_garden1[j].append(garden1[j][i]) + new_garden2[j].append(garden2[j][i]) + else: + new_garden1[j].append(garden2[j][i]) + new_garden2[j].append(garden1[j][i]) + + return [new_garden1, calculate_yields(new_garden1)], [new_garden2, calculate_yields(new_garden2)] + + +# Funkcja do mutacji danej planszy/ogrodu +def mutation(garden, not_used): + new_garden = copy.deepcopy(garden) + for i in range(NUM_X): + x = random.randint(0, 11) # wybieramy, w którym wierszu w i-tej kolumnie zmieniamy roślinę na inną + other_plants = [plant for plant in plants if plant != new_garden[x][i]] + new_garden[x][i] = random.choice(other_plants) + return [new_garden, calculate_yields(new_garden)] + + +# Funkcja do generowania pierwszego pokolenia +def generate(n): + generation = [] + for i in range(n * 3): + generation.append(generate_garden_with_yields()) + generation.sort(reverse=True, key=lambda x: x[1]) + return generation[:n] + + +# Funkcja do implementacji ruletki (sposobu wyboru) - sumuje wszystkie plony generacji +def sum_yields(x): + s = 0 + for i in range(len(x)): + s += x[i][1] + return s + + +if __name__ == '__main__': + roulette = True + attemps = 150 + iterat = 2500 + population = 100 + best = [] + for a in range(attemps): + generation = generate(population) + print(generation[0][1]) + for i in range(iterat): # ile iteracji - nowych pokoleń + print(a, i) + new_generation = generation[:(population // 7)] # dziedziczenie x najlepszych osobników + j = 0 + while j < ( + population - ( + population // 7)): # dobór reszty osobników do pełnej liczby populacji danego pokolenia + if roulette: # zasada ruletki -> "2 rzuty kulką" + s = sum_yields(generation) # suma wszystkich plnów całego pokolenia + z = [] + if s == 0: # wtedy każdy osobnik ma takie same szanse + z.append(random.randint(0, population - 1)) + z.append(random.randint(0, population - 1)) + else: + weights = [] # wagi prawdopodobieństwa dla każdego osobnika generacji + pos = [] # numery od 0 do 49 odpowiadające numerom osobnikom w generacji + for i in range(population): + weights.append(generation[i][1] / s) + pos.append(i) + z.append(random.choices(pos, weights)[0]) # wybranie osobnika według wag prawdopodobieństwa + z.append(random.choices(pos, weights)[0]) # wybranie osobnika według wag prawdopodobieństwa + else: # metoda rankingu + z = random.sample(range(0, int(population // 1.7)), 2) + + # krzyzowanie 90% szans, mutacja 10% szans + function = [divide_gardens, mutation] + weight = [0.9, 0.1] + fun = random.choices(function, weight)[0] + h = fun(generation[z[0]][0], generation[z[1]][0]) + if len(h[0]) == 2: + new_generation.append(h[0]) + new_generation.append(h[1]) + j += 2 + else: + new_generation.append(h) + j += 1 + + new_generation.sort(reverse=True, key=lambda x: x[1]) # sortowanie malejąco listy według wartości plonów + generation = new_generation[:population] + + best.append(generation[0]) + + best.sort(reverse=True, key=lambda x: x[1]) + + # Zapis do pliku + # for i in range(len(best)): + # print(best[i][1], calculate_yields(best[i][0])) + # + # + # with open(f'pole_pop{population}_iter{iterat}_{roulette}.json', 'w') as file: # zapis planszy/ogrodu do pliku json + # json.dump(best[0][0], file, indent=4) + # + # print("Dane zapisane do pliku") + + # Odczyt z pliku + # with open(f'pole_pop{population}_iter{iterat}_{roulette}.json', 'r') as file: + # garden_data = json.load(file) + # + # print("Odczytane dane ogrodu:") + # for row in garden_data: + # print(row) + # + # print(calculate_yields(garden_data)) + # if best[0][0] == garden_data: + # print("POPRAWNE: ", calculate_yields(garden_data), calculate_yields(best[0][0])) diff --git a/pole_pop120_iter2500_True.json b/pole_pop120_iter2500_True.json new file mode 100644 index 0000000..62b9994 --- /dev/null +++ b/pole_pop120_iter2500_True.json @@ -0,0 +1,266 @@ +[ + [ + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "corn", + "corn", + "corn", + "corn", + "corn", + "corn", + "corn", + "tomato", + "carrot", + "tomato", + "carrot", + "potato", + "potato", + "carrot", + "potato" + ], + [ + "carrot", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "corn", + "corn", + "corn", + "corn", + "corn", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "carrot", + "potato", + "potato", + "potato" + ], + [ + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "corn", + "corn", + "corn", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "carrot", + "potato", + "carrot" + ], + [ + "carrot", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "corn", + "corn", + "corn", + "corn", + "corn", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato" + ], + [ + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "corn", + "corn", + "corn", + "corn", + "corn", + "tomato", + "corn", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "carrot" + ], + [ + "corn", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "corn", + "corn", + "corn", + "corn", + "corn", + "corn", + "corn", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato" + ], + [ + "corn", + "corn", + "tomato", + "corn", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "corn", + "corn", + "corn", + "corn", + "corn", + "potato", + "carrot", + "tomato", + "carrot", + "tomato", + "carrot" + ], + [ + "tomato", + "corn", + "corn", + "corn", + "corn", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "corn", + "corn", + "corn", + "corn", + "corn", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato" + ], + [ + "carrot", + "potato", + "corn", + "corn", + "corn", + "corn", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "corn", + "corn", + "corn", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "carrot" + ], + [ + "potato", + "potato", + "corn", + "corn", + "corn", + "corn", + "corn", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "corn", + "corn", + "corn", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato" + ], + [ + "potato", + "carrot", + "tomato", + "corn", + "tomato", + "corn", + "corn", + "corn", + "tomato", + "carrot", + "tomato", + "corn", + "corn", + "corn", + "corn", + "tomato", + "carrot", + "carrot", + "tomato", + "carrot" + ], + [ + "carrot", + "tomato", + "carrot", + "tomato", + "carrot", + "tomato", + "corn", + "tomato", + "carrot", + "tomato", + "corn", + "corn", + "tomato", + "corn", + "tomato", + "carrot", + "tomato", + "tomato", + "carrot", + "tomato" + ] +] \ No newline at end of file