Brainteaser 1031: Definitive darts
From The Sunday Times, 2nd May 1982 [link]
We of the Private Bar of the Wapping Boar are an exclusive elite in darting fraternity; albeit we play the conventional darts of commoner folk and employ their customary board and rules. Our two opposing players throw three darts each, in turn. We require that the last (winning) dart of any game must score some double or the bull (50) — and must thereby raise the player’s score precisely to 501.
Last evening, two of our virtuosi played an epic exhibition; a series of games were all won by the first-to-throw and each game, required the least possible number of darts. The precision was faultless and no dart thrown by the eventual winner of any game ever exceeded the score of any preceding dart in that game. Later, our President eulogised and vouched that in the suite but recently enjoyed, every possible scoring sequence of winning games (within our definition) had been played just once.
“What about my record?” interceded that parvenu from the “public” (one we import to manage the chalks and call the 3 dart total following each throw). The low fellow claims a maximum possible of top score shouts “One hundred and eighty!!” in a demonstration match such as we’d just had; he insists his feat be recognised. The oaf must have his due if we are to use him anew, but there’s the rub — who scores for a scorer?
Readers, please say the maximum of correct “180” shouts to be credited in our circumstance.
This puzzle is included in the book The Sunday Times Book of Brainteasers (1994).
[teaser1031]


