-
Notifications
You must be signed in to change notification settings - Fork 0
/
Glouton.py
363 lines (284 loc) · 10.3 KB
/
Glouton.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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import os
import numpy as np
import heapq
# Set the syspath
f_name = "main.py"
a_path = str(os.path.abspath(__file__))
new_sys_entry = a_path[0:len(a_path) - len(f_name)]
print("Add " + new_sys_entry + "to sys path")
sys.path.insert(0, new_sys_entry)
# Initialize vars
TEAM_NAME = "JoJo Le Glouton"
maze = None
class PriorityQueue(object):
def __init__(self, heap = []):
heapq.heapify(heap)
self.heap = heap
def insert(self, node, priority = 0):
heapq.heappush(self.heap, (priority, node))
def pop(self):
return heapq.heappop(self.heap)[1]
class Dijkstra:
def __init__(self, maze, origin = None, goal = None):
self.graph = maze
self.setOrigin(origin)
self.setGoal(goal)
def setOrigin(self, n):
if n:
self.origin = n
def setGoal(self, n):
if isinstance(n, list):
self.goal = n.copy()
else:
self.goal = n
def clear(self):
self.pathArray = {}
self.dist = {}
self.Q = PriorityQueue()
self.dist[self.origin] = 0
for node in self.graph.nodes:
if node != self.origin:
self.pathArray[node] = None
self.dist[node] = np.inf
self.Q.insert(self.origin, self.dist[self.origin])
def process(self):
self.algorithm()
def algorithm(self):
if self.origin != self.goal:
self.clear()
while self.Q.heap:
u = self.Q.pop()
for v in self.graph.getNeighbors(u):
alt = self.dist[u] + self.graph.getDistance(u, v)
if alt < self.dist[v]:
self.dist[v] = alt
self.pathArray[v] = u
self.Q.insert(v, alt)
else:
pass
def reconstructPath(self, node):
#litteral_path = ""
total_distance = 0
current = node
real_path = []
while current != self.origin:
new = self.pathArray[current]
#litteral_path += self.graph.getMove(new, current)
total_distance += self.graph.getDistance(current, new)
real_path.append(current)
current = new
real_path.append(current)
return (total_distance, real_path[::-1])
def getResult(self, node = None):
if self.goal != self.origin:
if (not node) and (self.goal):
return self.reconstructPath(self.goal)
else:
return self.reconstructPath(node)
else:
return (0, [])
class Maze:
def __init__(self, mazeMap, mazeWidth, mazeHeight):
self.mazeMap = mazeMap
self.mazeWidth = mazeWidth
self.mazeHeight = mazeHeight
self.NB_NODES = self.mazeWidth * self.mazeHeight
self.nodes = list(self.mazeMap.keys())
# Init matrixMap
self.matrixMap = {}
# Init metagraph
self.distanceMetagraph = {} # matrice des distances du métagraph sous forme de dictionnaire
self.pathMetagraph = {} # matrice des chemins du métagraph sous forme de dictionnaire
def convertToMatrix(self):
"""
Prend en argument
Renvoie une un dictionnaire où les clefs sont les clefs sont des cases et où
np.inf signifie qu'il n'y a pas de passage direct entre les deux cases,
n>0 signifie qu'il y a passage en n coups et 0 que l'on reste sur la même case
"""
for n in self.nodes:
self.matrixMap[n] = {}
for n1 in self.nodes:
for n2 in self.nodes:
if n1 == n2:
self.matrixMap[n1][n1] = 0
elif n2 in self.mazeMap[n1]:
self.matrixMap[n1][n2] = self.mazeMap[n1][n2]
self.matrixMap[n2][n1] = self.mazeMap[n2][n1]
else:
self.matrixMap[n1][n2] = np.inf
self.matrixMap[n2][n1] = np.inf
def reversePath(self, path):
"""
Inverse un chemin symétriquement par rapport a la diagonale
:param path:
:return:
"""
r = ""
for l in path:
if l == 'D':
r += 'U'
if l == 'U':
r += 'D'
if l == 'R':
r += 'L'
if l == 'L':
r += 'R'
return r
def getDistance(self, from_location, to_location):
try:
return self.mazeMap[from_location][to_location]
except KeyError:
if to_location == from_location:
return 0
else:
return np.inf
def getMove(self, origin, goal):
"""
Retourne le mouvement à effectuer pour aller de origin à goal,
qui doivent être adjacentes
:param origin:
:param goal:
:return: bool
"""
if origin != goal:
i1, j1 = origin
i2, j2 = goal
if i1 - i2 == -1:
return 'D'
elif i1 - i2 == 1:
return 'U'
elif j1 - j2 == -1:
return 'R'
elif j1 - j2 == 1:
return 'L'
else:
return False
else:
return False
def getNeighbors(self, position):
return self.mazeMap[position].keys()
def convertMetaPathToRealPaths(self, pathMeta):
path = []
for k in range(len(pathMeta) - 1):
path.append(self.pathMetagraph[pathMeta[k]][pathMeta[k+1]])
return path
def convertToRealPath(self, origin, pathNodes):
L = [origin]
x, y = origin
for elt in pathNodes:
if elt == 'L':
y -= 1
if elt == 'U':
x -= 1
if elt == 'D':
x += 1
if elt == 'R':
y += 1
L.append((x, y))
return L
def concatPaths(self, paths):
r = ""
for path in paths:
r += path
return r
def createMetaGraph(self, cheeses_list):
"""
Create the pattern of the metagraph(distance between cheeses
:param nodes_list: List of currents cheeses
:return:
"""
cheeses_list = cheeses_list.copy()
dij = Dijkstra(self)
while cheeses_list:
n1 = cheeses_list[0]
# Calculate path and distance with Dijkstra
dij.setOrigin(n1)
dij.setGoal(cheeses_list)
dij.process()
for n2 in cheeses_list:
d, p = dij.getResult(n2)
self.coupleNodesInMetagraph(n1, n2, d, p)
cheeses_list.remove(n1) # On supprime le node en cours, pour accelerer le programme
cheeses_list.remove(self.getOpposite(n1)) if self.getOpposite(n1) != n1 else () # Par symetrie, on supprime l'opposé
#print(repr(len(self.distanceMetagraph[(12, 13)]))+ repr(self.distanceMetagraph[(12, 13)]))
def addNodeToMetagraph(self, node, nodes_list):
"""
Ajoute le noeud "node" par rapports aux nodes "nodes_list" qui doivent déja exister dans le metaGraph
:param node: noeud a ajouter ou uploader
:param nodes_list:
:return:
"""
# Create the list of unChecked nodes
checked_list = []
if node in self.distanceMetagraph:
for n in nodes_list:
if n not in self.distanceMetagraph[node]:
checked_list.append(n)
else:
checked_list = nodes_list
# Environ 10^-6 sec pour arriver là
if checked_list:
dij = Dijkstra(self, node, checked_list)
dij.process()
# Environ 0.02 sec pour un 25x25
for n in checked_list:
d, p = dij.getResult(n)
self.coupleNodesInMetagraph(node, n, d, p)
def coupleNodesInMetagraph(self, n1, n2, d, p, recurse = True):
"""
:param d: distance from n1 to n2
:param p: path from n1 to n2
"""
if n1 not in self.distanceMetagraph:
self.distanceMetagraph[n1] = {}
self.pathMetagraph[n1] = {}
self.distanceMetagraph[n1][n2] = d
self.pathMetagraph[n1][n2] = p
if n2 not in self.distanceMetagraph:
self.distanceMetagraph[n2] = {}
self.pathMetagraph[n2] = {}
self.distanceMetagraph[n2][n1] = d
self.pathMetagraph[n2][n1] = p[::-1] # Path from n2 to n1 is the opposite of the path from n1 to n2
# Add opposite
if recurse:
self.coupleNodesInMetagraph(self.getOpposite(n1), self.getOpposite(n2), d, list(map(self.getOpposite, p)), False)
def getOpposite(self, n):
x, y = n
return (self.mazeHeight - x - 1, self.mazeWidth - y - 1)
# Algoritms application
def getFastestPath(self, origin, goal):
try:
return (self.distanceMetagraph[origin][goal], self.pathMetagraph[origin][goal])
except KeyError:
print("## Need to calculate the fastest path from " + repr(origin) + " to " + repr(goal))
# Calculate path and distance with Astar
dij = Astar(self, origin, goal)
dij.process()
d, p = dij.getResult()
# Addto metagraph for a next time
self.coupleNodesInMetagraph(origin, goal, d, p)
return (d, p)
def getNearestNode(self, origin, nodes):
dij = Dijkstra(self)
dij.setOrigin(origin)
dij.setGoal(None)
dij.process()
n_list = [dij.getResult(n) for n in nodes]
n_list.sort()
#print(n_list)
return n_list[0] if len(n_list) > 0 else (0, [])
def preprocessing(mazeMap, mazeWidth, mazeHeight, playerLocation, opponentLocation, piecesOfCheese, timeAllowed):
global maze
print("Start preprocessing (" + str(timeAllowed) + ")")
maze = Maze(mazeMap, mazeWidth, mazeHeight)
print("")
def turn(mazeMap, mazeWidth, mazeHeight, playerLocation, opponentLocation, playerScore, opponentScore, piecesOfCheese, timeAllowed):
global maze
path = maze.getNearestNode(playerLocation, piecesOfCheese)[1]
action = maze.getMove(path[0], path[1])
print('[' + repr(action) + ']')
return action