Merge pull request 'Next increment - AI movement' (#18) from Automatic-robot-movement into main

Reviewed-on: s473601/Machine_learning_2023#18
Reviewed-by: Tim Barvenov <timbar@st.amu.edu.pl>
This commit is contained in:
Tim Barvenov 2023-04-21 10:14:31 +02:00
commit c95ce82ac4
5 changed files with 139 additions and 13 deletions

80
AI_brain/movement.py Normal file
View File

@ -0,0 +1,80 @@
from domain.commands.vacuum_move_command import VacuumMoveCommand
from domain.world import World
class State:
def __init__(self, x, y):
self.x = x
self.y = y
def __hash__(self):
return hash((self.x, self.y))
def __eq__(self, other):
return self.x == other.x and self.y == other.y
class StateGraphSearchBFS:
def __init__(self, world: World, start_state: State, goal_state: State):
self.start_state = start_state
self.goal_state = goal_state
self.visited = set()
self.parent = {}
self.actions = []
self.path = []
self.world = world
self.queue = []
def search(self):
self.queue.append(self.start_state)
self.visited.add(self.start_state)
while self.queue:
state = self.queue.pop(0)
if state == self.goal_state:
self.actions = self.get_actions()
self.path = self.get_path()
return True
for successor in self.successors(state):
if successor not in self.visited:
self.visited.add(successor)
self.parent[successor] = state
self.queue.append(successor)
return False
def successors(self, state):
new_successors = [
State(state.x + dx, state.y + dy)
for dx, dy in [(1, 0), (0, 1), (-1, 0), (0, -1)]
if self.world.accepted_move(state.x + dx, state.y + dy)
]
return new_successors
def get_actions(self):
actions = []
state = self.goal_state
while state != self.start_state:
parent_state = self.parent[state]
dx = state.x - parent_state.x
dy = state.y - parent_state.y
if dx == 1:
actions.append("RIGHT")
elif dx == -1:
actions.append("LEFT")
elif dy == 1:
actions.append("DOWN")
elif dy == -1:
actions.append("UP")
state = parent_state
actions.reverse()
return actions
def get_path(self):
path = []
state = self.goal_state
while state != self.start_state:
path.append((state.x, state.y))
state = self.parent[state]
path.append((self.start_state.x, self.start_state.y))
path.reverse()
return path

View File

@ -1,3 +1,4 @@
[APP] [APP]
cat = False cat = False
movment = human #(human, movment) movement = robot
#accept: human, robot

View File

@ -18,16 +18,7 @@ class VacuumMoveCommand(Command):
def run(self): def run(self):
end_x = self.vacuum.x + self.dx end_x = self.vacuum.x + self.dx
end_y = self.vacuum.y + self.dy end_y = self.vacuum.y + self.dy
if not self.world.accepted_move(end_x, end_y):
if (
end_x > self.world.width - 1
or end_y > self.world.height - 1
or end_x < 0
or end_y < 0
):
return
if self.world.is_obstacle_at(end_x, end_y):
return return
if self.world.is_garbage_at(end_x, end_y): if self.world.is_garbage_at(end_x, end_y):

View File

@ -33,3 +33,17 @@ class World:
def is_docking_station_at(self, x: int, y: int) -> bool: def is_docking_station_at(self, x: int, y: int) -> bool:
return bool(self.doc_station.x == x and self.doc_station.y == y) return bool(self.doc_station.x == x and self.doc_station.y == y)
def accepted_move(self, checking_x, checking_y):
if (
checking_x > self.width - 1
or checking_y > self.height - 1
or checking_x < 0
or checking_y < 0
):
return False
if self.is_obstacle_at(checking_x, checking_y):
return False
return True

44
main.py
View File

@ -11,6 +11,7 @@ from domain.entities.vacuum import Vacuum
from domain.entities.docking_station import Doc_Station from domain.entities.docking_station import Doc_Station
from domain.world import World from domain.world import World
from view.renderer import Renderer from view.renderer import Renderer
from AI_brain.movement import StateGraphSearchBFS, State
config = configparser.ConfigParser() config = configparser.ConfigParser()
@ -41,6 +42,43 @@ class Main:
pygame.quit() pygame.quit()
def run_robot(self):
self.renderer.render(self.world)
start_state = State(self.world.vacuum.x, self.world.vacuum.y)
end_state = State(self.world.doc_station.x, self.world.doc_station.y)
SGS_BFS = StateGraphSearchBFS(self.world, start_state, end_state)
if not SGS_BFS.search():
print("No solution")
exit(0)
SGS_BFS.actions.reverse()
while self.running:
self.renderer.render(self.world)
self.clock.tick(5)
if len(SGS_BFS.actions) > 0:
action_direction = SGS_BFS.actions.pop()
if action_direction == "UP":
self.commands.append(
VacuumMoveCommand(self.world, self.world.vacuum, (0, -1))
)
elif action_direction == "DOWN":
self.commands.append(
VacuumMoveCommand(self.world, self.world.vacuum, (0, 1))
)
elif action_direction == "LEFT":
self.commands.append(
VacuumMoveCommand(self.world, self.world.vacuum, (-1, 0))
)
elif action_direction == "RIGHT":
self.commands.append(
VacuumMoveCommand(self.world, self.world.vacuum, (1, 0))
)
self.update()
pygame.quit()
def process_input(self): def process_input(self):
for event in pygame.event.get(): for event in pygame.event.get():
if event.type == pygame.QUIT: if event.type == pygame.QUIT:
@ -79,7 +117,6 @@ def generate_world(tiles_x: int, tiles_y: int) -> World:
world.add_entity(Entity(temp_x, temp_y, "PEEL")) world.add_entity(Entity(temp_x, temp_y, "PEEL"))
world.vacuum = Vacuum(1, 1) world.vacuum = Vacuum(1, 1)
world.doc_station = Doc_Station(9, 8) world.doc_station = Doc_Station(9, 8)
print(config.getboolean("APP", "cat"))
if config.getboolean("APP", "cat"): if config.getboolean("APP", "cat"):
world.cat = Cat(7, 8) world.cat = Cat(7, 8)
world.add_entity(world.cat) world.add_entity(world.cat)
@ -93,4 +130,7 @@ def generate_world(tiles_x: int, tiles_y: int) -> World:
if __name__ == "__main__": if __name__ == "__main__":
app = Main() app = Main()
app.run() if config["APP"]["movement"] == "human":
app.run()
elif config["APP"]["movement"] == "robot":
app.run_robot()