There's a popular perspective that global rating has inflated in the past few years. What used to be the near-top players at 2k rating is now 2.4k and higher, with some reaching around 2.7k in mostly legitimate gameplay.
I hypothesized that the matchmaker -> global rating adjustment could be the source of that influx of rating for global.
From my limited understand of the code, here's what I understood looking at it:
The game rating adjuster breaks trueskill in 2 ways:
- It does not reduce the global rating of the opponent if they lose
- It only allows rating to move up
|
def compute_rating( |
|
self, |
|
ratings: RatingDict |
|
) -> RatingDict: |
|
""" |
|
Adjust one rating to bring it closer to a different base rating. For |
|
each player, this will rate the game with trueskill as if they |
|
played this game with the rating we are adjusting instead of the |
|
base rating. Adjustments are only returned under certain conditions to |
|
prevent rating manipulation. |
|
""" |
|
new_adjusted_ratings = {} |
|
for player_id, base_rating in self.base_ratings.items(): |
|
old_adjusted_rating = ratings[player_id] |
|
# Since we only adjust upwards, we should not adjust ratings that |
|
# are already higher than the base. |
|
if base_rating.displayed() < old_adjusted_rating.displayed(): |
|
continue |
|
# Make a copy of the base ratings, but substitute this player's |
|
# rating with the rating we are adjusting. |
|
old_ratings = dict(self.base_ratings) |
|
old_ratings[player_id] = old_adjusted_rating |
|
|
|
new_ratings = self.rater.compute_rating(old_ratings) |
|
new_adjusted_rating = new_ratings[player_id] |
|
self._logger.debug( |
|
"Got new adjusted rating for player %d: %s", |
|
player_id, |
|
new_adjusted_rating |
|
) |
|
if ( |
|
old_adjusted_rating.displayed() < |
|
new_adjusted_rating.displayed() <= |
|
config.RATING_ADJUSTMENT_MAX_RATING |
|
): |
|
new_adjusted_ratings[player_id] = new_adjusted_rating |
|
|
|
return new_adjusted_ratings |
|
|
As I understand in this function, the rating adjuster adjusts the real global rating history of the player.
|
async with self._db.acquire() as conn: |
|
# Fetch all players rating info from the database |
|
player_ratings = await self._get_all_player_ratings( |
|
conn, rater.player_ids |
|
) |
|
rating_result = await self._rate_for_leaderboard( |
|
conn, |
|
summary.game_id, |
|
summary.rating_type, |
|
player_ratings, |
|
rater |
|
) |
|
assert rating_result is not None |
|
rating_results.append(rating_result) |
|
|
|
# TODO: If we add hidden ratings, make sure to check for them here. |
|
# Hidden ratings should not affect global. |
|
# TODO: Use game_type == "matchmaker" instead? |
|
if summary.rating_type != RatingType.GLOBAL: |
|
self._logger.debug( |
|
"Performing global rating adjustment for players: %s", |
|
rater.player_ids |
|
) |
|
adjustment_rater = AdjustmentGameRater( |
|
rater, |
|
rating_result.old_ratings |
|
) |
|
global_rating_result = await self._rate_for_leaderboard( |
|
conn, |
|
summary.game_id, |
|
RatingType.GLOBAL, |
|
player_ratings, |
|
adjustment_rater, |
|
update_game_player_stats=False |
|
) |
|
if global_rating_result: |
|
rating_results.append(global_rating_result) |
|
|
|
for rating_result in rating_results: |
|
await self._publish_rating_changes( |
|
rating_result.game_id, |
|
rating_result.rating_type, |
|
rating_result.old_ratings, |
|
rating_result.new_ratings, |
|
rating_result.outcome_map |
|
) |
For a trueskill-compatible system, I would've expected something like adjusting only a secondary global visual rating. The visual rating would be given to clients to protect new players from player-led rating discrimination.
Team balance functions in matchmaker and custom game lobby should ideally be given the real rating, with any UI elements using visual rating, but if that's too difficult then just balancing by visual rating is possible too, since trueskill will calculate the results of such imbalanced non-50/50 games correctly as particularly expected/unexpected outcomes.
In theory there could be problems of players feeling like the game is creating unfair lobbies, but I think that visual rating being used for balance should be ok as it is under the reasonable assumption that matchmaker skill transfers to real games, and if real rating is used for balance then it should adjust close enough to visual rating to create fair lobbies within 10 games.
There's a popular perspective that global rating has inflated in the past few years. What used to be the near-top players at 2k rating is now 2.4k and higher, with some reaching around 2.7k in mostly legitimate gameplay.
I hypothesized that the matchmaker -> global rating adjustment could be the source of that influx of rating for global.
From my limited understand of the code, here's what I understood looking at it:
The game rating adjuster breaks trueskill in 2 ways:
server/server/rating_service/game_rater.py
Lines 74 to 112 in 736b238
As I understand in this function, the rating adjuster adjusts the real global rating history of the player.
server/server/rating_service/rating_service.py
Lines 153 to 198 in 736b238
For a trueskill-compatible system, I would've expected something like adjusting only a secondary global visual rating. The visual rating would be given to clients to protect new players from player-led rating discrimination.
Team balance functions in matchmaker and custom game lobby should ideally be given the real rating, with any UI elements using visual rating, but if that's too difficult then just balancing by visual rating is possible too, since trueskill will calculate the results of such imbalanced non-50/50 games correctly as particularly expected/unexpected outcomes.
In theory there could be problems of players feeling like the game is creating unfair lobbies, but I think that visual rating being used for balance should be ok as it is under the reasonable assumption that matchmaker skill transfers to real games, and if real rating is used for balance then it should adjust close enough to visual rating to create fair lobbies within 10 games.