Skip to content

Commit 6b1f206

Browse files
jonomonjonomon
authored andcommitted
Added creepTracker
1 parent ff60dd0 commit 6b1f206

File tree

2 files changed

+233
-0
lines changed

2 files changed

+233
-0
lines changed

sc2reader/events/tracker.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class TrackerEvent(object):
1010
def __init__(self, frames):
1111
#: The frame of the game this event was applied
1212
self.frame = frames
13+
self.second = frames >>4
1314

1415
def load_context(self, replay):
1516
pass

sc2reader/plugins/replay.py

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
123355
def SelectionTracker(replay):

0 commit comments

Comments
 (0)