-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmyAstar.py
120 lines (102 loc) · 4.69 KB
/
myAstar.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
""" generic A-Star path searching algorithm """
from abc import ABCMeta, abstractmethod
from heapq import heappush, heappop
__author__ = "Julien Rialland"
__copyright__ = "Copyright 2012-2017, J.Rialland"
__license__ = "BSD"
__version__ = "0.9"
__maintainer__ = __author__
__email__ = "[email protected]"
__status__ = "Production"
Infinite = float('inf')
class AStar:
__metaclass__ = ABCMeta
__slots__ = ()
class SearchNode:
__slots__ = ('data', 'gscore', 'fscore',
'closed', 'came_from', 'out_openset')
def __init__(self, data, gscore=Infinite, fscore=Infinite):
self.data = data
self.gscore = gscore
self.fscore = fscore
self.closed = False
self.out_openset = True
self.came_from = None
def __lt__(self, b):
return self.fscore < b.fscore
class SearchNodeDict(dict):
def __missing__(self, k):
v = AStar.SearchNode(k)
self.__setitem__(k, v)
return v
@abstractmethod
def heuristic_cost_estimate(self, current, goal, gscore=0):
"""Computes the estimated (rough) distance between a node and the goal, this method must be implemented in a subclass. The second parameter is always the goal."""
raise NotImplementedError
@abstractmethod
def distance_between(self, n1, n2, gscore=0):
"""Gives the real distance between two adjacent nodes n1 and n2 (i.e n2 belongs to the list of n1's neighbors).
n2 is guaranteed to belong to the list returned by the call to neighbors(n1).
This method must be implemented in a subclass."""
raise NotImplementedError
@abstractmethod
def neighbors(self, node):
"""For a given node, returns (or yields) the list of its neighbors. this method must be implemented in a subclass"""
raise NotImplementedError
def is_goal_reached(self, current, goal):
""" returns true when we can consider that 'current' is the goal"""
return current == goal
def reconstruct_path(self, last, reversePath=False):
def _gen():
current = last
while current:
yield current.data
current = current.came_from
if reversePath:
return _gen()
else:
return reversed(list(_gen()))
def astar(self, start, goal, reversePath=False, gscore_based=False):
if self.is_goal_reached(start, goal):
return [start]
searchNodes = AStar.SearchNodeDict()
startNode = searchNodes[start] = AStar.SearchNode(
start, gscore=.0, fscore=self.heuristic_cost_estimate(start, goal))
openSet = []
heappush(openSet, startNode)
while openSet:
current = heappop(openSet)
if self.is_goal_reached(current.data, goal):
return self.reconstruct_path(current, reversePath)
current.out_openset = True
current.closed = True
for neighbor in [searchNodes[n] for n in self.neighbors(current.data)]:
if neighbor.closed:
continue
tentative_gscore = current.gscore + \
(self.distance_between(current.data, neighbor.data, current.gscore) if gscore_based else \
self.distance_between(current.data, neighbor.data))
if tentative_gscore >= neighbor.gscore:
continue
neighbor.came_from = current
neighbor.gscore = tentative_gscore
neighbor.fscore = tentative_gscore + \
(self.heuristic_cost_estimate(neighbor.data, goal,neighbor.gscore) if gscore_based else \
self.heuristic_cost_estimate(neighbor.data, goal,neighbor.gscore))
if neighbor.out_openset:
neighbor.out_openset = False
heappush(openSet, neighbor)
return None
def find_path(start, goal, neighbors_fnct, reversePath=False, heuristic_cost_estimate_fnct=lambda a, b: Infinite, distance_between_fnct=lambda a, b: 1.0, is_goal_reached_fnct=lambda a, b: a == b):
"""A non-class version of the path finding algorithm"""
class FindPath(AStar):
def heuristic_cost_estimate(self, current, goal):
return heuristic_cost_estimate_fnct(current, goal)
def distance_between(self, n1, n2):
return distance_between_fnct(n1, n2)
def neighbors(self, node):
return neighbors_fnct(node)
def is_goal_reached(self, current, goal):
return is_goal_reached_fnct(current, goal)
return FindPath().astar(start, goal, reversePath)
__all__ = ['AStar', 'find_path']