How to Implement the Glicko Rating System in Python and Run Rated Events with Friends

Build a Glicko rating calculator in Python, then use it to seed brackets, plan a realistic schedule and run rated events for a group of friends.

13 min readPlayer Ratings0 viewsHow to Implement the Glicko Rating System in Python and Run Rated Events with Friends
— · 0 votes
Contents
  1. What does the Glicko rating system track?
  2. Should you use Glicko, Glicko-2 or Elo for a group of friends?
  3. How do you build the Glicko calculator in Python, step by step?
  4. Step 1: Define the player record and the results log
  5. Step 2: Write down the constants and formulas
  6. Step 3: Run the update in the correct order
  7. Step 4: Test the calculator against a known example
  8. How should rating periods match your event?
  9. How do you build a bracket using Glicko ratings?
  10. Choosing a format by number of participants
  11. Seeding a single-elimination bracket
  12. Pairing Swiss rounds with ratings
  13. How do you plan a realistic schedule for the event?
  14. How do you make the next round clear to everyone?
  15. How do you handle changes such as dropouts, late arrivals and draws?
  16. What are the most common mistakes when running Glicko events in Python?

To implement the Glicko rating system in Python, store a rating (r) and a rating deviation (RD) for every player, collect all match results inside one rating period, then update each player using everyone's pre-period values. New players start at 1500 with an RD of 350. Before each period, a player's RD grows to reflect inactivity. After the period, each rating moves according to results and opponent strength, and each RD shrinks according to how many games were played. Once the calculator works, the same numbers can seed brackets, pair rounds and show friends where everyone stands.

This guide covers the calculator first and then the event itself: choosing a format, building a bracket, planning a schedule and announcing the next round clearly. No event-planning experience is assumed.

What does the Glicko rating system track?

The Glicko rating system tracks two numbers per player: a rating, which estimates playing strength, and a rating deviation, which measures how uncertain that estimate is. A player rated 1600 with an RD of 50 is well known to the system. A player rated 1600 with an RD of 300 might really be much stronger or much weaker.

  • Rating (r): points on the same scale as Elo. Newcomers start at 1500.
  • Rating deviation (RD): also in rating points. Newcomers start at 350, which is the maximum. Roughly, a player's true strength lies within r plus or minus 2 × RD with about 95% confidence. For example, 1500 ± 700 for a newcomer, or 1600 ± 100 for a regular with RD 50.
  • Rating period: a block of time whose match results are processed together. For a friends' group, one event night usually works best as one rating period.

The RD is what makes Glicko useful for casual groups. Friends rarely play on a fixed schedule. Glicko lets a returning player's rating move quickly while it stays more cautious with regulars.

Should you use Glicko, Glicko-2 or Elo for a group of friends?

For most friend groups, Glicko (often called Glicko-1) is the best balance: it handles irregular attendance much better than Elo and is simpler to implement than Glicko-2.

SystemNumbers per playerHandles irregular attendanceImplementation effort in PythonGood fit when
EloRating onlyPoorly: an absent player's rating is treated as just as reliableVery lowThe same people play every week
GlickoRating and RDWell: RD grows while a player is inactiveLow: a few formulasAttendance varies and newcomers join
Glicko-2Rating, RD and volatilityWell, and also tracks how consistent a player isModerate: needs an iterative stepYou have many games per player per period

The rest of this article uses Glicko-1. If you later move to Glicko-2, the data model and event workflow stay the same. Only the update step changes.

How do you build the Glicko calculator in Python, step by step?

A working Glicko calculator in Python needs a player record, three formulas, an update routine that runs once per rating period, and a test against a known example. The steps below describe what each part must do, so you can write it in whatever style you prefer.

Step 1: Define the player record and the results log

  1. Create a player record with four fields: name, rating (start 1500), RD (start 350) and the number of the last rating period in which the player played.
  2. Keep all players in a dictionary keyed by name, so lookups during updates are simple.
  3. Store match results as rows with four values: period number, player A, player B and A's score. Use 1 for a win, 0.5 for a draw and 0 for a loss. Player B's score is 1 minus A's score.
  4. Save the results log to a plain CSV file. If a rating ever looks wrong, you can recompute every rating from scratch by replaying the log.

Step 2: Write down the constants and formulas

