add graphsearch

This commit is contained in:
Dominik Cupał 2021-04-13 09:55:19 +02:00
parent 42a04d00c5
commit d5be042dad
5 changed files with 338 additions and 16 deletions

View File

@ -1,8 +1,11 @@
#!/usr/bin/python3 #!/usr/bin/python3
import copy
from queue import Queue
import pygame import pygame
from config import * from config import *
from app.graphsearch import Node, Graphsearch
from app.board import Board from app.board import Board
from app.tractor import Tractor from app.tractor import Tractor
@ -14,6 +17,8 @@ class App:
self.__clock = None self.__clock = None
self.__board = Board() self.__board = Board()
self.__tractor = Tractor(self.__board) self.__tractor = Tractor(self.__board)
self.__bot_is_running = False
self.__moves = None
def initialize(self): def initialize(self):
pygame.init() pygame.init()
@ -68,6 +73,15 @@ class App:
if keys[pygame.K_r]: if keys[pygame.K_r]:
self.__tractor.rotate_right() self.__tractor.rotate_right()
if keys[pygame.K_b]:
self.get_moves_by_bfs()
if not self.__moves:
print(f"Bfs is failed")
else:
print(f"Bfs is succeed")
self.__bot_is_running = True
self.__tractor.move_by_bfs_handler(self.__moves)
def update_screen(self): def update_screen(self):
pygame.display.flip() pygame.display.flip()
@ -75,16 +89,24 @@ class App:
def quit(self): def quit(self):
pygame.quit() pygame.quit()
def get_moves_by_bfs(self):
x, y = self.__tractor.get_position()
node = Node(None, x, y, self.__tractor.get_direction(), 0, "initial state", "movement")
board = copy.deepcopy(self.__board)
self.__moves = Graphsearch.bfs(Queue(), Queue(), node,
lambda n=node, b=board: Graphsearch.succ(n, b),
lambda n=node: Graphsearch.goaltest(n), board)
def run(self): def run(self):
self.initialize() self.initialize()
while self.__running: while self.__running:
self.__clock.tick(FPS) self.__clock.tick(FPS)
for event in pygame.event.get(): for event in pygame.event.get():
self.event_handler(event) self.event_handler(event)
self.keys_pressed_handler() if not self.__bot_is_running:
self.keys_pressed_handler()
self.loop_handler() self.loop_handler()
self.update_screen() self.update_screen()

View File

@ -1,21 +1,29 @@
#!/usr/bin/python3 #!/usr/bin/python3
import copy
import pygame import pygame
import random import random
from app.fields import * from app.fields import *
from app.utils import get_class from app.utils import get_class
class Board: class Board:
def __init__(self): def __init__(self, fields=None):
self.__fields = [] if fields is None:
fields = []
self.__fields = fields
self.create_board() self.create_board()
self.fill() self.fill()
self.generate_board() self.generate_board()
# print(self.__fields) # print(self.__fields)
def get_fields(self): def get_fields(self) -> list:
return self.__fields return self.__fields
def get_field(self, x: int, y: int) -> BaseField:
return self.__fields[x][y]
def create_board(self): def create_board(self):
for i in range(HORIZONTAL_NUM_OF_FIELDS): for i in range(HORIZONTAL_NUM_OF_FIELDS):
self.__fields.append([]) self.__fields.append([])
@ -42,3 +50,9 @@ class Board:
pos_x = x * FIELD_SIZE pos_x = x * FIELD_SIZE
pos_y = y * FIELD_SIZE pos_y = y * FIELD_SIZE
field.draw_field(screen, pos_x, pos_y) field.draw_field(screen, pos_x, pos_y)
def print_board(self):
for i in range(HORIZONTAL_NUM_OF_FIELDS):
for j in range(VERTICAL_NUM_OF_FIELDS):
print(f"{j} - {type(self.__fields[i][j]).__name__}", end=" | ")
print()

155
app/graphsearch.py Normal file
View File

