Skip to content

Instantly share code, notes, and snippets.

@jordanbs
Created March 20, 2013 07:07
Show Gist options
  • Save jordanbs/5202847 to your computer and use it in GitHub Desktop.
Save jordanbs/5202847 to your computer and use it in GitHub Desktop.
usage: bracketgen.py [-h] [--go-blue] [--dfs] Download this file, and run with the following command: $ python ~/Downloads/bracketgen.py
#!/usr/bin/env python
#
# Copyright (C) 2013 jordanbs
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# ^^ That excludes the --go-blue parts. NO MODIFYING THAT WHATSOEVER.
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
import random
import argparse
gProbabilityTable = { (1, 1): 0.5,
(1, 2): 0.538,
(1, 3): 0.594,
(1, 4): 0.689,
(1, 5): 0.826,
(1, 6): 0.667,
(1, 7): 1.,
(1, 8): 0.806,
(1, 9): 0.906,
(1, 10): 1.,
(1, 11): 0.4,
(1, 12): 1.,
(1, 13): 1.,
(1, 16): 1.,
(2, 2): 0.5,
(2, 3): 0.614,
(2, 4): 0.444,
(2, 5): 0.2,
(2, 6): 0.697,
(2, 7): 0.738,
(2, 8): 0.571,
(2, 9): 1.,
(2, 10): 0.588,
(2, 11): 0.917,
(2, 12): 1.,
(2, 15): 0.946,
(3, 3): 0.5,
(3, 4): 0.833,
(3, 5): 0.5,
(3, 6): 0.532,
(3, 7): 0.667,
(3, 8): 1.,
(3, 9): 1.,
(3, 10): 0.692,
(3, 11): 0.714,
(3, 14): 0.857,
(4, 4): 0.5,
(4, 5): 0.549,
(4, 6): 0.333,
(4, 7): 0.667,
(4, 8): 0.25,
(4, 9): 0.667,
(4, 10): 1.,
(4, 12): 0.645,
(4, 13): 0.786,
(5, 5): 0.5,
(5, 6): 1.,
(5, 8): 0.25,
(5, 9): 0.333,
(5, 10): 1.,
(5, 12): 0.689,
(5, 13): 0.786,
(6, 6): 0.5,
(6, 7): 0.571,
(6, 8): 0.25,
(6, 10): 0.6,
(6, 11): 0.667,
(6, 14): 0.846,
(7, 7): 0.5,
(7, 10): 0.596,
(7, 14): 1.,
(7, 15): 1.,
(8, 8): 0.5,
(8, 9): 0.515,
(8, 11): 1.,
(8, 13): 1.,
(9, 9): 0.5,
(9, 10): 1.,
(10, 10): 0.5,
(10, 11): 0.,
(10, 14): 1.,
(10, 15): 1.,
(11, 11): 0.5,
(11, 14): 1.,
(12, 12): 0.5,
(12, 13): 0.8,
}
rand = random.SystemRandom().random
#random.seed(2)
#rand = random.random
class Team(object):
fmt = '%s (%s)'
fmt_len = len(fmt) - 2 * fmt.count('%')
def __init__(self, name, seed):
self.name = name
self.seed = seed
def __repr__(self):
return '<Team: %s, Seed: %s>' % (self.name, self.seed)
def __str__(self):
return self.fmt % (self.name, self.seed)
# must be in order
gTeams = [ Team('Louisville', 1),
Team('North Carolina A&T', 16),
Team('Colorado State', 8),
Team('Missouri', 9),
Team('Okalhoma State', 5),
Team('Oregon', 12),
Team('Saint Louis', 4),
Team('New Mexico State', 13),
Team('Memphis', 6),
Team('St. Mary\'s', 11),
Team('Michigan State', 3),
Team('Valparaiso', 14),
Team('Creighton', 7),
Team('Cincinnati', 10),
Team('Duke', 2),
Team('Albany', 15),
Team('Gonzaga', 1),
Team('Southern University', 16),
Team('Pittsburgh', 8),
Team('Wichita State', 9),
Team('Wisconsin', 5),
Team('Ole Miss', 12),
Team('Kansas State', 4),
Team('Boise St/La Salle', 13),
Team('Arizona', 6),
Team('Belmont', 11),
Team('New Mexico', 3),
Team('Harvard', 14),
Team('Notre Dame', 7),
Team('Iowa State', 10),
Team('Ohio State', 2),
Team('Iona', 15),
Team('Kansas', 1),
Team('Western Kentucky', 16),
Team('North Carolina', 8),
Team('Vilanova', 9),
Team('Virginia Commonwealth', 5),
Team('Akron', 12),
Team('Michigan', 4),
Team('South Dakota State', 13),
Team('UCLA', 6),
Team('Minnesota', 11),
Team('Florida', 3),
Team('Northwestern State', 14),
Team('San Diego State', 7),
Team('Oklahoma', 10),
Team('Georgetown', 2),
Team('Florida Gulf Coast', 15),
Team('Indiana', 1),
Team('LIU/JMU', 16),
Team('NC State', 8),
Team('Temple', 9),
Team('UNLV', 5),
Team('California', 12),
Team('Syracuse', 4),
Team('Montana', 13),
Team('Butler', 6),
Team('Bucknell', 11),
Team('Marquette', 3),
Team('Davidson', 14),
Team('Illinois', 7),
Team('Colorado', 10),
Team('Miami', 2),
Team('Pacific', 15)
]
class Bracket(object):
def __init__(self, dfs=False):
self.dfs = dfs
if dfs:
self._generate_bracket_tree()
else:
self._generate_bracket_queue()
def eval_tourney(self, go_blue=False):
# Depth first tournament
# only play a game when you need the victor for the next one
if self.dfs:
return self._eval_tourney_tree(self.champ_node, go_blue=go_blue)
# Breadth first tournament
# ya know, like how tournaments are normally played
return self._eval_tourney_queue(self.game_queue, go_blue=go_blue)
def _eval_tourney_tree(self, root_node, go_blue=False):
return root_node.eval_node(go_blue=go_blue)
def _eval_tourney_queue(self, game_queue, go_blue=False):
while len(game_queue) > 1:
team1 = game_queue.pop(0)
assert(len(game_queue) > 0)
team2 = game_queue.pop(0)
assert(isinstance(team1, Team) and isinstance(team2, Team))
winner = decide_game(team1, team2, go_blue=go_blue, auto=False)
game_queue.append(winner)
return winner
def _generate_top_nodes(self):
self.top_nodes = list()
game = None
for i, team in enumerate(gTeams):
if i % 2 == 0:
game = BracketNode()
self.top_nodes.append(game)
game.input1 = team
else:
game.input2 = team
def _generate_bracket_tree(self):
self._generate_top_nodes()
self.champ_node = BracketNode()
self._generate_bracket_helper(self.champ_node, 5)
def _generate_bracket_helper(self, node, rounds):
if rounds == 1:
node.input1 = self.top_nodes[0]
node.input2 = self.top_nodes[1]
del self.top_nodes[0], self.top_nodes[0]
return
node.input1 = BracketNode()
self._generate_bracket_helper(node.input1, rounds - 1)
node.input2 = BracketNode()
self._generate_bracket_helper(node.input2, rounds - 1)
def _generate_bracket_queue(self):
self.game_queue = list()
for team in gTeams:
self.game_queue.append(team)
class BracketNode(object):
def eval_node(self, go_blue=False):
assert(self.input1 and self.input2)
team1 = None
team2 = None
if isinstance(self.input1, Team):
team1 = self.input1
if isinstance(self.input2, Team):
team2 = self.input2
if team1 is None:
team1 = self.input1.eval_node(go_blue=go_blue)
if team2 is None:
team2 = self.input2.eval_node(go_blue=go_blue)
winner = decide_game(team1, team2, go_blue=go_blue)
return winner
def pred(team):
return team.name.startswith('Mich') and not team.name.endswith('ate')
def decide_game(team1, team2, go_blue=False, auto=False):
winner = None
odds = None
#ipdb.set_trace()
try:
odds = gProbabilityTable[(team1.seed, team2.seed)]
random_chance = rand()
#print('dice: %.2f, odds: %.2f' % (random_chance, odds))
if ((random_chance < odds or (go_blue and pred(team1))) and
not (go_blue and pred(team2))):
winner = team1
else:
odds = 1. - odds
winner = team2
except KeyError:
user_in = ''
print('')
print_game(team1, team2)
print('This matchup has never happened before!')
while not winner:
if auto:
if team1.seed < team2.seed:
winner = team1
else:
winner = team2
break
user_in = raw_input('Choose the victor: ')
if team1.name.lower().startswith(user_in.lower()):
winner = team1
if team2.name.lower().startswith(user_in.lower()):
if not winner:
winner = team2
else:
winner = None
print('')
assert(winner != None)
print_game(team1, team2, winner, odds)
return winner
gLongestNameLen = max([len(team.name) + len(str(team.seed)) + Team.fmt_len for team in gTeams])
def print_game(team1, team2, winner=None, odds=None):
team1_display = str(team1).rjust(gLongestNameLen)
team2_display = str(team2).ljust(gLongestNameLen)
display = '%s vs %s' % (team1_display, team2_display)
if winner:
winner_fmt = ' %s wins!'
winner_fmt_len = len(winner_fmt) - 2 * winner_fmt.count('%')
winner_display = (winner_fmt % winner).ljust(gLongestNameLen + winner_fmt_len)
display += winner_display
if odds:
display += ' %.0f%%' % (odds * 100)
else:
display += ' ...'
print(display)
def append_reflected_table(table):
reflected_table = dict()
for tup, prob in table.iteritems():
reflected_table[(tup[1], tup[0])] = 1. - prob
for key, value in reflected_table.iteritems():
table[key] = value
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--go-blue', action='store_false')
parser.add_argument('--dfs', action='store_true', help='Perform depth first tourney')
args = parser.parse_args()
append_reflected_table(gProbabilityTable)
bracket = Bracket(dfs=args.dfs)
return bracket.eval_tourney(go_blue=args.go_blue)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment