Inspiration
Slot machines are the textbook example of "random", but the House Edge challenge flipped that idea: these machines follow hidden rules. Each machine's payout odds depend on how its past spins line up, and those rules can drift slowly or flip suddenly, sometimes across a whole bank of machines at once. We were drawn to the detective side of it: given only a log of past spins, can we work out what each machine is "thinking" well enough to give the exact odds of its next spin?
What it does
For every machine at a given cutoff tick, House Edge outputs a full probability distribution over the next payout:
$$ \hat{\mathbf{p}} = \big(p_{0\times},\, p_{1\times},\, p_{2\times},\, p_{5\times},\, p_{20\times}\big), \qquad \sum_k \hat p_k = 1 $$
It doesn't guess a single winner. It's scored on how close its probabilities are to the simulator's true odds, using squared error averaged within each of four conditions (stable, short history, drift, shared change):
$$ R = \frac{1}{4}\sum_{c}\frac{E_c}{\max(E^{\text{ref}}_c,\,10^{-9})}, \qquad \text{points} = \frac{60}{1+R} $$
Our model reaches 56.06 / 60 on validation ($R \approx 0.07$), roughly 93% less error than the organizers' baseline. The best starter baseline scored 30.83.
How we built it
1. Find the hidden rule. Exploring the training logs, we found that a machine's win chance depends on whether two of its past payouts agree at a specific lag pair $(a,b) \in {(1,3),(2,5),(4,7)}$, with a machine-specific sign:
$$ P(\text{win}t) = \sigma!\big(\beta_0 + \beta_1\,\mathbb{1}[\,y{t-a} \text{ agrees with } y_{t-b}\,]\big) $$
Missing ticks are treated as unknown, not zero, and only ticks before the cutoff are ever used.
2. An ensemble of "detectives":
- Switch: per-machine logistic regressions fit at prediction time, in fixed, trend (gradual drift) and shared-flip variants, mixed by a softmax over their evidence.
- Bayes: Bayesian model averaging over ${\text{pair}} \times {\text{no change},\ \text{reset at } c,\ \text{flip}}$ using closed-form Beta-Bernoulli marginal likelihoods: $$ p(\mathcal{D}\mid H) = \prod_{\text{cells}} \frac{B(\alpha + w,\ \beta + \ell)}{B(\alpha,\beta)} $$
- GRU: a recurrent neural net (hidden size 128, 3 seeds), trained on GPU with PyTorch and run in pure numpy at inference.
- XGBoost: gradient-boosted trees on running lag-pair statistics, trained on GPU, exported to JSON and evaluated in numpy.
3. Payout size and calibration. Given a win, the size distribution is conditioned on the last two payout sizes and shrunk toward the machine's and machine type's mix. The final forecast is
$$ \hat{\mathbf{p}} = 0.7\cdot \text{calibrate}\big(0.5\,\text{Switch} + 0.2\,\text{Bayes} + 0.3\,\text{GRU}\big) + 0.3\cdot \text{XGBoost} $$
where calibration scales the win logit by $1.1$.
4. Discipline. Every change was its own saved iteration (16 in total), scored on a tuning half of validation and kept only if a separate audit half improved too. A 9-point leakage audit checked that no future tick ever leaks into a feature. The submission is a stdlib + numpy script that recreates our CSV exactly (max difference $0.0$) in about 90 seconds on one CPU core.
Challenges we ran into
- Hidden, moving targets. Some machines drift gradually while others flip at the same moment as the rest of their episode. One model couldn't handle both, which is why we blend several change hypotheses.
- Short histories. Some machines show only 64 ticks with gaps (40 recent and 24 sampled older ticks), so lags can't be read off row order. This is still our hardest condition.
- Big models didn't win. We tried a pretrained time-series transformer (Chronos), zero-shot and fine-tuned; it scored 29.6 to 36.5. We also tried a variable-order Markov model (PPM), LightGBM, CatBoost and learned stacking meta-models. Most lost to, or only tied, our structured ensemble. Knowing the structure of the problem beat raw model size.
- Overfitting validation. With only 96 validation episodes, it's easy to fool yourself. We rebuilt the pipeline with strict train/validation/test separation, which gave an honest estimate of about 55.7.
- Reproducibility. The runner is offline Python 3.13 with no GPU, so GPU-trained models had to be exported and re-run in numpy with a bit-exact match.
Accomplishments that we're proud of
- 56.06 / 60 on validation, up from a 30.83 baseline, with strong results in all four conditions.
- A fully reproducible, CPU-only entry: one command, under 2 minutes, an exact match to our submission.
- A transparent experiment trail: 16 versioned iterations, a leakage audit, and documented negative results.
What we learned
- Calibration beats confidence. Under a squared-error score against true odds, hedging well matters more than being bold.
- Structure first, then scale. A small model that matches how the data is generated beat billion-parameter forecasters.
- Validation is a budget. Every peek spends it, so honest held-out checks are worth more than a high leaderboard number.
- Ship early, improve one step at a time. A working MVP, then one change per iteration, made it easy to roll back.
What's next for House Edge
- Round 2 adaptation: re-discover lag pairs and retrain on the shifted patterns, with a one-command pipeline built for the 90-minute window.
- Bayesian online change-point detection to replace hand-tuned switch thresholds with an exact posterior over when the rule last changed.
- Simulation-trained transformers: train a sequence model on millions of synthetic machines with known true odds, so it learns to infer hidden rules directly.