A full, honest walkthrough of how we turn combine scores, draft positions, and live buzz data into game lines (win probability, point spread, total) and player props.
We borrow the arc modern sports analytics took: start from a box-score view (who won, final margin), then move to tracking data (every individual event). For science bowl, the "tracking data" is the buzz log — every buzz, who made it, on what subject, whether it was right, and where in the question it happened. That lets us rate players, not just teams, and simulate a game tossup by tossup.
The guiding principle throughout: be honest about uncertainty. NSBA games are high-variance — final margins swing wildly (SD ≈ 57 points) and are only weakly predictable. So the model's natural win probabilities are moderate, and we say so. Where we deliberately sharpen the output for an engaging market, we flag it.
The key idea: ratings are the fuel, the simulation is the engine. Everything upstream produces per-player ability numbers; the simulation turns those into a distribution of game outcomes.
Before any games are played, each player gets a starting ability in each of the six subjects (Biology, Chemistry, Computer Science, Earth/Space, Math, Physics). We use two signals for what each is best at:
We blend them with precision weighting — trust the combine more when it's clean, lean on the draft when it's messy:
So we keep the combine's category-by-category shape but shift the whole player to the draft-informed level. A strong player with a weak combine (drafted high anyway) gets pulled up; a late pick with a flashy combine gets pulled down. Players who didn't take the combine fall back to a draft-only flat profile.
Once games are played, we move each player's rating toward what we observe. This is essentially online Bradley–Terry — i.e. Elo — but generalized: ratings live per player × subject, and we update from the actual buzzes.
Direct attribution is the important part. The credit for a subject goes to the player who actually buzzed it — not split across the lineup by prior strength. This fixes ratings exactly where the prior was wrong: a player the combine underrated in a category rises the moment they score there. (This is the fix that corrected Sean's biology from 0.50 → 1.21 after a 3-for-3 Week 1.)
The learning rate K = 0.4 isn't a guess — it's fit by walk-forward backtest on the
prior season (nsba3): seed every player, replay all 53 games in order, predict each game out-of-sample, then
update. We sweep K and pick the value that minimizes out-of-sample Brier score. Updating beats the static seed
(accuracy 51% → 68%); past K ≈ 0.5 the model gets overconfident, so we sit at the calibrated edge.
The heart of the engine. For a single tossup in subject s between teams A and B, we model three
outcomes — A converts, B converts, or it goes dead (nobody answers):
This is Bradley–Terry with a third "phantom competitor" — the question itself, whose strength
c_s is how often that subject goes dead. Drop the dead term and it's exactly Elo's win formula.
We generalize plain Elo three ways:
c_s) — plain Elo can't say "CS goes dead 30% of the time."logsumexp (your best player in a subject dominates, with a little credit for depth), not one opaque number.The discrimination β = 0.296 and the per-subject deadness c_s are fit by maximum
likelihood on every historical tossup. β is small — meaning ability only weakly predicts any
single tossup. That's a real finding, and it's why honest game probabilities are moderate.
A game's margin and total are sums of many independent per-tossup outcomes, so by the Central Limit
Theorem they're approximately Normal. We compute the mean and variance in closed form — fast and exact, no
simulation. Per subject, with n_s tossups each worth v_s = 4 + 10·(bonus conversion):
The win probability comes from the Normal margin. We deliberately use a wide, empirically-measured spread
(RESID_SD = 56, the real game-to-game margin SD) rather than the engine's own narrow internal SD —
this keeps single-game probabilities realistic instead of wildly overconfident:
In NSBA, home court isn't a vague "+3 points." It's a concrete mechanic: the home team bans a category, removing one question from it. We model it inside the simulation, not as a fudge factor.
The home team picks the ban that most helps it — usually the away team's strongest subject. We model the choice softly (captains aren't perfectly optimal): for each candidate banned subject, recompute the home team's expected margin, then
λ is a prior until then.The raw engine margin is rescaled to real points by a regression on history
(spread ≈ 0.31 × raw; totals anchored to nsba3's regular-season scoring). The shrinkage factor reflects
how much the engine's confident-looking margins must be pulled toward reality.
NSBA scoring is always even (+4 / −4 / +10), so every margin and total is an even number. We exploit that:
spreads sit on odd integers, totals on .5 — neither can ever land on the actual result, so a bet
can never push (tie). Player-points props use a line of the form 4k+2 for the same reason (a score is
always a multiple of 4).
Pure model output on one week of high-variance data is near pick'em, which is boring for a market. We apply an explicit confidence gain (currently 2.5×) that widens the model's edge on the spread and win probability — clear games get clearer, true coin-flips stay coin-flips. This is a deliberate aggressiveness lever, not a calibration claim; the automated market maker will correct anything wrong. Totals are not gained (they're an environment estimate, not a confidence call).
Props fall right out of the same engine. We take each team's expected tossup wins in a subject and split them among players by their share of the team's strength in that subject:
These are honest expectations — no confidence gain — and they correctly surface the stars (a team's best
player gets the lion's share of their subject's tossups). O/U lines sit at 4k+2 so they never push.
| Source | What it gives |
|---|---|
| Combine (IRT) | Per-subject ability, the seed shape |
| Draft order | Overall level / GM consensus |
| Match JSON (questionLog) | Every buzz: player, subject, right/wrong, buzz position, real lineups |
| Scoresheets | Per-tossup outcomes when the rich JSON isn't available |
| Season futures | Conference-win / top-4 markets (used for preseason team strength) |