-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbitbracket.py
More file actions
130 lines (95 loc) · 3.58 KB
/
bitbracket.py
File metadata and controls
130 lines (95 loc) · 3.58 KB
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
"""
A simulator for binary tree like, single elimination brackets.
Functions:
champion: Return the champion of a bitbracket
simulate: Run n simulations of a tournament, return a Counter.
translate: Decode a bitbracket into tournament rounds.
"""
import collections
def champion(bitbracket, teams):
"""Return the winning team of the bitbracket."""
bracket = translate(bitbracket, teams)
return bracket[-1][0]
def simulate(teams, matchup, n=1000):
"""Simulate the tournament n times, record bitbracket outcomes.
Note that the returned Counter object has a most_common() method
to sort all by frequency. See python collections docs for more info.
Args:
teams: A list/tuple of team objects.
matchup: A function that takes two team objects and returns a 0
if the first team wins, else a 1 (second team wins).
n: The number of simulations to run; default is 1000.
Returns:
A collections.Counter object with bitbracket keys.
"""
_validate_teams(teams)
_validate_matchup(matchup, teams)
_validate_n(n)
counter = collections.Counter()
for _ in range(n):
bitbracket = _simulation_iteration(teams, matchup)
counter[bitbracket] += 1
return counter
def translate(bitbracket, teams):
"""Convert a bitbracket into nested lists of teams."""
_validate_bitbracket(bitbracket)
_validate_teams(teams)
teams = list(teams)
bracket = [teams.copy()]
while len(teams) > 1:
winning_teams = []
for i in range(len(teams)//2):
winner = bitbracket & 1
winning_teams.append(teams[i + winner])
teams.pop(i + (1 - winner))
bitbracket >>= 1
bracket.append(winning_teams)
return bracket
def _simulation_iteration(teams, matchup):
"""Run a single simulation and return a bitbracket."""
teams = list(teams)
bitbracket = 0
game = 0
while len(teams) > 1:
for i in range(len(teams)//2):
winner = matchup(teams[i], teams[i+1])
assert winner == 0 or winner == 1
teams.pop(i + (1 - winner))
# bitbracket <<= 1
# bitbracket += winner
bitbracket += winner*(2**game)
game += 1
# bitbracket = int(bin(bitbracket)[-1:1:-1], 2)
return bitbracket
def _validate_bitbracket(bitbracket):
"""Ensure the bitbracket is an integer."""
if type(bitbracket) is not int:
raise TypeError('bitbracket should be an integer.')
def _validate_n(n):
"""Ensure the number of iterations is valid."""
try:
assert type(n) is int
assert n > 0
except AssertionError:
raise ValueError('n must be an integer >= 1!')
def _validate_matchup(matchup, teams):
"""Ensure the matchup function works with team objects."""
if not callable(matchup):
raise TypeError('matchup must be a function!')
try:
winner = matchup(*teams[:2])
except BaseException:
raise ValueError('p must take two team object inputs!')
if winner not in (0, 1):
raise ValueError('matchup should return 0 or 1!')
def _validate_teams(teams):
"""Ensure the teams input is valid."""
if type(teams) not in (list, tuple):
raise TypeError('teams must be a list or tuple of team objects!')
num_teams = len(teams)
if num_teams < 2 or num_teams & (num_teams - 1):
raise ValueError('Invalid number of teams!')
if len(set(map(type, teams))) > 1:
raise TypeError('Team objects must all be the same type!')
if __name__ == '__main__':
pass