Case study · Personal project · 2026 — now

Sightstone

A draft engine you can argue with.

sightstone.lol ↗ (opens in a new tab)

18,127
professional games replayed to check the engine
49%
of real pro picks in its top six, against 9% by chance
567
unit tests
2 days
from the first commit to a first calibrated engine

In short

The need
My team drafts with thirty seconds per pick, and had no tool to prepare for it.
What I built
A draft room whose engine explains every suggestion, checked against pro data. My team uses it.
My role
All of it: product, architecture, the engine and its calibration. Alone, steering AI coding agents.

01 — The bet

A suggestion has to give its reasons.

Every score breaks down

Before every League of Legends game, the two teams take turns banning and picking champions: twenty choices, about thirty seconds each. Pro teams prepare that draft with analysts. My team at Lausanne Esport, playing semi-pro, had nothing like it. So I built what we were missing: scout the opponent, write the scenarios together, draft live on match night, then review the series.

Scenarios are written together: coach, jungler and mid on one canvas, live

At the centre sits the engine, and a product bet. A coach overrules a black box the first time it is wrong, and stops listening after that. So the engine is not a trained model but a sum of named weights. Every suggestion breaks down into those terms, fast enough to read on the clock.

Lee Sin for Orin, in the jungle: ranked first
  1. 01Lee Sin 7.17
    • S-tier for Orin+3.00
    • on form+1.00
    • 58% presence in pro play+1.09
    • in the plan “Plan A”+2.50
    • exposed to a counter they may take−0.42
  2. 02Jarvan IV 4.51
  3. 03Xin Zhao 3.85

Replayed pick by pick on 18,127 professional games, with every champion available, the pick the pro actually made is in its top six 49% of the time, against 9% by chance.

An illustration: the engine’s real default weights and formula, and this week’s pro presence, on a situation built for the example.

02 — The proof

Then I let the data overrule me.

A rule set in advance

The engine has two halves. The suggestions rank champions for the next pick. The verdict reads a whole draft and says which side it favours, from four readings: the matchup in each lane, pairs of champions, the shape of each composition, and the patch. Hand-set weights are only a hunch. So a job replays 18,127 professional games through both halves, scoring each game only with the patches played before it, and fits the verdict by logistic regression.

What the fit corrected in the verdict

Laneslog-odds per point of matchup strength
0.120.28
Pairs of championsworth less than I had assumed
0.720.46
Blue side, on its own0.135 log-odds, before any pick
53.4%
Judged on 6,093 held-out games: same ranking (AUC 0.543 against 0.546), better calibrated (log loss 0.688 against 0.691; a coin is 0.693)

So the fitted weights replaced mine, under a rule set before seeing the result: a fit goes live only if it is better calibrated and loses no more than half a point at ordering games. And I don’t hide it: a draft moves a game by a few points; the five players do the rest.

03 — A decision

Why it looks one pick ahead, and no further.

Not a matter of compute

A chess engine calculates many moves ahead. Sightstone looks only one pick ahead: before suggesting a champion, it checks what the other team could answer with, weighted by how likely they are to take it. Going deeper would cost almost nothing to compute, but it would make the tool worse, for four reasons.

A schematic: the size of a circle shows how likely it is. Up to the line, the engine relies on data; beyond it, every turn is a guess on a guess.
  1. The score is too faint.

    Chess suits deep search because checkmate is a clear result. A draft barely moves the outcome of a game (AUC 0.54), and a deep search on a score this noisy mostly finds the branches where the noise happened to look good.

  2. The opponent is not a perfect player.

    Classic search assumes they always play the hardest move for us. An amateur team plays what it knows, so the engine reads their likely picks from their own history, and that estimate blurs a little more with every turn.

  3. It would have to guess our own picks too.

    Either the engine plays against itself and reinforces its own biases, or it models the coach from about thirty games a split, which is not enough to learn from.

  4. The reason has to fit on one line.

    Today a coach reads “exposed to Fiora (62%) if they take it” and can check it right away. A search tree doesn’t sum up in one line.

If search ever earns a place, it will be at the end of the draft, with few turns left and every choice weighing more. At the first pick of a game, the engine finds the pro’s choice in its top six 71% of the time; at the last, 39%. The same backtest will decide: a two-turn search goes in only if that last number goes up.

04 — Architecture

Built for a thirty-second clock, and one developer.

Two constraints shaped the rest: a room drafting against the clock, and one person to keep it running. Champion data comes from Riot’s public CDN, so drafting needs no key. Long jobs, like importing pro drafts or refitting the weights, run on GitHub Actions rather than serverless. The easiest way to see what the clock demands is to follow a single pick.

  1. 01

    One read for the whole draft. When the room opens, each screen loads the board once. After that, the browser does the work.

  2. 02

    The click never waits. Whose turn is it? The lengths of four arrays are enough to tell, nothing is stored. The engine reranks in the tab, with no request.

  3. 03

    One round trip, one row. Each entry carries how many slots its screen could see, and Postgres appends only while the row agrees. A stale click is refused, not added.

  4. 04

    A signal, not a state. The tab that wrote tells the others the draft moved, never how. Intercepting the message teaches nothing.

  5. 05

    Every read goes back through the API. The other screens re-read the draft through the API, which checks the team, then rerank locally.

Next

My team uses it today. Next come amateur teams, so their drafts and debriefs feed the calibration; then pro teams, with GRID match data and a production key from Riot.