# publicdripton/groupelo

### Subversion checkout URL

You can clone with HTTPS or Subversion.

Fetching contributors…

Cannot retrieve contributors at this time

executable file 214 lines (182 sloc) 6.71 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 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 `#!/usr/bin/env python"""Calculate Elo ratings for a series of game results, where games caninclude variable numbers of players and teams.See http://en.wikipedia.org/wiki/Elo_rating_systemNew players start at 1500Rn = Ro + K(W-We)Rn is new ratingRo is previous ratingK is the same for every 2-player matchW is 1 for win, 0 for loss, 0.5 for drawWe is win expectancyFor 2 players, We = 1 / (10 ** (-delta/400) + 1)where delta is the difference in ratingsFor N players, assume each player played a game againsteach of his opponents. But reduce K so that multiplayermatches count the same as 2-player matches.We add 1 point per match, for "anti-deflation""""__copyright__ = "Copyright 2010 David Ripton"__license__ = "MIT"import sysimport itertoolsfrom collections import defaultdictSTARTING_RATING = 1500ANTI_DEFLATION = 1CATEGORIES = [    "overall",    "group", "solo",    "armed", "unarmed",    "tournament", "exhibition",]def constant_factory(value):    return itertools.repeat(value).nextdef explode(li):    """Convert a list of strings into a list of lists of strings. Each inner list is one team. """    result = []    for el in li:        li2 = el.split("&")        inner = [el2.strip() for el2 in li2]        result.append(inner)    return resultdef win_expectancy(r1, r2):    """Return the win expectancy for the player with rating r1 against the player with rating r2."""    return 1.0 / (10 ** ((r2 - r1) / 400.0) + 1)def rating_delta(r1, r2, w):    """Return the rating delta (relative to player with rating r1) for a match between players with ratings r1 and r2, and result w. w is 1 for a win for r1, 0 for a loss for r1, and 0.5 for a draw. """    k = 50    we = win_expectancy(r1, r2)    return k * (w - we)def bare_name(name):    """Return name without any trailing '*' or '!' characters."""    while name and (name[-1] == "*" or name[-1] == "!"):        name = name[:-1]    return nameclass Elo(object):    def __init__(self, category, lines):        assert category in CATEGORIES        self.category = category        self.lines = lines        # name: rating        self.ratings = defaultdict(constant_factory(STARTING_RATING))        # name: number of wins        self.wins = defaultdict(int)        # name: number of losses        self.losses = defaultdict(int)    def process(self, line):        """Process a line denoting one match, and update the ratings."""        line = line.strip()        if not line or line.startswith("#"):            return        parts = line.split(",")        assert len(parts) >= 4        game_id = parts[0].strip()        armed_unarmed = parts[1].strip()        tournament_exhibition = parts[2].strip()        winners = [parts[3]]        losers = parts[4:]        winner_lists = explode(winners)        loser_lists = explode(losers)        # Filter out results for the wrong category of fight        if self.category == "overall":            pass        elif self.category == "group":            if len(winner_lists[0]) == 1 and len(loser_lists) == 1:                return        elif self.category == "solo":            if len(winner_lists[0]) > 1 or len(loser_lists) > 1:                return        elif self.category == "armed":            if armed_unarmed != self.category:                return        elif self.category == "unarmed":            if armed_unarmed != self.category:                return        elif self.category == "tournament":            if tournament_exhibition != self.category:                return        elif self.category == "exhibition":            if tournament_exhibition != self.category:                return        # name: change in rating        deltas = defaultdict(int)        for winner_list in winner_lists:            for winner in winner_list:                name = bare_name(winner)                self.wins[name] += 1        for loser_list in loser_lists:            for loser in loser_list:                name = bare_name(loser)                self.losses[name] += 1        opponent_count = 0        for loser_list in loser_lists:            for loser in loser_list:                opponent_count += 1        # Assumes all teams have the same number of players.        ally_count = -1        for winner_list in winner_lists:            for winner in winner_list:                ally_count += 1        for ii, loser_list in enumerate(loser_lists):            for loser in loser_list:                loser = bare_name(loser)                for winner_list in winner_lists:                    for winner in winner_list:                        winner = bare_name(winner)                        wr = self.ratings[winner]                        lr = self.ratings[loser]                        delta = rating_delta(wr, lr, 1)                        deltas[winner] += delta                        deltas[loser] -= delta                # Only the losers after this one, to avoid double-counting.                for jj in xrange(ii + 1, len(loser_lists)):                    loser_list2 = loser_lists[jj]                    for loser2 in loser_list2:                        loser2 = bare_name(loser2)                        r1 = self.ratings[loser]                        r2 = self.ratings[loser2]                        delta = rating_delta(r1, r2, 0.5)                        deltas[loser] += delta                        deltas[loser2] -= delta        adjusted_deltas = {}        for key, value in deltas.iteritems():            if ally_count == 0:                adjusted_deltas[key] = value / opponent_count ** 0.5            else:                adjusted_deltas[key] = value / opponent_count        for name, delta in adjusted_deltas.iteritems():            self.ratings[name] += delta + ANTI_DEFLATION    def process_all(self):        """Process all lines."""        for line in self.lines:            self.process(line)    def output(self):        sorted_ratings = sorted((          (rating, name) for name, rating in self.ratings.iteritems()),          reverse=True)        print self.category        for rating, name in sorted_ratings:            print "%.3f %s (%d-%d)" % (rating, name,              self.wins[name], self.losses[name])        printdef main():    if len(sys.argv) > 1:        fn = sys.argv[1]        fil = open(fn)    else:        fil = sys.stdin    bytes = fil.read()    fil.close()    lines = bytes.split("\n")    for category in CATEGORIES:        elo = Elo(category, lines)        elo.process_all()        elo.output()if __name__ == "__main__":    main()`
Something went wrong with that request. Please try again.