Skip to content
Vamshi Jandhyala

Books · The Riddler

Chapter 176

Yahtzee Strategy

Riddler Express

You are playing a one-turn game of Yahtzee in which the only goal is to maximize the score on this single turn. After the second of three rolls, your five dice show 4,4,4,5,54, 4, 4, 5, 5. Keeping all five scores 2525 points for the full house. Rerolling the two 55s aims at the 5050-point Yahtzee but risks a lowly three- or four-of-a-kind, scored as the sum of the five dice. What is the best strategy?

The Riddler, FiveThirtyEight, March 9, 2018(original post)

Solution

The benchmark. Standing pat scores a flat 2525 points. Any reroll is worthwhile only if its expected score exceeds 2525. The decision is whether the upside of a Yahtzee outweighs the cost of frequently landing on a hand worth less than 2525.

Reroll the two 55s. Two fresh dice land in 6×6=366 \times 6 = 36 equally likely ways. Sort the outcomes by the resulting hand.

  • Both come up 44: 11 way out of 3636. Hand is five 44s, scored as a Yahtzee, 5050 points.

  • Both match (but not both 44): 55 ways (both 11, both 22, both 33, both 55, or both 66). Hand is three 44s plus a pair, a full house worth 2525.

  • Otherwise: 3030 ways. Hand is three or four 44s with two non-matching extras, scored as the sum of the five dice (the puzzle’s stated convention for "lowly three- or four-of-a-kind"). The two rerolled dice sum on average to 77, and the three kept 44s contribute 1212, so the average sum is 1919.

Expected score from rerolling the two 55s, Ere 5s=136 ⁣ ⁣50+536 ⁣ ⁣25+3036 ⁣ ⁣19=50+125+57036=7453620.69.E_{\text{re 5s}} = \frac{1}{36}\!\cdot\!50 + \frac{5}{36}\!\cdot\!25 + \frac{30}{36}\!\cdot\!19 = \frac{50 + 125 + 570}{36} = \frac{745}{36} \approx 20.69. That falls well short of the certain 2525.

Every other reroll is also worse than 2525. The same enumeration over each of the 3131 proper subsets of dice to reroll, with the scoring category chosen optimally after the third roll (Yahtzee, four-of-a-kind, full house, large or small straight, three-of-a-kind, or sum), gives the table below. The best reroll, "keep one 44 and one 55, reroll the other three", scores about 22.7622.76 points on average. Even that best alternative is short of 2525.

Strategy (kept dice) Expected score
Keep all (4,4,4,5,5)(4,4,4,5,5) 25.0025.00
Keep one 44 and one 55, reroll the rest 22.7622.76
Keep (4,4,5,5)(4,4,5,5), reroll one 44 22.3322.33
Keep (4,5,5)(4,5,5), reroll two 44s 22.1922.19
Keep two 55s, reroll three 44s 21.5321.53
Keep (4,4,5)(4,4,5), reroll the rest 21.3321.33
Keep (4,4,4)(4,4,4), reroll the two 55s 20.6920.69
Keep nothing, reroll all five 20.0620.06

So the best policy is to keep the hand exactly as it is. Keep all five dice and score the full house for 25 points.\boxed{\,\text{Keep all five dice and score the full house for } 25 \text{ points.}\,}

The computation

Enumerate every possible reroll subset of the hand (4,4,4,5,5)(4,4,4,5,5), average the optimal Yahtzee scoring category over the 6k6^k outcomes for kk dice rerolled, and return the best.

  1. For each subset of kept dice, list every outcome of the rerolled dice.

  2. Score each resulting hand by the maximum of (Yahtzee 5050, large straight 4040, small straight 3030, full house 2525, three- or four-of-a-kind = sum, plain sum).

  3. Average and report the best strategy.

from itertools import product, combinations

def score(d):
    s = sorted(d)
    cs = sorted([s.count(x) for x in set(s)], reverse=True)
    tot = sum(s)
    best = tot
    if cs[0] == 5: best = max(best, 50)
    if cs == [3, 2]: best = max(best, 25)
    uniq = sorted(set(s))
    if uniq == [1,2,3,4,5] or uniq == [2,3,4,5,6]:
        best = max(best, 40)
    for start in (1, 2, 3):
        if all(v in uniq for v in range(start, start + 4)):
            best = max(best, 30); break
    return best

hand = (4, 4, 4, 5, 5)
results = []
seen = set()
for r in range(6):
    for keep_idx in combinations(range(5), r):
        kept = tuple(sorted(hand[i] for i in keep_idx))
        if kept in seen: continue
        seen.add(kept)
        n_re = 5 - r
        if n_re == 0:
            ev = score(kept)
        else:
            tot = 0
            for out in product(range(1, 7), repeat=n_re):
                tot += score(kept + out)
            ev = tot / 6**n_re
        results.append((ev, kept))

results.sort(reverse=True)
for ev, kept in results[:5]:
    print(f"keep {kept}  EV = {ev:.4f}")

The script prints "keep (4,4,4,5,5)(4, 4, 4, 5, 5) EV =25.0000= 25.0000" at the top, confirming the boxed strategy.

