Skip to main content

ELO Rating System

DoubleCube.gg uses an ELO-based rating system to track player skill.

How It Works

Base Formula

The ELO system uses the standard formula with adjustments for backgammon:

Expected Score = 1 / (1 + 10^((OpponentRating - MyRating) / 400))
New Rating = Old Rating + K × (Actual - Expected)

K-Factor

The K-factor determines rating volatility:

Games PlayedK-Factor
0-1040
11-3032
30+24

New players have higher K-factors for faster calibration.

Match Adjustments

Ratings are calculated per match, not per game:

  • Match winner gets full win credit
  • Game stakes (cube value) affect point differential
  • Gammon/backgammon wins count more heavily

Rating Changes

Example Calculation

Player A (1200) vs Player B (1400):

Expected A = 1 / (1 + 10^((1400-1200)/400)) = 0.24
Expected B = 1 / (1 + 10^((1200-1400)/400)) = 0.76

If A wins:
New A = 1200 + 32 × (1.0 - 0.24) = 1224
New B = 1400 + 32 × (0.0 - 0.76) = 1376

An upset win results in larger rating changes.

Gammon Bonus

Winning by gammon or backgammon provides additional rating credit:

Win TypeRating Multiplier
Normal1.0×
Gammon1.2×
Backgammon1.5×

API Methods

Get Rating History

const history = await connection.invoke('GetRatingHistory', 50);
// Returns last 50 rating changes with dates

Get Leaderboard

const leaderboard = await connection.invoke('GetLeaderboard', 100);
// Returns top 100 players by rating

Get Rating Distribution

const distribution = await connection.invoke('GetRatingDistribution');
// Returns histogram of all player ratings

Rating Tiers

TierRating Range
Beginner< 1000
Intermediate1000 - 1200
Advanced1200 - 1400
Expert1400 - 1600
Master1600+

Implementation Details

The EloRatingService handles all calculations:

public interface IEloRatingService
{
(int newWinnerRating, int newLoserRating) CalculateNewRatings(
int winnerRating,
int loserRating,
bool isGammon);

int GetKFactor(int gamesPlayed);
}

Ratings are updated atomically when a match completes:

// In MatchService.CompleteMatchAsync()
var (newWinnerRating, newLoserRating) = _eloService.CalculateNewRatings(
winnerRating,
loserRating,
isGammon);

await _userRepository.UpdateRatingAsync(winnerId, newWinnerRating);
await _userRepository.UpdateRatingAsync(loserId, newLoserRating);

Starting Rating

New players start at 1200 rating, which represents an average player.