@ -0,0 +1,155 @@
from __future__ import annotations
import copy
from typing import Callable, Union
from queue import Queue
from app.board import Board
from app.tractor import Tractor
from app.fields import Clay, Sand, Plant, CROPS
from config import *
class Node:
def __init__(self, parent: Union[Node, None], x: int, y: int,
direction: float, amount_of_harvested_crops: int,
movement: str, action: str):
self.__x = x
self.__y = y
self.__parent = parent
self.__direction = direction
self.__movement = movement
self.__action = action
self.__amount_of_harvested_crops = amount_of_harvested_crops
# self.__type_field = type_field
def get_x(self) -> int:
return self.__x
def get_y(self) -> int:
return self.__y
def get_node(self) -> Node:
return self.__parent
def get_direction(self) -> float:
return self.__direction
def get_action(self) -> str:
return self.__action
def get_movement(self) -> str:
return self.__movement
def get_amount_of_harvested_crops(self) -> int:
return self.__amount_of_harvested_crops
class Graphsearch:
@staticmethod
def succ(item: Node, board: Board) -> list:
# list of tuples (movement,action),(x,y,direction, harvested_crops)
actions = []
x = item.get_x()
y = item.get_y()
current_harvested_crops = item.get_amount_of_harvested_crops()
# do action ex. harvest:
field = board.get_field(x, y)
action_name = A_DO_NOTHING
if isinstance(field, Clay):
# fertilize
action_name = A_FERTILIZE
field = Tractor.fertilize_clay_succ(field, board, x, y)
elif isinstance(field, Sand):
# sow, letter hydrate
action_name = A_SOW
field = Tractor.sow_succ(field)
field = Tractor.irrigate_sand_succ(field, board, x, y)
# action_name = A_HYDRATE
elif isinstance(field, Plant):
action_name = A_HYDRATE
field = Tractor.irrigate_plants_succ(field, board, x, y)
# hydrate
elif type(field).__name__ in CROPS:
# harvest
action_name = A_HARVEST
field = Tractor.harvest_crops_succ(board, x, y)
current_harvested_crops += 1
# print(current_harvested_crops)
# move
current_direction = item.get_direction()
tractor_move = Tractor.move_is_correct(x, y, current_direction)
# print(f"res: {tractor_move}")
if tractor_move is not None:
pos_x, pos_y = tractor_move
actions.append(((M_GO_FORWARD, action_name),
(pos_x, pos_y, current_direction, current_harvested_crops)))
rotated_direction = (current_direction - 90.0) % 360.0
actions.append(((M_ROTATE_LEFT, action_name), (x, y, rotated_direction, current_harvested_crops)))
rotated_direction = (current_direction + 90.0) % 360.0
actions.append(((M_ROTATE_RIGHT, action_name), (x, y, rotated_direction, current_harvested_crops)))
return actions
@staticmethod
def goaltest(item: Node) -> bool:
# print(item.get_amount_of_harvested_crops())
if item.get_amount_of_harvested_crops() == AMOUNT_OF_CROPS:
return True
else:
return False
@staticmethod
def get_all_moves(item: Node):
moves = []
str_moves = []
while item.get_node() is not None:
moves.append((item.get_movement(),item.get_action()))
str_moves.append(
f"{item.get_action()} - {item.get_movement()} - {item.get_x()}:{item.get_y()} {item.get_direction()}")
item = item.get_node()
print(str_moves[::-1])
return moves[::-1]
@staticmethod
def bfs(fringe: Queue, explored: Queue, istate: Node,
succ: Callable[[Node, Board], list],
goaltest: Callable[[Node], bool], board: Board):
print(f"Start bfs")
fringe.put(istate)
while True:
if fringe.empty():
# print(list(explored.queue))
# return Graphsearch.get_all_moves(explored.get())
return False
item = fringe.get()
if goaltest(item):
# board.print_board()
return Graphsearch.get_all_moves(item)
copied_item = copy.deepcopy(item)
explored.put(item)
for (action, state) in succ(copied_item, board):
# print(state)
fringe_items = []
explored_items = []
[fringe_items.append((i.get_x(), i.get_y(), i.get_direction()))
for i in fringe.queue]
[explored_items.append((i.get_x(), i.get_y(), i.get_direction()))
for i in explored.queue]
if state[:-1] not in fringe_items and state[:-1] not in explored_items:
n = Node(item, *state, *action)
fringe.put(n)

View File