Riddler Classic

It is the final turn of a Yahtzee game and the only category left to score is a large straight (all five dice consecutive, so 1,2,3,4,51,2,3,4,5 or 2,3,4,5,62,3,4,5,6). On the first of the three rolls you roll 1,2,4,5,X1, 2, 4, 5, X, where X3X \ne 3. You may reroll the XX in hope of a 33, or reroll the 11 and the XX in hope of landing some combination of 33 and 66. What is the best strategy for hitting a large straight?

The Riddler, FiveThirtyEight, March 9, 2018(original post)

Solution

The setup. Two rolls remain. Each roll lets you choose a subset of dice to keep and reroll the rest. Success means ending the third roll with a large straight.

Strategy A: reroll only XX. The hand becomes (1,2,4,5)(1, 2, 4, 5) plus one fresh die. Success requires that die to land on 33. On the second roll this happens with probability 1/61/6. If it fails, the same one-die reroll on the third roll has probability 1/61/6 as well. So PA=16+5616=11360.306.P_A = \frac{1}{6} + \frac{5}{6}\cdot\frac{1}{6} = \frac{11}{36} \approx 0.306.

Strategy B: reroll {1,X}\{1, X\}. Hold {2,4,5}\{2, 4, 5\} and reroll two fresh dice. Now there are two routes to a large straight: pair {1,3}\{1, 3\} for 1-2-3-4-51\text{-}2\text{-}3\text{-}4\text{-}5, or pair {3,6}\{3, 6\} for 2-3-4-5-62\text{-}3\text{-}4\text{-}5\text{-}6. Treat the two new dice as an ordered pair, with 3636 equally likely outcomes, and split by what the second roll produced. In each branch we play the third roll optimally.

Second-roll outcome Count of 3636 Best third-roll success
Large straight already 44 11
Has a 33, no 11 or 66 77 keep 33, reroll for {1,6}\{1,6\}: 1/31/3
Has a 11 or 66, no 33 1616 keep endpoint, reroll for 33: 1/61/6
All in {2,4,5}\{2,4,5\} 99 reroll both, hit pair: 1/91/9

The counts close: 4+7+16+9=364 + 7 + 16 + 9 = 36. Combining, PB=4361+73613+163616+93619=24216+14216+16216+6216=60216=10360.278.\begin{align*} P_B &= \frac{4}{36}\cdot 1 + \frac{7}{36}\cdot\frac{1}{3} + \frac{16}{36}\cdot\frac{1}{6} + \frac{9}{36}\cdot\frac{1}{9} \\ &= \frac{24}{216} + \frac{14}{216} + \frac{16}{216} + \frac{6}{216} = \frac{60}{216} = \frac{10}{36} \approx 0.278. \end{align*}

Compare. PA=11/36P_A = 11/36 and PB=10/36P_B = 10/36. The single-die reroll wins by 1/362.81/36 \approx 2.8 percentage points. Reroll only the X.PA=1136vs. PB=1036.\boxed{\,\text{Reroll only the } X.\quad P_A = \tfrac{11}{36}\, \text{vs.\ } P_B = \tfrac{10}{36}.\,} A full dynamic-programming sweep over every subset of dice to keep (not only the two named strategies) gives the same optimal probability 11/3611/36 for any starting X3X \ne 3, including X=6X = 6 (rerolling either the 11 or the XX is equivalent: hold {2,4,5,6}\{2, 4, 5, 6\} and reroll the 11 for a 33).

The computation

Solve the Yahtzee large-straight subproblem by backward induction over the sorted-hand state with the number of rolls remaining.

  1. Define V(h,k)V(h, k) = probability of finishing with a large straight when the current sorted hand is hh and kk rolls remain.

  2. At k=0k = 0, return 11 if hh is a large straight else 00.

  3. At k>0k > 0, maximize over all subsets of hh to keep, averaging VV over the 6rerolls6^{\text{rerolls}} outcomes.

from itertools import product, combinations
from functools import lru_cache

def is_LS(h):
    s = sorted(h)
    return s == [1,2,3,4,5] or s == [2,3,4,5,6]

@lru_cache(maxsize=None)
def V(h, k):
    if k == 0:
        return 1.0 if is_LS(h) else 0.0
    best = 0.0
    seen = set()
    for r in range(6):
        for keep_idx in combinations(range(5), r):
            kept = tuple(sorted(h[i] for i in keep_idx))
            if kept in seen: continue
            seen.add(kept)
            n_re = 5 - r
            if n_re == 0:
                v = 1.0 if is_LS(kept) else 0.0
            else:
                tot = 0.0
                for out in product(range(1,7), repeat=n_re):
                    tot += V(tuple(sorted(kept + out)), k - 1)
                v = tot / 6**n_re
            if v > best:
                best = v
    return best

for X in [1, 2, 4, 5, 6]:
    h = tuple(sorted((1, 2, 4, 5, X)))
    print(f"X={X}: optimal P = {V(h, 2):.6f} = 11/36 = {11/36:.6f}")

The script prints 11/360.30555611/36 \approx 0.305556 for every X3X \ne 3, matching the boxed answer.