-
Notifications
You must be signed in to change notification settings - Fork 0
/
teamBalancing.py
340 lines (321 loc) · 9.8 KB
/
teamBalancing.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# Tug 'O War Balanced Team Allocation
# Progressive Insurance, ETS Operations Intern Picnic
# Griffin Saiia
# Algorithm:
# - Determine no. of clusters ( poolSize / clusterSize )
# --> Make list for each cluster
# --> Each cluster is a tuple, (weight of cluster, cluster)
# - Sort pool by weight, max->min
# - Pool is tiered into rounds of picks. At the end of each round, each cluster
# is evaluated. Lightest cluster gets first pick next round.
# --> Evaluation is done by calculating the total weight of a team
# and sorting them from min-->max.
#
# This is essentially a greedy algorithm with K-mean sensibilities.
#
# Variable Key:
# pool: List of tuples, [contestant, strength weighting]
# poolSize: No. of contestants
# poolWeightSum: Sum of all weights in pool
# cluster: A team!
# clusterSize: Ideal team size (5 in our case)
# targetClusterAve: Target weight for a given clusters
# poolWeightSum / ( poolSize / cluster size )
# This isn't used anywhere in the program, but is
# a guide to measure how the algorithm holds up.
# tieredPool: The ordered pool segmented into rounds of picks
# (think an automated fantasy football draft)
# tier: One round of picks
import os
import sys
#target team size
clusterSize = 5
def main():
pool = start()
if(len(pool) > 2*clusterSize):
clusterList = setClusters(pool)
sortTupleList(pool, True)
tieredPool = tierPool(pool, clusterList)
allocateClusters(tieredPool, clusterList)
printClusters(clusterList)
targetClusterAve = totalWeight(pool) / ( len(pool) / clusterSize)
print("Target clusterweight: "+str(targetClusterAve))
writeOutClusters(clusterList)
else:
print("-----------------------------------------------------------")
print("-----------------------------------------------------------")
print("not a valid pool size, for your target cluster size. sorry.")
main()
# the wrapper for the program
def start():
print("-----------------------------------------------------------")
print("Welcome to the Progressive Tug O War Team Generator")
print(" o o o o o o ")
print(" |-<|-<|--<~~~~~~~~~~~~>--|>-|>-| ")
print(" |\ |\ |\ /| /| /| ")
print("-----------------------------------------------------------")
line = ""
pool = []
line = raw_input("manual entry or file entry? ")
while(1):
if(line == "manual"):
break
if(line == "file"):
break
# calls a testing method I wrote that skips the prompts
if(line == "test"):
break
print("not a recognized command.")
print("manual entry or file entry?")
line = raw_input("enter 'manual' or 'file': ")
if(line == 'manual'):
pool = manualEntry(pool)
if(line == "file"):
pool = fileEntry(pool)
if(line == "test"):
pool = test()
return pool
# allows for manual contestant entry
def manualEntry(pool):
print("enter contestants in the following format:")
print("<Firstname,Lastname> <Weight>")
print("enter 'oops' to remove last entry, and 'done' when finished")
line = ""
while(line != 'done'):
if(line != ""):
if(line == "oops"):
pool = removeLast(pool)
else:
try:
# formats the tuple for easy printing later
split = line.split(" ")
name = split[0].split(",")
tuple = [name[0]+" "+name[1], int(split[1])]
pool.append(tuple)
except IndexError:
print("please use the proper contestant entry format.")
line = raw_input("enter contestant: ")
print("current pool:")
printPool(pool)
print("pool size: "+str(len(pool)))
line = raw_input("do you need to add more contestants?(y/n) ")
if(line == "y"):
pool = manualEntry(pool)
line = raw_input("would you like to add contestants from a .txt file? (y/n) ")
if(line == "y"):
pool = fileEntry(pool)
return pool
# quick and dirty method to remove a unwanted entry
def removeLast(pool):
newPool = []
if(len(pool) > 1):
i = 0
while(i < (len(pool) - 1)):
newPool.append(pool[i])
i += 1
return newPool
else:
return newPool
# allows for mass contestant entry through a .txt file
def fileEntry(pool):
filename = ""
lines = []
while(1):
filename = raw_input("enter local .txt filename: ")
try:
with open(filename) as names:
lines = names.readlines()
names.close()
break
except:
print('File not found. :(')
for line in lines:
# formats the tuple for easy printing later
entry = line.split("\n")
split = entry[0].split(' ')
name = split[0].split(",")
tuple = [name[0]+" "+name[1], int(split[1])]
pool.append(tuple)
print("current pool:")
printPool(pool)
print("pool size: "+str(len(pool)))
line = raw_input("would you like to use an additional file? (y/n): ")
if(line == "y"):
pool = fileEntry(pool)
line = raw_input("any contestants to add manually? (y/n): ")
if(line == "y"):
pool = manualEntry(pool)
return pool
# initializes clusterList
def setClusters(pool):
numOfClusters = len(pool) / clusterSize
clusterList = []
i = 0
while(i < numOfClusters):
clusterList.append([0, []])
i += 1
return clusterList
# max/min ordering quicksort specific to a tuple list
# key: true, max ordering
# key: false, min ordering
def sortTupleList(tupleList, key):
sortTupleListHelper(tupleList, 0, len(tupleList) - 1, key)
# still just quicksort
def sortTupleListHelper(tupleList, first, last, key):
if(first < last):
split = tupleListPartition(tupleList, first, last, key)
sortTupleListHelper(tupleList, first, split - 1, key)
sortTupleListHelper(tupleList, split + 1, last, key)
# quicksort is so verbose but so so nice
def tupleListPartition(tupleList, first, last, key):
pivot = tupleList[first]
left = first + 1
right = last
done = False
while(not done):
# for sorting max->min
if(key):
while(left <= right and tupleList[left][1] >= pivot[1]):
left = left + 1
while(right >= left and tupleList[right][1] <= pivot[1]):
right = right-1
# for sorting min->max
else:
while(left <= right and tupleList[left][1] <= pivot[1]):
left = left + 1
while(right >= left and tupleList[right][1] >= pivot[1]):
right = right-1
if(right < left):
done = True
else:
hold = tupleList[left]
tupleList[left] = tupleList[right]
tupleList[right] = hold
hold = tupleList[first]
tupleList[first] = tupleList[right]
tupleList[right] = hold
return right
# method that tiers the presorted pool into rounds of picks
def tierPool(pool, clusterList):
tieredPool = []
numOfTiers = len(pool)/len(clusterList)
checkNumOfTiers = (len(pool)+0.0) / (len(clusterList)+0.0)
if( numOfTiers != checkNumOfTiers ):
numOfTiers += 1
i = 0
while(i < numOfTiers):
tieredPool.append([])
i += 1
i = 0
j = 0
# yeah I know double loops are gross
# could've made this more efficient with a little more thinking in a
# single loop, but this is a side project.
for tier in tieredPool:
j = 0
while(j < len(clusterList) and i < len(pool)):
tier.append(pool[i])
i += 1
j += 1
return tieredPool
# distributes participants to each cluster according to the tiers of the List
# and the evaluative ordering
def allocateClusters(tieredPool, clusterList):
# order that each cluster (referenced by index) gets to pick
pickOrder = []
# each tier is a round of picks
for tier in tieredPool:
i = 0
for contestant in tier:
# first round order is irrelevant
if(pickOrder == []):
clusterList[i][1].append(contestant)
else:
clusterList[pickOrder[i]][1].append(contestant)
i += 1
# pick order is set for next round
pickOrder = evaluateClusters(clusterList, pickOrder)
# this is only included for the organizer - shows how even the teams are
# once the program finishes running
for cluster in clusterList:
cluster[0] = totalWeight(cluster[1])
# gets the total weight of each team, and sorts the weights - lightest
# cluster gets first pick next round
def evaluateClusters(clusterList, pickOrder):
# zeros out the pickOrder
pickOrder = []
# weightsOfClusters is a list of tuples, [index of cluster, cluster weight]
weightsOfClusters = []
i = 0
# calculating all the cluster's weights
for cluster in clusterList:
weightsOfClusters.append([i, totalWeight(cluster[1])])
i += 1
# sorts weightsOfCluster min->max
sortTupleList(weightsOfClusters, False)
# sets pick order by pulling the indexes of the clusters from
# the sorted list of cluster weights
for each in weightsOfClusters:
pickOrder.append(each[0])
return pickOrder
# lil helper that totals the weights of a pool or cluster
def totalWeight(tupleList):
sum = 0
i = 0
while(i < len(tupleList)):
sum += tupleList[i][1]
i += 1
return sum
# printing method
def printPool(pool):
for tuple in pool:
print(" - "+tuple[0]+", "+str(tuple[1]))
# printing methodddds
def printClusters(clusterList):
print("Clusters:")
i = 1
for cluster in clusterList:
print("Team "+str(i)+"'s Weight: "+str(cluster[0]))
printPool(cluster[1])
i += 1
# writes teams without any of the weighting to a .txt file for distribution
def writeOutClusters(clusterList):
f = open("TugOWarTeams.txt", "w+")
i = 1
for cluster in clusterList:
f.write("Team "+str(i)+": ")
for tuple in cluster[1]:
f.write(tuple[0]+" ")
f.write("\n")
i += 1
print("file written")
# lil test method that references an early sign up sheet
def test():
pool = []
filename = "TOW_7_2.txt"
try:
with open(filename) as names:
lines = names.readlines()
names.close()
except FileNotFoundError:
print('File not found. :(')
for line in lines:
entry = line.split("\n")
split = entry[0].split(' ')
name = split[0].split(",")
tuple = [name[0]+" "+name[1], int(split[1])]
pool.append(tuple)
print("check over your current pool of "+str(len(pool))+" people:")
printPool(pool)
return pool
# to run it from command line
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print("")
print('Interrupted')
try:
sys.exit(0)
except SystemExit:
os._exit(0)