Compare commits

...

5 Commits

Author SHA1 Message Date
8204825a97 fix gen alg 2024-06-10 15:47:10 +02:00
4b828f878b fix gen alg 2024-06-10 15:41:01 +02:00
084e96ba7d fix gen alg 2024-06-10 15:35:08 +02:00
e56854690c Merge branch 'refs/heads/master' into genetical-algorithm 2024-06-08 12:55:03 +02:00
0c5532ac0d astar fix 2024-06-08 12:41:46 +02:00
3 changed files with 224 additions and 162 deletions

207
astar.py
View File

@ -3,124 +3,108 @@ from board import Board
from constant import width, height, rows, cols from constant import width, height, rows, cols
from tractor import Tractor from tractor import Tractor
import heapq import heapq
import math
fps = 2 fps = 2
WIN = pygame.display.set_mode((width, height)) WIN = pygame.display.set_mode((width, height))
pygame.display.set_caption('Inteligenty Traktor') pygame.display.set_caption('Inteligenty Traktor')
class Node: class Node:
def __init__(self, x, y): def __init__(self, state, parent=None, action=None, cost=0):
self.x = x self.state = state # Stan reprezentowany przez węzeł
self.y = y self.parent = parent # Węzeł rodzica
self.f = 0 self.action = action # Akcja prowadząca do tego stanu
self.g = 0 self.cost = cost # Koszt przejścia do tego stanu
self.h = 0 self.f = 0 # Wartość funkcji priorytetowej
self.cost = 1 self.tie_breaker = 0 # Wartość używana do rozwiązywania konfliktów priorytetów
self.visited = False
self.closed = False
self.parent = None
def __lt__(self, other): def __lt__(self, other):
# Porównanie węzłów w celu ustalenia kolejności w kolejce priorytetowej
if self.f == other.f:
return self.tie_breaker > other.tie_breaker # Większy tie_breaker ma wyższy priorytet
return self.f < other.f return self.f < other.f
# Jesli pare wezlow ma taie same f, to tilebreaker ustawia
def neighbors(self, grid): # prorytety akcje right i down maja wyzszy priorytet
ret = []
x, y = self.x, self.y
if x > 0 and grid[x - 1][y]:
ret.append(grid[x - 1][y])
if x < len(grid) - 1 and grid[x + 1][y]:
ret.append(grid[x + 1][y])
if y > 0 and grid[x][y - 1]:
ret.append(grid[x][y - 1])
if y < len(grid[0]) - 1 and grid[x][y + 1]:
ret.append(grid[x][y + 1])
return ret
def init(grid):
for x in range(len(grid)):
for y in range(len(grid[x])):
node = grid[x][y]
node.f = 0
node.g = 0
node.h = 0
node.cost = 1
node.visited = False
node.closed = False
node.parent = None
def heap():
return []
def search(grid, start, end, board, heuristic=None):
init(grid)
if heuristic is None:
heuristic = manhattan
open_heap = heap()
heapq.heappush(open_heap, start)
while open_heap:
current_node = heapq.heappop(open_heap)
if (current_node.x, current_node.y) == (end.x, end.y):
ret = []
while current_node.parent:
ret.append(current_node)
current_node = current_node.parent
ret.append(start)
ret_path = ret[::-1]
for node in ret_path:
print(f"({node.x}, {node.y}): {node.g}")
print("Znaleziono ścieżkę [(x,y)jako(kolumna,wiersz)] o koszcie:", ret_path[-1].g)
return ret_path, start
current_node.closed = True
for neighbor in current_node.neighbors(grid):
if neighbor.closed:
continue
g_score = current_node.g + board.get_cost(neighbor.x, neighbor.y)
been_visited = neighbor.visited
if not been_visited or g_score < neighbor.g:
neighbor.visited = True
neighbor.parent = current_node
neighbor.h = neighbor.h or heuristic((neighbor.x, neighbor.y), (end.x, end.y))
neighbor.g = g_score
neighbor.f = neighbor.g + neighbor.h
if not been_visited:
heapq.heappush(open_heap, neighbor)
print("Nie znaleziono ścieżki.")
return None
def manhattan(pos0, pos1): def manhattan(pos0, pos1):
# Heurystyka odległości Manhattan
d1 = abs(pos1[0] - pos0[0]) d1 = abs(pos1[0] - pos0[0])
d2 = abs(pos1[1] - pos0[1]) d2 = abs(pos1[1] - pos0[1])
return d1 + d2 return d1 + d2
def nastepnik(state, board):
# Funkcja generująca możliwe następne stany (akcje)
x, y = state
successors = []
actions = [('right', (x+1, y), 1), ('down', (x, y+1), 1), ('up', (x, y-1), 0), ('left', (x-1, y), 0)]
for action, next_state, tie_breaker in actions:
if 0 <= next_state[0] < cols and 0 <= next_state[1] < rows:
cost = board.get_cost(next_state[0], next_state[1])
successors.append((action, next_state, cost, tie_breaker))
return successors
def goal_test(state, goal):
# Czy dany stan jest stanem docelowym
return state == goal
def graphsearch(istate, goal, board, heuristic=manhattan):
# Algorytm przeszukiwania grafu
fringe = [] # Kolejka priorytetowa przechowująca węzły do odwiedzenia
explored = set() # Zbiór odwiedzonych stanów
start_node = Node(istate)
start_node.f = heuristic(istate, goal) # Obliczenie wartości heurystycznej dla stanu początkowego
start_node.tie_breaker = 0 # Ustawienie tie_breaker dla węzła startowego,
heapq.heappush(fringe, start_node)
while fringe:
elem = heapq.heappop(fringe)
if goal_test(elem.state, goal):
path = []
total_cost = elem.cost # Zapisanie całkowitego kosztu
while elem:
path.append((elem.state, elem.action))
elem = elem.parent
return path[::-1], total_cost # Zwrócenie ścieżki i kosztu
explored.add(elem.state)
for action_index, (action, state, cost, tie_breaker) in enumerate(nastepnik(elem.state, board)):
x = Node(state, parent=elem, action=action, cost=elem.cost + cost)
x.f = x.cost + heuristic(state, goal) # Obliczenie wartości funkcji priorytetowej
x.tie_breaker = elem.tie_breaker * 4 + action_index # Obliczanie tie_breaker na podstawie akcji
if state not in explored and not any(node.state == state for node in fringe):
heapq.heappush(fringe, x)
else:
for i, node in enumerate(fringe):
if node.state == state and (node.f > x.f or (node.f == x.f and node.tie_breaker < x.tie_breaker)):
fringe[i] = x
heapq.heapify(fringe)
break
print("Nie znaleziono ścieżki.")
return None, 0 # Zwrócenie ścieżki jako None i kosztu jako 0 w przypadku braku ścieżki
def main(): def main():
run = True run = True
clock = pygame.time.Clock() clock = pygame.time.Clock()
board = Board() board = Board()
board.load_images() board.load_images()
start_row, start_col = 0,0 start_state = (9, 9) # Stan początkowy
end_row, end_col = 9,9 goal_state = (0, 0) # Stan docelowy
tractor = Tractor(start_row, start_col) tractor = Tractor(start_state[1], start_state[0])
board.set_grass(start_row, start_col) board.set_grass(start_state[0], start_state[1]) # Ustawienie startowego pola jako trawę
board.set_grass(end_row, end_col) board.set_grass(goal_state[0], goal_state[1]) # Ustawienie docelowego pola jako trawę
grid = [[Node(x, y) for y in range(rows)] for x in range(cols)] path, total_cost = graphsearch(start_state, goal_state, board)
start = grid[start_row][start_col]
end = grid[end_row][end_col]
path, start_node = search(grid, start, end, board)
while run: while run:
clock.tick(fps) clock.tick(fps)
for event in pygame.event.get(): for event in pygame.event.get():
if event.type == pygame.QUIT: if event.type == pygame.QUIT:
run = False run = False
@ -129,39 +113,38 @@ def main():
run = False run = False
continue continue
next_node = path.pop(0) if path else start_node next_state, action = path.pop(0) if path else (start_state, None)
dx = next_node.x - tractor.col print(next_state) # Wypisanie następnego stanu
dy = next_node.y - tractor.row tractor.row, tractor.col = next_state[1], next_state[0]
tractor.row, tractor.col = next_node.y, next_node.x
if action == "right":
if dx > 0:
tractor.direction = "right" tractor.direction = "right"
elif dx < 0: elif action == "left":
tractor.direction = "left" tractor.direction = "left"
elif dy > 0: elif action == "down":
tractor.direction = "down" tractor.direction = "down"
elif dy < 0: elif action == "up":
tractor.direction = "up" tractor.direction = "up"
if board.is_weed(tractor.col, tractor.row ): # Aktualizacja planszy na podstawie położenia traktora
board.set_grass(tractor.col, tractor.row ) if board.is_weed(tractor.col, tractor.row):
board.set_grass(tractor.col, tractor.row)
elif board.is_dirt(tractor.col, tractor.row ): elif board.is_dirt(tractor.col, tractor.row):
board.set_soil(tractor.col, tractor.row ) board.set_soil(tractor.col, tractor.row)
elif board.is_soil(tractor.col, tractor.row):
elif board.is_soil(tractor.col, tractor.row ): board.set_carrot(tractor.col, tractor.row)
board.set_carrot(tractor.col, tractor.row )
board.draw_cubes(WIN) board.draw_cubes(WIN)
tractor.draw(WIN) tractor.draw(WIN)
pygame.display.update() pygame.display.update()
print(f"Całkowity koszt trasy: {total_cost}")
while True: while True:
for event in pygame.event.get(): for event in pygame.event.get():
if event.type == pygame.QUIT: if event.type == pygame.QUIT:
pygame.quit() pygame.quit()
return return
main() main()

