Compare commits

..

No commits in common. "8204825a977c351f830ddb22b1dab021e19f8721" and "068ee153a0510b8c5cd11bb0a7ca56e79c0bc359" have entirely different histories.

3 changed files with 162 additions and 224 deletions

187
astar.py
View File

@ -3,104 +3,120 @@ 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, state, parent=None, action=None, cost=0): def __init__(self, x, y):
self.state = state # Stan reprezentowany przez węzeł self.x = x
self.parent = parent # Węzeł rodzica self.y = y
self.action = action # Akcja prowadząca do tego stanu self.f = 0
self.cost = cost # Koszt przejścia do tego stanu self.g = 0
self.f = 0 # Wartość funkcji priorytetowej self.h = 0
self.tie_breaker = 0 # Wartość używana do rozwiązywania konfliktów priorytetów self.cost = 1
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
# prorytety akcje right i down maja wyzszy priorytet def neighbors(self, grid):
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_state = (9, 9) # Stan początkowy start_row, start_col = 0,0
goal_state = (0, 0) # Stan docelowy end_row, end_col = 9,9
tractor = Tractor(start_state[1], start_state[0]) tractor = Tractor(start_row, start_col)
board.set_grass(start_state[0], start_state[1]) # Ustawienie startowego pola jako trawę board.set_grass(start_row, start_col)
board.set_grass(goal_state[0], goal_state[1]) # Ustawienie docelowego pola jako trawę board.set_grass(end_row, end_col)
grid = [[Node(x, y) for y in range(rows)] for x in range(cols)]
start = grid[start_row][start_col]
end = grid[end_row][end_col]
path, start_node = search(grid, start, end, board)
path, total_cost = graphsearch(start_state, goal_state, board)
while run: while run:
clock.tick(fps) clock.tick(fps)
@ -113,38 +129,39 @@ def main():
run = False run = False
continue continue
next_state, action = path.pop(0) if path else (start_state, None) next_node = path.pop(0) if path else start_node
print(next_state) # Wypisanie następnego stanu dx = next_node.x - tractor.col
tractor.row, tractor.col = next_state[1], next_state[0] dy = next_node.y - tractor.row
tractor.row, tractor.col = next_node.y, next_node.x
if action == "right":
if dx > 0:
tractor.direction = "right" tractor.direction = "right"
elif action == "left": elif dx < 0:
tractor.direction = "left" tractor.direction = "left"
elif action == "down": elif dy > 0:
tractor.direction = "down" tractor.direction = "down"
elif action == "up": elif dy < 0:
tractor.direction = "up" tractor.direction = "up"
# Aktualizacja planszy na podstawie położenia traktora
if board.is_weed(tractor.col, tractor.row ): if board.is_weed(tractor.col, tractor.row ):
board.set_grass(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,7 +10,6 @@ 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")
@ -20,26 +19,10 @@ 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):
# Najpierw wypełniamy całą planszę trawą (kod 2) self.board = [[random.choice([0,1,2,3,4,5,6,7,8,9]) for _ in range(rows)] for _ in range(cols)]
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)
@ -50,7 +33,7 @@ class Board:
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)
@ -67,6 +50,7 @@ class Board:
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 = {
0: 100, #kamien 0: 100, #kamien
@ -94,7 +78,6 @@ 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
@ -110,10 +93,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_weed_positions(self): def get_dirt_positions(self):
weed_positions = [] dirt_positions = []
for row in range(rows): for row in range(rows):
for col in range(cols): for col in range(cols):
if self.is_weed(row, col): if self.is_dirt(row, col):
weed_positions.append([row, col]) dirt_positions.append([row, col])
return weed_positions return dirt_positions

View File

@ -5,34 +5,36 @@ 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()
weed_positions = board.get_weed_positions() dirt_positions = board.get_dirt_positions()
weed_count = len(weed_positions) dirt_count = len(dirt_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(weed_count)), weed_count, replace=False) single_route = np.random.choice(list(range(dirt_count)), dirt_count, replace=False)
population_set.append(single_route) population_set.append(single_route)
return np.array(population_set) #zwracamy 20 roznych losowych tras return np.array(population_set)
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_weed = weed_positions[route_indices[i]] current_dirt = dirt_positions[route_indices[i]]
next_weed = weed_positions[route_indices[i + 1]] next_dirt = dirt_positions[route_indices[i + 1]]
sum += manhattan(current_weed, next_weed) sum += manhattan(current_dirt, next_dirt)
return sum #zwracamy odleglosc (ilosc pol) dla danej trasy manhatanem return sum
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
@ -40,30 +42,29 @@ 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):
#RULETKA - czesciowo faworyzuje rozwiaznaia, wiekszy fitness wieksze szanse # Oblicz wartości fitness dla każdej trasy
# obliczamy wartości fitness (przystosowania) dla każdej trasy fitness_values = calculate_fitness(list_of_sums)
fitness_values = calculate_fitness(list_of_sums)#krotsze trasy maja miec wyzsze wartosci # Normalizuj wartości fitness, aby sumowały się do 1 (wymagane dla np.random.choice)
# 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()
# wybieramy indeksy rodziców na podstawie prawdopodobieństw # Wybierz rodziców na podstawie prawdopodobieństw (wartości fitness)
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)
# finalne trasy # Wybierz rzeczywiste 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]) #zwracami listy przodkow-rodzicow return np.array([progenitor_a, progenitor_b])
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 #loosyw punkt przeciecia ktory skleja nam nowa trase, wieksza szans na lepsza tarse return child
def population_mating(progenitor_list): def population_mating(progenitor_list):
new_population_set = [] new_population_set = []
@ -71,17 +72,24 @@ 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 # lista potomkow po krzyzowaniu return new_population_set
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
def mutation_of_child(child, mutation_rate=0.2):#procent moze pomoc w niezaklucaniu trasy gdy jesy duza trasa ale idk return child
'''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))#losowa szansa zamiany - mutacja x = np.random.randint(0, len(child))
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#zwrocenie bardziej roznorodnych potomkow return child'''
def mutate_population(new_population_set): def mutate_population(new_population_set):
@ -92,23 +100,6 @@ 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)
@ -116,7 +107,6 @@ 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
@ -125,64 +115,12 @@ 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()