From afdcac8b561e1f8d324d7bdac4e29799dced777a Mon Sep 17 00:00:00 2001 From: Eikthyrnir Date: Fri, 21 Apr 2023 13:11:52 +0200 Subject: [PATCH 1/3] now the vacuum can only go straight and rotate left/right to change direction --- .gitignore | 5 ++- AI_brain/template_bfs.py | 78 +++++++++++++++++++++++++++++++++++++++ domain/entities/vacuum.py | 1 + main.py | 53 +++++++++++++++++--------- view/renderer.py | 11 ++++++ 5 files changed, 129 insertions(+), 19 deletions(-) create mode 100644 AI_brain/template_bfs.py diff --git a/.gitignore b/.gitignore index 8278ed1..7efaafe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ /venv .DS_Store /.vscode -__pycache__ \ No newline at end of file +__pycache__ + +#PyCharm +.idea/ \ No newline at end of file diff --git a/AI_brain/template_bfs.py b/AI_brain/template_bfs.py new file mode 100644 index 0000000..02a258d --- /dev/null +++ b/AI_brain/template_bfs.py @@ -0,0 +1,78 @@ +import queue + +from domain.commands.vacuum_move_command import VacuumMoveCommand +from domain.world import World + + +class State: + def __init__(self, x, y, direction=(1, 0)): + self.x = x + self.y = y + self.direction = direction + + def __hash__(self): + return hash((self.x, self.y)) + + def __eq__(self, other): + return (self.x == other.x and self.y == other.y + and self.direction == other.direction) + + +class Node: + def __init__(self, state: State): + self.state = state + self.parent = None + self.action = None + + +def action_sequence(node: Node): + actions = [] + while node.parent: + actions.append(node.action) + node = node.parent + actions.reverse() + return actions + + +class TemplateBFS: + def __init__(self, world: World, start_state: State, goal_state: State): + self.world = world + self.start_state = start_state + self.goal_state = goal_state + self.fringe = queue.Queue() + self.enqueued_states = set() + self.explored = set() + self.actions = [] + + def search(self): + self.fringe.put(Node(self.start_state)) + + while self.fringe: + elem = self.fringe.get() + if elem.state == self.goal_state: + self.actions = action_sequence(elem) + return True + self.explored.add(elem.state) + + for (action, state) in self.successors(elem.state): + if state in self.explored or state in self.enqueued_states: + continue + next_node = Node(state) + next_node.action = action + next_node.parent = elem + self.fringe.put(next_node) + self.enqueued_states.add(state) + + return False + + def successors(self, state: State): + new_successors = [ + # ("GO", State(state.x + state.direction[0], state.y + state.direction[1], state.direction)), + # rotate right + ("RR", State(state.x, state.y, (-state.direction[1], state.direction[0]))), + # rotate left + ("RL", State(state.x, state.y, (state.direction[1], -state.direction[0]))), + ] + if self.world.accepted_move(state.x + state.direction[0], state.y + state.direction[1]): + new_successors.append(("GO", State(state.x + state.direction[0], state.y + state.direction[1], state.direction))) + return new_successors diff --git a/domain/entities/vacuum.py b/domain/entities/vacuum.py index 1ccbc14..54b86e0 100644 --- a/domain/entities/vacuum.py +++ b/domain/entities/vacuum.py @@ -5,6 +5,7 @@ from domain.world import World class Vacuum(Entity): def __init__(self, x: int, y: int): super().__init__(x, y, "VACUUM") + self.direction = (1, 0) self.battery = 100 self.cleaning_detergent = 100 self.container_filling = 0 diff --git a/main.py b/main.py index 8da28a6..0b23686 100644 --- a/main.py +++ b/main.py @@ -11,7 +11,8 @@ from domain.entities.vacuum import Vacuum from domain.entities.docking_station import Doc_Station from domain.world import World from view.renderer import Renderer -from AI_brain.movement import StateGraphSearchBFS, State +# from AI_brain.movement import StateGraphSearchBFS, State +from AI_brain.template_bfs import TemplateBFS, State config = configparser.ConfigParser() @@ -48,7 +49,8 @@ class Main: 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) + # SGS_BFS = StateGraphSearchBFS(self.world, start_state, end_state) + SGS_BFS = TemplateBFS(self.world, start_state, end_state) if not SGS_BFS.search(): print("No solution") exit(0) @@ -59,26 +61,41 @@ class Main: 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.handle_action1(action_direction) + self.handle_action2(action_direction) + self.update() pygame.quit() + def handle_action1(self, action): + if action == "UP": + self.commands.append( + VacuumMoveCommand(self.world, self.world.vacuum, (0, -1)) + ) + elif action == "DOWN": + self.commands.append( + VacuumMoveCommand(self.world, self.world.vacuum, (0, 1)) + ) + elif action == "LEFT": + self.commands.append( + VacuumMoveCommand(self.world, self.world.vacuum, (-1, 0)) + ) + elif action == "RIGHT": + self.commands.append( + VacuumMoveCommand(self.world, self.world.vacuum, (1, 0)) + ) + + def handle_action2(self, action): + if action == "GO": + self.commands.append( + VacuumMoveCommand(self.world, self.world.vacuum, self.world.vacuum.direction) + ) + elif action == "RR": + self.world.vacuum.direction = (-self.world.vacuum.direction[1], self.world.vacuum.direction[0]) + elif action == "RL": + self.world.vacuum.direction = (self.world.vacuum.direction[1], -self.world.vacuum.direction[0]) + def process_input(self): for event in pygame.event.get(): if event.type == pygame.QUIT: diff --git a/view/renderer.py b/view/renderer.py index 84dabde..e8294cb 100644 --- a/view/renderer.py +++ b/view/renderer.py @@ -159,6 +159,7 @@ class Renderer: draw_pos[1] + self.tile_height, ) self.screen.blit(text_surface, text_pos) + sprite = self.create_vacuum_sprite(entity) if "DOC_STATION" in entity.type: draw_pos = ( (entity.x - 0.1) * self.tile_width, @@ -166,6 +167,16 @@ class Renderer: ) self.screen.blit(sprite, draw_pos) + def create_vacuum_sprite(self, vacuum): + angles = { + (1, 0): 0, + (-1, 0): 180, + (0, 1): 270, + (0, -1): 90, + } + init_sprite = self.sprites.get(vacuum.type, None) + return pygame.transform.rotate(init_sprite, angles[vacuum.direction]) + def draw_sprite(self, x: int, y: int, sprite_name: str): self.screen.blit( self.sprites[sprite_name], (x * self.tile_width, y * self.tile_height) -- 2.20.1 From f4ad111371ef9b49c7a9332766371ecca5dd89d4 Mon Sep 17 00:00:00 2001 From: Eikthyrnir Date: Mon, 1 May 2023 23:59:15 +0200 Subject: [PATCH 2/3] refactoring --- AI_brain/{movement.py => go_any_direction.py} | 2 +- .../{template_bfs.py => rotate_and_go_bfs.py} | 15 ++++++++---- main.py | 24 +++++++++++-------- 3 files changed, 25 insertions(+), 16 deletions(-) rename AI_brain/{movement.py => go_any_direction.py} (98%) rename AI_brain/{template_bfs.py => rotate_and_go_bfs.py} (84%) diff --git a/AI_brain/movement.py b/AI_brain/go_any_direction.py similarity index 98% rename from AI_brain/movement.py rename to AI_brain/go_any_direction.py index acd4063..74f36b9 100644 --- a/AI_brain/movement.py +++ b/AI_brain/go_any_direction.py @@ -14,7 +14,7 @@ class State: return self.x == other.x and self.y == other.y -class StateGraphSearchBFS: +class GoAnyDirectionBFS: def __init__(self, world: World, start_state: State, goal_state: State): self.start_state = start_state self.goal_state = goal_state diff --git a/AI_brain/template_bfs.py b/AI_brain/rotate_and_go_bfs.py similarity index 84% rename from AI_brain/template_bfs.py rename to AI_brain/rotate_and_go_bfs.py index 02a258d..966650d 100644 --- a/AI_brain/template_bfs.py +++ b/AI_brain/rotate_and_go_bfs.py @@ -1,6 +1,5 @@ import queue -from domain.commands.vacuum_move_command import VacuumMoveCommand from domain.world import World @@ -34,7 +33,7 @@ def action_sequence(node: Node): return actions -class TemplateBFS: +class RotateAndGoBFS: def __init__(self, world: World, start_state: State, goal_state: State): self.world = world self.start_state = start_state @@ -49,7 +48,7 @@ class TemplateBFS: while self.fringe: elem = self.fringe.get() - if elem.state == self.goal_state: + if self.is_goal(elem.state): self.actions = action_sequence(elem) return True self.explored.add(elem.state) @@ -67,12 +66,18 @@ class TemplateBFS: def successors(self, state: State): new_successors = [ - # ("GO", State(state.x + state.direction[0], state.y + state.direction[1], state.direction)), # rotate right ("RR", State(state.x, state.y, (-state.direction[1], state.direction[0]))), # rotate left ("RL", State(state.x, state.y, (state.direction[1], -state.direction[0]))), ] if self.world.accepted_move(state.x + state.direction[0], state.y + state.direction[1]): - new_successors.append(("GO", State(state.x + state.direction[0], state.y + state.direction[1], state.direction))) + new_successors.append( + ("GO", State(state.x + state.direction[0], state.y + state.direction[1], state.direction))) return new_successors + + def is_goal(self, state: State) -> bool: + return ( + state.x == self.goal_state.x + and state.y == self.goal_state.y + ) diff --git a/main.py b/main.py index 0b23686..e97ac54 100644 --- a/main.py +++ b/main.py @@ -12,7 +12,7 @@ from domain.entities.docking_station import Doc_Station from domain.world import World from view.renderer import Renderer # from AI_brain.movement import StateGraphSearchBFS, State -from AI_brain.template_bfs import TemplateBFS, State +from AI_brain.rotate_and_go_bfs import RotateAndGoBFS, State config = configparser.ConfigParser() @@ -49,22 +49,26 @@ class Main: 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) - SGS_BFS = TemplateBFS(self.world, start_state, end_state) - if not SGS_BFS.search(): + # path_searcher = GoAnyDirectionBFS(self.world, start_state, end_state) + path_searcher = RotateAndGoBFS(self.world, start_state, end_state) + if not path_searcher.search(): print("No solution") exit(0) - SGS_BFS.actions.reverse() + path_searcher.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() + for event in pygame.event.get(): + if event.type == pygame.QUIT: + self.running = False + + if len(path_searcher.actions) > 0: + action_direction = path_searcher.actions.pop() # self.handle_action1(action_direction) self.handle_action2(action_direction) - self.update() + self.update() + self.renderer.render(self.world) + self.clock.tick(5) pygame.quit() -- 2.20.1 From 2d4cc41157c8ecd12866a60ec3f0ea214596921b Mon Sep 17 00:00:00 2001 From: Eikthyrnir Date: Tue, 2 May 2023 00:02:00 +0200 Subject: [PATCH 3/3] refactoring --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index e97ac54..0bdd36a 100644 --- a/main.py +++ b/main.py @@ -11,7 +11,7 @@ from domain.entities.vacuum import Vacuum from domain.entities.docking_station import Doc_Station from domain.world import World from view.renderer import Renderer -# from AI_brain.movement import StateGraphSearchBFS, State +# from AI_brain.movement import GoAnyDirectionBFS, State from AI_brain.rotate_and_go_bfs import RotateAndGoBFS, State -- 2.20.1