View File

@ -10,6 +10,7 @@ class Board:
self.load_costs() self.load_costs()
def load_images(self): def load_images(self):
self.grass = pygame.image.load("board/grass.png") self.grass = pygame.image.load("board/grass.png")
self.dirt = pygame.image.load("board/dirt.png") self.dirt = pygame.image.load("board/dirt.png")
@ -19,37 +20,52 @@ class Board:
self.carrot = pygame.image.load("board/carrot.png") self.carrot = pygame.image.load("board/carrot.png")
def generate_board(self): def generate_board(self):
self.board = [[random.choice([0,1,2,3,4,5,6,7,8,9]) for _ in range(rows)] for _ in range(cols)] # Najpierw wypełniamy całą planszę trawą (kod 2)
self.board = [[2 for _ in range(rows)] for _ in range(cols)]
# Losowo wybieramy 5 unikalnych pozycji dla chwastów
weed_positions = random.sample([(row, col) for row in range(rows) for col in range(cols)], 5)
# Umieszczamy chwasty na wylosowanych pozycjach
for row, col in weed_positions:
self.board[row][col] = 1 # 1 oznacza chwast
# Teraz losowo umieszczamy inne elementy, omijając pozycje chwastów
for row in range(rows):
for col in range(cols):
if (row, col) not in weed_positions:
# Losujemy typ terenu, ale pomijamy kod 1 (chwast)
self.board[row][col] = random.choice([0, 2, 3, 4, 5, 6, 7, 8, 9])
def draw_cubes(self, win): def draw_cubes(self, win):
for row in range(rows): for row in range(rows):
for col in range(cols): for col in range(cols):
cube_rect = pygame.Rect(row * size, col * size, size, size) cube_rect = pygame.Rect(row * size, col * size, size, size)
cube=self.board[row][col] cube = self.board[row][col]
if row==4 and col==4: if row == 4 and col == 4:
win.blit(self.grass, cube_rect) win.blit(self.grass, cube_rect)
elif cube == 0: elif cube == 0:
rock_scale = pygame.transform.scale(self.rock, (size, size)) rock_scale = pygame.transform.scale(self.rock, (size, size))
win.blit(self.dirt, cube_rect) win.blit(self.dirt, cube_rect)
win.blit(rock_scale, cube_rect) #win.blit(rock_scale, cube_rect)
elif cube == 1: elif cube == 1:
weed_scale = pygame.transform.scale(self.weeds, (size,size)) weed_scale = pygame.transform.scale(self.weeds, (size, size))
win.blit(self.grass, cube_rect) win.blit(self.grass, cube_rect)
win.blit(weed_scale, cube_rect) win.blit(weed_scale, cube_rect)
elif cube in(2,3,4,5): elif cube in (2, 3, 4, 5):
win.blit(self.grass, cube_rect) win.blit(self.grass, cube_rect)
elif cube == 10: elif cube == 10:
win.blit(self.soil, cube_rect) win.blit(self.soil, cube_rect)
elif cube == 11: elif cube == 11:
carrot_scale = pygame.transform.scale(self.carrot, (size,size)) carrot_scale = pygame.transform.scale(self.carrot, (size, size))
win.blit(self.carrot, cube_rect) win.blit(self.carrot, cube_rect)
win.blit(carrot_scale, cube_rect) win.blit(carrot_scale, cube_rect)
else: else:
win.blit(self.dirt, cube_rect) win.blit(self.dirt, cube_rect)
def load_costs(self): def load_costs(self):
self.costs = { self.costs = {
@ -78,6 +94,7 @@ class Board:
def is_weed(self,row,col): def is_weed(self,row,col):
return self.board[row][col] == 1 return self.board[row][col] == 1
def set_grass(self,row,col): def set_grass(self,row,col):
self.board[row][col]=2 self.board[row][col]=2
@ -93,10 +110,10 @@ class Board:
def set_carrot(self, row, col): def set_carrot(self, row, col):
self.board[row][col] = 11 self.board[row][col] = 11
def get_dirt_positions(self): def get_weed_positions(self):
dirt_positions = [] weed_positions = []
for row in range(rows): for row in range(rows):
for col in range(cols): for col in range(cols):
if self.is_dirt(row, col): if self.is_weed(row, col):
dirt_positions.append([row, col]) weed_positions.append([row, col])
return dirt_positions return weed_positions

View File

@ -5,36 +5,34 @@ from constant import width, height, size, rows, cols
from board import Board from board import Board
from tractor import Tractor from tractor import Tractor
routes_num = 20 # Ilość ścieżek routes_num = 20 # Ilość ścieżek
board = Board() board = Board()
dirt_positions = board.get_dirt_positions() weed_positions = board.get_weed_positions()
dirt_count = len(dirt_positions) weed_count = len(weed_positions)
def manhattan(a, b): def manhattan(a, b):
return abs(a[0] - b[0]) + abs(a[1] - b[1]) return abs(a[0] - b[0]) + abs(a[1] - b[1])
def find_routes(routes_num): def find_routes(routes_num):
population_set = [] # zapisujemy trasy - losowe ułóżenia population_set = [] # zapisujemy trasy - losowe ułóżenia
for i in range(routes_num): for i in range(routes_num):
# losowo wygenerowane kolejności na trasie # losowo wygenerowane kolejności na trasie
single_route = np.random.choice(list(range(dirt_count)), dirt_count, replace=False) single_route = np.random.choice(list(range(weed_count)), weed_count, replace=False)
population_set.append(single_route) population_set.append(single_route)
return np.array(population_set) return np.array(population_set) #zwracamy 20 roznych losowych tras
def sum_up_for_route(route_indices): def sum_up_for_route(route_indices):
sum = 0 sum = 0
for i in range(len(route_indices) - 1): for i in range(len(route_indices) - 1):
current_dirt = dirt_positions[route_indices[i]] current_weed = weed_positions[route_indices[i]]
next_dirt = dirt_positions[route_indices[i + 1]] next_weed = weed_positions[route_indices[i + 1]]
sum += manhattan(current_dirt, next_dirt) sum += manhattan(current_weed, next_weed)
return sum return sum #zwracamy odleglosc (ilosc pol) dla danej trasy manhatanem
def routes_sum(population_set): # zapisujemy na liście finalne sumy odległości dla każdej z tras
def routes_sum(population_set): # zapisujemy na liście finalne sumy odległości dla każdej z opcji tras
list_of_sums = np.zeros(routes_num) list_of_sums = np.zeros(routes_num)
for i in range(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 list_of_sums[i] = sum_up_for_route(population_set[i]) # wywołujemy dla każdej trasy na liście
@ -42,29 +40,30 @@ def routes_sum(population_set): # zapisujemy na liście finalne sumy odległoś
def calculate_fitness(distances): def calculate_fitness(distances):
# Odwrotność odległości jako fitness # odwrotność odległości jako fitness
# Dodajemy małą wartość (np. 1) aby uniknąć dzielenia przez zero # dodajemy małą wartość (np. 1) aby uniknąć dzielenia przez zero
return 1 / (distances + 1) return 1 / (distances + 1)
def selection(population_set, list_of_sums): def selection(population_set, list_of_sums):
# Oblicz wartości fitness dla każdej trasy #RULETKA - czesciowo faworyzuje rozwiaznaia, wiekszy fitness wieksze szanse
fitness_values = calculate_fitness(list_of_sums) # obliczamy wartości fitness (przystosowania) dla każdej trasy
# Normalizuj wartości fitness, aby sumowały się do 1 (wymagane dla np.random.choice) 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)
probabilities = fitness_values / fitness_values.sum() probabilities = fitness_values / fitness_values.sum()
# Wybierz rodziców na podstawie prawdopodobieństw (wartości fitness) # wybieramy indeksy rodziców na podstawie prawdopodobieństw
progenitor_indices_a = np.random.choice(range(len(population_set)), len(population_set), p=probabilities, replace=True) 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) progenitor_indices_b = np.random.choice(range(len(population_set)), len(population_set), p=probabilities, replace=True)
# Wybierz rzeczywiste trasy # finalne trasy
progenitor_a = population_set[progenitor_indices_a] progenitor_a = population_set[progenitor_indices_a]
progenitor_b = population_set[progenitor_indices_b] progenitor_b = population_set[progenitor_indices_b]
return np.array([progenitor_a, progenitor_b]) return np.array([progenitor_a, progenitor_b]) #zwracami listy przodkow-rodzicow
def one_point_crossover(parent_a, parent_b): #krzyzowanie jednopunktowe def one_point_crossover(parent_a, parent_b): #krzyzowanie jednopunktowe
crossover_point = np.random.randint(1, len(parent_a)) 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]])) child = np.concatenate((parent_a[:crossover_point], [x for x in parent_b if x not in parent_a[:crossover_point]]))
return child return child #loosyw punkt przeciecia ktory skleja nam nowa trase, wieksza szans na lepsza tarse
def population_mating(progenitor_list): def population_mating(progenitor_list):
new_population_set = [] new_population_set = []
@ -72,24 +71,17 @@ def population_mating(progenitor_list):
progenitor_a, progenitor_b = progenitor_list[0][i], progenitor_list[1][i] progenitor_a, progenitor_b = progenitor_list[0][i], progenitor_list[1][i]
child = one_point_crossover(progenitor_a, progenitor_b) child = one_point_crossover(progenitor_a, progenitor_b)
new_population_set.append(child) new_population_set.append(child)
return new_population_set return new_population_set # lista potomkow po krzyzowaniu
def mutation_of_child(child):
for i in range(dirt_count): # dla każdego elementu dajemy losową szansę zamiany int *rate
x = np.random.randint(0, dirt_count)
y = np.random.randint(0, dirt_count)
child[x], child[y] = child[y], child[x] # zamiana miejscami
return child def mutation_of_child(child, mutation_rate=0.2):#procent moze pomoc w niezaklucaniu trasy gdy jesy duza trasa ale idk
'''def mutation_of_child(child, mutation_rate=0.1):#procent moze pomoc w niezaklucaniu trasy gdy jesy duza trasa ale idk
num_mutations = int(len(child) * mutation_rate) num_mutations = int(len(child) * mutation_rate)
for _ in range(num_mutations): for _ in range(num_mutations):
x = np.random.randint(0, len(child)) x = np.random.randint(0, len(child))#losowa szansa zamiany - mutacja
y = np.random.randint(0, len(child)) y = np.random.randint(0, len(child))
child[x], child[y] = child[y], child[x] child[x], child[y] = child[y], child[x]
return child''' return child#zwrocenie bardziej roznorodnych potomkow
def mutate_population(new_population_set): def mutate_population(new_population_set):
@ -100,6 +92,23 @@ def mutate_population(new_population_set):
if __name__ == '__main__': if __name__ == '__main__':
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
population_set = find_routes(routes_num) population_set = find_routes(routes_num)
list_of_sums = routes_sum(population_set) list_of_sums = routes_sum(population_set)
@ -107,6 +116,7 @@ if __name__ == '__main__':
new_population_set = population_mating(progenitor_list) new_population_set = population_mating(progenitor_list)
final_mutated_population = mutate_population(new_population_set) final_mutated_population = mutate_population(new_population_set)
final_route = [-1, np.inf, np.array([])] # format listy final_route = [-1, np.inf, np.array([])] # format listy
for i in range(20): for i in range(20):
list_of_sums = routes_sum(final_mutated_population) list_of_sums = routes_sum(final_mutated_population)
# zapisujemy najlepsze rozwiązanie # zapisujemy najlepsze rozwiązanie
@ -115,12 +125,64 @@ if __name__ == '__main__':
final_route[1] = list_of_sums.min() final_route[1] = list_of_sums.min()
final_route[2] = np.array(final_mutated_population)[list_of_sums.min() == list_of_sums] final_route[2] = np.array(final_mutated_population)[list_of_sums.min() == list_of_sums]
progenitor_list = selection(population_set, list_of_sums) progenitor_list = selection(population_set, list_of_sums)
new_population_set = population_mating(progenitor_list) new_population_set = population_mating(progenitor_list)
final_mutated_population = mutate_population(new_population_set) final_mutated_population = mutate_population(new_population_set)
print(f"Najlepsza trasa znaleziona w iteracji: {final_route[0]}") print(f"Najlepsza trasa znaleziona w iteracji: {final_route[0]}")
print(f"Minimalna suma odległości: {final_route[1]}") print(f"Minimalna suma odległości: {final_route[1]}")
print(f"Kolejne pola: {final_route[2]}")
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()