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:
2026-09-19 10:09:24 +08:00
parent ebeccc937d
commit 93d041b004
15 changed files with 651 additions and 17 deletions
+12
View File
@@ -91,6 +91,18 @@ pub async fn my_matches(cursor: Option<&str>) -> Result<MatchesPage, String> {
resp.json().await.map_err(|e| e.to_string())
}
/// Fetch the caller's Elo ratings (one row per game type played).
pub async fn my_ratings() -> Result<RatingsPage, String> {
let resp = Request::get("/api/me/ratings")
.send()
.await
.map_err(|e| e.to_string())?;
if !resp.ok() {
return Err(server_error(resp.status()));
}
resp.json().await.map_err(|e| e.to_string())
}
pub async fn leaderboard() -> Result<LeaderboardPage, String> {
let resp = Request::get("/api/leaderboard")
.send()