Add chess-style Elo ratings for players
Each player's rating starts at 1500 and updates transactionally with every finished match: a team's rating is the mean of its two members and the standard K=32 formula decides the zero-sum delta applied to both members of a team. Ratings are per game type in a new player_rating table; match_player records each match's elo_delta. - GET /api/leaderboard exposes elo and sorts by it - GET /api/me/matches includes per-player elo deltas - new GET /api/me/ratings returns the caller's rating per game type - frontend: Elo column on the leaderboard, per-match delta in the history page, current rating in the lobby - python -m tavolo.backfill_elo recomputes all ratings from the recorded match history (one-off backfill for existing matches)
This commit is contained in:
@@ -184,6 +184,10 @@ pub struct MatchPlayer {
|
||||
pub seat: usize,
|
||||
pub team: String,
|
||||
pub won: bool,
|
||||
/// Elo change this match produced for the player; absent for matches
|
||||
/// recorded before ratings existed.
|
||||
#[serde(default)]
|
||||
pub elo_delta: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
@@ -201,6 +205,9 @@ pub struct MatchSummary {
|
||||
pub finished_at: String,
|
||||
#[serde(default)]
|
||||
pub you_won: bool,
|
||||
/// The viewer's Elo change in this match; absent when unrated.
|
||||
#[serde(default)]
|
||||
pub your_elo_delta: Option<i32>,
|
||||
#[serde(default)]
|
||||
pub players: Vec<MatchPlayer>,
|
||||
}
|
||||
@@ -213,16 +220,38 @@ pub struct MatchesPage {
|
||||
pub next_cursor: Option<String>,
|
||||
}
|
||||
|
||||
fn default_elo() -> i32 {
|
||||
1500
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
pub struct LeaderboardEntry {
|
||||
pub user_sub: String,
|
||||
pub display_name: String,
|
||||
/// Chess-style Elo rating for the requested game type.
|
||||
#[serde(default = "default_elo")]
|
||||
pub elo: i32,
|
||||
pub matches: i32,
|
||||
pub wins: i32,
|
||||
pub points: i32,
|
||||
}
|
||||
|
||||
/// The caller's Elo rating for one game type (GET /api/me/ratings).
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
pub struct PlayerRating {
|
||||
pub game_type: String,
|
||||
pub rating: i32,
|
||||
pub matches_played: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct RatingsPage {
|
||||
#[serde(default)]
|
||||
pub results: Vec<PlayerRating>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct LeaderboardPage {
|
||||
#[serde(default)]
|
||||
|
||||
Reference in New Issue
Block a user