-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSearchAlgorithms.py
197 lines (122 loc) · 5.29 KB
/
SearchAlgorithms.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
from PriorityQueue import PriorityQueue
from Node import Node
NO_PATH = "no path"
MAX_DEPTH = 20
# Implementation of Best First Search (BFS) algorithm as part of UCS and A* algorithms.
def best_first_search(problem, f):
# Create a node with the start state.
node = Node(problem.start_state, [], 0, 0)
# Define priority queue with priority function f.
frontier = PriorityQueue(f)
expanded_nodes = 0
frontier.push(node, expanded_nodes, 0)
closed_list = set()
# As long the frontier is not empty.
while frontier.heap:
node = frontier.pop()
# If we got to goal.
if problem.is_goal(node.state):
return node.solution() + " " + str(expanded_nodes)
expanded_nodes += 1
closed_list.add(node.state)
# For each child of the current node.
for child in node.expand(problem):
if child.state not in closed_list and child not in frontier:
frontier.push(child, expanded_nodes, child.action_priority())
elif child in frontier and f(child) < frontier[child]:
del frontier[child]
frontier.push(child, expanded_nodes, child.action_priority())
# If no solution was found.
return NO_PATH
# Implementation of Uniform Cost Search (UCS) algorithm.
def uniform_cost_search(problem):
def g(node): # g cost function - returns the cost of the path leading to node.
return node.path_cost
return best_first_search(problem, f=g) # f = g
def a_star(problem):
def g(node): # g cost function - returns the cost of the path leading to node.
return node.path_cost
def h(node): # Heuristic function, based on Diagonal distance.
D, D2 = 1, 1 # When D = 1 and D2 = 1, this is called the Chebyshev distance,
goal_s = problem.goal_state
# Calculate the difference between the x coordinates and the y coordinates.
dx = abs(node.state[0] - goal_s[0])
dy = abs(node.state[1] - goal_s[1])
return D * (dx + dy) + (D2 - 2 * D) * min(dx, dy)
return best_first_search(problem, f=lambda n: g(n) + h(n))
# Implementation of Depth Limited Search (dfs-l) algorithm as part of IDS algorithm.
def depth_limited_search(problem, limit):
# Create a node with the start state.
node = Node(problem.start_state, [], 0, 0)
# Here the frontier is a stack data structure.
frontier = [node]
expanded_nodes = 0
# As long the frontier is not empty.
while frontier:
node = frontier.pop()
# If we got to the goal.
if problem.is_goal(node.state):
return node.solution(), expanded_nodes
expanded_nodes += 1
# If node's depth is under the depth limit.
if node.depth < limit:
frontier.extend(node.expand(problem)[::-1])
return None, expanded_nodes
# Implementation of Iterative Deepening Search (IDS) algorithm.
def iterative_deepening_search(problem):
total_expanded_nodes = 0
# Limit to depth up to 20.
for depth in range(MAX_DEPTH):
result, expanded_nodes = depth_limited_search(problem, depth)
total_expanded_nodes += expanded_nodes
if result: # If we found a solution
return "{} {}".format(result, total_expanded_nodes)
# If no solution was found.
return NO_PATH
# Implementation of Iterative Deepening A* (IDA*) algorithm.
def iterative_deepening_a_star(problem):
def g(node): # g cost function - returns the cost of the path leading to node.
return node.path_cost
def h(node): # Heuristic function, based on Diagonal distance.
D, D2 = 1, 1 # When D = 1 and D2 = 1, this is called the Chebyshev distance,
goal_s = problem.goal_state
# Calculate the difference between the x coordinates and the y coordinates.
dx = abs(node.state[0] - goal_s[0])
dy = abs(node.state[1] - goal_s[1])
return D * (dx + dy) + (D2 - 2 * D) * min(dx, dy)
# Performing DFS according to f, as part of the Iterative Deepening A* (IDA*) algorithm.
def dfs_f(node, _g, f_limit):
nonlocal new_limit
nonlocal expanded_nodes
new_f = _g + h(node)
if new_f > f_limit:
new_limit = min(new_limit, new_f)
return None
# If we got to goal.
if problem.is_goal(node.state):
return node.solution()
expanded_nodes += 1
# Limit to depth up to 20.
if node.depth < MAX_DEPTH:
# For each of the node childs.
for c in node.expand(problem):
sol = dfs_f(c, g(c), f_limit)
if sol: # If not none.
return sol
return None
# Create a node with the start state.
start_node = Node(problem.start_state, [], 0, 0)
f_limit = 0
expanded_nodes = 0
new_limit = h(start_node)
if new_limit == 0: # If the goal state is also the start state.
return "{} {}".format(start_node.solution(), expanded_nodes)
# Loop while resources are available.
while f_limit != new_limit:
f_limit = new_limit
new_limit = float('inf')
solution = dfs_f(start_node, 0, f_limit)
if solution: # If solution was found
return "{} {}".format(solution, expanded_nodes)
# If no solution was found.
return NO_PATH