How Turnpoint works
Where the numbers come from, how the two trained models work, and how good they are. Want to see it first? Open a sample analysis.
1. The data
Everything comes from Riot Games' official API: match results, and a timeline of every game with each player's gold, XP, CS and position once a minute, plus every kill and objective. Riot allows 100 requests every 2 minutes, so every request goes through one rate-limited client that waits its turn and retries when Riot says "slow down".
A finished game never changes, so each match and timeline is fetched once and kept in Postgres forever. A player's rank and match list go stale, so those expire after a day or when you press Update. Most page views make no Riot calls at all.
2. Turning points
The match page compares you with the enemy player in your role, minute by minute, and looks for the moments the gap moved: a death, a lost objective, a missed wave. It's plain code, not machine learning: pure functions that take a game in and give findings out, each with unit tests on small made-up games.
It describes what happened and leaves the judging to you. A gank into a losing lane can be right or wrong depending on things the data can't see.
3. Win chance
At every minute, each team's chance of winning, like the line on a sports broadcast. It's logistic regression: the model looks at how far ahead one team is (gold, XP, dragons, Baron, inhibitors and more), multiplies each lead by a weight it learned, adds them up and squashes the total into a chance between 0% and 100%. An even game is always exactly 50%.
It learned its weights from 18,178 ranked games on patches 16.16 to 16.20. The ones that matter:
| Lead | Weight |
|---|---|
| gold lead, per 1,000 | 0.397 |
| inhibitors down lead | 0.373 |
| dragon lead | 0.259 |
| Elder buff (1 blue, -1 red) | 0.246 |
| gold lead per 1,000, times minutes / 30 | -0.206 |
| XP lead, per 1,000 | 0.094 |
Kills and towers get almost no weight. Not because they don't matter, but because they pay gold, and the gold lead already counts that gold. The negative "times minutes / 30" weight means a gold lead counts for less late in the game, when everyone is close to a full build.
Try it yourself
How good is it?
Tested on games it never saw during training (5-fold cross-validation, with every minute of a game kept in the same group so it can't peek at the result). The comparison is the simplest rule there is: "the team with more gold wins".
| Minute | Model | More gold wins |
|---|---|---|
| 10:00 | 70% | 70% |
| 15:00 | 77% | 76% |
| 20:00 | 80% | 78% |
| 25:00 | 82% | 81% |
| 30:00 | 82% | 80% |
Gold explains most of a game, so the gold rule is hard to beat early. The model pulls ahead later, when inhibitors, Baron and Elder start deciding games. It only ever runs on finished games: Riot doesn't allow predictions during a live game.
These numbers flatter the model. They include the last moment of each game, when the winner is nearly certain, so a game surrendered at 20:10 adds an easy guess to the 20:00 row. A fairer check is planned.
Does rank matter?
Maybe leads are worth less in Iron, where games get thrown more often. To check, each rank (and Emerald+ and Master+) got a model of its own, trained only on games at that rank, and both were tested on that rank's games they hadn't seen. These are counted like the table above, so they flatter the models too.
| Rank | Games | Own model at 20:00 | All ranks at 20:00 | 3k gold up at 20:00 |
|---|---|---|---|---|
| Iron | 1,356 | 81% | 81% | 74% |
| Bronze | 1,133 | 79% | 79% | 72% |
| Silver | 1,331 | 80% | 79% | 73% |
| Gold | 1,461 | 78% | 78% | 72% |
| Platinum | 1,475 | 78% | 78% | 70% |
| Emerald | 1,305 | 80% | 80% | 70% |
| Diamond | 2,086 | 79% | 79% | 71% |
| Master | 2,907 | 80% | 80% | 71% |
| Grandmaster | 2,600 | 80% | 80% | 73% |
| Challenger | 2,444 | 80% | 80% | 73% |
| Emerald+ | 11,342 | 80% | 80% | 72% |
| Master+ | 7,951 | 80% | 80% | 72% |
No rank's own model did clearly better than the all-ranks model, so every game uses the all-ranks one. A lead is worth about the same in Iron as in Challenger, and the all-ranks model learned from far more games.
4. Game score
Every player gets a score from 0 to 100, with MVP for the best player on the winning team and ACE for the best on the losing team. It's also logistic regression, on ten end-of-game stats (KDA, deaths, kill participation, damage, gold share, CS, vision, objective damage and more), trained to predict who won.
Two details make it fair across roles. Each stat is first compared with what's normal for your role, since a support's damage is low by design. And the score is a percentile, not a chance of winning: 73 means you rated higher than 73% of players in your role in our games, so a great game in a loss still scores well.
The current model was trained on 86 games. On 35,326 saved games it didn't train on, it picks the winning side 81% of the time, where a coin flip would get 50%.
5. What it won't do
No live-game help, no rank or MMR guesses, and no telling you what you should have done. Riot's developer policy rules those out, and they'd be wrong often enough that you'd stop trusting the rest.
The code is open: Source on GitHub, with the full write-ups in docs/win-chance.md and docs/game-score.md.