@ -1,5 +1,8 @@
#!/usr/bin/python3 #!/usr/bin/python3
from __future__ import annotations
import random import random
import threading
from queue import Queue
import pygame import pygame
import os import os
@ -21,22 +24,22 @@ class Tractor(BaseField):
super().__init__(os.path.join(RESOURCE_DIR, f"{TRACTOR}.{PNG}")) super().__init__(os.path.join(RESOURCE_DIR, f"{TRACTOR}.{PNG}"))
self.__pos_x = (int(HORIZONTAL_NUM_OF_FIELDS / 2) - 1) * FIELD_SIZE self.__pos_x = (int(HORIZONTAL_NUM_OF_FIELDS / 2) - 1) * FIELD_SIZE
self.__pos_y = (int(VERTICAL_NUM_OF_FIELDS / 2) - 1) * FIELD_SIZE self.__pos_y = (int(VERTICAL_NUM_OF_FIELDS / 2) - 1) * FIELD_SIZE
self.__angle = 0.0 self.__direction = 0.0
self.__move = FIELD_SIZE self.__move = FIELD_SIZE
self.__board = board self.__board = board
self.__harvested_corps = [] self.__harvested_corps = []
def draw(self, screen: pygame.Surface): def draw(self, screen: pygame.Surface):
self.draw_field(screen, self.__pos_x + FIELD_SIZE / 2, self.__pos_y + FIELD_SIZE / 2, self.draw_field(screen, self.__pos_x + FIELD_SIZE / 2, self.__pos_y + FIELD_SIZE / 2,
is_centered=True, size=(FIELD_SIZE, FIELD_SIZE), angle=self.__angle) is_centered=True, size=(FIELD_SIZE, FIELD_SIZE), angle=self.__direction)
# Key methods handlers # Key methods handlers
def move(self): def move(self):
if self.__angle == 0.0: if self.__direction == 0.0:
self.move_right() self.move_right()
elif self.__angle == 90.0: elif self.__direction == 90.0:
self.move_up() self.move_up()
elif self.__angle == 180.0: elif self.__direction == 180.0:
self.move_left() self.move_left()
else: else:
self.move_down() self.move_down()
@ -58,10 +61,10 @@ class Tractor(BaseField):
self.__pos_x += self.__move self.__pos_x += self.__move
def rotate_left(self): def rotate_left(self):
self.__angle = (self.__angle - 90.0) % 360.0 self.__direction = (self.__direction - 90.0) % 360.0
def rotate_right(self): def rotate_right(self):
self.__angle = (self.__angle + 90.0) % 360.0 self.__direction = (self.__direction + 90.0) % 360.0
def hydrate(self): def hydrate(self):
if self.check_field(Sand): if self.check_field(Sand):
@ -75,6 +78,19 @@ class Tractor(BaseField):
print("Hydrate plant") print("Hydrate plant")
self.irrigate_plants(field) self.irrigate_plants(field)
def hydrate_sand(self):
if self.check_field(Sand):
field = self.get_field_from_board()
if not field.is_hydrated and field.is_sowed:
print('Hydrate soil')
self.irrigate_sand(field)
def hydrate_plant(self):
if self.check_field(Plant):
field = self.get_field_from_board()
if not field.is_hydrated:
print("Hydrate plant")
self.irrigate_plants(field)
def sow(self): def sow(self):
field = self.get_field_from_board() field = self.get_field_from_board()
if self.check_field(Sand) and not field.is_sowed: if self.check_field(Sand) and not field.is_sowed:
@ -102,11 +118,13 @@ class Tractor(BaseField):
def irrigate_plants(self, field: Plant): def irrigate_plants(self, field: Plant):
field.is_hydrated = True field.is_hydrated = True
self.do_time_action(CROPS) # self.do_time_action(CROPS)
self.do_action(CROPS)
def irrigate_sand(self, field: Sand): def irrigate_sand(self, field: Sand):
field.is_hydrated = True field.is_hydrated = True
self.do_time_action(PLANTS) # self.do_time_action(PLANTS)
self.do_action(PLANTS)
def harvest_crops(self, field: Crops): def harvest_crops(self, field: Crops):
self.__harvested_corps.append(type(field).__name__) self.__harvested_corps.append(type(field).__name__)
@ -135,6 +153,9 @@ class Tractor(BaseField):
x, y = self.get_position() x, y = self.get_position()
return self.__board.get_fields()[x][y] return self.__board.get_fields()[x][y]
def get_field_from_board_by_positions(self, x, y):
return self.__board.get_fields()[x][y]
def get_position(self): def get_position(self):
x = self.__pos_x // FIELD_SIZE x = self.__pos_x // FIELD_SIZE
y = self.__pos_y // FIELD_SIZE y = self.__pos_y // FIELD_SIZE
@ -153,3 +174,88 @@ class Tractor(BaseField):
def __str__(self): def __str__(self):
x, y = self.get_position() x, y = self.get_position()
return f"Position: {x}:{y} - {type(self.__board.get_fields()[x][y]).__name__}" return f"Position: {x}:{y} - {type(self.__board.get_fields()[x][y]).__name__}"
def get_direction(self):
return self.__direction
def move_by_bfs_handler(self, moves: list[tuple[str, str]]):
thread = threading.Thread(target=self.move_by_bfs, args=(moves,))
thread.start()
def move_by_bfs(self, moves: list[tuple[str, str]]):
print(moves)
print(f"Length of Moves {len(moves)} - {3**len(moves)}")
while len(moves) > 0:
movement, action = moves.pop(0)
# do action
time.sleep(0.5)
print(f"Action {action}")
if action == A_FERTILIZE:
self.fertilize()
elif action == A_SOW:
self.sow()
self.hydrate()
elif action == A_HYDRATE:
self.hydrate()
elif action == A_HARVEST:
self.harvest()
time.sleep(1)
# move
print(f"Move {movement}")
if movement == M_GO_FORWARD:
self.move()
elif movement == M_ROTATE_LEFT:
self.rotate_left()
elif movement == M_ROTATE_RIGHT:
self.rotate_right()
time.sleep(TIME_OF_MOVING)
@staticmethod
def move_is_correct(x: int, y: int, direction: float) -> Union[(int, int), None]:
pos_x = x * FIELD_SIZE
pos_y = y * FIELD_SIZE
if direction == D_NORTH and pos_y - FIELD_SIZE >= 0:
return x, y - 1
if direction == D_SOUTH and pos_y + 2 * FIELD_SIZE <= HEIGHT:
return x, y + 1
if direction == D_WEST and pos_x - FIELD_SIZE >= 0:
return x - 1, y
if direction == D_EAST and pos_x + 2 * FIELD_SIZE <= WIDTH:
return x + 1, y
return None
@staticmethod
def fertilize_clay_succ(field: Clay, board: Board, x: int, y: int):
field.is_fertilized = True
return Tractor.do_action_succ(board, x, y, (Sand.__name__,))
@staticmethod
def sow_succ(field: Sand):
field.is_sowed = True
return field
@staticmethod
def irrigate_plants_succ(field: Plant, board: Board, x: int, y: int):
field.is_hydrated = True
return Tractor.do_action_succ(board, x, y, CROPS)
@staticmethod
def irrigate_sand_succ(field: Sand, board: Board, x: int, y: int):
field.is_hydrated = True
return Tractor.do_action_succ(board, x, y, PLANTS)
@staticmethod
def harvest_crops_succ(board: Board, x: int, y: int):
# Tractor.__harvested_corps.append(type(field).__name__)
return Tractor.do_action_succ(board, x, y, SOILS)
@staticmethod
def do_action_succ(board: Board, x: int, y: int, types: tuple):
choosen_type = random.choice(types)
obj = get_class("app.fields", choosen_type)
board.get_fields()[x][y] = obj()
return obj()

