Skip to content

Elo & Strength Estimation

pawnbook tracks two separate rating numbers: an Elo derived from game outcomes, and a playing-strength Elo derived from move quality. They measure different things and are displayed separately.

Game Elo

Game Elo works like standard chess Elo: win against a higher-rated opponent, gain more points; lose to a lower-rated opponent, lose more points. The formula uses the standard expected-score calculation with a ±400 centipawn difference clamp (FIDE convention).

K-factors (how many points each game is worth):

PhaseKCondition
Provisional40First 15 games
Mid-range20Elo < 2100
Established10Elo ≥ 2100

Floor: Elo cannot go below 100.

When Elo is NOT updated:

  • The game was marked unranked (player toggle or drawfish opponent)
  • The coach fired an alert during the game — any game with a coach intervention is immediately de-ranked
  • Analysis did not complete successfully

The Elo update is applied after analysis finishes and is delivered in the second game_over WebSocket message. The first game_over (at game end) has null Elo values.

Playing-strength Elo

Playing-strength Elo is computed independently of game outcomes. You can lose a game while showing strong play; you can win while playing weakly. The strength estimate reflects the quality of your moves, not the result.

Method: Regan-Haworth scaled error

For each eligible ply, pawnbook computes:

scaled_error = ln(1 + min(cpLoss, 300) / 100)

The mean scaled error (ASE) over eligible plies is then converted to an Elo estimate via a linear calibration.

This formula is from Regan & Haworth (2011), adapted for the engine and time controls available here.

Eligible plies

Not every position is informative about skill. The following are excluded from the sample:

  • Forced moves: only one legal move — no choice was made
  • Mate evaluations: the position is already decided
  • Decided positions: |cp_white| > 600 — too far gone to distinguish skill levels

Calibration

The conversion from ASE to Elo is a linear function derived from WLS regression:

  • Anchor: Maia-1600 ASE ≈ 0.2638
  • Slope: approximately 6500 Elo per ASE unit

The coefficients are versioned in calibration/strength-model.json. To update them with new data, run scripts/refit-strength.js (requires ≥ 20 samples spanning ≥ 3 distinct Elo levels).

Reported values

For each game with enough eligible plies:

  • Elo estimate (integer)
  • Standard error (integer) — the ±band around the estimate
  • Sample size (n eligible plies)
  • ASE and its standard deviation

Single-game noise

One game gives a rough ±250–300 Elo band. This is an irreducible property of the measurement method — not a software limitation. The Kaggle "Finding Elo" competition (world-class effort on a much larger dataset) left a residual standard deviation of ~195 Elo per game. pawnbook's band is honest about this.

Rolling aggregate

To narrow the band, pawnbook computes an inverse-variance weighted mean over the last 10 eligible games. The rolling aggregate is displayed alongside the single-game estimate. More games → narrower band.

The rolling estimate is what "your strength" means in pawnbook's stats and on the dashboard. The single-game estimates feed into it and are also visible in the per-game review.

Released under the MIT License.