• Emily Zhang
  • Fiddler

Can You Win at Non-Traditional Blackjack?

2024 May 242024 May 24 Problem Back

This Week's Fiddler

We first want to find all the integer partitions of 21 which repeats no parts and uses no part larger than 10. We can do this manually or with some code:

from functools import cache


@cache
def list_partitions(n: int, max_part: int) -> list[tuple[int, ...]]:
    if n < 0 or max_part < 0:
        return []
    if n == 0:
        return [()]
    ans = []
    for k in range(max_part, 0, -1):
        ans.extend(
            [
                (k, *partition)
                for partition in list_partitions(n - k, min(max_part, k - 1))
            ]
        )
    return ans


print(list_partitions(21, 10))
[(10, 9, 2), (10, 8, 3), (10, 8, 2, 1), (10, 7, 4), (10, 7, 3, 1), (10, 6, 5), (10, 6, 4, 1), (10, 6, 3, 2), (10, 5, 4, 2), (10, 5, 3, 2, 1), (9, 8, 4), (9, 8, 3, 1), (9, 7, 5), (9, 7, 4, 1), (9, 7, 3, 2), (9, 6, 5, 1), (9, 6, 4, 2), (9, 6, 3, 2, 1), (9, 5, 4, 3), (9, 5, 4, 2, 1), (8, 7, 6), (8, 7, 5, 1), (8, 7, 4, 2), (8, 7, 3, 2, 1), (8, 6, 5, 2), (8, 6, 4, 3), (8, 6, 4, 2, 1), (8, 5, 4, 3, 1), (7, 6, 5, 3), (7, 6, 5, 2, 1), (7, 6, 4, 3, 1), (7, 5, 4, 3, 2), (6, 5, 4, 3, 2, 1)]

What we're interested in is each partition's length. Because we're always going to draw until we either win or bust, the order of our winning hands don't matter. For a kkk-sized partition, we have a 1(10k)\frac{1}{10\choose k}(k10​)1​ chance of getting said particular win condition. Our total probability of winning is thus the sum of all these chances.

There are 7 partitions with 3 cards, 16 partitions with 4 cards, 9 partitions with 5 cards, and 1 partition with 6 cards. Hence, our answer:

7(103)+16(104)+9(105)+1(106)=740\frac{7}{10\choose3} + \frac{16}{10\choose4} + \frac{9}{10\choose5} + \frac{1}{10\choose6} = \frac{7}{40}(310​)7​+(410​)16​+(510​)9​+(610​)1​=407​

Answer: 7/40 (17.5%)


Extra Credit

Note that if we play ultra-conservatively, 10 has to be in our winning hand. If we suppose that 10 isn't in our winning hand, then one of our cards that we're waiting on must be lower than 10. To get exactly 21, this means that our hand total must at some point be 12 or greater. This puts us at risk for busting when drawing a 10. Thus, all winning hands must include a 10.

For the vast majority of winning hands, they follow a structure of drawing 10 last (this is important, as drawing 10 early means other large cards, such as 9, give us a chance of busting). The number of such hands is the number of partitions of 11 we can make. The following code finds all the partitions we need, using the same list_partitions() function from earlier:

...
def list_partitions(n: int, max_part: int) -> list[tuple[int, ...]]:
    ...


print(list_partitions(11, 9))
[(9, 2), (8, 3), (8, 2, 1), (7, 4), (7, 3, 1), (6, 5), (6, 4, 1), (6, 3, 2), (5, 4, 2), (5, 3, 2, 1)]

For each partition of size kkk, there are k!k!k! ways of arranging the partition items and (9−k)!(9-k)!(9−k)! ways of arranging the items outside the partition. Note that the 10 card's position must be fixed, lying between the cards in the partition and the cards outside the partition. With 10!10!10! total orderings, this makes a chance of (9−k)!k!10!\frac{(9-k)!k!}{10!}10!(9−k)!k!​ for each partition.

There are, however, exactly two winning hands not discussed here: 10-2-9 and 2-10-9. These are the only two hands that work that do not end in 10. It works out because 10 is exhausted earlier in the hand, allowing 9 to be the largest card remaining and letting you go safely to 12 (but no further). Because we must add to 12 with 10 and a different card, 2 is the only other option. Thus, both orderings that satisfy these conditions are valid hands. Each of the hands has a chance of 7!10!\frac{7!}{10!}10!7!​ (as there are 7!7!7! ways to arrange the remaining 7 cards).

