Merge pull request 'Route_planning_bfs' (#2) from Route_planning_bfs into master

Reviewed-on: #2
This commit is contained in:
s481894 2024-04-20 14:51:35 +02:00
commit 81602fb3cf
22 changed files with 354 additions and 23 deletions

Binary file not shown.

View File

@ -12,6 +12,9 @@ GROUND = ""
# path to ground image
GREY = (20, 17, 17, 255)
DIRECTION_NORTH = 1
DIRECTION_EAST = 2
DIRECTION_SOUTH = 3
DIRECTION_WEST = 4

View File

@ -1,12 +1,13 @@
# create a field here : 1: add tiles, 2: place them
import pygame
import random
from area.constants import WIDTH,HEIGHT,FIELD_WIDTH,FIELD_HEIGHT,TILE_SIZE,GREY,ROWS,COLS
from area.tractor import Tractor
from area.constants import WIDTH,FIELD_WIDTH,TILE_SIZE,GREY,ROWS,COLS
from tile import Tile
from ground import Dirt
tiles = []
tractor = Tractor(0*TILE_SIZE, 0*TILE_SIZE)
fieldX = (WIDTH-FIELD_WIDTH)/2
# in center of the screen
fieldY = 100
@ -17,16 +18,19 @@ def positionFieldElements():
for t in tiles:
t.x += fieldX
t.y += fieldY
tractor.x += fieldX
tractor.y += fieldY
def createTiles():
for y in range(0, COLS):
for x in range(0, ROWS):
tile = Tile(x*TILE_SIZE, y*TILE_SIZE)
dirt = Dirt(random.randint(1, 100), random.randint(1, 100))
dirt.pests_and_weeds()
tile.ground = dirt
tile.randomizeContent()
tiles.append(tile)
positionFieldElements()
return tiles
def createField(win):
createTiles()
@ -35,11 +39,17 @@ def createField(win):
image = pygame.transform.scale(image, (TILE_SIZE, TILE_SIZE))
win.blit(image, (t.x, t.y))
pygame.display.flip()
imageTractor = pygame.image.load(tractor.image).convert_alpha()
imageTractor = pygame.transform.scale(imageTractor, (TILE_SIZE, TILE_SIZE))
win.blit(imageTractor, (tractor.x, tractor.y))
pygame.display.flip()
def drawWindow(win):
win.fill(GREY)
createField(win)
pygame.display.flip()
def get_tile_coordinates(index):
if index < len(tiles):
tile = tiles[index]
return tile.x, tile.y
else:
return None

View File

@ -1,9 +1,14 @@
from crop_protection_product import CropProtectionProduct
from area.constants import TILE_SIZE, DIRECTION_EAST, DIRECTION_SOUTH, DIRECTION_WEST, DIRECTION_NORTH
from area.field import fieldX, fieldY, tiles
import pygame
import time
class Tractor:
x = None
y = None
direction = None #direction takes values in the range of 1 to 4 (1->North, 2->East etc...)
image = None
cypermetryna = CropProtectionProduct("pests", "cereal")
diflufenikan = CropProtectionProduct("weeds", "cereal")
@ -13,10 +18,25 @@ class Tractor:
metazachlor = CropProtectionProduct("weeds", "vegetable")
# etc
def __init__(self, x, y):
def __init__(self, x, y, direction, tractor_start, tractor_end):
self.x = x
self.y = y
self.image = 'resources/images/tractor.png'
self.rect = pygame.Rect(x, y, TILE_SIZE, TILE_SIZE)
self.direction = direction
self.image = pygame.image.load('resources/images/tractor_right.png').convert_alpha()
self.tractor_start = tractor_start #important for bfs - prevents from spawning obstacles on these positions
self.tractor_end = tractor_end #
if (self.direction==1):
self.image = pygame.image.load('resources/images/tractor_up.png').convert_alpha()
elif (self.direction==3):
self.image = pygame.image.load('resources/images/tractor_down.png').convert_alpha()
elif (self.direction==4):
self.image = pygame.image.load('resources/images/tractor_left.png').convert_alpha()
def work_on_field(self, tile, ground, plant1):
if plant1 is None:
@ -58,3 +78,84 @@ class Tractor:
if ground.nutrients_level < plant1.nutrients_requirements:
ground.nutrients_level += 20
print("Tractor added some nutrients")
def move(self):
if self.direction == DIRECTION_EAST:
self.rect.x += TILE_SIZE
elif self.direction == DIRECTION_WEST:
self.rect.x -= TILE_SIZE
elif self.direction == DIRECTION_NORTH:
self.rect.y -= TILE_SIZE
elif self.direction == DIRECTION_SOUTH:
self.rect.y += TILE_SIZE
def rotate_to_right(self):
if self.direction == 4:
self.direction = 1
else:
self.direction += 1
self.image = pygame.transform.rotate(self.image, -90)
def rotate_to_left(self):
if self.direction == 1:
self.direction = 4
else:
self.direction -= 1
self.image = pygame.transform.rotate(self.image, 90)
#checks if we can move further and if we won't get out of boundaries - for bfs:
def can_it_move_node(node):
if node.get_direction() == DIRECTION_EAST and node.get_x() + TILE_SIZE < 830: #last tile on the west has y:797
return "move east"
elif node.get_direction() == DIRECTION_WEST and node.get_x() - TILE_SIZE >=fieldX:
return "move west"
elif node.get_direction() == DIRECTION_NORTH and node.get_y() - TILE_SIZE >=fieldY:
return "move north"
elif node.get_direction() == DIRECTION_SOUTH and node.get_y() + TILE_SIZE < 760: #last tile on the south has y:727
return "move south"
else:
return False
#checks if there's an obstacle on the given coordinates (important for bfs):
def is_obstacle(self, x, y):
for tile in tiles:
if tile.x == x and tile.y == y:
ground = tile.ground
if ground.obstacle and self.tractor_start != (x, y) and self.tractor_end != (x,y):
return True
return False
def draw_tractor(self, win):
imageTractor = pygame.transform.scale(self.image, (TILE_SIZE, TILE_SIZE))
win.blit(imageTractor, (self.rect.x, self.rect.y))
pygame.display.flip()
#translates move_list generated by bfs into the actual movement:
def do_actions(tractor, WIN, move_list):
trail = pygame.image.load("resources/images/background.jpg").convert_alpha()
trail = pygame.transform.scale(trail, (TILE_SIZE, TILE_SIZE))
pygame.display.update()
for move in move_list:
WIN.blit(trail, (tractor.rect.x, tractor.rect.y, TILE_SIZE, TILE_SIZE))
if move == "move":
tractor.move()
elif move == "rotate_right":
tractor.rotate_to_right()
elif move == "rotate_left":
tractor.rotate_to_left()
tractor.draw_tractor(WIN)
pygame.display.update()
time.sleep(1)

171
source/bfs.py Normal file
View File

@ -0,0 +1,171 @@
from area.constants import TILE_SIZE
import copy
from area.tractor import Tractor
class Istate:
def __init__(self, x, y, direction ):
self.x = x
self.y = y
self.direction = direction
def get_x(self):
return self.x
def set_x(self, _x):
self.x = _x
def get_y(self):
return self.y
def set_y(self, _y):
self.y = _y
def get_direction(self):
return self.direction
def set_direction(self, _direction):
self.parent = _direction
class Node:
def __init__(self, x, y, direction, parent, action):
self.x = x
self.y = y
self.direction = direction
self.action = action
self.parent = parent
#getters and setters:
def get_parent(self):
return self.parent
def set_parent(self, _parent):
self.parent = _parent
def get_action(self):
return self.action
def set_action(self, _action):
self.parent = _action
def get_x(self):
return self.x
def set_x(self, _x):
self.x = _x
def get_y(self):
return self.y
def set_y(self, _y):
self.y = _y
def get_direction(self):
return self.direction
def set_direction(self, _direction):
self.parent = _direction
def copy(self):
return copy.copy(self)
def goal_test(elem, goalstate):
if elem.get_x() == goalstate[0] and elem.get_y() == goalstate[1]:
return True
else:
return False
#State as a tuple (x,y,direction)
#actions(string): move, rotate_to_left, rotate_to_right
#main search function:
def graphsearch(istate, succ, goaltest, tractor):
fringe = []
explored = []
node = Node(istate.get_x(), istate.get_y(), istate.get_direction(), None, None)
fringe.append(node)
while True:
if not fringe:
return False
elem = fringe.pop(0)
temp = copy.copy(elem)
if goal_test(elem, goaltest) is True: #jesli True zwroc ciag akcji
return get_moves(elem)
explored.append(elem)
for (action, state) in succ(temp, tractor): #dla wszystkich mozliwych stanow i akcjach otrzymanych dla danego wierzcholka
fringe_tuple = []
explored_tuple = []
for node in fringe:
fringe_tuple.append((node.get_x(), node.get_y(), node.get_direction()))
for node in explored:
explored_tuple.append((node.get_x(), node.get_y(), node.get_direction()))
if state not in fringe_tuple and state not in explored_tuple:
x = Node(state[0], state[1], state[2], elem, action)
fringe.append(x)
#funkcja nastepnika - jakie akcje sa mozlwie na danym polu i jaki bedzie stan po wykonaniu tych akcji
def succ(elem, tractor):
actions_states = []
temp = copy.copy(elem.get_direction())
if temp == 1:
temp = 4
else:
temp -= 1
actions_states.append(("rotate_left", (elem.get_x(), elem.get_y(), temp)))
temp = copy.copy(elem.get_direction())
if temp == 4:
temp = 1
else:
temp += 1
actions_states.append(("rotate_right", (elem.get_x(), elem.get_y(), temp)))
temp_move_east = elem.get_x() + TILE_SIZE
temp_move_west = elem.get_x() - TILE_SIZE
temp_move_north = elem.get_y() - TILE_SIZE
temp_move_south = elem.get_y() + TILE_SIZE
if Tractor.can_it_move_node(elem) == "move east" and not Tractor.is_obstacle(tractor, temp_move_east, elem.get_y()):
actions_states.append(("move", (temp_move_east, elem.get_y(), elem.get_direction())))
elif Tractor.can_it_move_node(elem) == "move west" and not Tractor.is_obstacle(tractor, temp_move_west, elem.get_y()):
actions_states.append(("move", (temp_move_west, elem.get_y(), elem.get_direction())))
elif Tractor.can_it_move_node(elem) == "move north" and not Tractor.is_obstacle(tractor,elem.get_x(), temp_move_north):
actions_states.append(("move", (elem.get_x(), temp_move_north, elem.get_direction())))
elif Tractor.can_it_move_node(elem) == "move south" and not Tractor.is_obstacle(tractor, elem.get_x(), temp_move_south):
actions_states.append(("move", (elem.get_x(), temp_move_south, elem.get_direction())))
return actions_states
#returns list of actions
def get_moves(elem):
move_list = []
while (elem.get_parent() != None):
move_list.append(elem.get_action())
elem = elem.get_parent()
move_list.reverse()
return move_list

View File

@ -20,7 +20,7 @@ class Dirt:
elif i == 4:
self.weed = True
self.pest = True
elif i == 5 or i == 6 or i == 7:
elif i == 5:
self.obstacle = True
# add init, getters,setters

View File

@ -2,12 +2,14 @@ import pygame
import time
import random
from area.constants import WIDTH, HEIGHT
from area.constants import WIDTH, HEIGHT, TILE_SIZE
from area.field import drawWindow
from area.tractor import Tractor
from area.field import tiles
from area.tractor import Tractor, do_actions
from area.field import tiles, fieldX, fieldY
from area.field import get_tile_coordinates
from ground import Dirt
from plant import Plant
from bfs import graphsearch, Istate, succ
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Intelligent tractor')
@ -16,24 +18,64 @@ def main():
run = True
window = drawWindow(WIN)
pygame.display.update()
#getting coordinates of our "goal tile":
tile_index=127
tile_x, tile_y = get_tile_coordinates(tile_index)
if tile_x is not None and tile_y is not None:
print(f"Coordinates of tile {tile_index} are: ({tile_x}, {tile_y})")
else: print("Such tile does not exist")
#mark the goal tile:
tiles[tile_index].image = "resources/images/sampling_goal.png"
image = pygame.image.load(tiles[tile_index].image).convert()
image = pygame.transform.scale(image, (TILE_SIZE, TILE_SIZE))
WIN.blit(image, (tiles[tile_index].x, tiles[tile_index].y))
pygame.display.flip()
#graphsearch activation:
istate = Istate(170, 100, 2) #initial state
goaltest = []
goaltest.append(tile_x) #final state (consists of x and y because direction doesnt matter)
goaltest.append(tile_y)
tractor = Tractor(0*TILE_SIZE, 0*TILE_SIZE, 2, None, None)
tractor.rect.x += fieldX
tractor.rect.y += fieldY
tractor.tractor_start = ((istate.get_x(), istate.get_y()))
tractor.tractor_end = ((goaltest[0], goaltest[1]))
moves = (graphsearch(istate, succ, goaltest, tractor))
print(moves)
#main loop:
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#small test:
time.sleep(2)
#small test of work_on_field method:
time.sleep(1)
tile1 = tiles[0]
p1 = Plant('wheat', 'cereal', random.randint(1,100), random.randint(1,100), random.randint(1,100))
d1 = Dirt(random.randint(1, 100), random.randint(1,100))
d1.pests_and_weeds()
tile1.ground=d1
t1 = Tractor(10, 10)
t1.work_on_field(tile1, d1, p1)
time.sleep(3)
#movement based on route-planning (test):
tractor.draw_tractor(WIN)
time.sleep(1)
if moves != False:
do_actions(tractor, WIN, moves)
tractor.work_on_field(tile1, d1, p1)
time.sleep(30)
print("\n")
# in loop move tractor
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

View File

@ -43,5 +43,9 @@ class Tile:
else:
self.image = "resources/images/dirt.png"
self.photo = "resources/images/background.jpg"
ground = self.ground
if ground.obstacle and self.x != 170 and self.y != 100: #warunek po and do zmiany
self.image = "resources/images/rock_dirt.png"
# DISCLAMER check column and choose plant type ("potato","wheat" etc.)