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]
|
||||
cat = False
|
||||
movment = human #(human, movment)
|
||||
movement = robot
|
||||
#accept: human, robot
|
@ -18,16 +18,7 @@ class VacuumMoveCommand(Command):
|
||||
def run(self):
|
||||
end_x = self.vacuum.x + self.dx
|
||||
end_y = self.vacuum.y + self.dy
|
||||
|
||||
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):
|
||||
if not self.world.accepted_move(end_x, end_y):
|
||||
return
|
||||
|
||||
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:
|
||||
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.world import World
|
||||
from view.renderer import Renderer
|
||||
from AI_brain.movement import StateGraphSearchBFS, State
|
||||
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
@ -41,6 +42,43 @@ class Main:
|
||||
|
||||
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):
|
||||
for event in pygame.event.get():
|
||||
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.vacuum = Vacuum(1, 1)
|
||||
world.doc_station = Doc_Station(9, 8)
|
||||
print(config.getboolean("APP", "cat"))
|
||||
if config.getboolean("APP", "cat"):
|
||||
world.cat = Cat(7, 8)
|
||||
world.add_entity(world.cat)
|
||||
@ -93,4 +130,7 @@ def generate_world(tiles_x: int, tiles_y: int) -> World:
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = Main()
|
||||
if config["APP"]["movement"] == "human":
|
||||
app.run()
|
||||
elif config["APP"]["movement"] == "robot":
|
||||
app.run_robot()
|
||||
|
Loading…
Reference in New Issue
Block a user