Glicko uses one fixed constant, q = ln(10) / 400 ≈ 0.0057565. It also uses one tuning constant, c, which controls how fast RD grows during inactivity.

  • RD inflation before a period: new RD = minimum of (square root of (RD² + c² × t), 350), where t is the number of rating periods since the player last played.
  • The g function: g(RD) = 1 / square root of (1 + 3 × q² × RD² / π²). It reduces the weight of results against opponents whose ratings are uncertain.
  • Expected score: E = 1 / (1 + 10^(−g(RDj) × (r − rj) / 400)), where rj and RDj belong to the opponent. E is between 0 and 1.
  • d²: d² = 1 / (q² × sum over opponents of g(RDj)² × E × (1 − E)).
  • New rating: r′ = r + q / (1/RD² + 1/d²) × sum over opponents of g(RDj) × (s − E), where s is the actual score.
  • New RD: RD′ = square root of (1 / (1/RD² + 1/d²)).

Choosing c: decide how many inactive periods it should take for a well-known player's RD to drift back to 350. Then solve c = square root of ((350² − typical RD²) / periods). Here is an example. Suppose a regular has an RD around 50, and you want them treated as fully unknown after 24 missed event nights. Then c = square root of ((122,500 − 2,500) / 24) = square root of 5,000 ≈ 70.7.

Step 3: Run the update in the correct order

  1. At the start of a rating period, inflate the RD of every player who will play, using the time since their last period.
  2. Take a snapshot of every rating and RD. All calculations in this period must read from the snapshot, never from values already updated.
  3. For each player who played, gather all of their match results from this period, together with their opponents' snapshot ratings and RDs.
  4. Compute E and g for each game, then d², then the new rating and new RD.
  5. Write all new values only after every player has been calculated, and record the current period as each player's last period.
  6. Leave players who did not play unchanged. Their RD inflation is applied when they next appear.

The snapshot rule is the most common source of bugs. If player A's rating updates before player B's calculation reads it, the order of the loop changes the results. Glicko is designed to treat all games in a period as if they happened at the same time.

Step 4: Test the calculator against a known example

A standard check for a Glicko implementation is this case. A player rated 1500 with RD 200 beats a 1400 player (RD 30), loses to a 1550 player (RD 100) and loses to a 1700 player (RD 300). With no RD inflation applied, the correct result is a new rating of about 1464 and a new RD of about 151.4. If your Python code produces these values, the core formulas are right.

Two further quick checks:

  • A newcomer at 1500/350 who beats another newcomer should gain far more points than a veteran with RD 50 would in the same game.
  • Two equal players who draw should keep their ratings while both RDs shrink.

How should rating periods match your event?

For a friends' event, treat one event night or one weekend as one rating period, and run the rating updates once, after the last game. Glicko works best when each player has several games inside a period. Updating after every single game makes the system behave less as intended.

Practical consequences:

  • During the event, pair rounds using the ratings from the start of the night. Do not use ratings that change between rounds.
  • Track standings during the night by match points (wins, draws, losses), not by rating.
  • Publish updated ratings after the event as a summary of the night.

How do you build a bracket using Glicko ratings?

Use the Glicko ratings from before the event to seed players, then place seeds so the strongest players meet as late as possible. First decide whether you need a knockout bracket or a format where everyone keeps playing.

Choosing a format by number of participants

FormatMatches for n playersRoundsGames per playerBest for
Round robinn × (n − 1) / 2n − 1 (even n) or n (odd n, with byes)n − 1Up to about 6 players, or long events
SwissAbout n / 2 per roundUsually about log2(n), rounded upSame as rounds6 to 16 or more players in one evening
Single eliminationn − 1log2(n), rounded up1 to log2(n)A quick finale with a clear winner

Knockout brackets give losing players few games, so Glicko learns little about them. If good ratings matter to your group, prefer Swiss or round robin. You can add a short knockout final for the top players.

Seeding a single-elimination bracket

  1. Sort players by rating, highest first. Decide how to treat uncertain players. You can seed by plain rating, or by a conservative value such as rating minus 2 × RD. The conservative option stops a brand-new 1500/350 player from taking a top seed by default.
  2. Round the bracket size up to the next power of two (4, 8, 16).
  3. For 8 slots, pair seeds 1 v 8, 4 v 5, 3 v 6 and 2 v 7. With this layout, seeds 1 and 2 can only meet in the final.
  4. If there are fewer players than slots, the empty slots become byes for the top seeds.

