From d0c3403cba8cf9fcbf8a95f641c735efe9a755ea Mon Sep 17 00:00:00 2001 From: Yannik Schmidt Date: Fri, 19 Jun 2020 01:43:10 +0200 Subject: [PATCH] implement balance query --- python/backends/entities/Players.py | 3 +++ python/backends/trueskillWrapper.py | 37 +++++++++++++++++------------ python/httpAPI.py | 31 +++++++++++++++++------- 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/python/backends/entities/Players.py b/python/backends/entities/Players.py index 824168a..bfd4d54 100644 --- a/python/backends/entities/Players.py +++ b/python/backends/entities/Players.py @@ -37,6 +37,9 @@ class PlayerInRound(Player): def __str__(self): return "PlayerInRound: N: {} ID: {} Team: {}".format(self.name, self.id, self.team) +def playerInRoundFromJson(jsonDict): + return PlayerInRound(jsonDict["id"], jsonDict["name"], jsonDict["team"], timestamp=dt.datetime.now()) + class PlayerInDatabase(Player): def __init__(self, playerId, name, rating, wins, games): diff --git a/python/backends/trueskillWrapper.py b/python/backends/trueskillWrapper.py index 46d9ec1..4b60610 100644 --- a/python/backends/trueskillWrapper.py +++ b/python/backends/trueskillWrapper.py @@ -1,9 +1,9 @@ #!/usr/bin/python3 -from trueskill import TrueSkill, Rating -import scipy +import trueskill +import scipy.stats import math -env = TrueSkill(draw_probability=0, mu=1500, sigma=833, tau=40, backend='mpmath') +env = trueskill.TrueSkill(draw_probability=0, mu=1500, sigma=833, tau=40, backend='mpmath') env.make_as_global() ##################################################### @@ -71,9 +71,9 @@ def evaluateRound(r): def newRating(mu=None, sigma=None): if mu and sigma: - return Rating(mu=mu, sigma=sigma) + return trueskill.Rating(mu=mu, sigma=sigma) elif mu: - return Rating(mu=mu, sigma=env.sigma) + return trueskill.Rating(mu=mu, sigma=env.sigma) else: return env.create_rating() @@ -86,13 +86,13 @@ def balance(players, buddies=None): def predictOutcome(teamA, teamB): '''Predict outcome of a game between team a and team b returns: (0|1, confidence)''' - + ratingsA = [ p.rating for p in teamA ] ratingsB = [ p.rating for p in teamB ] muTeamA = sum([ r.mu for r in ratingsA]) muTeamB = sum([ r.mu for r in ratingsB]) - sigmaTeamA = math.sqrt(sum([ r.sigma**2 for r in ratingsA])) - sigmaTeamB = math.sqrt(sum([ r.sigma**2 for r in ratingsB])) + sigmaTeamA = sum([ r.sigma for r in ratingsA]) + sigmaTeamB = sum([ r.sigma for r in ratingsB]) # probabilty that a random point from normDistTeamA is greater # than a random point from normDistB is normA - normB and then the @@ -103,12 +103,19 @@ def predictOutcome(teamA, teamB): return (0, prob) elif prob < 0.5: return (1, 1-prob) + else: + raise ValueError("Probability was NAN, team rating must have been malformed.") +def balance(players, buddies=None): + sortedByRating = sorted(players, key=lambda p: env.expose(p.rating)) + teamA = [] + teamB = [] + for i in range(0, len(players)): + if i % 2 == 0: + teamA += [sortedByRating[i]] + else: + teamB += [sortedByRating[i]] -def quality(teamA, teamB): - '''Take two teams of players and calculate a game quality''' - - ratingsA = [ p.rating for p in teamA ] - ratingsB = [ p.rating for p in teamB ] - - return trueskill.quality(ratingsA, ratingsB) + prediction, confidence = predictOutcome(teamA, teamB) + quality = 1-abs(0.5 - confidence) + return ((teamA, teamB), quality) diff --git a/python/httpAPI.py b/python/httpAPI.py index 77a2a37..c9abd1c 100644 --- a/python/httpAPI.py +++ b/python/httpAPI.py @@ -2,6 +2,7 @@ import backends.database as db import backends.trueskillWrapper as ts +import backends.entities.Players as players import backends.eventStream import Round import json @@ -28,20 +29,32 @@ def getPlayer(): def getOutcomePrediction(): '''Make a prediction based tww submitted teams of players''' - teamB = [ db.getPlayer(pJson["id"]) for pJson in flask.request.get("teamA") ] - teamB = [ db.getPlayer(pJson["id"]) for pJson in flask.request.get("teamB") ] - cnameTeamA = flask.request.get("cnameTeamA") - cnameTeamB = flask.request.get("cnameTeamB") + teamA = filter(lambda x: x["team"] % 2 == 0, flask.request.json["players"]) + teamB = filter(lambda x: x["team"] % 2 == 1, flask.request.json["players"]) - quality = ts.quality(teamA, teamB) - prediction, confidence = ts.predict(teamA, teamB) + + playersA = [ db.getOrCreatePlayer(players.playerInRoundFromJson(pJson)) for pJson in teamA ] + playersB = [ db.getOrCreatePlayer(players.playerInRoundFromJson(pJson)) for pJson in teamB ] + + prediction, confidence = ts.predictOutcome(playersA, playersB) + quality = 1-abs(0.5-confidence) + balanceSuggestion, qualityOfSuggestion = ts.balance(playersA+playersB) retData = dict() - retData.update( { "cnameTeamA" : cnameTeamA } ) - retData.update( { "cnameTeamB" : cnameTeamB } ) retData.update( { "quality" : quality } ) retData.update( { "prediction" : prediction } ) - retData.update( { "confidence" : confidence } ) + retData.update( { "prediction-confidence" : confidence } ) + + balanceSuggestionJson = { + "teamA" : [ json.loads(p.toJson()) for p in balanceSuggestion[0] ], + "teamB" : [ json.loads(p.toJson()) for p in balanceSuggestion[1] ] + } + + retData.update( { "teamA-orig" : [ json.loads(p.toJson()) for p in playersA ] }) + retData.update( { "teamB-orig" : [ json.loads(p.toJson()) for p in playersB ] }) + + retData.update( { "qualityOfSuggestion" : qualityOfSuggestion } ) + retData.update( { "balanceSuggestion" : balanceSuggestionJson } ) return flask.json.jsonify(retData)