-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMaze_Algorithm.py
More file actions
289 lines (232 loc) · 11 KB
/
Copy pathMaze_Algorithm.py
File metadata and controls
289 lines (232 loc) · 11 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
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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
from Module.DB import *
def route_two_pins(grid, source: tuple, targets: list) -> list:
"""
@brief Routing two pins from single source to the nearest target.
@param source: The source node
@param target: The list of target nodes
@return path: The path from source to the nearest target
@return step: The step count of each node
"""
# Wave propagation using BFS to get step count
print(" >> Wave Propagation...", end="")
target = bfs_multi_target(grid, source, targets) # multi target breadth first search
if target:
print("Backtracking...", end="")
path = dfs_backtrack(grid, source, target) # depth first search backtracking
if path:
print("Success. Path Length: {}".format(len(path)))
else:
path = None
print("Failed.")
else:
# set path to None
path = None
print("Failed.")
return path
def route_multi_pins(grid, pins: list) -> list:
"""
@brief Routing multiple pins using the method of multiple sources in routed path.
@param pins The pins in list form
@return The path and step count of each node
"""
# initialize variables
paths = []
# Phase 1: Get the first path
targets = pins.copy()
source = targets.pop(0)
path = route_two_pins(grid, source, targets) # route two pins
if path: # if path found
# self.grid.addObstacle_coord(path) # mark path as obstacle
paths.append(path) # add path to list
targets.remove(path[-1]) # remove target from pins
else:
return None
# Phase 2: Get the rest of the paths
sources = targets.copy() # set the remain pins as sources
targets = paths[0].copy() # set the first target as target
while sources: # while there are sources
source = sources.pop(0) # set path nodes as source
path = route_two_pins(grid, source, targets) # route two pins
if path: # if path found
# self.grid.addObstacle_coord(path) # mark path as obstacle
paths.append(path) # add path to list
targets.extend(path) # add path to targets
# not necessary but this is for reiteration of whole grid creation
else:
return None
path_len = sum([len(path) for path in paths])
print("Total Path Length: {}".format(path_len))
return paths
def route_multi_pins_2(grid, pins: list) -> list:
"""
@brief Routing multiple pins using the method of multiple sources in routed path.
@param pins The pins in list form
@return The path and step count of each node
"""
# initialize variables
paths = []
# initialize source and target
targets = pins.copy()
source = targets.pop(0)
# Phase 1: Get the first path
path = route_two_pins(grid, source, [targets[0]]) # route two pins
if path: # if path found
# self.grid.addObstacle_coord(path) # mark path as obstacle
paths.append(path) # add path to list
targets.remove(path[-1]) # remove target from pins
sources = path.copy() # add path to sources
else:
return None
# Phase 2: Get the rest of the paths based on heuristic
while targets: # while there are targets
best_len = 99999999
src_tar_idx = [0, 0]
for i, source in enumerate(sources):
for j, target in enumerate(targets):
# get length
# path_len = abs(target[0] - source[0]) + abs(target[1] - source[1]) + abs(target[2] - source[2])
path_len = abs(target.x - source.x) + abs(target.y - source.y) + abs(target.z - source.z)
best_len = path_len if path_len < best_len else best_len
src_tar_idx = [i, j] if path_len == best_len else src_tar_idx
if sources[src_tar_idx[0]].x == targets[src_tar_idx[1]].x and sources[src_tar_idx[0]].y == targets[src_tar_idx[1]].y and sources[src_tar_idx[0]].z == targets[src_tar_idx[1]].z:
targets.remove(sources[src_tar_idx[0]])
continue
path = route_two_pins(grid, sources[src_tar_idx[0]], [targets[src_tar_idx[1]]]) # route two pins
if path: # if path found
paths.append(path) # add path to list
targets.remove(path[-1]) # remove target from pins
sources.extend(path) # add path to sources
else:
print("Failed to route path. func: route_multi_pins_2")
print("source: {}, target: {}".format((sources[src_tar_idx[0]].x, sources[src_tar_idx[0]].y, sources[src_tar_idx[0]].z), (targets[src_tar_idx[1]].x, targets[src_tar_idx[1]].y, targets[src_tar_idx[1]].z)))
return None
return paths
def route_multi_pins_group(grid, pins: list) -> list:
"""
@brief Routing multiple pins using the method of multiple sources in routed path.
@param pins The pins in list form
@return The path and step count of each node
"""
# initialize variables
paths = []
group = []
# get the group of pins from the pin list
for pin_list in pins:
# if there is only one pin in the group
if len(pin_list) == 1:
group.append(pin_list)
continue
# in each group of pins, route the pins
path = route_multi_pins_2(grid, pin_list)
if path:
# get each nodes from the path and make it a list
node = [coor for seg in path for coor in seg]
# add the list of nodes (unique) to the group
group.append(list(set(node)))
else:
# need to reiterate the whole grid creation
return None
# 1st group of pins
sources = group.pop(0) if group else None
while group:
# get the next group of pins
targets = group.pop(0)
best_len = 99999999
src_tar_idx = [0, 0]
# compare the nodes from the source and target group
for i, source in enumerate(sources):
for j, target in enumerate(targets):
# get the best length from the different nodes
path_len = abs(target.x - source.x) + abs(target.y - source.y) + abs(target.z - source.z)
best_len = path_len if path_len < best_len else best_len
src_tar_idx = [i, j] if path_len == best_len else src_tar_idx
if sources[src_tar_idx[0]].x == targets[src_tar_idx[1]].x and sources[src_tar_idx[0]].y == targets[src_tar_idx[1]].y and sources[src_tar_idx[0]].z == targets[src_tar_idx[1]].z:
targets.remove(sources[src_tar_idx[0]])
continue
# route two pins after get the best length
path = route_two_pins(grid, sources[src_tar_idx[0]], [targets[src_tar_idx[1]]]) # route two pins
if path:
paths.append(path) # add path to list
sources.extend(path) # add path to sources
# not necessary but this is for reiteration of whole grid creation
else:
return None
return paths
def bfs_multi_target(grid, source, targets: list):
"""
@brief Breath first search algorithm for step counting.
"""
# initialization
for lay in grid:
for row in lay:
for node in row:
# initialize visited and step
node.visited = False
node.step = None
# initialize queue
queue = []
queue.append(source)
# mark source as visited
source.visited = True
source.step = 0
while queue:
# dequeue
curr_node = queue.pop(0)
# add neighbors to queue
for neighbor in curr_node.get_neighbors():
# if neighbor not visited
if not neighbor.visited:
# if neighbor is target (destination reached)
if neighbor in targets:
neighbor.visited = True # mark neighbor as visited
neighbor.step = curr_node.step + 1 # increment step count
return neighbor # exit function
# if neighbor is not an obstacle
if not neighbor.obstacle:
neighbor.visited = True # mark neighbor as visited
neighbor.step = curr_node.step + 1 # increment step count
queue.append(neighbor)
# all neighbors visited and no path found
print(">> Wave Prop: No Path Found.")
return None
def dfs_backtrack(grid, source, target) -> list:
"""
@brief Depth first search algorithm for backtracking (Iterative method).
@param source: The source node
@param target: The target node
@param steps: The step count of each node
@return path: The path from source to target
"""
# initialize visited and steps matrix
for lay in grid:
for row in lay:
for node in row:
# initialize visited
node.visited = False
# initialize stack
stack = []
stack.append(target)
# mark target as visited
target.visited = True
path = []
while stack:
# pop
curr_node = stack.pop()
# mark current as visited
curr_node.visited = True
path.append(curr_node)
# add neighbors to stack
for neighbor in curr_node.get_neighbors():
# if neighbor not visited and neighbor has a step count
if not neighbor.visited and neighbor.step is not None:
# if neighbor is source (destination reached)
if neighbor == source:
path.append(neighbor) # add to path
path.reverse() # reverse path
return path
# if neighbor has one step count less than current node
if neighbor.step == curr_node.step - 1:
stack.append(neighbor)
# all neighbors visited and no path found
print(">> Backtrack: No Path Found.")
return None