add graphsearch
This commit is contained in:
parent
42a04d00c5
commit
d5be042dad
@ -1,8 +1,11 @@
|
||||
#!/usr/bin/python3
|
||||
import copy
|
||||
from queue import Queue
|
||||
|
||||
import pygame
|
||||
|
||||
from config import *
|
||||
from app.graphsearch import Node, Graphsearch
|
||||
from app.board import Board
|
||||
from app.tractor import Tractor
|
||||
|
||||
@ -14,6 +17,8 @@ class App:
|
||||
self.__clock = None
|
||||
self.__board = Board()
|
||||
self.__tractor = Tractor(self.__board)
|
||||
self.__bot_is_running = False
|
||||
self.__moves = None
|
||||
|
||||
def initialize(self):
|
||||
pygame.init()
|
||||
@ -68,6 +73,15 @@ class App:
|
||||
if keys[pygame.K_r]:
|
||||
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):
|
||||
pygame.display.flip()
|
||||
@ -75,15 +89,23 @@ class App:
|
||||
def quit(self):
|
||||
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):
|
||||
self.initialize()
|
||||
|
||||
while self.__running:
|
||||
self.__clock.tick(FPS)
|
||||
|
||||
for event in pygame.event.get():
|
||||
self.event_handler(event)
|
||||
|
||||
if not self.__bot_is_running:
|
||||
self.keys_pressed_handler()
|
||||
|
||||
self.loop_handler()
|
||||
|
20
app/board.py
20
app/board.py
@ -1,21 +1,29 @@
|
||||
#!/usr/bin/python3
|
||||
import copy
|
||||
|
||||
import pygame
|
||||
import random
|
||||
|
||||
from app.fields import *
|
||||
from app.utils import get_class
|
||||
|
||||
|
||||
class Board:
|
||||
def __init__(self):
|
||||
self.__fields = []
|
||||
def __init__(self, fields=None):
|
||||
if fields is None:
|
||||
fields = []
|
||||
self.__fields = fields
|
||||
self.create_board()
|
||||
self.fill()
|
||||
self.generate_board()
|
||||
# print(self.__fields)
|
||||
|
||||
def get_fields(self):
|
||||
def get_fields(self) -> list:
|
||||
return self.__fields
|
||||
|
||||
def get_field(self, x: int, y: int) -> BaseField:
|
||||
return self.__fields[x][y]
|
||||
|
||||
def create_board(self):
|
||||
for i in range(HORIZONTAL_NUM_OF_FIELDS):
|
||||
self.__fields.append([])
|
||||
@ -42,3 +50,9 @@ class Board:
|
||||
pos_x = x * FIELD_SIZE
|
||||
pos_y = y * FIELD_SIZE
|
||||
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
155
app/graphsearch.py
Normal 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)
|
124
app/tractor.py
124
app/tractor.py
@ -1,5 +1,8 @@
|
||||
#!/usr/bin/python3
|
||||
from __future__ import annotations
|
||||
import random
|
||||
import threading
|
||||
from queue import Queue
|
||||
|
||||
import pygame
|
||||
import os
|
||||
@ -21,22 +24,22 @@ class Tractor(BaseField):
|
||||
super().__init__(os.path.join(RESOURCE_DIR, f"{TRACTOR}.{PNG}"))
|
||||
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.__angle = 0.0
|
||||
self.__direction = 0.0
|
||||
self.__move = FIELD_SIZE
|
||||
self.__board = board
|
||||
self.__harvested_corps = []
|
||||
|
||||
def draw(self, screen: pygame.Surface):
|
||||
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
|
||||
def move(self):
|
||||
if self.__angle == 0.0:
|
||||
if self.__direction == 0.0:
|
||||
self.move_right()
|
||||
elif self.__angle == 90.0:
|
||||
elif self.__direction == 90.0:
|
||||
self.move_up()
|
||||
elif self.__angle == 180.0:
|
||||
elif self.__direction == 180.0:
|
||||
self.move_left()
|
||||
else:
|
||||
self.move_down()
|
||||
@ -58,10 +61,10 @@ class Tractor(BaseField):
|
||||
self.__pos_x += self.__move
|
||||
|
||||
def rotate_left(self):
|
||||
self.__angle = (self.__angle - 90.0) % 360.0
|
||||
self.__direction = (self.__direction - 90.0) % 360.0
|
||||
|
||||
def rotate_right(self):
|
||||
self.__angle = (self.__angle + 90.0) % 360.0
|
||||
self.__direction = (self.__direction + 90.0) % 360.0
|
||||
|
||||
def hydrate(self):
|
||||
if self.check_field(Sand):
|
||||
@ -75,6 +78,19 @@ class Tractor(BaseField):
|
||||
print("Hydrate plant")
|
||||
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):
|
||||
field = self.get_field_from_board()
|
||||
if self.check_field(Sand) and not field.is_sowed:
|
||||
@ -102,11 +118,13 @@ class Tractor(BaseField):
|
||||
|
||||
def irrigate_plants(self, field: Plant):
|
||||
field.is_hydrated = True
|
||||
self.do_time_action(CROPS)
|
||||
# self.do_time_action(CROPS)
|
||||
self.do_action(CROPS)
|
||||
|
||||
def irrigate_sand(self, field: Sand):
|
||||
field.is_hydrated = True
|
||||
self.do_time_action(PLANTS)
|
||||
# self.do_time_action(PLANTS)
|
||||
self.do_action(PLANTS)
|
||||
|
||||
def harvest_crops(self, field: Crops):
|
||||
self.__harvested_corps.append(type(field).__name__)
|
||||
@ -135,6 +153,9 @@ class Tractor(BaseField):
|
||||
x, y = self.get_position()
|
||||
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):
|
||||
x = self.__pos_x // FIELD_SIZE
|
||||
y = self.__pos_y // FIELD_SIZE
|
||||
@ -153,3 +174,88 @@ class Tractor(BaseField):
|
||||
def __str__(self):
|
||||
x, y = self.get_position()
|
||||
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()
|
||||
|
29
config.py
29
config.py
@ -3,11 +3,14 @@
|
||||
import os
|
||||
|
||||
__all__ = (
|
||||
'WIDTH', 'HEIGHT', 'FIELD_SIZE',
|
||||
'WIDTH', 'HEIGHT', 'FIELD_SIZE', 'TIME_OF_MOVING',
|
||||
'VERTICAL_NUM_OF_FIELDS', 'HORIZONTAL_NUM_OF_FIELDS',
|
||||
'FPS', 'CAPTION', 'RESOURCE_DIR', 'TRACTOR', 'PNG',
|
||||
'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:
|
||||
@ -40,5 +43,27 @@ SUNFLOWER = '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
|
||||
TIME_OF_GROWING = 2
|
||||
TIME_OF_MOVING = 2
|
||||
|
Loading…
Reference in New Issue
Block a user