Merge pull request 'a-star-implementation' (#22) from a-star-implementation into main

Reviewed-on: s473601/Machine_learning_2023#22
Reviewed-by: Tim Barvenov <timbar@st.amu.edu.pl>
This commit is contained in:
Tim Barvenov 2023-05-14 14:27:45 +02:00
commit ddb69cc1ec
4 changed files with 189 additions and 62 deletions

View File

@ -0,0 +1,113 @@
import heapq
from domain.world import World
class State:
def __init__(self, x, y, direction=(1, 0), entity=None):
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
)
def heuristic(self, goal_state):
return abs(self.x - goal_state.x) + abs(self.y - goal_state.y)
class Node:
def __init__(self, state: State, g_score: int, goal_state: State):
self.state = state
self.g_score = g_score
self.f_score = g_score + state.heuristic(goal_state)
self.parent = None
self.action = None
def __lt__(self, other):
return self.f_score < other.f_score
def action_sequence(node: Node):
actions = []
while node.parent:
actions.append(node.action)
node = node.parent
actions.reverse()
return actions
class RotateAndGoAStar:
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 = []
self.enqueued_states = set()
self.explored = set()
self.actions = []
def get_g_score(self, state):
return self.world.get_cost(state.x, state.y)
def search(self):
heapq.heappush(
self.fringe, Node(self.start_state, 0, self.goal_state)
)
while self.fringe:
elem = heapq.heappop(self.fringe)
if self.is_goal(elem.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:
continue
new_g_score = new_g_score = elem.g_score + self.world.get_cost(state.x, state.y)
if state not in self.enqueued_states:
next_node = Node(state, new_g_score, self.goal_state)
next_node.action = action
next_node.parent = elem
heapq.heappush(self.fringe, next_node)
self.enqueued_states.add(state)
elif new_g_score < self.get_g_score(state):
for node in self.fringe:
if node.state == state:
node.g_score = new_g_score
node.f_score = (
new_g_score + node.state.heuristic(self.goal_state)
)
node.parent = elem
node.action = action
heapq.heapify(self.fringe)
break
return False
def successors(self, state: State):
new_successors = [
("RR", State(state.x, state.y, (-state.direction[1], state.direction[0]))),
("RL", State(state.x, state.y, (state.direction[1], -state.direction[0]))),
]
next_x = state.x + state.direction[0]
next_y = state.y + state.direction[1]
if self.world.accepted_move(next_x, next_y):
new_successors.append(
("GO", State(next_x, next_y, 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 )

View File

@ -56,3 +56,5 @@ Summary:
The "Automatic cleaning robot" project is a simple yet educational programming project. Users are tasked with specifying the positions that the robot should clean, as well as the coordinates of obstacles. The robot, built using artificial intelligence, is responsible for avoiding obstacles, making decisions in case of random events, and cleaning the designated points. The project was written in Python with the use of artificial intelligence. The analysis of images is based on neural networks. The "Automatic cleaning robot" project is a simple yet educational programming project. Users are tasked with specifying the positions that the robot should clean, as well as the coordinates of obstacles. The robot, built using artificial intelligence, is responsible for avoiding obstacles, making decisions in case of random events, and cleaning the designated points. The project was written in Python with the use of artificial intelligence. The analysis of images is based on neural networks.
****** ******
******

View File

@ -4,6 +4,7 @@ from domain.entities.entity import Entity
class World: class World:
def __init__(self, width: int, height: int) -> object: def __init__(self, width: int, height: int) -> object:
self.costs = [[1000 for j in range(height)] for i in range(width)]
self.width = width self.width = width
self.height = height self.height = height
self.dust = [[[] for j in range(height)] for i in range(width)] self.dust = [[[] for j in range(height)] for i in range(width)]
@ -53,3 +54,5 @@ class World:
return False return False
return True return True
def get_cost(self, x, y):
return self.costs[x][y]

17
main.py
View File

@ -14,7 +14,8 @@ 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 GoAnyDirectionBFS, State # from AI_brain.movement import GoAnyDirectionBFS, State
from AI_brain.rotate_and_go_bfs import RotateAndGoBFS, State # from AI_brain.rotate_and_go_bfs import RotateAndGoBFS, State
from AI_brain.rotate_and_go_astar import RotateAndGoAStar, State
config = configparser.ConfigParser() config = configparser.ConfigParser()
@ -52,7 +53,8 @@ class Main:
end_state = State(self.world.doc_station.x, self.world.doc_station.y) end_state = State(self.world.doc_station.x, self.world.doc_station.y)
# path_searcher = GoAnyDirectionBFS(self.world, start_state, end_state) # path_searcher = GoAnyDirectionBFS(self.world, start_state, end_state)
path_searcher = RotateAndGoBFS(self.world, start_state, end_state) # path_searcher = RotateAndGoBFS(self.world, start_state, end_state)
path_searcher = RotateAndGoAStar(self.world, start_state, end_state)
if not path_searcher.search(): if not path_searcher.search():
print("No solution") print("No solution")
exit(0) exit(0)
@ -134,7 +136,7 @@ class Main:
def generate_world(tiles_x: int, tiles_y: int) -> World: def generate_world(tiles_x: int, tiles_y: int) -> World:
world = World(tiles_x, tiles_y) world = World(tiles_x, tiles_y)
for _ in range(10): for _ in range(35):
temp_x = randint(0, tiles_x - 1) temp_x = randint(0, tiles_x - 1)
temp_y = randint(0, tiles_y - 1) temp_y = randint(0, tiles_y - 1)
world.add_entity(Garbage(temp_x, temp_y)) world.add_entity(Garbage(temp_x, temp_y))
@ -149,9 +151,16 @@ def generate_world(tiles_x: int, tiles_y: int) -> World:
world.add_entity(Entity(8, 8, "PLANT2")) world.add_entity(Entity(8, 8, "PLANT2"))
world.add_entity(Entity(9, 3, "PLANT3")) world.add_entity(Entity(9, 3, "PLANT3"))
world.add_entity(Earring(5, 5)) world.add_entity(Earring(5, 5))
return world
for x in range(world.width):
for y in range(world.height):
if world.is_garbage_at(x, y):
world.costs[x][y] = 1
else:
world.costs[x][y] = 10
return world
if __name__ == "__main__": if __name__ == "__main__":
app = Main() app = Main()
if config["APP"]["movement"] == "human": if config["APP"]["movement"] == "human":