@@ -120,6 +120,238 @@ def APMTracker(replay):
120
120
121
121
return replay
122
122
123
+ @plugin
124
+ def CreepTracker (replay ):
125
+ from itertools import izip_longest , dropwhile
126
+ from random import random
127
+ from math import sqrt , pi
128
+
129
+ def add_to_list (control_pid ,unit_id ,unit_location ,\
130
+ unit_type ,event_time , creep_generating_units_list ):
131
+ length_cgu_list = len (creep_generating_units_list [control_pid ])
132
+ if length_cgu_list == 0 :
133
+ creep_generating_units_list [control_pid ].append ([(unit_id , unit_location ,unit_type ,event_time )])
134
+ else :
135
+ previous_list = creep_generating_units_list [control_pid ][length_cgu_list - 1 ][:]
136
+ previous_list .append ((unit_id , unit_location ,unit_type ,event_time ))
137
+ creep_generating_units_list [control_pid ].append (previous_list )
138
+
139
+ def in_circles (point_x ,point_y ,cgu_radius ):
140
+ for cgu in cgu_radius :
141
+ circle_x = cgu [0 ][0 ]
142
+ circle_y = cgu [0 ][1 ]
143
+ radius = cgu [1 ]
144
+ distance = (circle_x - point_x )** 2 + (circle_y - point_y )** 2
145
+ if distance < (radius * radius ):
146
+ return 1
147
+ return 0
148
+
149
+ def distance (point1 ,point2 ):
150
+ distance = (point1 [0 ]- point2 [0 ])** 2 + (point1 [1 ]- point2 [1 ])** 2
151
+ return distance
152
+
153
+ def calculate_area (cgu_radius ):
154
+ if len (cgu_radius )== 1 :
155
+ print "Area Calculated" ,pi * (cgu_radius [0 ][1 ]** 2 )
156
+ return pi * (cgu_radius [0 ][1 ]** 2 )
157
+
158
+ # from cgu_radius get a square which surrounds maximum
159
+ # possible area that the creep lies in
160
+ max_x = max (cgu_radius , key = lambda x : x [0 ][0 ]+ x [1 ])
161
+ max_y = max (cgu_radius , key = lambda x : x [0 ][1 ]+ x [1 ])
162
+ min_x = min (cgu_radius , key = lambda x : x [0 ][0 ] - x [1 ])
163
+ min_y = min (cgu_radius , key = lambda x : x [0 ][1 ] - x [1 ])
164
+
165
+ max_x = max_x [0 ][0 ] + max_x [1 ]
166
+ max_y = max_y [0 ][1 ] + max_y [1 ]
167
+ min_x = min_x [0 ][0 ] - min_x [1 ]
168
+ min_y = min_y [0 ][1 ] - min_y [1 ]
169
+
170
+ area = 0
171
+ for x in range (min_x ,max_x ):
172
+ for y in range (min_y ,max_y ):
173
+ if in_circles (x ,y ,cgu_radius ):
174
+ area += 1
175
+ return area
176
+
177
+
178
+ def single_linkage_clustering (cgu_points , maxR ,labels = 0 ,cgu_length = 0 ):
179
+ inf = - 1000
180
+ if labels == 0 :
181
+ labels = [0 for x in cgu_points ]
182
+ cgu_length = len (cgu_points )
183
+ if len (cgu_points ) == 1 :
184
+ return [0 ]
185
+
186
+ # calculate distance array
187
+ distance_array = list ()
188
+ for i in range (len (cgu_points )):
189
+ i_lengths = list ()
190
+ for j in range (len (cgu_points )):
191
+ if i != j :
192
+ if cgu_points [i ][0 ] == inf and cgu_points [j ][0 ] == inf :
193
+ i_lengths .append (- inf )
194
+ else :
195
+ i_lengths .append (distance (cgu_points [i ], cgu_points [j ]))
196
+
197
+ distance_array .append (i_lengths )
198
+
199
+ #Find closest point distance for each point
200
+ min_array = map (lambda x :min (x ), distance_array )
201
+
202
+ #combine 2 points with smallest distance
203
+ min_distance = min (min_array )
204
+ if min_distance < maxR :
205
+ point1 = min_array .index (min_distance )
206
+ point2 = min_array [point1 + 1 :].index (min_distance )+ point1 + 1
207
+ #label each cgu points
208
+ current_label = max (labels )+ 1 if labels [point1 ] == 0 and \
209
+ labels [point2 ] == 0 else max (labels [point1 ],labels [point2 ])
210
+ labels [point1 ] = current_label
211
+ labels [point2 ] = current_label
212
+ labels .append (current_label )
213
+
214
+ new_x = (cgu_points [point1 ][0 ] + cgu_points [point2 ][0 ])/ 2
215
+ new_y = (cgu_points [point1 ][1 ] + cgu_points [point2 ][1 ])/ 2
216
+ cgu_points [point1 ]= (inf ,inf )
217
+ cgu_points [point2 ]= (inf ,inf )
218
+ cgu_points .append ((new_x ,new_y ))
219
+
220
+ labels = single_linkage_clustering (cgu_points ,maxR ,labels ,cgu_length )
221
+ return labels [0 :cgu_length ]
222
+ else :
223
+ return_value = labels [0 :cgu_length ]
224
+ return return_value
225
+
226
+
227
+ #Get Map Size
228
+ mapinfo = replay .map .archive .read_file ('MapInfo' )
229
+ mapSizeX = ord (mapinfo [16 ])
230
+ mapSizeY = ord (mapinfo [20 ])
231
+ mapSize = mapSizeX * mapSizeX / 100
232
+
233
+ creep_generating_units_list = dict ()
234
+
235
+ for player in replay .players :
236
+ player .creep_spread_by_minute = defaultdict (int )
237
+ player .max_creep_spread = int ()
238
+ creep_generating_units_list [player .pid ] = list ()
239
+ try :
240
+ replay .tracker_events
241
+ except AttributeError :
242
+ print "Replay does not have tracker events"
243
+ return replay
244
+
245
+ for tracker_event ,game_event in izip_longest (replay .tracker_events ,replay .game_events ):
246
+
247
+ # First search things that generate creep
248
+ # Tumor, hatcheries, nydus and overlords generating creep
249
+
250
+ if tracker_event and tracker_event .name == "UnitInitEvent" :
251
+ units = ["CreepTumor" , "Hatchery" ,"Nydus" ] # check nydus name
252
+ if tracker_event .unit_type_name in units :
253
+ add_to_list (tracker_event .control_pid ,tracker_event .unit_id ,\
254
+ (tracker_event .x , tracker_event .y ), \
255
+ tracker_event .unit_type_name ,\
256
+ tracker_event .second ,\
257
+ creep_generating_units_list )
258
+
259
+ if game_event and game_event .name == "AbilityEvent" :
260
+
261
+ if game_event .ability_name == "GenerateCreep" :
262
+ add_to_list (game_event .control_pid ,game_event .unit_id ,\
263
+ (game_event .x , game_event .y ), \
264
+ game_event .unit_type_name ,\
265
+ game_event .second ,\
266
+ creep_generating_units_list )
267
+
268
+ # # Removes creep generating units that were destroyed
269
+ if tracker_event and tracker_event .name == "UnitDiedEvent" :
270
+ for player in creep_generating_units_list :
271
+ length_cgu_list = len (creep_generating_units_list [player ])
272
+ if length_cgu_list == 0 :
273
+ break
274
+ cgu_per_player = creep_generating_units_list [player ][length_cgu_list - 1 ]
275
+ creep_generating_died = dropwhile (lambda x : x [0 ] != tracker_event .unit_id , \
276
+ cgu_per_player )
277
+ for creep_generating_died_unit in creep_generating_died :
278
+
279
+ cgu_per_player .remove (creep_generating_died_unit )
280
+ creep_generating_units_list [player ].append (cgu_per_player )
281
+
282
+ #reduce all events to last event in the minute
283
+ last_minute_found = 0
284
+ for player in replay .player :
285
+ cgu_per_player_new = list ()
286
+ for cgu_per_player in creep_generating_units_list [player ]:
287
+ if len (cgu_per_player ) == 0 :
288
+ continue
289
+ cgu_last_event_time = cgu_per_player [- 1 ][3 ]
290
+
291
+ if (cgu_last_event_time / 60 )> last_minute_found :
292
+ last_minute_found = cgu_last_event_time / 60
293
+ cgu_per_player_new .append (cgu_per_player )
294
+ cgu_per_player = cgu_per_player_new
295
+
296
+
297
+ max_creep_spread = defaultdict ()
298
+ for player in replay .player :
299
+ # convert cg u list into centre of circles and radius
300
+ unit_name_to_radius = {'CreepTumor' : 15 , "Hatchery" :17 ,\
301
+ "GenerateCreep" : 10 , "Nydus" : 5 }
302
+
303
+ max_creep_spread [player ] = 0
304
+
305
+ for index ,cgu_per_player in enumerate (creep_generating_units_list [player ]):
306
+
307
+ cgu_radius = map (lambda x : (x [1 ], unit_name_to_radius [x [2 ]]),\
308
+ cgu_per_player )
309
+ cgu_points = map (lambda x : x [0 ],cgu_radius )
310
+
311
+ if cgu_points :
312
+ labels = single_linkage_clustering (cgu_points ,350 )
313
+ else :
314
+ if index != 0 :
315
+ replay .player [player ].creep_spread_by_minute [cgu_last_event_time + 1 ] = 0
316
+ continue
317
+
318
+ area = 0
319
+ #if all labels 0 (all separate clusters) calculate it separately
320
+ if max (labels ) == 0 :
321
+ for cgu_radi in cgu_radius :
322
+ area += pi * cgu_radi [1 ]** 2
323
+
324
+ cgu_last_event_time = cgu_per_player [- 1 ][3 ]/ 60
325
+ replay .player [player ].creep_spread_by_minute [cgu_last_event_time ] = area / mapSize
326
+ continue
327
+
328
+ count = 0
329
+ while True :
330
+ clusters = filter (lambda x : True if x [0 ] == count else \
331
+ False , zip (labels ,cgu_radius ) )
332
+
333
+ cgu_clusters = map (lambda x :x [1 ], clusters )
334
+ if count == 0 :
335
+ for cgu_radi in cgu_clusters :
336
+ area += pi * cgu_radi [1 ]** 2
337
+ count += 1
338
+ continue
339
+ if len (clusters ) == 0 :
340
+ break
341
+ count += 1
342
+ area += calculate_area (cgu_clusters )
343
+ cgu_last_event_time = cgu_per_player [- 1 ][3 ]/ 60
344
+ replay .player [player ].creep_spread_by_minute [cgu_last_event_time ] = area / mapSize
345
+
346
+ if area > max_creep_spread [player ]:
347
+ max_creep_spread [player ] = area
348
+
349
+
350
+ for player in replay .player :
351
+ replay .player [player ].max_creep_spread = max_creep_spread [player ]/ mapSize
352
+
353
+ return replay
354
+
123
355
124
356
@plugin
125
357
def SelectionTracker (replay ):
0 commit comments