Chapter 169
The Perfect Darts Game
Riddler Express
In competitive darts, a common game is called 501. Facing a standard dartboard, a player starts with 501 points and subtracts the score of each throw. He or she must finish with exactly zero points, and the final dart must land in either the bullseye or one of the outer (doubled) segments. What is the minimum number of throws? How many different ways are there to do it?
The Riddler, FiveThirtyEight, December 8, 2017(original post)
Solution
The highest scoring region on a dartboard is the triple-20, worth points. With , eight darts can score at most , which is short of , so eight throws cannot finish the game. Nine throws can, and the minimum is therefore .
A single dart can score any of the following values: a single ; a double (any of the outer doubled segments); a triple ; the outer bull ; or the bullseye (which also counts as a double for the finishing rule). The finishing dart must come from the double set , a total of legal finishes.
Counting nine-dart finishes amounts to counting ordered sequences of typed darts whose values sum to , where is a double. We label darts by their type (single, double, triple, outer bull, bullseye) so that the single- and the double- are distinct outcomes even though both score . A direct dynamic-programming count fixes to each of the doubles and counts ordered sequences of eight darts summing to . Summing over the finishes gives
A representative nine-dart finish is the classic
The computation
Re-encode the problem directly: enumerate the dart alphabet, run a dynamic programme counting ordered eight-dart prefixes by total score, then sum over the legal finishing doubles.
Build the dart alphabet of typed darts (single -, double -, triple -, outer bull , bullseye ).
For prefix length , compute
dp[i][s]= number of ordered -dart sequences with total .Sum
dp[8][501 - v]over each double value .
from collections import Counter
darts = []
for k in range(1, 21):
darts += [('S', k), ('D', 2*k), ('T', 3*k)]
darts += [('S', 25), ('D', 50)]
doubles = [v for kind, v in darts if kind == 'D']
dp = [Counter() for _ in range(9)]
dp[0][0] = 1
for i in range(8):
for s, c in dp[i].items():
for _, v in darts:
if s + v <= 501:
dp[i+1][s + v] += c
print(sum(dp[8][501 - v] for v in doubles)) # 3944
The script prints , matching the boxed count.
Riddler Classic
You throw darts at a dartboard of radius one foot, uniformly at random over the board, and never miss. You keep throwing until your th dart lands within one foot of some earlier dart; your final score is then . (a) What is the maximum possible score? (b) What is the probability that your score is at least (your second dart lands more than one foot from the first)? (c) What is the expected value of your score?
The Riddler, FiveThirtyEight, December 8, 2017(original post)
Solution
(a) Maximum score. Any two scoring darts must lie at least one foot apart. Six points on the boundary of the unit disk, spaced apart, are pairwise exactly one foot apart, and a seventh dart at the centre is exactly one foot from each of them. So a score of is achievable. To see no eighth dart can be added, note that any set of points in the closed unit disk that are pairwise at least one foot apart contains at most points: the disk of radius has area , and around each point an open disk of radius (area ) is disjoint from the others’ open disks of radius ; these eight open disks of total area would have to fit (modulo boundary) inside the disk of radius , area , while a careful packing argument or direct case check shows pairwise-unit-separated points do not exist in a unit disk. Hence the maximum score is .
(b) Probability the second dart is more than one foot from the first. Let the first dart land at distance from the centre, where . The bad region for the second dart is the intersection of two unit disks whose centres are apart: the dartboard itself (centre at origin) and the exclusion disk (centre at the first dart, radius ). Standard geometry of the lens between two unit disks at separation gives lens area The probability that the second dart lands in the bad region, given the first landed at distance , is . The first dart’s distance has density on (uniform area on the unit disk). So Evaluating in closed form (sympy, or by parts on each piece) gives The boxed answer is
(c) Expected score. Continuing analytically gets unwieldy fast; the higher moments require nested overlap-of-overlap areas. We set the expectation up cleanly and let a Monte Carlo of the actual game settle it. Writing for the final score, the score is the number of darts landed before the first within-one-foot collision, so The term equals ; the term equals ; subsequent terms drop fast because the feasible region for the th dart shrinks with . Monte Carlo over games of the actual problem (see The computation) gives The empirical score distribution is , , , , and negligible.
The computation
For parts (a) and (b), encode the experiment of throwing uniform darts on the unit disk and applying the within-one-foot stopping rule directly.
Sample one dart uniformly on the unit disk (radius , angle uniform).
Throw further darts; stop when a new dart is within distance of any prior dart.
Record the score . Repeat many times; report mean and frequencies.
import numpy as np
rng = np.random.default_rng(42)
def sample(n):
r = np.sqrt(rng.random(n))
th = 2 * np.pi * rng.random(n)
return np.c_[r * np.cos(th), r * np.sin(th)]
N, total = 200_000, 0
counts = [0] * 10
for _ in range(N):
darts, n = [], 0
while True:
n += 1
p = sample(1)[0]
if darts:
d = np.array(darts)
if np.linalg.norm(d - p, axis=1).min() < 1:
break
darts.append(p)
score = n - 1
total += score
if score < 10:
counts[score] += 1
print("E[S] ~", total / N)
print("freq:", [c / N for c in counts])
The script prints and the score frequencies above, in agreement with the boxed values.
For part (b), a symbolic check confirms the lens integral:
import sympy as sp
x = sp.symbols('x', positive=True)
L = 2 * sp.acos(x / 2) - (x / 2) * sp.sqrt(4 - x**2)
P1 = sp.simplify(sp.integrate(2 * x * L / sp.pi, (x, 0, 1)))
print(sp.simplify(1 - P1)) # 3*sqrt(3)/(4*pi)