Traktor/gen_algorithm.py

187 lines
7.2 KiB
Python
Raw Normal View History

2024-06-07 13:03:38 +02:00
import numpy as np
import random
import pygame
from constant import width, height, size, rows, cols
from board import Board
from tractor import Tractor
routes_num = 20 # Ilość ścieżek
board = Board()
2024-06-10 15:35:08 +02:00
weed_positions = board.get_weed_positions()
weed_count = len(weed_positions)
2024-06-07 13:03:38 +02:00
def manhattan(a, b):
return abs(a[0] - b[0]) + abs(a[1] - b[1])
def find_routes(routes_num):
population_set = [] # zapisujemy trasy - losowe ułóżenia
for i in range(routes_num):
# losowo wygenerowane kolejności na trasie
2024-06-10 15:35:08 +02:00
single_route = np.random.choice(list(range(weed_count)), weed_count, replace=False)
2024-06-07 13:03:38 +02:00
population_set.append(single_route)
2024-06-10 15:35:08 +02:00
return np.array(population_set) #zwracamy 20 roznych losowych tras
2024-06-07 13:03:38 +02:00
def sum_up_for_route(route_indices):
2024-06-10 15:41:01 +02:00
2024-06-07 13:03:38 +02:00
sum = 0
for i in range(len(route_indices) - 1):
2024-06-10 15:35:08 +02:00
current_weed = weed_positions[route_indices[i]]
next_weed = weed_positions[route_indices[i + 1]]
sum += manhattan(current_weed, next_weed)
return sum #zwracamy odleglosc (ilosc pol) dla danej trasy manhatanem
2024-06-07 13:03:38 +02:00
2024-06-10 15:35:08 +02:00
def routes_sum(population_set): # zapisujemy na liście finalne sumy odległości dla każdej z tras
2024-06-07 13:03:38 +02:00
list_of_sums = np.zeros(routes_num)
for i in range(routes_num):
list_of_sums[i] = sum_up_for_route(population_set[i]) # wywołujemy dla każdej trasy na liście
return list_of_sums
def calculate_fitness(distances):
2024-06-10 15:35:08 +02:00
# odwrotność odległości jako fitness
# dodajemy małą wartość (np. 1) aby uniknąć dzielenia przez zero
2024-06-07 13:03:38 +02:00
return 1 / (distances + 1)
def selection(population_set, list_of_sums):
2024-06-10 15:35:08 +02:00
#RULETKA - czesciowo faworyzuje rozwiaznaia, wiekszy fitness wieksze szanse
# obliczamy wartości fitness (przystosowania) dla każdej trasy
fitness_values = calculate_fitness(list_of_sums)#krotsze trasy maja miec wyzsze wartosci
# normalizujemy wartości fitness, aby sumowały się do 1 (wymagane dla np.random.choice)
2024-06-07 13:03:38 +02:00
probabilities = fitness_values / fitness_values.sum()
2024-06-10 15:35:08 +02:00
# wybieramy indeksy rodziców na podstawie prawdopodobieństw
2024-06-07 13:03:38 +02:00
progenitor_indices_a = np.random.choice(range(len(population_set)), len(population_set), p=probabilities, replace=True)
progenitor_indices_b = np.random.choice(range(len(population_set)), len(population_set), p=probabilities, replace=True)
2024-06-10 15:35:08 +02:00
# finalne trasy
2024-06-07 13:03:38 +02:00
progenitor_a = population_set[progenitor_indices_a]
progenitor_b = population_set[progenitor_indices_b]
2024-06-10 15:35:08 +02:00
return np.array([progenitor_a, progenitor_b]) #zwracami listy przodkow-rodzicow
2024-06-07 13:03:38 +02:00
def one_point_crossover(parent_a, parent_b): #krzyzowanie jednopunktowe
crossover_point = np.random.randint(1, len(parent_a))
child = np.concatenate((parent_a[:crossover_point], [x for x in parent_b if x not in parent_a[:crossover_point]]))
2024-06-10 15:35:08 +02:00
return child #loosyw punkt przeciecia ktory skleja nam nowa trase, wieksza szans na lepsza tarse
2024-06-07 13:03:38 +02:00
def population_mating(progenitor_list):
new_population_set = []
for i in range(len(progenitor_list[0])):
progenitor_a, progenitor_b = progenitor_list[0][i], progenitor_list[1][i]
child = one_point_crossover(progenitor_a, progenitor_b)
new_population_set.append(child)
2024-06-10 15:35:08 +02:00
return new_population_set # lista potomkow po krzyzowaniu
2024-06-07 13:03:38 +02:00
2024-06-10 15:35:08 +02:00
def mutation_of_child(child, mutation_rate=0.2):#procent moze pomoc w niezaklucaniu trasy gdy jesy duza trasa ale idk
2024-06-07 13:03:38 +02:00
num_mutations = int(len(child) * mutation_rate)
for _ in range(num_mutations):
2024-06-10 15:35:08 +02:00
x = np.random.randint(0, len(child))#losowa szansa zamiany - mutacja
2024-06-07 13:03:38 +02:00
y = np.random.randint(0, len(child))
child[x], child[y] = child[y], child[x]
2024-06-10 15:35:08 +02:00
return child#zwrocenie bardziej roznorodnych potomkow
2024-06-07 13:03:38 +02:00
def mutate_population(new_population_set):
final_mutated_population = []
for child in new_population_set:
final_mutated_population.append(mutation_of_child(child)) # dodajemy zmutowane dziecko do finalnej listy
return final_mutated_population
if __name__ == '__main__':
2024-06-10 15:35:08 +02:00
pygame.init()
WIN = pygame.display.set_mode((width, height))
pygame.display.set_caption('Trasa Traktora')
clock = pygame.time.Clock()
board = Board()
board.load_images()
weed_positions = [(col, row) for col in range(cols) for row in range(rows) if board.is_weed(col, row)]
weed_count = len(weed_positions)
board.set_grass(9, 9) # pozycja startowa
tractor = Tractor(9, 9) # Start traktora
# Inicjalizacja final_route
final_route = [0, float('inf'), np.array([])]
# [0]: indeks iteracji, [1]: najlepsza suma odległości, [2]: najlepsza trasa
2024-06-07 13:03:38 +02:00
population_set = find_routes(routes_num)
list_of_sums = routes_sum(population_set)
progenitor_list = selection(population_set, list_of_sums)
new_population_set = population_mating(progenitor_list)
final_mutated_population = mutate_population(new_population_set)
final_route = [-1, np.inf, np.array([])] # format listy
2024-06-10 15:35:08 +02:00
2024-06-07 13:03:38 +02:00
for i in range(20):
list_of_sums = routes_sum(final_mutated_population)
# zapisujemy najlepsze rozwiązanie
if list_of_sums.min() < final_route[1]:
final_route[0] = i
final_route[1] = list_of_sums.min()
final_route[2] = np.array(final_mutated_population)[list_of_sums.min() == list_of_sums]
progenitor_list = selection(population_set, list_of_sums)
new_population_set = population_mating(progenitor_list)
final_mutated_population = mutate_population(new_population_set)
print(f"Najlepsza trasa znaleziona w iteracji: {final_route[0]}")
print(f"Minimalna suma odległości: {final_route[1]}")
2024-06-10 15:35:08 +02:00
run = True
current_target_index = 0
best_routes = final_route[2] #tablica z najlepszymi trasami
visited_fields = []
while run:
clock.tick(2) # FPS
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
for route in best_routes:
if current_target_index < len(route):
current_weed = weed_positions[route[current_target_index]]
# ruch w kierunku bieżącego celu
if tractor.col < current_weed[0]:
tractor.col += 1
tractor.direction = "right"
elif tractor.col > current_weed[0]:
tractor.col -= 1
tractor.direction = "left"
elif tractor.row < current_weed[1]:
tractor.row += 1
tractor.direction = "down"
elif tractor.row > current_weed[1]:
tractor.row -= 1
tractor.direction = "up"
current_position = (tractor.col, tractor.row)
if current_position not in visited_fields:
visited_fields.append(current_position)
# Jeśli traktor dotarł do celu
if (tractor.col, tractor.row) == current_weed:
current_target_index += 1
# Aktualizacja planszy
if board.is_weed(tractor.col, tractor.row):
board.set_carrot(tractor.col, tractor.row)
board.draw_cubes(WIN)
tractor.draw(WIN)
pygame.display.update()
print("Odwiedzone pola:")
for field in visited_fields:
print(field)
pygame.quit()