SzIProjekt/Kinga Jagodzińska_Algorytm genetyczny.md

134 lines
6.1 KiB
Markdown

# *Sztuczna inteligencja - projekt zespołowy - Autonomiczny Traktor*
#### autorzy: Aleksandra Werda, Natalia Wiśniewska, Kinga Jagodzińska, Aleksandra Jonas
***
## Kinga Jagodzińska - podprojekt: Sadzenie przy użyciu algorytmu genetycznego
___
Zadaniem tego podprojektu było dobranie nasion do zasadzenia na pustych polach przy użyciu algorytmu genetycznego, tak aby bazując na ich dopasowaniu do sadzonek na sąsiednich polach, wybór był jak najbardziej optymalny.
Najpierw używamy stworzonej funkcji *empty()* do przeliczenia ilości pustych pól na planszy (*length*) oraz ich położenia na planszy (*index_gen*). Jest to konieczne, ponieważ rodzaje sadzonek na polach w programie są dobierane losowo i liczba pustych pól przy każdym uruchomieniu programu się zmienia.
```
def empty(self):
for x in range(25):
if self.game.fields[x] == "puste":
self.length = self.length + 1
self.index_gen.append(x)
```
Następnie tworzymy pierwszą generację rodziców. Każdy rodzic jest tablicą o długości równej liczbie pustych pól i do tej tablicy losowo zostają przydzielone sadzonki. Każdego nowo wygenerowanego rodzica dodajemy do tablicy tablic *dna*.
```
def dna_create(self):
self.dna = []
for x in range(self.max_gen):
temp = []
for y in range(self.length):
temp.append(random.choice(["żyto", "jęczmień", "owies", "marchew", "rzodkiew", "pietruszka"]))
self.dna.append(temp)
```
W funkcji *algorytm()* tworzymy tablicę *l*, która będzie zawierać sumę dopasowania dobranych sadzonek do sąsiednich pól. Definiujemy zmienną *first* jako True. Reszta funkcji znajduje się w pętli *while*. Jeśli w tablicy tablic *dna* mamy pierwszą generację rodziców, dodajemy do tablicy *l* odpowiadający im wynik używając funkcji *l_score*. Jeśli jest to późniejsza generacja do tablicy dodajemy tylko potomstwo, którego wynik jeszcze nie został dodany.
```
def algorytm(self):
l = []
first = True
self.empty()
self.dna_create()
while True:
if first == True:
for x in range(len(self.dna)):
l.append(self.l_score(self.dna[x]))
first = False
elif first == False:
for x in range(int(self.max_gen * 1 / 3)):
l.append(self.l_score(self.dna[x + int(self.max_gen * 2 / 3)]))
```
Jeśli algorytm odnajdzie możliwie najbardziej optymalną możliwość lub przejdziemy przez 200 generacji, algorytm się kończy, zwracając nam odpowiadającą tablicę z tablicy *dna*.
```
if max(l) >= self.length or self.pokolenie > 200:
return self.dna[l.index(max(l))]
```
Następnie pozbywamy się 1/3 wszystkich tablic z tablicy *dna* pozostawiając te, które mają najwyższy odpowiadający im wynik w *l*.
```
for x in range(int(self.max_gen * 1 / 3)):
del self.dna[l.index(min(l))]
l.remove(min(l))
```
Dla każdej pary rodziców w *dna* tworzymy potomstwo, którego elementy są dobierane naprzemiennie.
```
for x in range(int(self.max_gen * 1 / 3)):
temp = []
for y in range(self.length):
if y % 2 == 0:
temp.append(self.dna[x][y])
if y % 2 == 1:
temp.append(self.dna[int(self.max_gen * 2 / 3) - 1 - x][y])
```
Każdy potomek ma 40% szansy na zmianę maksymalnie trzech elementów.
```
if random.randint(0, 100) <= 40:
for x in range(3):
temp[random.randint(0, self.length - 1)] = random.choice(["żyto", "jęczmień", "owies", "marchew", "rzodkiew", "pietruszka"])
```
Następnie dodajemy potomka do tablicy tablic *dna* oraz zwiększamy numer kolejnego pokolenia.
```
self.dna.append(temp)
self.pokolenie = self.pokolenie + 1
```
Wcześniej wspomniana funkcja l_score sumuje iloczyny dopasowania wybranego rodzaju nasion do posadzenia na wybranym polu do rodzaju sadzonek na sąsiednich polach, także tych, które mają dopiero zostać zasadzone. Dopasowanie pomiędzy konkretnymi roślinami zostało ustalone odgórnie.
```
def l_score(self, tab):
suma1 = 0
for x in range(len(tab)):
suma = 1
for y in self.game.neighbours[self.index_gen[x]]:
if y in self.index_gen:
som = tab[self.index_gen.index(y)]
else:
som = self.game.fields[y]
if tab[x] == "żyto":
if som == "żyto":
suma = suma * 0.5
...
```
Funkcję *algorytm()* wywołujemy w *init()* znajdującym się w *tractor. py*, by później wykorzystać ją w sadzeniu podczas poruszania się traktorem po planszy.
```
self.sadzonka = self.gen.algorytm()
```
if pygame.key.get_pressed()[pygame.K_SPACE]:
pole = int(self.pos.y // 144 * 5 + self.pos.x // 144)
if pole in self.gen.index_gen and pole not in self.road:
self.game.fields[pole] = self.sadzonka[self.gen.index_gen.index(pole)]
if len(self.road) != 0:
if self.road[0] == pole + 1:
self.pos.x = self.pos.x + 144
elif self.road[0] == pole - 1:
self.pos.x = self.pos.x - 144
elif self.road[0] == pole + 5:
self.pos.y = self.pos.y + 144
elif self.road[0] == pole - 5:
self.pos.y = self.pos.y - 144
self.road.pop(0)
Dodatkowo została stworzona funkcja *best_path()*, która wybiera najlepszą drogę pomiędzy pustymi polami zwracając szczególną uwagę na kolejność przejścia.
```
def best_path(self):
best = 999999
best_route = []
for x in permutations(self.gen.index_gen, self.gen.length):
# punkt początkowy
start = 0
droga = [0]
dlug = 0
for y in x:
temp1 = self.algo(start, y)
if temp1[0] == droga[len(droga)-1]:
del temp1[0]
droga.extend(temp1)
start = y
for z in temp1:
dlug = dlug + self.f_score[z]
if dlug < best:
best_route = droga
best = dlug
return best_route