DEV Community

Betkyo Research
Betkyo Research

Posted on AI-assisted

What a random scratch ticket should look like (and why the 2003 one didn't)

In 2003 a Toronto geostatistician, Mohan Srivastava, noticed that an Ontario tic-tac-toe scratch ticket could be read before it was scratched. The ticket printed eight tic-tac-toe boards on its face, 72 visible numbers from 1 to 39. Numbers that appeared exactly once on the ticket — he called them singletons — were almost always the hidden numbers under the latex, and three singletons in a line marked a winner about 90% of the time. He reported it and the game was pulled.

The interesting engineering question is the inverse one: what would a ticket look like if its face really were random? If you know that, you know what a leak looks like.

Draw 72 numbers independently and uniformly from 1..39. The chance that a given visible number appears exactly once on the ticket is the chance none of the other 71 cells repeat it:

p_single = (38/39) ** 71        # ≈ 0.158
expected_singletons = 72 * p_single   # ≈ 11.4
Enter fullscreen mode Exit fullscreen mode

So a random ticket carries about eleven singletons, scattered with no relation to anything. Here is the same thing by simulation, plus the count of three-singleton lines on the eight boards (a 3×3 board has 8 lines):

import random
from collections import Counter

LINES = [(0,1,2),(3,4,5),(6,7,8),(0,3,6),(1,4,7),(2,5,8),(0,4,8),(2,4,6)]

def ticket():
    return [random.randint(1, 39) for _ in range(72)]

def singleton_lines(t):
    c = Counter(t)
    single = [c[x] == 1 for x in t]
    boards = [single[i*9:(i+1)*9] for i in range(8)]
    return sum(all(b[i] for i in line) for b in boards for line in LINES)

N = 100_000
s = sum(sum(1 for x in Counter(t).values() if x == 1) for t in (ticket() for _ in range(N))) / N
l = sum(singleton_lines(ticket()) for _ in range(N)) / N
print(f"singletons per ticket ≈ {s:.2f}, three-singleton lines per ticket ≈ {l:.3f}")
Enter fullscreen mode Exit fullscreen mode

On my run (100,000 tickets): 11.37 singletons and 0.236 three-singleton lines per ticket, with 20.6% of tickets showing at least one such line. So on a genuinely random face, one ticket in five would show a line of singletons by pure chance, and it would mean nothing — the hidden numbers would have no reason to match it.

The 2003 ticket was not that. Its face was written by the same program that decided which tickets win, and a program that has to print a fixed number of winners per pack has to coordinate the face with the latex. Srivastava's singletons were not a statistical accident; they were the generator's fingerprints. The lesson generalises to anything that promises randomness but must also hit a quota: the quota is information, and information leaks.

The full story, including what a scratch card drawn from a committed seed does differently (no pack, no quota, each card an independent draw you can recompute afterwards), is on the Betkyo Journal: https://betkyo.com/en/blog/mohan-srivastava-the-statistician-who-read-scratch-tickets-without-scratching-them/

Figures from Wired ("Cracking the Scratch Lottery Code", 2011) and NPR (2011); the simulation is mine.

Top comments (0)