-
Notifications
You must be signed in to change notification settings - Fork 74
Expand file tree
/
Copy pathida_star.py
More file actions
128 lines (102 loc) · 4.39 KB
/
ida_star.py
File metadata and controls
128 lines (102 loc) · 4.39 KB
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
121
122
123
124
125
126
127
128
import time
from .finder import Finder, MAX_RUNS, TIME_LIMIT
from ..core.diagonal_movement import DiagonalMovement
from ..core.heuristic import manhattan, octile
from ..core.node import Node
class IDAStarFinder(Finder):
"""
Iterative Deeping A Star (IDA*) path-finder.
Recursion based on:
http://www.apl.jhu.edu/~hall/AI-Programming/IDA-Star.html
Path retracing based on:
V. Nageshwara Rao, Vipin Kumar and K. Ramesh
"A Parallel Implementation of Iterative-Deeping-A*", January 1987.
ftp://ftp.cs.utexas.edu/.snapshot/hourly.1/pub/AI-Lab/tech-reports/
UT-AI-TR-87-46.pdf
based on the JavaScript implementation by Gerard Meier
(www.gerardmeier.com)
"""
def __init__(self, heuristic=None, weight=1,
diagonal_movement=DiagonalMovement.never,
time_limit=TIME_LIMIT,
max_runs=MAX_RUNS,
track_recursion=True):
super(IDAStarFinder, self).__init__(
heuristic=heuristic, weight=weight,
diagonal_movement=diagonal_movement,
weighted=False,
time_limit=time_limit,
max_runs=max_runs)
self.track_recursion = track_recursion
if not heuristic:
if diagonal_movement == DiagonalMovement.never:
self.heuristic = manhattan
else:
# When diagonal movement is allowed the manhattan heuristic is
# not admissible it should be octile instead
self.heuristic = octile
def search(self, node, g, cutoff, path, depth, end, grid):
self.runs += 1
self.keep_running()
self.nodes_visited += 1
f = g + self.apply_heuristic(node, end) * self.weight
# We've searched too deep for this iteration.
if f > cutoff:
return f
if node == end:
if len(path) < depth:
path += [None] * (depth - len(path) + 1)
path[depth] = node
return node
neighbors = self.find_neighbors(grid, node)
# Sort the neighbors, gives nicer paths. But, this deviates
# from the original algorithm - so I left it out
# TODO: make this an optional parameter
# def sort_neighbors(a, b):
# return self.apply_heuristic(a, end) - \
# self.apply_heuristic(b, end)
# sorted(neighbors, sort_neighbors)
min_t = float('inf')
for neighbor in neighbors:
if self.track_recursion:
# Retain a copy for visualisation. Due to recursion, this
# node may be part of other paths too.
neighbor.retain_count += 1
neighbor.tested = True
t = self.search(neighbor, g + self.calc_cost(node, neighbor),
cutoff, path, depth + 1, end, grid)
if isinstance(t, Node):
if len(path) < depth:
path += [None] * (depth - len(path) + 1)
path[depth] = node
return t
# Decrement count, then determine whether it's actually closed.
if self.track_recursion:
neighbor.retain_count -= 1
if neighbor.retain_count == 0:
neighbor.tested = False
if t < min_t:
min_t = t
return min_t
def find_path(self, start, end, grid):
self.start_time = time.time() # execution time limitation
self.runs = 0 # count number of iterations
self.nodes_visited = 0 # for statistics
# initial search depth, given the typical heuristic contraints,
# there should be no cheaper route possible.
cutoff = self.apply_heuristic(start, end)
while True:
path = []
# search till cut-off depth:
t = self.search(start, 0, cutoff, path, 0, end, grid)
if isinstance(t, bool) and not t:
# only when an error occured we return "False"
break
# If t is a node, it's also the end node. Route is now
# populated with a valid path to the end node.
if isinstance(t, Node):
return [(node.x, node.y) for node in path], self.runs
# Try again, this time with a deeper cut-off. The t score
# is the closest we got to the end node.
cutoff = t
return [], self.runs