def test_exercise(): player = Glicko2Entry(1500, 200, 0.06) glicko2_update( player, [ (Glicko2Entry(100, 100), 0), (Glicko2Entry(30000, 10000), 1), (Glicko2Entry(1500, 100), 1), (Glicko2Entry(1500, 100), 0), ], )
def test_glicko2(): glicko2_configure( tao=0.5, min_rd=10, max_rd=500, ) player = Glicko2Entry(1500, 200, 0.06) a = Glicko2Entry(1400, 30, 0.06) b = Glicko2Entry(1550, 100, 0.06) c = Glicko2Entry(1700, 300, 0.06) player = glicko2_update(player, [(a, 1), (b, 0), (c, 0)]) assert round(player.rating, 1) == 1464.1 assert round(player.deviation, 1) == 151.5
def test_copy(): player = Glicko2Entry(1500, 200, 0.06) copy = player.copy() assert copy.rating == player.rating assert copy.deviation == player.deviation assert copy.volatility == player.volatility assert copy.mu == player.mu assert copy.phi == player.phi
def process_game(self, game: GameRecord) -> Glicko2Analytics: if game.black_manual_rank_update is not None: self._storage.set( game.black_id, Glicko2Entry(rank_to_rating(game.black_manual_rank_update))) if game.white_manual_rank_update is not None: self._storage.set( game.white_id, Glicko2Entry(rank_to_rating(game.white_manual_rank_update))) ## Only count the first timeout in correspondence games as a ranked loss if game.timeout and game.speed == 3: # correspondence timeout player_that_timed_out = game.black_id if game.black_id != game.winner_id else game.white_id skip = self._storage.get_timeout_flag( game.black_id) or self._storage.get_timeout_flag(game.white_id) self._storage.set_timeout_flag(player_that_timed_out, True) if skip: return Glicko2Analytics(skipped=True, game=game) if game.speed == 3: # clear corr. timeout flags self._storage.set_timeout_flag(game.black_id, True) self._storage.set_timeout_flag(game.white_id, True) window = (int(game.ended) // 86400) * 86400 black_base = self._storage.get_first_rating_older_than( game.black_id, window) white_base = self._storage.get_first_rating_older_than( game.white_id, window) black_cur = self._storage.get(game.black_id) white_cur = self._storage.get(game.white_id) self._storage.add_match_history(game.black_id, game.ended, (game, white_cur)) self._storage.add_match_history(game.white_id, game.ended, (game, black_cur)) updated_black = glicko2_update(black_base, [ (opponent.copy( (1 if past_game.black_id != game.black_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap)), past_game.winner_id == game.black_id) for past_game, opponent in self._storage.get_matches_newer_or_equal_to(game.black_id, window) ]) updated_white = glicko2_update(white_base, [ (opponent.copy( (1 if past_game.black_id != game.white_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap)), past_game.winner_id == game.white_id) for past_game, opponent in self._storage.get_matches_newer_or_equal_to(game.white_id, window) ]) self._storage.set(game.black_id, updated_black) self._storage.set(game.white_id, updated_white) self._storage.add_rating_history(game.black_id, game.ended, updated_black) self._storage.add_rating_history(game.white_id, game.ended, updated_white) return Glicko2Analytics( skipped=False, game=game, expected_win_rate=black_cur.expected_win_probability( white_cur, get_handicap_adjustment(black_cur.rating, game.handicap), ignore_g=True), black_rating=black_cur.rating, white_rating=white_cur.rating, black_deviation=black_cur.deviation, white_deviation=white_cur.deviation, black_rank=rating_to_rank(black_cur.rating), white_rank=rating_to_rank(white_cur.rating), black_updated_rating=updated_black.rating, white_updated_rating=updated_white.rating, )
def test_nop(): player = Glicko2Entry(1500, 200, 0.06) p = glicko2_update(player, []) assert p.rating == player.rating
def test_expected_win_probability(): player = Glicko2Entry(1500, 200, 0.06) assert player.expected_win_probability(player, 0) == 0.5
def test_expansion(): player = Glicko2Entry(1500, 200, 0.06) player.expand_deviation_because_no_games_played(1) assert round(player.deviation, 1) == 200.3
def test_str(): player = Glicko2Entry(1500, 200, 0.06) assert isinstance(str(player), str)
def process_game(self, game: GameRecord) -> Glicko2Analytics: ## Only count the first timeout in correspondence games as a ranked loss if game.timeout and game.speed == 3: # correspondence timeout player_that_timed_out = game.black_id if game.black_id != game.winner_id else game.white_id skip = self._storage.get_timeout_flag( game.black_id) or self._storage.get_timeout_flag(game.white_id) self._storage.set_timeout_flag(player_that_timed_out, True) if skip: return Glicko2Analytics(skipped=True, game=game) if game.speed == 3: # clear corr. timeout flags self._storage.set_timeout_flag(game.black_id, True) self._storage.set_timeout_flag(game.white_id, True) ## read base rating (last rating before the current rating period) window = (int(game.ended) // window_width) * window_width black_base = self._storage.get_first_rating_older_than( game.black_id, window).copy() white_base = self._storage.get_first_rating_older_than( game.white_id, window).copy() ## since we do not update deviation in periods without games, we have to do update it now if there are empty periods size the base rating was calclulated black_base_time = self._storage.get_first_timestamp_older_than( game.black_id, window) white_base_time = self._storage.get_first_timestamp_older_than( game.white_id, window) if black_base_time is not None: black_base.expand_deviation_because_no_games_played( int((game.ended - black_base_time) / no_games_window_witdh)) if white_base_time is not None: white_base.expand_deviation_because_no_games_played( int((game.ended - white_base_time) / no_games_window_witdh)) ## store games in the match history self._storage.add_match_history(game.black_id, game.ended, (game, white_base)) self._storage.add_match_history(game.white_id, game.ended, (game, black_base)) ## update ratings updated_black = glicko2_update(black_base, [ (opponent.copy( (1 if past_game.black_id != game.black_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap)), past_game.winner_id == past_game.black_id) for past_game, opponent in self._storage.get_matches_newer_or_equal_to(game.black_id, window) ]) updated_white = glicko2_update(white_base, [ (opponent.copy( (1 if past_game.black_id != game.white_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap)), past_game.winner_id == past_game.white_id) for past_game, opponent in self._storage.get_matches_newer_or_equal_to(game.white_id, window) ]) # do not decrease rating if player won or increase if she lost # users complain when their rating drops after they won a game, even if it is only by a few points. This happens # regular since the deviation becomes lower with each game played in a period. # Here we accept the rating system to be slightly less accurate for the sake of user experience. Since we use # the base rating of both players when updating the ratings, this only affects future rating updates if this # game happens to be the last game in the rating period of the affected player. black_cur = self._storage.get(game.black_id).copy() white_cur = self._storage.get(game.white_id).copy() if (game.winner_id == game.black_id and updated_black.rating - black_cur.rating < 0) or \ (game.winner_id != game.black_id and updated_black.rating - black_cur.rating > 0): updated_black = Glicko2Entry(rating=black_cur.rating, deviation=updated_black.deviation, volatility=updated_black.volatility) if (game.winner_id == game.white_id and updated_white.rating - white_cur.rating < 0) or \ (game.winner_id != game.white_id and updated_white.rating - white_cur.rating > 0): updated_white = Glicko2Entry(rating=white_cur.rating, deviation=updated_white.deviation, volatility=updated_white.volatility) self._storage.set(game.black_id, updated_black) self._storage.set(game.white_id, updated_white) self._storage.add_rating_history(game.black_id, game.ended, updated_black) self._storage.add_rating_history(game.white_id, game.ended, updated_white) return Glicko2Analytics( skipped=False, game=game, expected_win_rate=black_cur.expected_win_probability( white_cur, get_handicap_adjustment(black_cur.rating, game.handicap), ignore_g=True), black_rating=black_cur.rating, white_rating=white_cur.rating, black_deviation=black_cur.deviation, white_deviation=white_cur.deviation, black_rank=rating_to_rank(black_cur.rating), white_rank=rating_to_rank(white_cur.rating), black_updated_rating=updated_black.rating, white_updated_rating=updated_white.rating, )
def process_game(self, game: GameRecord) -> Dict[str, Glicko2Analytics]: global ALWAYS_USE_OVERALL ret = {} overall_storage = self._storages['999-999'] for speed in [game.speed, 999]: for size in [game.size, 999]: k = '%d-%d' % (speed, size) storage = self._storages[k] if game.black_manual_rank_update is not None: storage.set( game.black_id, Glicko2Entry( rank_to_rating(game.black_manual_rank_update))) if game.white_manual_rank_update is not None: storage.set( game.white_id, Glicko2Entry( rank_to_rating(game.white_manual_rank_update))) ## Only count the first timeout in correspondence games as a ranked loss if game.timeout and game.speed == 3: # correspondence timeout player_that_timed_out = game.black_id if game.black_id != game.winner_id else game.white_id skip = storage.get_timeout_flag( game.black_id) or storage.get_timeout_flag( game.white_id) storage.set_timeout_flag(player_that_timed_out, True) if skip: ret[k] = Glicko2Analytics(skipped=True, game=game) continue if game.speed == 3: # clear corr. timeout flags storage.set_timeout_flag(game.black_id, True) storage.set_timeout_flag(game.white_id, True) black = storage.get(game.black_id) white = storage.get(game.white_id) if ALWAYS_USE_OVERALL: src_black = overall_storage.get(game.black_id) src_white = overall_storage.get(game.white_id) else: src_black = black src_white = white updated_black = glicko2_update( black, [( src_white.copy(-get_handicap_adjustment( src_white.rating, game.handicap)), game.winner_id == game.black_id, )], ) updated_white = glicko2_update( white, [( src_black.copy( get_handicap_adjustment(src_black.rating, game.handicap)), game.winner_id == game.white_id, )], ) storage.set(game.black_id, updated_black) storage.set(game.white_id, updated_white) #storage.add_rating_history(game.black_id, game.ended, updated_black) #storage.add_rating_history(game.white_id, game.ended, updated_white) ret[k] = Glicko2Analytics( skipped=False, game=game, expected_win_rate=black.expected_win_probability( white, get_handicap_adjustment(black.rating, game.handicap), ignore_g=True), black_rating=black.rating, white_rating=white.rating, black_deviation=black.deviation, white_deviation=white.deviation, black_rank=rating_to_rank(black.rating), white_rank=rating_to_rank(white.rating), black_updated_rating=updated_black.rating, white_updated_rating=updated_white.rating, ) return ret
def process_game(self, game: GameRecord) -> Glicko2Analytics: if game.black_manual_rank_update is not None: self._storage.set(game.black_id, Glicko2Entry(rank_to_rating(game.black_manual_rank_update))) if game.white_manual_rank_update is not None: self._storage.set(game.white_id, Glicko2Entry(rank_to_rating(game.white_manual_rank_update))) ## Only count the first timeout in correspondence games as a ranked loss if game.timeout and game.speed == 3: # correspondence timeout player_that_timed_out = game.black_id if game.black_id != game.winner_id else game.white_id skip = self._storage.get_timeout_flag(game.black_id) or self._storage.get_timeout_flag(game.white_id) self._storage.set_timeout_flag(player_that_timed_out, True) if skip: return Glicko2Analytics(skipped=True, game=game) if game.speed == 3: # clear corr. timeout flags self._storage.set_timeout_flag(game.black_id, True) self._storage.set_timeout_flag(game.white_id, True) black = self._storage.get(game.black_id) white = self._storage.get(game.white_id) updated_black = glicko2_update( black, [ ( white.copy(-get_handicap_adjustment(white.rating, game.handicap)), game.winner_id == game.black_id, ) ], ) updated_white = glicko2_update( white, [ ( black.copy(get_handicap_adjustment(black.rating, game.handicap)), game.winner_id == game.white_id, ) ], ) self._storage.set(game.black_id, updated_black) self._storage.set(game.white_id, updated_white) #self._storage.add_rating_history(game.black_id, game.ended, updated_black) #self._storage.add_rating_history(game.white_id, game.ended, updated_white) return Glicko2Analytics( skipped=False, game=game, expected_win_rate=black.expected_win_probability( white, get_handicap_adjustment(black.rating, game.handicap), ignore_g=True ), black_rating=black.rating, white_rating=white.rating, black_deviation=black.deviation, white_deviation=white.deviation, black_rank=rating_to_rank(black.rating), white_rank=rating_to_rank(white.rating), black_updated_rating=updated_black.rating, white_updated_rating=updated_white.rating, )