@@ -120,6 +120,238 @@ def APMTracker(replay):
120120
121121 return replay
122122
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+
123355
124356@plugin
125357def SelectionTracker (replay ):
0 commit comments