Jim Randell 8:25 am on 8 July 2025 Permalink |
It is not possible to score 501 in fewer than 9 darts, as 60 is the maximum possible score with a single dart, and 501 / 60 > 8.
But it is possible to score 501 in 9 darts, as 501 = 3 × 167, and 167 = 60 (treble 20) + 57 (treble 19) + 50 (bull). So we can finish the 9 dart run on a bull.
We need to find sequences of 9 darts, with scores in non-increasing order, that score a total of 501, and finish on a double.
And this corresponds to the winners throws. The winner went first, so the loser in each match only threw 6 darts, and as the scorer shouted “180” the maximum possible number of times, the loser must have thrown 6×60 for 2 shouts of “180”.
This Python 3 program runs in 67ms. (Internal runtime is 1.3ms).
Run: [ @codepad ]
from enigma import (irange, union, seq_items, chunk, printf) # possible scores with 3 darts single = union([irange(1, 20), [25]]) double = set(2 * x for x in single) treble = set(3 * x for x in single if x != 25) darts = sorted(union([single, double, treble]), reverse=1) # score <t> in <k> darts (finishing with a double) # using non-increasing subsets of darts[i..] def score(t, k, i=0, ss=[]): x = darts[i] # finish on a double if k == 1: if t in double and not (x < t): yield ss + [t] elif not (x * k < t): # score the next dart for (j, x) in seq_items(darts, i): if x < t: yield from score(t - x, k - 1, j, ss + [x]) # score 501 in 9 darts, count the number of matches, and "180" shouts n = s = 0 for ss in score(501, 9): n += 1 gs = list(chunk(ss, 3)) x = sum(sum(g) == 180 for g in gs) s += x printf("[{n}: {gs}; {x} shouts]") # total number of shouts t = s + 2 * n printf("total shouts = {t} [from {n} matches]")Solution: There were 62 shouts of “180”.
There were 18 matches. The winners scores as follows:
This accounts for 26 of the shouts.
The loser in each match accounts for another 2 shouts per match, giving 26 + 18×2 = 62 shouts in total.
LikeLike
Frits 12:12 pm on 10 July 2025 Permalink |
@Jim, as “darts” is non-increasing the check in line 20 can be done outside the loop (if needed at all as x always seems to be less than t).
LikeLike
Jim Randell 12:36 pm on 11 July 2025 Permalink |
@Frits: Yes, we could skip any elements that are not less than the remaining total, and then just process all the remaining elements without testing (as they are in decreasing order). But I didn’t think it was worth the additional complication.
But we do need the test, for example, if we wanted to find a 2 dart finish from 37 we would call [[
score(37, 2)]].LikeLike
Frits 6:08 pm on 8 July 2025 Permalink |
N = 9 # number of throws needed # return a valid loop structure string, indent after every "for" statement def indent(s): res, ind = "", 0 for ln in s: res += " " * ind + ln + "\n" if len(ln) > 2 and ln[:3] == "for": ind += 2 return res syms = "ABCDEFGHIJKLMNOPQR" throws = ["X"] + list(syms[:N]) varstring = "[" + ', '.join(throws[1:]) + "]" # possible scores with 3 darts single = set(range(1, 21)) | {25} double = {2 * x for x in single} treble = {3 * x for x in single if x != 25} # determine mininum for the first 8 throws mn = 501 - (7 * max(treble) + max(double)) # darts to be used for the first 8 throws darts = sorted([x for x in single | double | treble if x >= mn], reverse=True) # generate an execution string with 8 loops exprs = [f"X = {max(treble)}; cnt = 0"] # X is a dummy variable for i, t in enumerate(throws[1:], 1): if i < N: exprs.append(f"for {t} in [x for x in {darts} if x <= {throws[i - 1]}]:") if i < N - 1: exprs.append(f"if sum([{', '.join(throws[1:i + 1])}]) + " f"{N - i - 1} * {throws[i]} + {max(double)} < 501: break") else: # most inner loop exprs.append(f"{throws[N]} = 501 - sum([{', '.join(throws[1:-1])}])") exprs.append(f"if {throws[N]} > {throws[N - 1]}: break") exprs.append(f"if not ({throws[N]} in {double}): continue") # the loser in each game accounts for another 2 shouts per game exprs.append(f"cnt += sum({varstring}[3 * i:3 * (i + 1)] == " f"[max(treble)] * 3 for i in range(3)) + 2") #exprs.append(f"print({varstring})") exprs = indent(exprs) exprs += f"print('answer:', cnt)" #print(exprs) exec(exprs)The program generates (and executes) the following code:
X = 60; cnt = 0 for A in [x for x in [60, 57, 54, 51, 50, 48, 45, 42, 40, 39, 38, 36, 34, 33, 32] if x <= X]: if sum([A]) + 7 * A + 50 < 501: break for B in [x for x in [60, 57, 54, 51, 50, 48, 45, 42, 40, 39, 38, 36, 34, 33, 32] if x <= A]: if sum([A, B]) + 6 * B + 50 < 501: break for C in [x for x in [60, 57, 54, 51, 50, 48, 45, 42, 40, 39, 38, 36, 34, 33, 32] if x <= B]: if sum([A, B, C]) + 5 * C + 50 < 501: break for D in [x for x in [60, 57, 54, 51, 50, 48, 45, 42, 40, 39, 38, 36, 34, 33, 32] if x <= C]: if sum([A, B, C, D]) + 4 * D + 50 < 501: break for E in [x for x in [60, 57, 54, 51, 50, 48, 45, 42, 40, 39, 38, 36, 34, 33, 32] if x <= D]: if sum([A, B, C, D, E]) + 3 * E + 50 < 501: break for F in [x for x in [60, 57, 54, 51, 50, 48, 45, 42, 40, 39, 38, 36, 34, 33, 32] if x <= E]: if sum([A, B, C, D, E, F]) + 2 * F + 50 < 501: break for G in [x for x in [60, 57, 54, 51, 50, 48, 45, 42, 40, 39, 38, 36, 34, 33, 32] if x <= F]: if sum([A, B, C, D, E, F, G]) + 1 * G + 50 < 501: break for H in [x for x in [60, 57, 54, 51, 50, 48, 45, 42, 40, 39, 38, 36, 34, 33, 32] if x <= G]: I = 501 - sum([A, B, C, D, E, F, G, H]) if I > H: break if not (I in {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 50}): continue cnt += sum([A, B, C, D, E, F, G, H, I][3 * i:3 * (i + 1)] == [max(treble)] * 3 for i in range(3)) + 2 print('answer:', cnt)LikeLike