From 4b760e5f84ab84f0c048ea9557d6a215b782687e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Ke=C3=9Fler?= Date: Fri, 22 Dec 2023 11:20:58 +0100 Subject: [PATCH] Variant ratings: Constant sum of rating Whenever a variants gains/loses points now, we ensure that the sum of all variant ratings of that type stays constant by adjusting all other variants in the opposite direction. --- install/database_schema.sql | 8 ++++---- src/constants.py | 2 +- src/ratings.py | 41 ++++++++++++++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/install/database_schema.sql b/install/database_schema.sql index 4cae8aa..ef8a9fd 100644 --- a/install/database_schema.sql +++ b/install/database_schema.sql @@ -263,14 +263,14 @@ CREATE TABLE variant_base_ratings ( DROP TABLE IF EXISTS variant_ratings CASCADE; CREATE TABLE variant_ratings ( /** This should reference the game that triggered the elo update */ - league_id INTEGER NOT NULL REFERENCES games (league_id), - variant_id SMALLINT NOT NULL REFERENCES variants (id), - + league_id INTEGER NOT NULL REFERENCES games (league_id), + variant_id SMALLINT NOT NULL REFERENCES variants (id), num_players SMALLINT NOT NULL, + change REAL NOT NULL, value_after REAL NOT NULL, - PRIMARY KEY (league_id, variant_id) + PRIMARY KEY (league_id, variant_id, num_players) ); DROP TABLE IF EXISTS user_base_ratings CASCADE; diff --git a/src/constants.py b/src/constants.py index 3173140..24ca63f 100644 --- a/src/constants.py +++ b/src/constants.py @@ -43,7 +43,7 @@ FORBIDDEN_GAME_OPTIONS = [ # Cache time (in seconds) for history requests of players # In case of frequent reruns (especially during development), we do not want to stress the server too much. -USER_HISTORY_CACHE_TIME = 5 * 60 +USER_HISTORY_CACHE_TIME = 60 * 60 # Fraction of seeds which is assumed to be unwinnable UNWINNABLE_SEED_FRACTION = 0.02 diff --git a/src/ratings.py b/src/ratings.py index 7d3505c..49e6c42 100644 --- a/src/ratings.py +++ b/src/ratings.py @@ -196,13 +196,52 @@ def process_rating_of_next_game() -> bool: # Calculate changes in rating user_changes, variant_change = compute_rating_changes(user_ratings, games_played, variant_rating, score == 5 * num_suits) - # Update database for variants + # Update database for the played variant cur.execute("INSERT INTO variant_ratings (league_id, variant_id, num_players, change, value_after) " "VALUES (%s, %s, %s, %s, %s)", (league_id, variant_id, num_players, variant_change, variant_rating + variant_change) ) # Note: We do not commit here, only after players have been processed as well + # Now, we get a list of all other variants of the same rating type, we will also need to update these. + cur.execute( + "SELECT DISTINCT ON (variants.id, variant_base_ratings.num_players)" + " variants.id," + " variant_base_ratings.num_players," + " COALESCE(variant_ratings.value_after, variant_base_ratings.rating) AS current_rating " + "FROM variants " + "LEFT OUTER JOIN variant_base_ratings" + " ON variants.id = variant_base_ratings.variant_id " + "LEFT OUTER JOIN variant_ratings " + " ON variant_ratings.variant_id = variant_base_ratings.variant_id" + " AND variant_ratings.num_players = variant_base_ratings.num_players " + "WHERE" + " variants.rating_type = %s" + " AND NOT" + " (variants.id = %s AND variant_base_ratings.num_players = %s)" + "GROUP BY (" + " variants.id, variant_base_ratings.num_players, variant_base_ratings.rating," + " variant_ratings.league_id, variant_ratings.value_after, variant_base_ratings.num_players" + " ) " + "ORDER BY variants.id, variant_base_ratings.num_players, league_id DESC", + (rating_type, variant_id, num_players) + ) + + # This ensures that the sum of ratings of variants of the same type stays constant (up to rounding). + variants_of_same_rating_type = cur.fetchall() + other_variant_change = -variant_change / len(variants_of_same_rating_type) + other_variant_vals = [] + for (other_variant_id, other_num_players, current_rating) in variants_of_same_rating_type: + other_variant_vals.append((league_id, other_variant_id, other_num_players, other_variant_change, current_rating + other_variant_change)) + + # This updates the variants now + psycopg2.extras.execute_values( + cur, + "INSERT INTO variant_ratings (league_id, variant_id, num_players, change, value_after) " + "VALUES %s", + other_variant_vals + ) + user_ratings_vals = [] for user_id, change in user_changes.items(): user_ratings_vals.append((league_id, user_id, rating_type, change, user_ratings[user_id] + change))