Example with 6 players: use an 8-slot bracket. Seeds 7 and 8 do not exist, so seeds 1 and 2 get byes. Round 1 is 4 v 5 and 3 v 6. In round 2, seed 1 plays the winner of 4 v 5 and seed 2 plays the winner of 3 v 6. The two winners meet in the final. Byes are not match results and are not entered into Glicko.

Pairing Swiss rounds with ratings

  1. Round 1: sort by rating and split the list in half. The top half plays the bottom half in order. With 8 players, that means 1 v 5, 2 v 6, 3 v 7 and 4 v 8.
  2. Later rounds: group players by match points. Within each group, pair higher-rated with lower-rated players.
  3. Avoid rematches. If a pairing repeats, swap with the nearest player in the same or the next score group.
  4. With an odd number of players, give a bye to the lowest-ranked player who has not had one yet. Award the bye as a win in match points only, not in the rating data.

How do you plan a realistic schedule for the event?

Schedule length equals the number of waves of games multiplied by the time per wave, plus breaks. A wave is one set of games played at the same time. The number of waves per round depends on how many tables or boards you have.

  1. Estimate typical game length for your game. Time a few casual games if unsure.
  2. Add a changeover buffer for reporting results, resetting and finding the next opponent. Five minutes is a reasonable starting assumption.
  3. Waves per round = games per round ÷ tables available, rounded up.
  4. Total time = rounds × waves per round × (game length + buffer) + breaks.

Example: 8 friends, 2 tables, 15-minute games and a 5-minute buffer. Each round has 4 games, which means 2 waves of 20 minutes, so 40 minutes per round.

  • A full round robin needs 7 rounds: 7 × 40 = 280 minutes, or 4 hours 40 minutes before breaks. That is too long for one evening.
  • A 3-round Swiss needs 3 × 40 = 120 minutes. Add one 15-minute break and the total is 2 hours 15 minutes. That is realistic for an evening, and every player gets 3 rated games.
  • A single-elimination bracket needs 40 + 20 + 20 = 80 minutes, but four players play only once.

Place breaks between rounds, not in the middle of one. Put the longest break after the round where the most games are likely to overrun. Start rounds at announced clock times rather than as soon as the previous round ends. This keeps late games from pushing back the whole night.

How do you make the next round clear to everyone?

After each round, announce three things together: the pairings for the next round, the table or board for each game, and the start time. Confusion usually comes from players not knowing whom they play next or when.

  • Post a single standings list after every round with name, match points and pre-event rating. Mark players with high RD, for example over 200, as provisional so nobody reads too much into their number.
  • Pair the next round only after every result of the current round is recorded. Confirm each result with both players before entering it.
  • Keep one shared screen, whiteboard or group message as the single place for pairings and standings.
  • After the event, share the updated ratings. Display ratings and RDs as whole numbers, and optionally show a range such as 1580 ± 120 so friends see the uncertainty.

How do you handle changes such as dropouts, late arrivals and draws?

In Glicko, only games that were actually played become match results, so most changes simply mean leaving data out rather than fixing it later.

  • Dropout mid-event: remove the player from future pairings. Their completed games still count in the rating update. Unplayed games are not entered as losses.
  • Late arrival: in a Swiss event, add them from the next round with zero match points, or give them a round-1 bye in match points. A first-time player starts at 1500 with RD 350.
  • Odd numbers after a change: recheck the bye rule. One player sits out each round, and byes never enter the rating log.
  • Draws: enter a score of 0.5 for each player. Glicko handles draws directly.
  • Forfeits: decide in advance. A common choice is to award the win for standings but not to record it as a rated game, because no game took place.
  • Corrections: fix the row in the results log and recompute the rating period. Keeping the full log makes this safe.

What are the most common mistakes when running Glicko events in Python?

Most errors come from processing order and data entry, not from the formulas.

  • Updating ratings after each game instead of once per rating period.
  • Reading already-updated ratings during the same period instead of the snapshot.
  • Forgetting to inflate RD for returning players before the period starts.
  • Entering byes or unplayed forfeits as wins or losses.
  • Mixing up whose score is stored in a result row. Always store it from player A's side.
  • Choosing a format that gives most players only one game, which leaves their RD high and their ratings barely informed.

With a tested calculator, a clean results log and one rating period per event night, a group of friends can run fair, well-seeded events. Each update makes the next bracket a little more accurate.

Related articles