# *Sztuczna inteligencja - projekt zespołowy - Autonomiczny Traktor* #### autorzy: Aleksandra Werda, Natalia Wiśniewska, Kinga Jagodzińska, Aleksandra Jonas *** ## Planowanie ruchu - schemat procedury przeszukiwania grafu stanów z uwzględnieniem kosztu - strategia A* ___ Drugim zadaniem dotyczącym projektu jest zastosowanie strategii przeszukiwania przestrzeni stanów do problemu planowania ruchu agenta na kracie. ## Heurystyka - założenie — odległość pomiędzy sąsiadującymi polami wynosi 2, tyle samo co koszt wjazdu na puste pole - s — pole, na którym jesteśmy - f — pole końcowe - dla s = f program kończy pracę, bo znajdujemy się już w położeniu końcowym, dlatego pominęłyśmy ten przypadek w definicji heurystyki. ``` def hscore(self, s, f): if f > s: a_h = (f - s) // 5 else: a_h = (s - f) // 5 if f % 5 >= s % 5: b_h = f % 5 - s % 5 else: b_h = s % 5 - f % 5 + 1 return 2 * m.sqrt(a_h * 2 + b_h * 2) ``` Wpierw obliczamy wysokość trójkąta, jaki tworzą obecne i końcowe pole, w celu wyznaczenia przeciwprostokątnej — odległość między s i f. ## Funkcja następnika Tworzymy listę, do której będziemy kolejno generować sąsiadów dla danego x. ``` def neighbours(self): self.neighbours = list(range(25)) self.neighbours[0] = [1, 5] self.neighbours[4] = [3, 9] self.neighbours[20] = [15, 21] self.neighbours[24] = [19, 23] for x in range(1, 4): self.neighbours[x] = [x - 1, x + 5, x + 1] for x in range(5, 16, 5): self.neighbours[x] = [x - 5, x + 1, x + 5] for x in range(9, 20, 5): self.neighbours[x] = [x - 5, x - 1, x + 5] for x in range(21, 24): self.neighbours[x] = [x - 1, x - 5, x + 1] for x in [6, 7, 8, 11, 12, 13, 16, 17, 18]: self.neighbours[x] = [x - 5, x - 1, x + 1, x + 5] ``` ## Główna pętla strategii przeszukiwania Zaczynamy od znalezienia w open_set pola o najniższym f. ``` while open_set: # Szukanie pola w open_set z najniższym f temp1 = max(self.f_score) + 1 x = 0 for i in range(len(open_set)): if self.f_score[open_set[i]] <= temp1: x = open_set[i] temp1 = self.f_score[open_set[i]] ``` Jeżeli to pole odpowiada temu, do którego chcemy dojść — wywołujemy funkcję tworzącą ścieżkę z pola wyjściowego. Póki nie — pomijamy. ``` if x == koniec: return self.reconstruct_path(self.came_from, koniec) ``` Usuwamy x z open_set i wrzucamy do closed_set, aby upewnić się, że nie będziemy go ponownie sprawdzać. ``` open_set.remove(x) closed_set.append(x) ``` Następnie sprawdzamy sąsiadów: ``` for y in self.neighbours[x]: ``` To tutaj chcieliśmy mieć pewność, że nie trafimy ponownie na x. ``` if y in closed_set: continue ``` Chcemy również sprawdzić, czy jeżeli y jest już w open_set, to nie istnieje krótsze przejście np. z pola startowego przez inne pole na y, niż bezpośrednio na pole y. ``` tentative_g_score = self.g_score[x] + self.game.fields[y][3] if y not in open_set: open_set.append(y) tentative_is_better = True elif tentative_g_score < self.g_score[y]: tentative_is_better = True ``` Wyliczamy g i f — przewidywany dystans od startu do celu, dla y. ``` if tentative_is_better == True: self.came_from[y] = x self.g_score[y] = tentative_g_score self.f_score[y] = self.g_score[y] + self.hscore(y, koniec) ``` ## Tworzenie ścieżki Przechodzimy po tablicy poprzedników, aż dojdziemy do początku — pola, które nie ma poprzednika. ``` def reconstruct_path(self, came_from, current): total_path = [current] while came_from[current] != 0: current = came_from[current] total_path.insert(0, current) return total_path ``` ## Koszt podróży na pola uprawne Zmiany w projekcie zaszły również w pliku run.py, gdzie generujemy losowo pola — każdy kolor odpowiada innej uprawie i etapie jej wzrostu. Mamy tutaj tablicę tablic. Każda z nich zawiera kolejno informacje o: rodzaju upraw, glebie, nawodnieniu pola oraz koszcie podróży na pole z daną uprawą. ``` def randomize_field(self): for x in range(25): temp = [] # nasiona temp.append(random.choice(["żyto", "jęczmień", "owies", "marchew", "rzodkiew", "pietruszka", "puste"])) # gleba temp.append(random.choice([True, False])) # woda temp.append(random.choice([True, False])) # # growth rate # temp.append(random) # # cost if temp[0] == "żyto": temp.append(10) elif temp[0] == "jęczmień": temp.append(12) elif temp[0] == "owies": temp.append(8) elif temp[0] == "marchew": temp.append(14) elif temp[0] == "rzodkiew": temp.append(7) elif temp[0] == "pietruszka": temp.append(6) elif temp[0] == "puste": temp.append(2) else: temp.append(0) self.fields.append(temp) ```