just started learning some search algorithms. So I've kinda hit a roadblock (in concept too) about the Floyd Warshall's algorithm for the shortest path. The problem is that I've been given a grid with randomly generated obstacles and I've got to locate the shortest path from start to finish. Now the algorithm does work with graphs, but with grids I'm not very sure.
I'm referencing AIMA's code for uninformed search.
class Problem(object):
"""The abstract class for a formal problem. A new domain subclasses this,
overriding `actions` and `results`, and perhaps other methods.
The default heuristic is 0 and the default action cost is 1 for all states.
When yiou create an instance of a subclass, specify `initial`, and `goal` states
(or give an `is_goal` method) and perhaps other keyword args for the subclass."""
def __init__(self, initial=None, goal=None, **kwds):
self.__dict__.update(initial=initial, goal=goal, **kwds)
def actions(self, state): raise NotImplementedError
def result(self, state, action): raise NotImplementedError
def is_goal(self, state): return state == self.goal
def action_cost(self, s, a, s1): return 1
def h(self, node): return 0
def __str__(self):
return '{}({!r}, {!r})'.format(
type(self).__name__, self.initial, self.goal)
class Node:
"A Node in a search tree."
def __init__(self, state, parent=None, action=None, path_cost=0):
self.__dict__.update(state=state, parent=parent, action=action, path_cost=path_cost)
def __repr__(self): return '<{}>'.format(self.state)
def __len__(self): return 0 if self.parent is None else (1 + len(self.parent))
def __lt__(self, other): return self.path_cost < other.path_cost
failure = Node('failure', path_cost=math.inf) # Indicates an algorithm couldn't find a solution.
cutoff = Node('cutoff', path_cost=math.inf) # Indicates iterative deepening search was cut off.
def expand(problem, node):
"Expand a node, generating the children nodes."
s = node.state
for action in problem.actions(s):
s1 = problem.result(s, action)
cost = node.path_cost + problem.action_cost(s, action, s1)
yield Node(s1, node, action, cost)
def path_actions(node):
"The sequence of actions to get to this node."
if node.parent is None:
return []
return path_actions(node.parent) + [node.action]
def path_states(node):
"The sequence of states to get to this node."
if node in (cutoff, failure, None):
return []
return path_states(node.parent) + [node.state]
My Grid problem is like this
class GridProblem(Problem):
"""Finding a path on a 2D grid with obstacles. Obstacles are (x, y) cells."""
def __init__(self, initial=(0.5, 0.5), goal=(1.5, 2.5), obstacles=(),
grid_size=0.2, **kwds):
Problem.__init__(self, initial=initial, goal=goal, obstacles=set(obstacles) - {initial, goal},
grid_size=grid_size, **kwds)
self.directions = [(0, -1), (-1, 0), (1, 0), (0, +1)]
self.gridmap = set()
# Method for converting real map location to a grid map, there is a tricky problem
self.to_grid = lambda state: (int(state[0]*10/self.grid_size/10), int(state[1]*10/self.grid_size/10))
self.generate_gridmap()
def generate_gridmap(self):
for p in self.obstacles:
self.gridmap.add(self.to_grid(p))
# Remove initial point and goal point from grid map
self.gridmap = self.gridmap - {self.to_grid(self.initial), self.to_grid(self.goal)}
def result(self, state, action):
"Both states and actions are represented by (x, y) pairs."
result_grid_loc = self.to_grid(state)
return action if action not in self.gridmap else state
def actions(self, state):
"""You can move one cell in any of `directions` to a non-obstacle cell."""
x, y = state
grid_p = self.to_grid(state)
action_list = []
for (dx, dy) in self.directions:
next_p = (grid_p[0]+dx, grid_p[1]+dy)
if next_p not in self.gridmap:
action_list.append((round(next_p[0]*self.grid_size, 3), \
round(next_p[1]*self.grid_size, 3)))
return action_list
def is_goal(self, state):
return self.to_grid(state) == self.to_grid(self.goal)
def action_cost(self, s, action, s1): return euclidean_distance(s, s1)
And just to sample, I'm using BFS and DFS as well, they go like this:
def breadth_first_search(problem):
"Search shallowest nodes in the search tree first."
frontier = FIFOQueue([Node(problem.initial)])
global reached # <<<<<<<<<<< Only change here
reached = set()
while frontier:
node = frontier.pop()
if problem.is_goal(node.state):
return node
for child in expand(problem, node):
s = child.state
if s not in reached:
reached.add(s)
frontier.appendleft(child)
return failure
Can somebody guide me in a direction so I can code Floyd Warshall something similar to how I did BFS?