-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAStar.py
345 lines (311 loc) · 15.3 KB
/
AStar.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
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
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
from Graph import Graph
from Vertex import Vertex
from Edge import Edge
import pygame as pg
'''
The AStar class inherits the Graph class
'''
class AStar(Graph):
'''
#
# delay: seconds between each iteration when visualizing is turned on
# visual: Turns pygame visualization on/off
#
'''
def __init__(self, delay = 0.001, visual = True):
super().__init__()
self.pygame = visual
self.background = None
self.LIGHTGREY = (180,180,180)
self.DARKGREEN = (0,255,0)
self.PINK = (255,200,200)
self.GREEN=(200,255,200)
self.WHITE = (245,245,245)
self.BLACK=(0,0,0)
self.RED = (255,0,0)
self.BLUE = (0,0,255)
self.delay = delay
# walls define obstacles in grid, e.g. walls, boxes etc, by defining each position in grid that is part of an obstacle
self.walls = []
'''
#
# Defines obstacles provided in removevertecies from the graph by removing all edges
# to vertecies that is defined as an obstacle
#
'''
def removeVertecies(self, removevertecies = []):
err_v = []
err_e = []
# Removing the vertecies
for r in removevertecies:
self.walls.append(self.vertecies[r])
self.vertecies.pop(r)
# Checking the edges ...
for v in self.vertecies:
vtex = self.vertecies[v]
for edge in vtex.adjecent:
vertex = edge.vertex
if vertex.name not in self.vertecies:
err_v.append(vtex)
err_e.append(edge)
for i in range(0,len(err_v)):
err_v[i].adjecent.remove(err_e[i])
return removevertecies
'''
#
# Read in the list of obstacles (defined by tag "remove"), the startnode from which traversal is defined,
# and the targetnode
#
'''
def readLimitations(self,filename):
import pandas as pd
from collections import namedtuple
columnnames = ['item', 'valuelist']
df = pd.read_csv(filename, error_bad_lines=False,
encoding='latin-1', warn_bad_lines=False,
names=columnnames, header = None, sep = ':')
for i in range(0,3):
if df.iat[i,0] == 'startvertex':
startVertex = df.iat[i,1]
elif df.iat[i,0] == 'targetvertex':
targetVertex = df.iat[i,1]
elif df.iat[i,0] == 'remove':
removed = self.removeVertecies(df.iat[i,1].split(';'))
return startVertex, targetVertex, removed
'''
# Initialize pygame visualization, if visualization has been defined
# Creates a dynamic visual grid according to the number of columns and rows
# read/defined during initialization. The visual limitations are defined by 1000x1000 pixels
'''
def initPygame(self):
if self.pygame:
xmin = ymin = xmax = ymax = 0
for v in self.vertecies:
vertex = self.vertecies[v]
x,y = vertex.position()
if x < xmin:
xmin = x
elif x > xmax:
xmax = x
if y < ymin:
ymin = y
elif y > ymax:
ymax = y
pg.init()
w,h = 600,600
self.xboxsize = int(w / ((xmax + 1) - xmin))
self.yboxsize = int(w / ((ymax + 1) - ymin))
w = self.xboxsize * ((int(w/self.xboxsize)))
h = self.yboxsize * ((int(h/self.yboxsize)))
self.background = pg.display.set_mode((w,h))
background = self.background
self.clock = pg.time.Clock()
self.clock.tick()
for c in range (0,(int(w/self.xboxsize)) + 1):
for l in range (0,(int(h/self.yboxsize)) + 1):
pg.draw.rect(background,self.WHITE,(c * self.xboxsize, l * self.yboxsize, self.xboxsize, self.yboxsize))
pg.draw.line(background,self.BLACK, (c * self.xboxsize, l * self.yboxsize), (c * self.xboxsize + self.xboxsize, l * self.yboxsize))
pg.draw.line(background,self.BLACK, (c * self.xboxsize, l * self.yboxsize), (c * self.xboxsize, l * self.yboxsize + self.yboxsize))
pg.draw.line(background,self.BLACK, (c * self.xboxsize + self.xboxsize, l * self.yboxsize), (c * self.xboxsize + self.xboxsize, l * self.yboxsize + self.yboxsize))
pg.draw.line(background,self.BLACK, (c * self.xboxsize, l * self.yboxsize + self.yboxsize), (c * self.xboxsize + self.xboxsize, l * self.yboxsize + self.yboxsize))
for wall in self.walls:
self.pygameState(wall, self.BLACK)
pg.display.flip()
'''
# Draw a box, representing the current vertex in the position defined by the name of the vertex.
# The color-parameter defines the (R,G,B)-value of the box
# If no visualization is used (self.pygame == False), no box is visualized
'''
def pygameState(self, current, color):
import time
if self.pygame:
background = self.background
x,y = current.position()
pg.draw.rect(background,color,(x * self.xboxsize, y * self.yboxsize, self.xboxsize, self.yboxsize))
pg.draw.line(background,self.BLACK, (x * self.xboxsize, y * self.yboxsize), (x * self.xboxsize + self.xboxsize, y * self.yboxsize))
pg.draw.line(background,self.BLACK, (x * self.xboxsize, y * self.yboxsize), (x * self.xboxsize, y * self.yboxsize + self.yboxsize))
pg.draw.line(background,self.BLACK, (x * self.xboxsize + self.xboxsize, y * self.yboxsize), (x * self.xboxsize + self.xboxsize, y * self.yboxsize + self.yboxsize))
pg.draw.line(background,self.BLACK, (x * self.xboxsize, y * self.yboxsize + self.yboxsize), (x * self.xboxsize + self.xboxsize, y * self.yboxsize + self.yboxsize))
if color not in [self.BLUE, self.RED, self.BLACK]:
time.sleep(self.delay)
pass
pg.display.flip()
'''
#
# Defining the heuristics used to calculate the estimated distance between the node being handled (startVertexName),
# and the targetnode (targetVertexName)
# Please note that the name of the vertecies are passed, not the vertex itself. The name is used for lookup
# in the list of vertecies in the graph.
# Further, the name of the vertex has the syntax: xNNyMM, where NN and MM are numerical and indicates column and row.
# E.g. x4y15 means column 4 of row 15.
# By identifying the column and row of a vertex, the estimated shorted distance between two vertecies may be
# calculated using the Manhatten distance
#
'''
def heuristics(self, startVertexName = None, targetVertexName = None):
if not startVertexName or not targetVertexName:
raise KeyError("VertexLookup need the names of the Vertecies addressed.")
if startVertexName not in self.vertecies:
raise KeyError("Node/Vertex defined as FROM-vertex is not present in graph")
if targetVertexName not in self.vertecies:
raise KeyError("Node/Vertex defined as TO-vertex is not present in graph")
xstart, ystart = self.vertecies[startVertexName].position()
xend, yend = self.vertecies[targetVertexName].position()
#
# Manhatten heuristics
#
dx = abs(xstart - xend)
dy = abs(ystart - yend)
D = 1
return D * (dx + dy)
'''
#
# The Dijkstra's algorithm has been adapted to support visualization as defined by the graph
# It has further been adopted to present a targetVetrx even though Dijkstra's has no use of
# it during execution. The tragetVertex is used only for visualization purposes.
'''
def Dijkstra(self, startVertexName = None, targetVertexName = None):
self.initPygame()
# Check to see that startvertex is in Graph
if startVertexName not in self.vertecies:
raise KeyError("Start node not present in graph")
# Reset visited and previous pointer before running algorithm
vertex = self.vertecies[startVertexName]
vertex.distance = distance = weight = 0
previous_node = None
startNode = self.vertecies[startVertexName]
toNode = self.vertecies[targetVertexName]
#
# Create priority queue, priority = current weight on edge ...
# No duplicate edges in queue allowed
#
edge = Edge(0, vertex)
from queue import PriorityQueue
priqueue = PriorityQueue()
# Defines enqueue/dequeue methods on priqueue
def enqueue(data):
priqueue.put(data)
def dequeue():
return priqueue.get()
enqueue(edge)
while not priqueue.empty():
# Get the element with lowest priority (i.e. weight on edge)
edge = dequeue()
eyeball = edge.vertex
self.pygameState(eyeball, self.GREEN)
self.pygameState(startNode,self.BLUE)
self.pygameState(toNode,self.RED)
# If not visited previously, we need to define the distance
if not eyeball.known:
eyeball.distance = distance
eyeball.previous = previous_node
eyeball.known = True
# If the vertex pointed to by the edge has an adjecency list, we need to iterate on it
for adjecentedge in eyeball.adjecent:
if not adjecentedge.vertex.known:
adjecentedge.vertex.distance = eyeball.distance + adjecentedge.weight
adjecentedge.vertex.previous = eyeball
adjecentedge.vertex.known = True
enqueue(adjecentedge)
self.pygameState(adjecentedge.vertex,self.PINK)
else:
if adjecentedge.vertex.distance > eyeball.distance + adjecentedge.weight:
adjecentedge.vertex.distance = eyeball.distance + adjecentedge.weight
adjecentedge.vertex.previous = eyeball
enqueue(adjecentedge)
self.pygameState(eyeball,self.LIGHTGREY)
for n in self.getPath(startVertexName, targetVertexName):
self.pygameState(n,self.DARKGREEN)
return self.getPath(startVertexName, targetVertexName)
'''
###############################################################################
#
# def AStarSearch(self, startVertexName = None, targetVertexName = None)
#
# Implement your code below.
# Please note that no other parts of this code or provided code should be altered
#
###############################################################################
'''
def AStarSearch(self, startVertexName = None, targetVertexName = None):
self.initPygame()
# Check to see that startvertex is in Graph
if startVertexName not in self.vertecies:
raise KeyError("Start node not present in graph")
# Check to see that targetvertex is in Graph
if targetVertexName not in self.vertecies:
raise KeyError("Target node not present in graph")
# Reset visited and previous pointer before running algorithm
startNode = self.vertecies[startVertexName]
startNode.g = distance = weight = 0
startNode.h = self.heuristics(startNode.name, targetVertexName)
startNode.f = startNode.g + startNode.h
toNode = self.vertecies[targetVertexName]
#
# Frontier is a priority queue, priority = F(n) = G(n) + H(n)
# No duplicate edges in queue allowed
#
from queue import PriorityQueue
frontier = PriorityQueue()
frontierList = list()
frontier.put(startNode)
frontierList.append(startNode)
while not frontier.empty():
# Get the element with lowest priority (i.e. weight on edge)
eyeball = frontier.get()
eyeball2 = min(frontierList)
frontierList.remove(eyeball)
if eyeball2 != eyeball:
print("They are not equal")
#eyeball = edge.vertex
self.pygameState(eyeball, self.GREEN)
self.pygameState(startNode,self.BLUE)
self.pygameState(toNode,self.RED)
if eyeball == toNode: # This is to stop searching when target is found
break
eyeball.known = True
# If the vertex pointed to by the edge has an adjecency list, we need to iterate on it
for adjecentedge in eyeball.adjecent:
if adjecentedge.vertex not in frontierList and not adjecentedge.vertex.known:
adjecentedge.vertex.previous = eyeball
adjecentedge.vertex.g = eyeball.g + adjecentedge.weight
adjecentedge.vertex.h = self.heuristics(adjecentedge.vertex.name, targetVertexName)
adjecentedge.vertex.f = adjecentedge.vertex.g + adjecentedge.vertex.h
frontier.put(adjecentedge.vertex)
if adjecentedge.vertex not in frontierList:
frontierList.append(adjecentedge.vertex)
self.pygameState(adjecentedge.vertex,self.PINK)
elif adjecentedge.vertex.g > eyeball.g + adjecentedge.weight:
adjecentedge.vertex.g = eyeball.g + adjecentedge.weight
adjecentedge.vertex.f = adjecentedge.vertex.g + adjecentedge.vertex.h
adjecentedge.vertex.previous = eyeball
if adjecentedge.vertex.known:
adjecentedge.vertex.known = False
frontier.put(adjecentedge.vertex)
if adjecentedge.vertex not in frontierList:
frontierList.append(adjecentedge.vertex)
frontierList.append(adjecentedge.vertex)
self.pygameState(eyeball,self.LIGHTGREY)
for n in self.getPath(startVertexName, targetVertexName):
self.pygameState(n,self.DARKGREEN)
return self.getPath(startVertexName, targetVertexName)
astar = AStar(delay = 0, visual = True)
#astar.readFile('minigraf.txt')
#startVertexName, targetVertexName, removed = astar.readLimitations('minigraf_xtras.txt')
#astar.readFile('astjernegraf.txt')
#startVertexName, targetVertexName, removed = astar.readLimitations('xtras.txt')
#astar.readFile('biggraph.txt')
#startVertexName, targetVertexName, removed = astar.readLimitations('biggraph_xtras.txt')
astar.readFile('AStarObligGraf.txt')
startVertexName, targetVertexName, removed = astar.readLimitations('AStarObligGraf_xtras.txt')
#astar.Dijkstra(startVertexName,targetVertexName)
astar.AStarSearch(startVertexName, targetVertexName)
if astar.pygame:
from pygame.locals import *
while astar.pygame:
for events in pg.event.get():
if events.type == QUIT:
exit(0)
pg.quit()
else:
print(astar.getPathAsString(startVertexName, targetVertexName))