Compare commits
13 Commits
ab14a34ecf
...
d0e8a47da1
Author | SHA1 | Date | |
---|---|---|---|
d0e8a47da1 | |||
d150e9c21d | |||
c0fe5766b0 | |||
92c893c917 | |||
7c50b44417 | |||
e448c80739 | |||
eb17e42686 | |||
af15a10048 | |||
|
702eff581a | ||
03434fdb79 | |||
637b7567b4 | |||
2017b89aa4 | |||
ae462e2920 |
284
AStar.py
Normal file
284
AStar.py
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
"""
|
||||||
|
f(n) = g(n) + h(n)
|
||||||
|
g(n) = dotychczasowy koszt -> dodać currentCost w Node lub brać koszt na nowo przy oddtawrzaniu ścieżki
|
||||||
|
h(n) = abs(state['x'] - goalTreassure[0]) + abs(state['y'] - goalTreassure[1]) -> odległość Manhatan -> można zrobić jeszcze drugą wersje gdzie mnoży się razy 5.5 ze wzgledu na średni koszt przejścia
|
||||||
|
Należy zaimplementować kolejkę priorytetową oraz zaimplementować algorytm przeszukiwania grafu stanów z uwzględnieniem kosztu za pomocą przerobienia algorytmu przeszukiwania grafu stanów
|
||||||
|
"""
|
||||||
|
import random
|
||||||
|
import pygame
|
||||||
|
import Node
|
||||||
|
import BFS
|
||||||
|
from displayControler import NUM_X, NUM_Y
|
||||||
|
from Pole import stoneList
|
||||||
|
from queue import PriorityQueue
|
||||||
|
|
||||||
|
|
||||||
|
def getRandomGoalTreasure():
|
||||||
|
while True:
|
||||||
|
goalTreasure = (random.randint(0, NUM_X - 1), random.randint(0, NUM_Y - 1)) # Współrzędne celu
|
||||||
|
if goalTreasure not in stoneList:
|
||||||
|
break
|
||||||
|
return goalTreasure
|
||||||
|
|
||||||
|
|
||||||
|
def heuristic(state, goal):
|
||||||
|
# Oblicz odległość Manhattanowską między aktualnym stanem a celem
|
||||||
|
manhattan_distance = abs(state['x'] - goal[0]) + abs(state['y'] - goal[1])
|
||||||
|
return manhattan_distance
|
||||||
|
|
||||||
|
|
||||||
|
'''def get_cost_for_plant(plant_name):
|
||||||
|
plant_costs = {
|
||||||
|
"pszenica": 7,
|
||||||
|
"kukurydza": 9,
|
||||||
|
"ziemniak": 2,
|
||||||
|
"slonecznik": 5,
|
||||||
|
"borowka": 3,
|
||||||
|
"winogrono": 4,
|
||||||
|
"mud": 15,
|
||||||
|
"dirt": 0,
|
||||||
|
}
|
||||||
|
if plant_name in plant_costs:
|
||||||
|
return plant_costs[plant_name]
|
||||||
|
else:
|
||||||
|
# Jeśli nazwa rośliny nie istnieje w słowniku, zwróć domyślną wartość
|
||||||
|
return 0
|
||||||
|
'''
|
||||||
|
|
||||||
|
def A_star(istate, pole, goalTreasure):
|
||||||
|
# goalTreasure = (random.randint(0,NUM_X-1), random.randint(0,NUM_Y-1))
|
||||||
|
# #jeśli chcemy używać random musimy wykreslić sloty z kamieniami, ponieważ tez mogą się wylosować i wtedy traktor w ogóle nie rusza
|
||||||
|
#lub zrobić to jakoś inaczej, np. funkcja szukająca najmniej nawodnionej rośliny
|
||||||
|
|
||||||
|
# przeniesione wyżej do funkcji getRandomGoalTreasure, wykorzystywana jest w App.py
|
||||||
|
# while True:
|
||||||
|
# goalTreasure = (random.randint(0, NUM_X - 1), random.randint(0, NUM_Y - 1)) # Współrzędne celu
|
||||||
|
# if goalTreasure not in stoneList:
|
||||||
|
# break
|
||||||
|
|
||||||
|
fringe = PriorityQueue() # Kolejka priorytetowa dla wierzchołków do rozpatrzenia
|
||||||
|
explored = [] # Lista odwiedzonych stanów
|
||||||
|
obrot = 1
|
||||||
|
|
||||||
|
# Tworzenie węzła początkowego
|
||||||
|
x = Node.Node(istate)
|
||||||
|
x.g = 0
|
||||||
|
x.h = heuristic(x.state, goalTreasure)
|
||||||
|
fringe.put((x.g + x.h, x)) # Dodanie węzła do kolejki
|
||||||
|
total_cost = 0
|
||||||
|
|
||||||
|
while not fringe.empty():
|
||||||
|
_, elem = fringe.get() # Pobranie węzła z najniższym priorytetem
|
||||||
|
|
||||||
|
if BFS.goalTest3(elem.state, goalTreasure): # Sprawdzenie, czy osiągnięto cel
|
||||||
|
path = []
|
||||||
|
cost_list=[]
|
||||||
|
while elem.parent is not None: # Odtworzenie ścieżki
|
||||||
|
path.append([elem.parent, elem.action])
|
||||||
|
elem = elem.parent
|
||||||
|
for node, action in path:
|
||||||
|
# Obliczanie kosztu ścieżki dla każdego pola i wyświetlanie
|
||||||
|
plant_cost = get_plant_name_and_cost_from_coordinates(node.state['x'],node.state['y'], pole)
|
||||||
|
if action == "left" or action == "right": # Liczenie kosztu tylko dla pól nie będących obrotami
|
||||||
|
total_cost += obrot
|
||||||
|
cost_list.append(obrot)
|
||||||
|
else:
|
||||||
|
total_cost += plant_cost
|
||||||
|
cost_list.append(plant_cost)
|
||||||
|
return path,cost_list,total_cost
|
||||||
|
|
||||||
|
explored.append(elem.state)
|
||||||
|
|
||||||
|
for resp in succ3A(elem.state):
|
||||||
|
child_state = resp[1]
|
||||||
|
if child_state not in explored:
|
||||||
|
child = Node.Node(child_state)
|
||||||
|
child.parent = elem
|
||||||
|
child.action = resp[0]
|
||||||
|
|
||||||
|
# Pobranie nazwy rośliny z danego slotu na podstawie współrzędnych
|
||||||
|
plant_cost = get_plant_name_and_cost_from_coordinates(child_state['x'], child_state['y'], pole)
|
||||||
|
# Pobranie kosztu dla danej rośliny
|
||||||
|
#plant_cost = get_cost_for_plant(plant_name)
|
||||||
|
|
||||||
|
if child.action == "left" or child.action == "right":
|
||||||
|
child.g = elem.g + obrot
|
||||||
|
else:
|
||||||
|
child.g = elem.g + plant_cost
|
||||||
|
|
||||||
|
# Obliczenie heurystyki dla dziecka
|
||||||
|
child.h = heuristic(child.state, goalTreasure)
|
||||||
|
|
||||||
|
in_fringe = False
|
||||||
|
for priority, item in fringe.queue:
|
||||||
|
if item.state == child.state:
|
||||||
|
in_fringe = True
|
||||||
|
if priority > child.g + child.h:
|
||||||
|
# Jeśli znaleziono węzeł w kolejce o gorszym priorytecie, zastąp go nowym
|
||||||
|
fringe.queue.remove((priority, item))
|
||||||
|
fringe.put((child.g + child.h, child))
|
||||||
|
break
|
||||||
|
|
||||||
|
if not in_fringe:
|
||||||
|
# Jeśli stan dziecka nie jest w kolejce, dodaj go do kolejki
|
||||||
|
fringe.put((child.g + child.h, child))
|
||||||
|
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.QUIT:
|
||||||
|
quit()
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_plant_name_and_cost_from_coordinates(x, y, pole):
|
||||||
|
if (x, y) in pole.slot_dict: # Sprawdzenie, czy podane współrzędne znajdują się na polu
|
||||||
|
slot = pole.slot_dict[(x, y)] # Pobranie slotu na podstawie współrzędnych
|
||||||
|
if slot.plant: # Sprawdzenie, czy na slocie znajduje się roślina
|
||||||
|
return slot.plant.stan.koszt # Zwrócenie nazwy rośliny na slocie
|
||||||
|
else:
|
||||||
|
return 0 # jeśli na slocie nie ma rośliny
|
||||||
|
else:
|
||||||
|
return 0 # jeśli podane współrzędne są poza polem
|
||||||
|
|
||||||
|
|
||||||
|
#to ogólnie identyczna funkcja jak w BFS ale nie chciałam tam ruszać, żeby przypadkiem nie zapsuć do BFS,
|
||||||
|
#tylko musiałam dodac sprawdzenie kolizji, bo traktor brał sloty z Y których nie ma na planszy
|
||||||
|
def succ3A(state):
|
||||||
|
resp = []
|
||||||
|
if state["direction"] == "N":
|
||||||
|
if state["y"] > 0 and (state['x'], state["y"] - 1) not in stoneList:
|
||||||
|
resp.append(["forward", {'x': state["x"], 'y': state["y"]-1, 'direction': state["direction"]}])
|
||||||
|
resp.append(["right", {'x': state["x"], 'y': state["y"], 'direction': "E"}])
|
||||||
|
resp.append(["left", {'x': state["x"], 'y': state["y"], 'direction': "W"}])
|
||||||
|
elif state["direction"] == "S":
|
||||||
|
if state["y"] < NUM_Y - 1 and (state['x'], state["y"] + 1) not in stoneList:
|
||||||
|
resp.append(["forward", {'x': state["x"], 'y': state["y"]+1, 'direction': state["direction"]}])
|
||||||
|
resp.append(["right", {'x': state["x"], 'y': state["y"], 'direction': "W"}])
|
||||||
|
resp.append(["left", {'x': state["x"], 'y': state["y"], 'direction': "E"}])
|
||||||
|
elif state["direction"] == "E":
|
||||||
|
if state["x"] < NUM_X - 1 and (state['x'] + 1, state["y"]) not in stoneList:
|
||||||
|
resp.append(["forward", {'x': state["x"]+1, 'y': state["y"], 'direction': state["direction"]}])
|
||||||
|
resp.append(["right", {'x': state["x"], 'y': state["y"], 'direction': "S"}])
|
||||||
|
resp.append(["left", {'x': state["x"], 'y': state["y"], 'direction': "N"}])
|
||||||
|
else: #state["direction"] == "W"
|
||||||
|
if state["x"] > 0 and (state['x'] - 1, state["y"]) not in stoneList:
|
||||||
|
resp.append(["forward", {'x': state["x"]-1, 'y': state["y"], 'direction': state["direction"]}])
|
||||||
|
resp.append(["right", {'x': state["x"], 'y': state["y"], 'direction': "N"}])
|
||||||
|
resp.append(["left", {'x': state["x"], 'y': state["y"], 'direction': "S"}])
|
||||||
|
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def heuristic2(state, goal):
|
||||||
|
# Oblicz odległość Manhattanowską między aktualnym stanem a celem
|
||||||
|
manhattan_distance = (abs(state['x'] - goal[0]) + abs(state['y'] - goal[1])) * 2.5
|
||||||
|
return manhattan_distance
|
||||||
|
|
||||||
|
|
||||||
|
def A_star2(istate, pole, goalTreasure):
|
||||||
|
# goalTreasure = (random.randint(0,NUM_X-1), random.randint(0,NUM_Y-1))
|
||||||
|
# #jeśli chcemy używać random musimy wykreslić sloty z kamieniami, ponieważ tez mogą się wylosować i wtedy traktor w ogóle nie rusza
|
||||||
|
#lub zrobić to jakoś inaczej, np. funkcja szukająca najmniej nawodnionej rośliny
|
||||||
|
|
||||||
|
# przeniesione wyżej do funkcji getRandomGoalTreasure, wykorzystywana jest w App.py
|
||||||
|
# while True:
|
||||||
|
# goalTreasure = (random.randint(0, NUM_X - 1), random.randint(0, NUM_Y - 1)) # Współrzędne celu
|
||||||
|
# if goalTreasure not in stoneList:
|
||||||
|
# break
|
||||||
|
fringe = PriorityQueue() # Kolejka priorytetowa dla wierzchołków do rozpatrzenia
|
||||||
|
explored = [] # Lista odwiedzonych stanów
|
||||||
|
obrot = 1
|
||||||
|
|
||||||
|
# Tworzenie węzła początkowego
|
||||||
|
x = Node.Node(istate)
|
||||||
|
x.g = 0
|
||||||
|
x.h = heuristic2(x.state, goalTreasure)
|
||||||
|
fringe.put((x.g + x.h, x)) # Dodanie węzła do kolejki
|
||||||
|
total_cost=0
|
||||||
|
|
||||||
|
while not fringe.empty():
|
||||||
|
_, elem = fringe.get() # Pobranie węzła z najniższym priorytetem
|
||||||
|
|
||||||
|
if BFS.goalTest3(elem.state, goalTreasure): # Sprawdzenie, czy osiągnięto cel
|
||||||
|
path = []
|
||||||
|
cost_list=[]
|
||||||
|
while elem.parent is not None: # Odtworzenie ścieżki
|
||||||
|
path.append([elem.parent, elem.action])
|
||||||
|
elem = elem.parent
|
||||||
|
for node, action in path:
|
||||||
|
# Obliczanie kosztu ścieżki dla każdego pola i wyświetlanie
|
||||||
|
plant_cost = get_plant_name_and_cost_from_coordinates(node.state['x'],node.state['y'], pole)
|
||||||
|
if action == "left" or action == "right": # Liczenie kosztu tylko dla pól nie będących obrotami
|
||||||
|
total_cost += obrot
|
||||||
|
cost_list.append(obrot)
|
||||||
|
else:
|
||||||
|
total_cost += plant_cost
|
||||||
|
cost_list.append(plant_cost)
|
||||||
|
return path,cost_list,total_cost
|
||||||
|
|
||||||
|
explored.append(elem.state)
|
||||||
|
|
||||||
|
for resp in succ3A(elem.state):
|
||||||
|
child_state = resp[1]
|
||||||
|
if child_state not in explored:
|
||||||
|
child = Node.Node(child_state)
|
||||||
|
child.parent = elem
|
||||||
|
child.action = resp[0]
|
||||||
|
|
||||||
|
# Pobranie nazwy rośliny z danego slotu na podstawie współrzędnych
|
||||||
|
plant_cost = get_plant_name_and_cost_from_coordinates(child_state['x'], child_state['y'], pole)
|
||||||
|
|
||||||
|
if child.action == "left" or child.action == "right":
|
||||||
|
child.g = elem.g + obrot
|
||||||
|
else:
|
||||||
|
child.g = elem.g + plant_cost
|
||||||
|
# Obliczenie heurystyki dla dziecka
|
||||||
|
child.h = heuristic2(child.state, goalTreasure)
|
||||||
|
|
||||||
|
in_fringe = False
|
||||||
|
for priority, item in fringe.queue:
|
||||||
|
if item.state == child.state:
|
||||||
|
in_fringe = True
|
||||||
|
if priority > child.g + child.h:
|
||||||
|
# Jeśli znaleziono węzeł w kolejce o gorszym priorytecie, zastąp go nowym
|
||||||
|
fringe.queue.remove((priority, item))
|
||||||
|
fringe.put((child.g + child.h, child))
|
||||||
|
break
|
||||||
|
|
||||||
|
if not in_fringe:
|
||||||
|
# Jeśli stan dziecka nie jest w kolejce, dodaj go do kolejki
|
||||||
|
fringe.put((child.g + child.h, child))
|
||||||
|
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.QUIT:
|
||||||
|
quit()
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
"""
|
||||||
|
TO TEST SPEED OF ASTAR
|
||||||
|
|
||||||
|
test_speed = False
|
||||||
|
|
||||||
|
if test_speed:
|
||||||
|
time1 = 0
|
||||||
|
time2 = 0
|
||||||
|
cost1 = 0
|
||||||
|
cost2 = 0
|
||||||
|
for i in range(500):
|
||||||
|
print(i)
|
||||||
|
start = time.time()
|
||||||
|
aStarRoot, cost_list, total_cost = AStar.A_star({'x': 0, 'y': 0, 'direction': "E"}, pole, goalTreasure)
|
||||||
|
end = time.time()
|
||||||
|
time1 += end - start
|
||||||
|
cost1 += total_cost
|
||||||
|
start = time.time()
|
||||||
|
aStarRoot2, cost_list, total_cost = AStar.A_star2({'x': 0, 'y': 0, 'direction': "E"}, pole, goalTreasure)
|
||||||
|
end = time.time()
|
||||||
|
time2 += end - start
|
||||||
|
cost2 += total_cost
|
||||||
|
print(time1, time2)
|
||||||
|
print(float(cost1 / 1000), float(cost2 / 1000))
|
||||||
|
"""
|
60
App.py
60
App.py
@ -8,6 +8,17 @@ import Image
|
|||||||
import Osprzet
|
import Osprzet
|
||||||
import Ui
|
import Ui
|
||||||
import BFS
|
import BFS
|
||||||
|
import AStar
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
bfs1_flag=False
|
||||||
|
bfs2_flag=False #Change this lines to show different bfs implementation
|
||||||
|
bfs3_flag=False
|
||||||
|
Astar = False
|
||||||
|
Astar2 = True
|
||||||
|
if bfs3_flag or Astar or Astar2:
|
||||||
|
Pole.stoneFlag = True
|
||||||
|
|
||||||
|
|
||||||
pygame.init()
|
pygame.init()
|
||||||
@ -17,13 +28,11 @@ FPS=5
|
|||||||
clock=pygame.time.Clock()
|
clock=pygame.time.Clock()
|
||||||
image_loader=Image.Image()
|
image_loader=Image.Image()
|
||||||
image_loader.load_images()
|
image_loader.load_images()
|
||||||
pole=Pole.Pole(screen,image_loader)
|
goalTreasure = AStar.getRandomGoalTreasure() # nie wiem czy to najlepsze miejsce, obecnie pole zawiera pole gasStation, które służy do renderowania odpowiedniego zdjęcia
|
||||||
|
pole=Pole.Pole(screen,image_loader, goalTreasure)
|
||||||
pole.draw_grid() #musi byc tutaj wywołane ponieważ inicjalizuje sloty do slownika
|
pole.draw_grid() #musi byc tutaj wywołane ponieważ inicjalizuje sloty do slownika
|
||||||
ui=Ui.Ui(screen)
|
ui=Ui.Ui(screen)
|
||||||
#Tractor creation
|
#Tractor creation
|
||||||
bfs1_flag=True
|
|
||||||
bfs2_flag=False #Change this lines to show different bfs implementation
|
|
||||||
bfs3_flag=False
|
|
||||||
traktor_slot = pole.get_slot_from_cord((0, 0))
|
traktor_slot = pole.get_slot_from_cord((0, 0))
|
||||||
traktor = Tractor.Tractor(traktor_slot, screen, Osprzet.opryskiwacz,clock,bfs2_flag)
|
traktor = Tractor.Tractor(traktor_slot, screen, Osprzet.opryskiwacz,clock,bfs2_flag)
|
||||||
|
|
||||||
@ -39,7 +48,10 @@ def init_demo(): #Demo purpose
|
|||||||
clock.tick(FPS)
|
clock.tick(FPS)
|
||||||
if(start_flag):
|
if(start_flag):
|
||||||
ui.render_text_to_console(string_to_print="Przejazd inicjalizujacy- traktor sprawdza poziom nawodnienia")
|
ui.render_text_to_console(string_to_print="Przejazd inicjalizujacy- traktor sprawdza poziom nawodnienia")
|
||||||
traktor.initial_move(pole)
|
if not bfs1_flag:
|
||||||
|
time.sleep(2)
|
||||||
|
else:
|
||||||
|
traktor.initial_move(pole)
|
||||||
traktor.reset_pos(pole)
|
traktor.reset_pos(pole)
|
||||||
clock.tick(20)
|
clock.tick(20)
|
||||||
ui.clear_console()
|
ui.clear_console()
|
||||||
@ -61,12 +73,46 @@ def init_demo(): #Demo purpose
|
|||||||
print_to_console("Traktor porusza sie obliczona sciezka BFS")
|
print_to_console("Traktor porusza sie obliczona sciezka BFS")
|
||||||
traktor.move_by_root(bfsRoot2, pole, [traktor.irrigateSlot])
|
traktor.move_by_root(bfsRoot2, pole, [traktor.irrigateSlot])
|
||||||
if(bfs3_flag):
|
if(bfs3_flag):
|
||||||
bfsRoot3 = BFS.BFS2({'x': 0, 'y': 0, 'direction': "E"})
|
bfsRoot3 = BFS.BFS3({'x': 0, 'y': 0, 'direction': "E"})
|
||||||
#displayControler: NUM_X: 20, NUM_Y: 12 (skarb) CHANGE THIS IN DCON BY HAND!!!!!!!!
|
#displayControler: NUM_X: 20, NUM_Y: 12 (skarb) CHANGE THIS IN DCON BY HAND!!!!!!!!
|
||||||
bfsRoot3.reverse()
|
bfsRoot3.reverse()
|
||||||
print_to_console("Traktor porusza sie obliczona sciezka BFS")
|
print_to_console("Traktor porusza sie obliczona sciezka BFS")
|
||||||
traktor.move_by_root(bfsRoot3, pole, [traktor.irrigateSlot])
|
traktor.move_by_root(bfsRoot3, pole, [traktor.irrigateSlot])
|
||||||
|
if (Astar):
|
||||||
|
aStarRoot,cost_list,total_cost= AStar.A_star({'x': 0, 'y': 0, 'direction': "E"}, pole, goalTreasure)
|
||||||
|
if aStarRoot:
|
||||||
|
print("Pełna ścieżka agenta:")
|
||||||
|
aStarRoot.reverse()
|
||||||
|
cost_list.reverse()
|
||||||
|
i=0
|
||||||
|
for node in aStarRoot:
|
||||||
|
state = node[0].state # Pobranie stanu z obiektu Node
|
||||||
|
action = node[1] # Pobranie akcji
|
||||||
|
print("Współrzędne pola:", state['x'], state['y'], "- Akcja:",action,"- Koszt: ",cost_list[i])
|
||||||
|
i=i+1
|
||||||
|
print_to_console("Traktor porusza się obliczoną ścieżką A*")
|
||||||
|
traktor.move_by_root(aStarRoot, pole, [traktor.irrigateSlot])
|
||||||
|
print("Koszt:", total_cost)
|
||||||
|
else:
|
||||||
|
print_to_console("Nie można znaleźć ścieżki A*") # Wyświetl komunikat, jeśli nie znaleziono ścieżki
|
||||||
|
if (Astar2):
|
||||||
|
|
||||||
|
aStarRoot2,cost_list, total_cost= AStar.A_star2({'x': 0, 'y': 0, 'direction': "E"}, pole, goalTreasure)
|
||||||
|
if aStarRoot2:
|
||||||
|
print("Pełna ścieżka agenta:")
|
||||||
|
aStarRoot2.reverse()
|
||||||
|
cost_list.reverse()
|
||||||
|
i=0
|
||||||
|
for node in aStarRoot2:
|
||||||
|
state = node[0].state # Pobranie stanu z obiektu Node
|
||||||
|
action = node[1] # Pobranie akcji
|
||||||
|
print("Współrzędne pola:", state['x'], state['y'], "- Akcja:",action,"- Koszt: ",cost_list[i])
|
||||||
|
i=i+1
|
||||||
|
print_to_console("Traktor porusza się obliczoną ścieżką A*")
|
||||||
|
traktor.move_by_root(aStarRoot2, pole, [traktor.irrigateSlot])
|
||||||
|
print("Koszt:", total_cost)
|
||||||
|
else:
|
||||||
|
print_to_console("Nie można znaleźć ścieżki A*") # Wyświetl komunikat, jeśli nie znaleziono ścieżki
|
||||||
|
|
||||||
|
|
||||||
start_flag=False
|
start_flag=False
|
||||||
@ -106,3 +152,5 @@ def get_info(old_info):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
23
BFS.py
23
BFS.py
@ -3,6 +3,7 @@ import random
|
|||||||
import pygame
|
import pygame
|
||||||
import Node
|
import Node
|
||||||
from displayControler import NUM_X, NUM_Y
|
from displayControler import NUM_X, NUM_Y
|
||||||
|
from Pole import stoneList
|
||||||
|
|
||||||
|
|
||||||
def goalTest1(hIndex):
|
def goalTest1(hIndex):
|
||||||
@ -93,31 +94,31 @@ def BFS1(istate):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def goalTest2(state, goalTreassure):
|
def goalTest3(state, goalTreassure):
|
||||||
if state["x"] == goalTreassure[0] and state["y"] == goalTreassure[1]:
|
if state["x"] == goalTreassure[0] and state["y"] == goalTreassure[1]:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def succ2(state):
|
def succ3(state):
|
||||||
resp = []
|
resp = []
|
||||||
if state["direction"] == "N":
|
if state["direction"] == "N":
|
||||||
if state["y"] > 0:
|
if state["y"] > 0 and (state['x'], state["y"] - 1) not in stoneList:
|
||||||
resp.append(["forward", {'x': state["x"], 'y': state["y"]-1, 'direction': state["direction"]}])
|
resp.append(["forward", {'x': state["x"], 'y': state["y"]-1, 'direction': state["direction"]}])
|
||||||
resp.append(["right", {'x': state["x"], 'y': state["y"], 'direction': "E"}])
|
resp.append(["right", {'x': state["x"], 'y': state["y"], 'direction': "E"}])
|
||||||
resp.append(["left", {'x': state["x"], 'y': state["y"], 'direction': "W"}])
|
resp.append(["left", {'x': state["x"], 'y': state["y"], 'direction': "W"}])
|
||||||
elif state["direction"] == "S":
|
elif state["direction"] == "S":
|
||||||
if state["y"] < NUM_Y:
|
if state["y"] < NUM_Y - 1 and (state['x'], state["y"] + 1) not in stoneList:
|
||||||
resp.append(["forward", {'x': state["x"], 'y': state["y"]+1, 'direction': state["direction"]}])
|
resp.append(["forward", {'x': state["x"], 'y': state["y"]+1, 'direction': state["direction"]}])
|
||||||
resp.append(["right", {'x': state["x"], 'y': state["y"], 'direction': "W"}])
|
resp.append(["right", {'x': state["x"], 'y': state["y"], 'direction': "W"}])
|
||||||
resp.append(["left", {'x': state["x"], 'y': state["y"], 'direction': "E"}])
|
resp.append(["left", {'x': state["x"], 'y': state["y"], 'direction': "E"}])
|
||||||
elif state["direction"] == "E":
|
elif state["direction"] == "E":
|
||||||
if state["x"] < NUM_X:
|
if state["x"] < NUM_X - 1 and (state['x'] + 1, state["y"]) not in stoneList:
|
||||||
resp.append(["forward", {'x': state["x"]+1, 'y': state["y"], 'direction': state["direction"]}])
|
resp.append(["forward", {'x': state["x"]+1, 'y': state["y"], 'direction': state["direction"]}])
|
||||||
resp.append(["right", {'x': state["x"], 'y': state["y"], 'direction': "S"}])
|
resp.append(["right", {'x': state["x"], 'y': state["y"], 'direction': "S"}])
|
||||||
resp.append(["left", {'x': state["x"], 'y': state["y"], 'direction': "N"}])
|
resp.append(["left", {'x': state["x"], 'y': state["y"], 'direction': "N"}])
|
||||||
else: #state["zwrot"] == "W"
|
else: #state["zwrot"] == "W"
|
||||||
if state["x"] > 0:
|
if state["x"] > 0 and (state['x'] - 1, state["y"]) not in stoneList:
|
||||||
resp.append(["forward", {'x': state["x"]-1, 'y': state["y"], 'direction': state["direction"]}])
|
resp.append(["forward", {'x': state["x"]-1, 'y': state["y"], 'direction': state["direction"]}])
|
||||||
resp.append(["right", {'x': state["x"], 'y': state["y"], 'direction': "N"}])
|
resp.append(["right", {'x': state["x"], 'y': state["y"], 'direction': "N"}])
|
||||||
resp.append(["left", {'x': state["x"], 'y': state["y"], 'direction': "S"}])
|
resp.append(["left", {'x': state["x"], 'y': state["y"], 'direction': "S"}])
|
||||||
@ -125,14 +126,14 @@ def succ2(state):
|
|||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
def check2(tab, state):
|
def check3(tab, state):
|
||||||
for i in tab:
|
for i in tab:
|
||||||
if i.state == state:
|
if i.state == state:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def BFS2(istate):
|
def BFS3(istate):
|
||||||
goalTreassuere = (random.randint(0,NUM_X-1), random.randint(0,NUM_Y-1))
|
goalTreassuere = (random.randint(0,NUM_X-1), random.randint(0,NUM_Y-1))
|
||||||
print(goalTreassuere)
|
print(goalTreassuere)
|
||||||
fringe = []
|
fringe = []
|
||||||
@ -148,7 +149,7 @@ def BFS2(istate):
|
|||||||
|
|
||||||
elem = fringe.pop(0)
|
elem = fringe.pop(0)
|
||||||
|
|
||||||
if goalTest2(elem.state, goalTreassuere):
|
if goalTest3(elem.state, goalTreassuere):
|
||||||
x = elem
|
x = elem
|
||||||
tab = []
|
tab = []
|
||||||
while x.parent != None:
|
while x.parent != None:
|
||||||
@ -158,8 +159,8 @@ def BFS2(istate):
|
|||||||
|
|
||||||
explored.append(elem)
|
explored.append(elem)
|
||||||
|
|
||||||
for resp in succ2(elem.state):
|
for resp in succ3(elem.state):
|
||||||
if check2(fringe, resp[1]) and check2(explored, resp[1]):
|
if check3(fringe, resp[1]) and check3(explored, resp[1]):
|
||||||
x = Node.Node(resp[1])
|
x = Node.Node(resp[1])
|
||||||
x.parent = elem
|
x.parent = elem
|
||||||
x.action = resp[0]
|
x.action = resp[0]
|
||||||
|
28
Image.py
28
Image.py
@ -7,23 +7,37 @@ class Image:
|
|||||||
self.plants_image_dict={}
|
self.plants_image_dict={}
|
||||||
self.tractor_image=None
|
self.tractor_image=None
|
||||||
self.garage_image=None
|
self.garage_image=None
|
||||||
|
self.stone_image=None
|
||||||
|
self.gasStation_image=None
|
||||||
def load_images(self):
|
def load_images(self):
|
||||||
files_plants={0:"borowka",
|
files_plants={
|
||||||
|
0:"borowka",
|
||||||
1:"kukurydza",
|
1:"kukurydza",
|
||||||
2:"pszenica",
|
2:"pszenica",
|
||||||
3:"slonecznik",
|
3:"slonecznik",
|
||||||
4:"winogrono",
|
4:"winogrono",
|
||||||
5:"ziemniak"}
|
5:"ziemniak",
|
||||||
|
6:"dirt",
|
||||||
|
7:"mud",
|
||||||
|
8:"road"}
|
||||||
for index in files_plants:
|
for index in files_plants:
|
||||||
plant_image=pygame.image.load("images/plants/"+files_plants[index]+".jpg")
|
if index >= 6:
|
||||||
|
plant_image = pygame.image.load("images/" + files_plants[index] + ".jpg")
|
||||||
|
else:
|
||||||
|
plant_image=pygame.image.load("images/plants/"+files_plants[index]+".jpg")
|
||||||
plant_image=pygame.transform.scale(plant_image,(dCon.CUBE_SIZE,dCon.CUBE_SIZE))
|
plant_image=pygame.transform.scale(plant_image,(dCon.CUBE_SIZE,dCon.CUBE_SIZE))
|
||||||
self.plants_image_dict[files_plants[index]]=plant_image
|
self.plants_image_dict[files_plants[index]]=plant_image
|
||||||
tractor_image=pygame.image.load("images/traktor.png")
|
tractor_image=pygame.image.load("images/traktor.png")
|
||||||
tractor_image=pygame.transform.scale(tractor_image,(dCon.CUBE_SIZE,dCon.CUBE_SIZE))
|
tractor_image=pygame.transform.scale(tractor_image,(dCon.CUBE_SIZE,dCon.CUBE_SIZE))
|
||||||
garage=pygame.image.load("images/garage.png")
|
garage=pygame.image.load("images/garage.png")
|
||||||
self.garage_image=pygame.transform.scale(garage,(dCon.CUBE_SIZE,dCon.CUBE_SIZE))
|
self.garage_image=pygame.transform.scale(garage,(dCon.CUBE_SIZE,dCon.CUBE_SIZE))
|
||||||
|
stone=pygame.image.load("images/stone.png")
|
||||||
|
self.stone_image=pygame.transform.scale(stone,(dCon.CUBE_SIZE,dCon.CUBE_SIZE))
|
||||||
|
gasStation=pygame.image.load("images/gasStation.png")
|
||||||
|
self.gasStation_image=pygame.transform.scale(gasStation,(dCon.CUBE_SIZE,dCon.CUBE_SIZE))
|
||||||
|
|
||||||
def return_random_plant(self):
|
def return_random_plant(self):
|
||||||
x=random.randint(0,5)
|
x=random.randint(0,7)
|
||||||
keys=list(self.plants_image_dict.keys())
|
keys=list(self.plants_image_dict.keys())
|
||||||
plant=keys[x]
|
plant=keys[x]
|
||||||
return (plant,self.plants_image_dict[plant])
|
return (plant,self.plants_image_dict[plant])
|
||||||
@ -33,3 +47,9 @@ class Image:
|
|||||||
|
|
||||||
def return_garage(self):
|
def return_garage(self):
|
||||||
return self.garage_image
|
return self.garage_image
|
||||||
|
|
||||||
|
def return_stone(self):
|
||||||
|
return self.stone_image
|
||||||
|
|
||||||
|
def return_gasStation(self):
|
||||||
|
return self.gasStation_image
|
||||||
|
5
Node.py
5
Node.py
@ -6,3 +6,8 @@ class Node:
|
|||||||
def __init__(self, state):
|
def __init__(self, state):
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
"""
|
||||||
|
Definicja metody __lt__ (less than), która jest wymagana do porównywania obiektów typu Node.
|
||||||
|
"""
|
||||||
|
return self.g + self.h < other.g + other.h
|
20
Pole.py
20
Pole.py
@ -5,13 +5,18 @@ import pygame
|
|||||||
import time
|
import time
|
||||||
import Ui
|
import Ui
|
||||||
import math
|
import math
|
||||||
|
import random
|
||||||
|
|
||||||
|
stoneList = [(3,3), (3,4), (3,5), (3,6), (4,6), (5,6), (6,6), (7,6), (8,6), (9,6), (10,6), (11,6), (12,6), (13,6), (14,6), (15,6), (16,6), (16,7), (16,8), (16,9)]
|
||||||
|
stoneFlag = False
|
||||||
|
|
||||||
class Pole:
|
class Pole:
|
||||||
def __init__(self,screen,image_loader):
|
def __init__(self,screen,image_loader, gasStation = (-1, -1)):
|
||||||
self.screen=screen
|
self.screen=screen
|
||||||
self.slot_dict={} #Slot are stored in dictionary with key being a Tuple of x and y coordinates so top left slot key is (0,0) and value is slot object
|
self.slot_dict={} #Slot are stored in dictionary with key being a Tuple of x and y coordinates so top left slot key is (0,0) and value is slot object
|
||||||
self.ui=Ui.Ui(screen)
|
self.ui=Ui.Ui(screen)
|
||||||
self.image_loader=image_loader
|
self.image_loader=image_loader
|
||||||
|
self.gasStation=gasStation
|
||||||
|
|
||||||
def get_slot_from_cord(self,coordinates):
|
def get_slot_from_cord(self,coordinates):
|
||||||
(x_axis,y_axis)=coordinates
|
(x_axis,y_axis)=coordinates
|
||||||
@ -35,13 +40,20 @@ class Pole:
|
|||||||
slot_dict[coordinates].draw()
|
slot_dict[coordinates].draw()
|
||||||
garage=self.slot_dict[(0,0)]
|
garage=self.slot_dict[(0,0)]
|
||||||
garage.set_garage_image()
|
garage.set_garage_image()
|
||||||
|
if stoneFlag:
|
||||||
|
for i in stoneList:
|
||||||
|
st=self.slot_dict[i]
|
||||||
|
st.set_stone_image()
|
||||||
|
if self.gasStation[0] != -1:
|
||||||
|
st=self.slot_dict[self.gasStation]
|
||||||
|
st.set_gasStation_image()
|
||||||
|
|
||||||
def randomize_colors(self):
|
def randomize_colors(self):
|
||||||
pygame.display.update()
|
pygame.display.update()
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
self.ui.render_text("Randomizing Crops")
|
#self.ui.render_text("Randomizing Crops")
|
||||||
for coordinates in self.slot_dict:
|
for coordinates in self.slot_dict:
|
||||||
if(coordinates==(0,0)):
|
if(coordinates==(0,0) or coordinates in stoneList or coordinates == self.gasStation):
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
self.slot_dict[coordinates].set_random_plant()
|
self.slot_dict[coordinates].set_random_plant()
|
||||||
@ -65,3 +77,5 @@ class Pole:
|
|||||||
collided=self.get_slot_from_cord((mouse_x,mouse_y))
|
collided=self.get_slot_from_cord((mouse_x,mouse_y))
|
||||||
return collided.print_status()
|
return collided.print_status()
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
41
Roslina.py
41
Roslina.py
@ -32,20 +32,49 @@ class Roslina:
|
|||||||
- słonecznik: +3
|
- słonecznik: +3
|
||||||
- borówka: +5
|
- borówka: +5
|
||||||
- winogrono: +4
|
- winogrono: +4
|
||||||
"""
|
|
||||||
|
Koszt (0-15):
|
||||||
|
- pszenica: 7
|
||||||
|
- kukurydza: 9
|
||||||
|
- ziemniak: 2
|
||||||
|
- słonecznik: 5
|
||||||
|
- borówka: 3
|
||||||
|
- winogrono: 4
|
||||||
|
- szuter (ścieżka): 0
|
||||||
|
- błoto: 15
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, nazwa, stan, srodek):
|
def __init__(self, nazwa, stan, srodek):
|
||||||
self.nazwa = nazwa
|
self.nazwa = nazwa
|
||||||
self.stan = stan
|
self.stan = stan
|
||||||
self.srodek = srodek
|
self.srodek = srodek
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, nazwa):
|
||||||
|
self.nazwa = nazwa
|
||||||
|
self.stan = Stan.Stan()
|
||||||
|
if nazwa == "dirt":
|
||||||
|
self.stan.koszt = 0
|
||||||
|
self.stan.nawodnienie = 100
|
||||||
|
elif nazwa == "mud":
|
||||||
|
self.stan.koszt = 15
|
||||||
|
self.stan.nawodnienie = 100
|
||||||
|
else:
|
||||||
|
self.stan.set_random()
|
||||||
|
if nazwa == "pszenica":
|
||||||
|
self.stan.koszt = 7
|
||||||
|
elif nazwa == "kukurydza":
|
||||||
|
self.stan.koszt = 9
|
||||||
|
elif nazwa == "ziemniak":
|
||||||
|
self.stan.koszt = 2
|
||||||
|
elif nazwa == "slonecznik":
|
||||||
|
self.stan.koszt = 5
|
||||||
|
elif nazwa == "borowka":
|
||||||
|
self.stan.koszt = 3
|
||||||
|
else: # winogrono
|
||||||
|
self.stan.koszt = 4
|
||||||
|
self.srodek = None
|
||||||
|
|
||||||
def __init__(self,nazwa):
|
|
||||||
self.nazwa=nazwa
|
|
||||||
self.stan=Stan.Stan()
|
|
||||||
self.stan.set_random()
|
|
||||||
self.srodek=None
|
|
||||||
|
|
||||||
def checkSrodek(self):
|
def checkSrodek(self):
|
||||||
#może wykorzystać AI do porównywania zdjęć
|
#może wykorzystać AI do porównywania zdjęć
|
||||||
|
17
Slot.py
17
Slot.py
@ -23,7 +23,12 @@ class Slot:
|
|||||||
pygame.display.update()
|
pygame.display.update()
|
||||||
|
|
||||||
def redraw_image(self):
|
def redraw_image(self):
|
||||||
self.set_image()
|
self.mark_visited()
|
||||||
|
|
||||||
|
def mark_visited(self):
|
||||||
|
plant,self.plant_image=self.image_loader.return_plant('road')
|
||||||
|
self.screen.blit(self.plant_image, (self.x_axis * dCon.CUBE_SIZE, self.y_axis * dCon.CUBE_SIZE))
|
||||||
|
pygame.draw.rect(self.screen, Colors.BLACK, self.field, BORDER_THICKNESS)
|
||||||
|
|
||||||
def color_change(self,color):
|
def color_change(self,color):
|
||||||
self.plant=color
|
self.plant=color
|
||||||
@ -45,6 +50,15 @@ class Slot:
|
|||||||
self.screen.blit(self.plant_image, (self.x_axis * dCon.CUBE_SIZE, self.y_axis * dCon.CUBE_SIZE))
|
self.screen.blit(self.plant_image, (self.x_axis * dCon.CUBE_SIZE, self.y_axis * dCon.CUBE_SIZE))
|
||||||
pygame.draw.rect(self.screen, Colors.BLACK, self.field, BORDER_THICKNESS)
|
pygame.draw.rect(self.screen, Colors.BLACK, self.field, BORDER_THICKNESS)
|
||||||
|
|
||||||
|
def set_stone_image(self):
|
||||||
|
self.plant_image=self.image_loader.return_stone()
|
||||||
|
self.screen.blit(self.plant_image, (self.x_axis * dCon.CUBE_SIZE, self.y_axis * dCon.CUBE_SIZE))
|
||||||
|
pygame.draw.rect(self.screen, Colors.BLACK, self.field, BORDER_THICKNESS)
|
||||||
|
|
||||||
|
def set_gasStation_image(self):
|
||||||
|
self.plant_image=self.image_loader.return_gasStation()
|
||||||
|
self.screen.blit(self.plant_image, (self.x_axis * dCon.CUBE_SIZE, self.y_axis * dCon.CUBE_SIZE))
|
||||||
|
pygame.draw.rect(self.screen, Colors.BLACK, self.field, BORDER_THICKNESS)
|
||||||
|
|
||||||
def random_plant(self): #Probably will not be used later only for demo purpouse
|
def random_plant(self): #Probably will not be used later only for demo purpouse
|
||||||
return self.image_loader.return_random_plant()
|
return self.image_loader.return_random_plant()
|
||||||
@ -57,6 +71,7 @@ class Slot:
|
|||||||
|
|
||||||
def print_status(self):
|
def print_status(self):
|
||||||
return f"wspolrzedne: (X:{self.x_axis} Y:{self.y_axis}) "+self.plant.report_status()
|
return f"wspolrzedne: (X:{self.x_axis} Y:{self.y_axis}) "+self.plant.report_status()
|
||||||
|
|
||||||
def irrigatePlant(self):
|
def irrigatePlant(self):
|
||||||
self.plant.stan.nawodnienie = 100
|
self.plant.stan.nawodnienie = 100
|
||||||
|
|
||||||
|
3
Stan.py
3
Stan.py
@ -7,6 +7,7 @@ class Stan:
|
|||||||
wzrost = None #[int] 0-100 (75-100: scinanie), wzrasta w zaleznosci od rosliny: aktualizowane bedzie "w tle"
|
wzrost = None #[int] 0-100 (75-100: scinanie), wzrasta w zaleznosci od rosliny: aktualizowane bedzie "w tle"
|
||||||
choroba = None #[string] brak, grzyb, bakteria, pasożyt
|
choroba = None #[string] brak, grzyb, bakteria, pasożyt
|
||||||
akcja = None #[Akcja]
|
akcja = None #[Akcja]
|
||||||
|
koszt = None #[int] 0-15, im więcej tym trudniej wjechać
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -48,4 +49,4 @@ class Stan:
|
|||||||
return self.nawodnienie
|
return self.nawodnienie
|
||||||
|
|
||||||
def report_all(self):
|
def report_all(self):
|
||||||
return f"Nawodnienie: {self.nawodnienie} Zyznosc: {self.zyznosc} Wzrost: {self.wzrost} Choroba: {self.choroba}"
|
return f"Nawodnienie: {self.nawodnienie} Zyznosc: {self.zyznosc} Wzrost: {self.wzrost} Choroba: {self.choroba} Koszt wejścia: {self.koszt}"
|
17
Tractor.py
17
Tractor.py
@ -1,6 +1,8 @@
|
|||||||
import time
|
import time
|
||||||
import pygame
|
import pygame
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
import Pole
|
||||||
import displayControler as dCon
|
import displayControler as dCon
|
||||||
import Slot
|
import Slot
|
||||||
import Osprzet
|
import Osprzet
|
||||||
@ -137,13 +139,14 @@ class Tractor:
|
|||||||
def snake_move(self,pole,x,y):
|
def snake_move(self,pole,x,y):
|
||||||
next_slot_coordinates=(x,y)
|
next_slot_coordinates=(x,y)
|
||||||
if(self.do_move_if_valid(pole,next_slot_coordinates)):
|
if(self.do_move_if_valid(pole,next_slot_coordinates)):
|
||||||
if x == 0 and y == 0:
|
if (x,y) not in Pole.stoneList:
|
||||||
hydrateIndex = -1
|
if x == 0 and y == 0:
|
||||||
elif pole.get_slot_from_cord((x,y)).get_hydrate_stats() < 60:
|
hydrateIndex = -1
|
||||||
hydrateIndex = 0
|
elif pole.get_slot_from_cord((x,y)).get_hydrate_stats() < 60:
|
||||||
else:
|
hydrateIndex = 0
|
||||||
hydrateIndex = 1
|
else:
|
||||||
self.slot_hydrate_dict[(x,y)]= hydrateIndex #Budowanie slownika slotow z poziomem nawodnienia dla traktorka
|
hydrateIndex = 1
|
||||||
|
self.slot_hydrate_dict[(x,y)]= hydrateIndex #Budowanie slownika slotow z poziomem nawodnienia dla traktorka
|
||||||
self.clock.tick(10)
|
self.clock.tick(10)
|
||||||
for event in pygame.event.get():
|
for event in pygame.event.get():
|
||||||
if event.type == pygame.QUIT:
|
if event.type == pygame.QUIT:
|
||||||
|
1
Ui.py
1
Ui.py
@ -25,6 +25,7 @@ class Ui:
|
|||||||
|
|
||||||
def render_text_to_console(self,string_to_print):
|
def render_text_to_console(self,string_to_print):
|
||||||
font=pygame.font.Font(self.font,self.font_size)
|
font=pygame.font.Font(self.font,self.font_size)
|
||||||
|
string_to_print=str(string_to_print)
|
||||||
self.break_string_to_console(string_to_print)
|
self.break_string_to_console(string_to_print)
|
||||||
for string in self.to_print:
|
for string in self.to_print:
|
||||||
text=font.render(string,True,Colors.BLACK,Colors.WHITE)
|
text=font.render(string,True,Colors.BLACK,Colors.WHITE)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
CUBE_SIZE = 64
|
CUBE_SIZE = 64
|
||||||
NUM_X = 6
|
NUM_X = 20
|
||||||
NUM_Y = 3
|
NUM_Y = 12
|
||||||
|
|
||||||
#returns true if tractor can move to specified slot
|
#returns true if tractor can move to specified slot
|
||||||
def isValidMove(x, y):
|
def isValidMove(x, y):
|
||||||
|
BIN
images/dirt.jpg
Normal file
BIN
images/dirt.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 412 KiB |
BIN
images/gasStation.png
Normal file
BIN
images/gasStation.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 82 KiB |
BIN
images/mud.jpg
Normal file
BIN
images/mud.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 285 KiB |
BIN
images/road.jpg
Normal file
BIN
images/road.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
BIN
images/stone.png
Normal file
BIN
images/stone.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 549 KiB |
Loading…
Reference in New Issue
Block a user