Next increment - AI movement #18
80
AI_brain/movement.py
Normal file
80
AI_brain/movement.py
Normal 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
|
@ -1,3 +1,4 @@
|
|||||||
[APP]
|
[APP]
|
||||||
cat = False
|
cat = False
|
||||||
movment = human #(human, movment)
|
movement = robot
|
||||||
|
#accept: human, robot
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
42
main.py
42
main.py
@ -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()
|
||||||
|
if config["APP"]["movement"] == "human":
|
||||||
app.run()
|
app.run()
|
||||||
|
elif config["APP"]["movement"] == "robot":
|
||||||
|
app.run_robot()
|
||||||
|
Loading…
Reference in New Issue
Block a user