Calculate the ratings improvement for a given player, provided their opponents and
corresponding results versus them.
"""
+ if len(opponents) == 0 or len(results) == 0:
+ return player
+
p_g2 = player.to_glicko2()
gs = []
.filter(PlayerGameStat.player_id > 2)\
.all()
+ return pgstats_raw
+
except Exception as e:
log.error("Error fetching player_game_stat records for game {}".format(game.game_id))
log.error(e)
raise e
+ def _filter_pgstats(self, game, pgstats_raw):
+ """
+ Filter the raw game stats so that all of them are Glicko-eligible.
+
+ :param pgstats_raw: the list of raw PlayerGameStat
+ :return: list of PlayerGameStat
+ """
pgstats = []
for pgstat in pgstats_raw:
# ensure warmup isn't included in the pgstat records
k = KREDUCTION.eval(pgstat.alivetime.total_seconds(), game.duration.total_seconds())
if k <= 0.0:
continue
+ elif pgstat.player_id <= 2:
+ continue
else:
pgstats.append(pgstat)
def _load_glicko_wip(self, player_id, game_type_cd, category):
"""
- Retrieve a PlayerGlicko record from the database.
+ Retrieve a PlayerGlicko record from the database or local cache.
:param player_id: the player ID to fetch
:param game_type_cd: the game type code
return wip
- def load(self, game_id):
+ def load(self, game_id, game=None, pgstats=None):
"""
Load all of the needed information from the database. Compute results for each player pair.
"""
- game = self._load_game(game_id)
- pgstats = self._load_pgstats(game)
+ if game is None:
+ game = self._load_game(game_id)
+
+ if pgstats is None:
+ pgstats = self._load_pgstats(game)
+
+ pgstats = self._filter_pgstats(game, pgstats)
+
game_type_cd = game.game_type_cd
category = game.category
new_pg = rate(wip.pg, wip.opponents, wip.results)
log.debug("New rating for player {} before factors: mu={} phi={} sigma={}"
- .format(pg.player_id, new_pg.mu, new_pg.phi, new_pg.sigma))
+ .format(new_pg.player_id, new_pg.mu, new_pg.phi, new_pg.sigma))
avg_k_factor = sum(wip.k_factors)/len(wip.k_factors)
avg_ping_factor = LATENCY_TREND_FACTOR * sum(wip.ping_factors)/len(wip.ping_factors)
log.debug("New rating for player {} after factors: mu={} phi={} sigma={}"
.format(wip.pg.player_id, wip.pg.mu, wip.pg.phi, wip.pg.sigma))
- def save(self, session):
+ def save(self):
"""
Put all changed PlayerElo and PlayerGameStat instances into the
session to be updated or inserted upon commit.
"""
for wip in self.wips.values():
- session.add(wip.pg)
+ self.session.add(wip.pg)
- session.commit()
+ self.session.commit()
def main():
from sqlalchemy import Sequence
from sqlalchemy.orm.exc import NoResultFound
from xonstat.elo import EloProcessor
+from xonstat.glicko import GlickoProcessor
from xonstat.models import DBSession, Server, Map, Game, PlayerGameStat, PlayerWeaponStat
from xonstat.models import PlayerRank, PlayerCaptime, PlayerGameFragMatrix
from xonstat.models import TeamGameStat, PlayerGameAnticheat, Player, Hashkey, PlayerNick
self.weapons)
-def elo_submission_category(submission):
- """Determines the Elo category purely by what is in the submission data."""
+def game_category(submission):
+ """Determines the game's category purely by what is in the submission data."""
mod = submission.mod
vanilla_allowed_weapons = {"shotgun", "devastator", "blaster", "mortar", "vortex", "electro",
return game_type_cd not in {'cts'}
-def gametype_elo_eligible(game_type_cd):
- """True of the game type should process Elos. False otherwise."""
+def gametype_rating_eligible(game_type_cd):
+ """True of the game type should process ratings (Elo/Glicko). False otherwise."""
return game_type_cd in {'duel', 'dm', 'ca', 'ctf', 'tdm', 'ka', 'ft'}
def create_game(session, game_type_cd, server_id, map_id, match_id, start_dt, duration, mod,
- winner=None):
+ winner=None, category=None):
"""
Creates a game. Parameters:
start_dt - when the game started (datetime object)
duration - how long the game lasted
winner - the team id of the team that won
+ category - the category of the game
"""
seq = Sequence('games_game_id_seq')
game_id = session.execute(seq)
game.duration = duration
+ game.category = category
+
try:
session.query(Game).filter(Game.server_id == server_id)\
.filter(Game.match_id == match_id).one()
map_id=gmap.map_id,
match_id=submission.match_id,
start_dt=datetime.datetime.utcnow(),
- duration=submission.duration
+ duration=submission.duration,
+ category=game_category(submission)
)
events_by_hashkey = {elem["P"]: elem for elem in submission.humans + submission.bots}
players_by_hashkey = get_or_create_players(session, events_by_hashkey)
pgstats = []
- elo_pgstats = []
+ rating_pgstats = []
player_ids = []
hashkeys_by_player_id = {}
for hashkey, player in players_by_hashkey.items():
frag_matrix = create_frag_matrix(session, submission.player_indexes, pgstat, events)
- # player ranking opt-out
+ # player rating opt-out
if 'r' in events and events['r'] == '0':
- log.debug("Excluding player {} from ranking calculations (opt-out)"
+ log.debug("Excluding player {} from rating calculations (opt-out)"
.format(pgstat.player_id))
- else:
- elo_pgstats.append(pgstat)
+ elif pgstat.player_id > 2:
+ rating_pgstats.append(pgstat)
if player.player_id > 1:
create_anticheats(session, pgstat, game, player, events)
for events in submission.teams:
create_team_stat(session, game, events)
- if server.elo_ind and gametype_elo_eligible(submission.game_type_cd):
- ep = EloProcessor(session, game, elo_pgstats)
+ rating_eligible = gametype_rating_eligible(submission.game_type_cd)
+ if rating_eligible and server.elo_ind and len(rating_pgstats) > 1:
+ # calculate Elo ratings
+ ep = EloProcessor(session, game, rating_pgstats)
ep.save(session)
elos = ep.wip
+
+ # calculate Glicko ratings
+ gp = GlickoProcessor(session)
+ gp.load(game.game_id, game, rating_pgstats)
+ gp.process()
+ gp.save()
else:
elos = {}