@@ -118,6 +118,238 @@ def APMTracker(replay):
118118
119119 return replay
120120
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+
121353
122354@plugin
123355def SelectionTracker (replay ):
0 commit comments