View File

@ -3,11 +3,14 @@
import os import os
__all__ = ( __all__ = (
'WIDTH', 'HEIGHT', 'FIELD_SIZE', 'WIDTH', 'HEIGHT', 'FIELD_SIZE', 'TIME_OF_MOVING',
'VERTICAL_NUM_OF_FIELDS', 'HORIZONTAL_NUM_OF_FIELDS', 'VERTICAL_NUM_OF_FIELDS', 'HORIZONTAL_NUM_OF_FIELDS',
'FPS', 'CAPTION', 'RESOURCE_DIR', 'TRACTOR', 'PNG', 'FPS', 'CAPTION', 'RESOURCE_DIR', 'TRACTOR', 'PNG',
'SAND', 'CLAY', 'GRASS', 'CORN', 'SUNFLOWER', 'SAND', 'CLAY', 'GRASS', 'CORN', 'SUNFLOWER',
'FIELD_TYPES','TIME_OF_GROWING' 'FIELD_TYPES', 'TIME_OF_GROWING', 'AMOUNT_OF_CROPS',
'M_GO_FORWARD', 'M_ROTATE_LEFT', 'M_ROTATE_RIGHT',
'A_SOW', 'A_HARVEST', 'A_HYDRATE', 'A_FERTILIZE', 'A_DO_NOTHING',
'D_NORTH', 'D_EAST', 'D_SOUTH', 'D_WEST'
) )
# Board settings: # Board settings:
@ -40,5 +43,27 @@ SUNFLOWER = 'sunflower'
FIELD_TYPES = (SAND, CLAY, GRASS, CORN, SUNFLOWER) FIELD_TYPES = (SAND, CLAY, GRASS, CORN, SUNFLOWER)
# Directions
D_NORTH = 90.0
D_EAST = 0.0
D_SOUTH = 270.0
D_WEST = 180.0
# Goal Test
AMOUNT_OF_CROPS = 5
# Movements:
M_GO_FORWARD = "go forward"
M_ROTATE_LEFT = "rotate left"
M_ROTATE_RIGHT = "rotate right"
# Actions:
A_SOW = "sow"
A_HARVEST = "harvest"
A_HYDRATE = "hydrate"
A_FERTILIZE = "fertilize"
A_DO_NOTHING = "do nothing"
# Times # Times
TIME_OF_GROWING = 2 TIME_OF_GROWING = 2
TIME_OF_MOVING = 2