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