For 4 partitions of size 2, 5 partitions of size 3, and 1 partition of size 4, not forgetting the two extra winning hands, this makes the total chance:

4⋅(9−2)!⋅2!+5⋅(9−3)!⋅3!+1⋅(9−4)!⋅4!+2⋅7!10!=13630\frac{4\cdot(9-2)!\cdot2! + 5\cdot(9-3)!\cdot3! + 1\cdot(9-4)!\cdot4! + 2\cdot7!}{10!} = \frac{13}{630}10!4⋅(9−2)!⋅2!+5⋅(9−3)!⋅3!+1⋅(9−4)!⋅4!+2⋅7!​=63013​

Funnily enough, because the deck size is rather small (there are only 10!10!10! possible permutations), we can also just brute-force all the decks and see which ones fit our criteria:

Brute-force code
import itertools
from collections import Counter
from fractions import Fraction


winning_hands = Counter()
total = 0
for deck in itertools.permutations(range(1, 11)):
    total += 1
    remaining = set(deck)
    card_sum = 0
    for i, card in enumerate(deck):
        remaining.remove(card)
        card_sum += card
        if card_sum == 21:
            winning_hands.update([deck[: i + 1]])
            break
        if card_sum + max(remaining) > 21:
            break

total_wins = winning_hands.total()
print(
    f"Probability (brute force): {Fraction(total_wins, total)} ({total_wins / total})"
)
print("Winning hands:")
print(list(winning_hands.keys()))

 

Probability (brute force): 13/630 (0.020634920634920634)
Winning hands:
[(1, 2, 3, 5, 10), (1, 2, 5, 3, 10), (1, 2, 8, 10), (1, 3, 2, 5, 10), (1, 3, 5, 2, 10), (1, 3, 7, 10), (1, 4, 6, 10), (1, 5, 2, 3, 10), (1, 5, 3, 2, 10), (1, 6, 4, 10), (1, 7, 3, 10), (1, 8, 2, 10), (2, 1, 3, 5, 10), (2, 1, 5, 3, 10), (2, 1, 8, 10), (2, 3, 1, 5, 10), (2, 3, 5, 1, 10), (2, 3, 6, 10), (2, 4, 5, 10), (2, 5, 1, 3, 10), (2, 5, 3, 1, 10), (2, 5, 4, 10), (2, 6, 3, 10), (2, 8, 1, 10), (2, 9, 10), (2, 10, 9), (3, 1, 2, 5, 10), (3, 1, 5, 2, 10), (3, 1, 7, 10), (3, 2, 1, 5, 10), (3, 2, 5, 1, 10), (3, 2, 6, 10), (3, 5, 1, 2, 10), (3, 5, 2, 1, 10), (3, 6, 2, 10), (3, 7, 1, 10), (3, 8, 10), (4, 1, 6, 10), (4, 2, 5, 10), (4, 5, 2, 10), (4, 6, 1, 10), (4, 7, 10), (5, 1, 2, 3, 10), (5, 1, 3, 2, 10), (5, 2, 1, 3, 10), (5, 2, 3, 1, 10), (5, 2, 4, 10), (5, 3, 1, 2, 10), (5, 3, 2, 1, 10), (5, 4, 2, 10), (5, 6, 10), (6, 1, 4, 10), (6, 2, 3, 10), (6, 3, 2, 10), (6, 4, 1, 10), (6, 5, 10), (7, 1, 3, 10), (7, 3, 1, 10), (7, 4, 10), (8, 1, 2, 10), (8, 2, 1, 10), (8, 3, 10), (9, 2, 10), (10, 2, 9)]

This matches what we got earlier, and it lists all the winning hands!

So our chance of winning in one particular round is 13/630 (about 2.06%). What does that make our expected number of rounds until we win? Well that's just a geometric distribution, whose expected value is 1p\frac{1}{p}p1​, where ppp is the probability of success for one round. Thus, our answer is 63013\frac{630}{13}13630​, or about 48.4615 rounds.

Answer: 630/13 (about 48.4615)

Back to Fiddler list
© 2024 Emily Bradon Zhang·Support Gaza Recovery