<!-- canonical: https://efficientnewlanguage.org/eml-p/projects/P049-blackjack/ | updated: 2026-10-10 -->

# P049 Blackjack

Blackjack against the dealer with one deck and a stack of chips: hit, stand or double, aces counted 1 or 11 with soft totals shown, the dealer drawing to 16 and standing on every 17, a blackjack paying 3 to 2. The deck is shuffled by a Fisher-Yates shuffle on EML's own random numbers, so a seed deals the same cards every time.

EML-P project `projects/blackjack` in the EML language repo: 3 module(s), entry `main.eml`, terminal UI. There, `eml project run projects/blackjack` runs it and `eml project verify projects/blackjack` replays every session under CPython (two hash seeds) and in the interpreter; the site build replays every session in the interpreter again and publishes a session only if its screen matches.

Built on verified corpus cases: the-shuffle-left-one-in-place-as-expected (https://efficientnewlanguage.org/cases/880-the-shuffle-left-one-in-place-as-expected/).

## Sessions

### bad-input - interpreter: byte-equal to the golden

Input:

```text
0
x
1
0
101
abc

1
10
x
h
d
s
1
100
d
h
h
h
1
2
9
1001
x

2
10

2
10
7
1
10
3
```

Screen:

```text
== Blackjack ==
Get closer to 21 than the dealer without going over. The dealer draws to 16 and stands on 17.
A new game: 100 chips, seed 2026.

1) play a hand  2) new game  3) quit
choice> 0
Pick a number from 1 to 3.

1) play a hand  2) new game  3) quit
choice> x
Pick a number from 1 to 3.

1) play a hand  2) new game  3) quit
choice> 1
The deck is shuffled.
bet (1 to 100)> 0
Type a bet from 1 to 100.
bet (1 to 100)> 101
Type a bet from 1 to 100.
bet (1 to 100)> abc
Type a bet from 1 to 100.
bet (1 to 100)> 
Cancelled.

1) play a hand  2) new game  3) quit
choice> 1
bet (1 to 100)> 10
Dealer: 6D ??
You:    2C AH - soft 13
h) hit  s) stand  d) double> x
Type h to take a card, s to stay, or d to double your bet for one card.
h) hit  s) stand  d) double> h
You draw 6H: 2C AH 6H - soft 19
h) hit  s) stand> d
Type h to take a card or s to stay.
h) hit  s) stand> s
The dealer turns over KS: 6D KS - 16
The dealer draws 3H: 6D KS 3H - 19
A push at 19 - your bet comes back.
Chips 100 after 1 hand: 0 won, 0 lost, 1 pushed.

1) play a hand  2) new game  3) quit
choice> 1
bet (1 to 100)> 100
Dealer: 9H ??
You:    AC 2D - soft 13
h) hit  s) stand> d
Type h to take a card or s to stay.
h) hit  s) stand> h
You draw 4H: AC 2D 4H - soft 17
h) hit  s) stand> h
You draw 8D: AC 2D 4H 8D - 15
h) hit  s) stand> h
You draw 10H: AC 2D 4H 8D 10H - 25, bust
Bust - you lose 100.
Chips 0 after 2 hands: 0 won, 1 lost, 1 pushed.
You are out of chips - start a new game to play on.

1) play a hand  2) new game  3) quit
choice> 1
You have no chips left - start a new game.

1) play a hand  2) new game  3) quit
choice> 2
chips (10 to 1000)> 9
Type a number from 10 to 1000.
chips (10 to 1000)> 1001
Type a number from 10 to 1000.
chips (10 to 1000)> x
Type a number from 10 to 1000.
chips (10 to 1000)> 
Cancelled.

1) play a hand  2) new game  3) quit
choice> 2
chips (10 to 1000)> 10
seed (0 to 999999999)> 
Cancelled.

1) play a hand  2) new game  3) quit
choice> 2
chips (10 to 1000)> 10
seed (0 to 999999999)> 7
A new game: 10 chips, seed 7.

1) play a hand  2) new game  3) quit
choice> 1
The deck is shuffled.
bet (1 to 10)> 10
Dealer: 4C 6S
        10
You:    AH QS - blackjack
Blackjack! You win 15.
Chips 25 after 1 hand: 1 won, 0 lost, 0 pushed.

1) play a hand  2) new game  3) quit
choice> 3
Bye.
```

### basic - interpreter: byte-equal to the golden

Input:

```text
1
25
h
s
1
15
d
1
20
d
1
5
1
10
h
h
s
1
15
d
1
5
d
1
20
h
h
1
7
3
```

Screen:

```text
== Blackjack ==
Get closer to 21 than the dealer without going over. The dealer draws to 16 and stands on 17.
A new game: 100 chips, seed 2026.

1) play a hand  2) new game  3) quit
choice> 1
The deck is shuffled.
bet (1 to 100)> 25
Dealer: 6D ??
You:    2C AH - soft 13
h) hit  s) stand  d) double> h
You draw 6H: 2C AH 6H - soft 19
h) hit  s) stand> s
The dealer turns over KS: 6D KS - 16
The dealer draws 3H: 6D KS 3H - 19
A push at 19 - your bet comes back.
Chips 100 after 1 hand: 0 won, 0 lost, 1 pushed.

1) play a hand  2) new game  3) quit
choice> 1
bet (1 to 100)> 15
Dealer: 9H ??
You:    AC 2D - soft 13
h) hit  s) stand  d) double> d
You double to 30 and draw 4H: AC 2D 4H - soft 17
The dealer turns over 10D: 9H 10D - 19
The dealer wins, 19 against 17 - you lose 30.
Chips 70 after 2 hands: 0 won, 1 lost, 1 pushed.

1) play a hand  2) new game  3) quit
choice> 1
bet (1 to 70)> 20
Dealer: 10H ??
You:    8D 3C - 11
h) hit  s) stand  d) double> d
You double to 40 and draw 5S: 8D 3C 5S - 16
The dealer turns over 9D: 10H 9D - 19
The dealer wins, 19 against 16 - you lose 40.
Chips 30 after 3 hands: 0 won, 2 lost, 1 pushed.

1) play a hand  2) new game  3) quit
choice> 1
bet (1 to 30)> 5
Dealer: 8H KC
        18
You:    QS AD - blackjack
Blackjack! You win 7.
Chips 37 after 4 hands: 1 won, 2 lost, 1 pushed.

1) play a hand  2) new game  3) quit
choice> 1
bet (1 to 37)> 10
Dealer: 10C ??
You:    5C 4S - 9
h) hit  s) stand  d) double> h
You draw 4C: 5C 4S 4C - 13
h) hit  s) stand> h
You draw 7H: 5C 4S 4C 7H - 20
h) hit  s) stand> s
The dealer turns over 8C: 10C 8C - 18
You win 10, 20 against 18.
Chips 47 after 5 hands: 2 won, 2 lost, 1 pushed.

1) play a hand  2) new game  3) quit
choice> 1
bet (1 to 47)> 15
Dealer: 4D ??
You:    8S 7S - 15
h) hit  s) stand  d) double> d
You double to 30 and draw JS: 8S 7S JS - 25, bust
Bust - you lose 30.
Chips 17 after 6 hands: 2 won, 3 lost, 1 pushed.

1) play a hand  2) new game  3) quit
choice> 1
bet (1 to 17)> 5
Dealer: JH ??
You:    5H 5D - 10
h) hit  s) stand  d) double> d
You double to 10 and draw 3S: 5H 5D 3S - 13
The dealer turns over 2H: JH 2H - 12
The dealer draws 2S: JH 2H 2S - 14
The dealer draws KD: JH 2H 2S KD - 24, bust
The dealer busts - you win 10.
Chips 27 after 7 hands: 3 won, 3 lost, 1 pushed.

1) play a hand  2) new game  3) quit
choice> 1
The deck is shuffled.
bet (1 to 27)> 20
Dealer: JC ??
You:    7D 5S - 12
h) hit  s) stand> h
You draw 4D: 7D 5S 4D - 16
h) hit  s) stand> h
You draw 8S: 7D 5S 4D 8S - 24, bust
Bust - you lose 20.
Chips 7 after 8 hands: 3 won, 4 lost, 1 pushed.

1) play a hand  2) new game  3) quit
choice> 1
bet (1 to 7)> 7
Dealer: AS JH
        blackjack
You:    10D 2S - 12
The dealer has blackjack - you lose 7.
Chips 0 after 9 hands: 3 won, 5 lost, 1 pushed.
You are out of chips - start a new game to play on.

1) play a hand  2) new game  3) quit
choice> 3
Bye.
```

## Modules

### main.eml

```eml
# P049 blackjack: get closer to 21 than the dealer without going over. Aces
# count 1 or 11, faces 10. The dealer draws to 16 and stands on 17, a
# blackjack pays 3 to 2, and the deck is shuffled by EML's own random
# numbers, so a seed deals the same cards every time.
import cards
import rng

def trim(s):
    0 => i
    len(s) => j
    while i < j and s[i] == " ":
        i + 1 => i
    while j > i and s[j - 1] == " ":
        j - 1 => j
    return s[i:j]

def number(s):
    if s == "" or len(s) > 9:
        return -1
    0 => n
    for c in s:
        if not (c in "0123456789"):
            return -1
        n * 10 + int(c) => n
    return n

def counted(n, word):
    if n == 1:
        return "1 " + word
    return str(n) + " " + word + "s"

def start(chips, seed):
    ("A new game: " + str(chips) + " chips, seed " + str(seed) + ".") ^0
    # [chips, the generator, the deck, next card, hands, won, lost, pushed]
    return [chips, rng.Rng(seed), [], 0, 0, 0, 0, 0]

def shuffle(game):
    cards.shuffled(cards.new_deck(), game[1]) => game[2]
    0 => game[3]

def draw(game):
    if game[3] == len(game[2]):
        # Only a very long hand gets here; the cards on the table are not
        # taken out of the new deck.
        "The deck has run out - a new one is shuffled." ^0
        shuffle(game)
    game[2][game[3]] => card
    game[3] + 1 => game[3]
    return card

def table(player, dealer, hide):
    ("Dealer: " + cards.shown(dealer, hide)) ^0
    if not hide:
        ("        " + cards.described(dealer)) ^0
    ("You:    " + cards.shown(player, False) + " - " + cards.described(player)) ^0

def settle(game, won):
    game[0] + won => game[0]
    game[4] + 1 => game[4]
    if won > 0:
        game[5] + 1 => game[5]
    elif won < 0:
        game[6] + 1 => game[6]
    else:
        game[7] + 1 => game[7]
    ("Chips " + str(game[0]) + " after " + counted(game[4], "hand") + ": " + str(game[5]) + " won, " + str(game[6]) + " lost, " + str(game[7]) + " pushed.") ^0
    if game[0] == 0:
        "You are out of chips - start a new game to play on." ^0

def ask_bet(game):
    while True:
        trim(input("bet (1 to " + str(game[0]) + ")> ")) => answer
        if answer == "":
            return -1
        number(answer) => n
        if n >= 1 and n <= game[0]:
            return n
        ("Type a bet from 1 to " + str(game[0]) + ".") ^0

def play_hand(game):
    if game[0] == 0:
        "You have no chips left - start a new game." ^0
        return game
    if len(game[2]) - game[3] < 15:
        shuffle(game)
        "The deck is shuffled." ^0
    ask_bet(game) => bet
    if bet == -1:
        "Cancelled." ^0
        return game
    # Dealt in turn, as at a table: you, the dealer, you, the dealer - the
    # dealer's second card face down.
    [draw(game)] => player
    [draw(game)] => dealer
    player + [draw(game)] => player
    dealer + [draw(game)] => dealer
    if cards.blackjack(player) or cards.blackjack(dealer):
        table(player, dealer, False)
        cards.natural_result(player, dealer, bet) => won
        if won == 0:
            "Both have blackjack - a push, your bet comes back." ^0
        elif won > 0:
            ("Blackjack! You win " + str(won) + ".") ^0
        else:
            ("The dealer has blackjack - you lose " + str(bet) + ".") ^0
        settle(game, won)
        return game
    table(player, dealer, True)
    bet => stake
    True => asking
    while asking and cards.value(player)[0] < 21:
        (len(player) == 2 and game[0] >= bet * 2) => can_double
        if can_double:
            trim(input("h) hit  s) stand  d) double> ")) => action
        else:
            trim(input("h) hit  s) stand> ")) => action
        if action == "h":
            draw(game) => card
            player + [card] => player
            ("You draw " + cards.name(card) + ": " + cards.shown(player, False) + " - " + cards.described(player)) ^0
        elif action == "s":
            False => asking
        elif action == "d" and can_double:
            bet * 2 => stake
            draw(game) => card
            player + [card] => player
            ("You double to " + str(stake) + " and draw " + cards.name(card) + ": " + cards.shown(player, False) + " - " + cards.described(player)) ^0
            False => asking
        elif can_double:
            "Type h to take a card, s to stay, or d to double your bet for one card." ^0
        else:
            "Type h to take a card or s to stay." ^0
    if cards.value(player)[0] > 21:
        ("Bust - you lose " + str(stake) + ".") ^0
        settle(game, 0 - stake)
        return game
    ("The dealer turns over " + cards.name(dealer[1]) + ": " + cards.shown(dealer, False) + " - " + cards.described(dealer)) ^0
    while not cards.dealer_stands(dealer):
        draw(game) => card
        dealer + [card] => dealer
        ("The dealer draws " + cards.name(card) + ": " + cards.shown(dealer, False) + " - " + cards.described(dealer)) ^0
    cards.result(player, dealer, stake) => won
    cards.value(player)[0] => p
    cards.value(dealer)[0] => d
    if d > 21:
        ("The dealer busts - you win " + str(won) + ".") ^0
    elif won > 0:
        ("You win " + str(won) + ", " + str(p) + " against " + str(d) + ".") ^0
    elif won < 0:
        ("The dealer wins, " + str(d) + " against " + str(p) + " - you lose " + str(stake) + ".") ^0
    else:
        ("A push at " + str(p) + " - your bet comes back.") ^0
    settle(game, won)
    return game

def ask_number(prompt, low, high):
    while True:
        trim(input(prompt + " (" + str(low) + " to " + str(high) + ")> ")) => answer
        if answer == "":
            return -1
        number(answer) => n
        if n >= low and n <= high:
            return n
        ("Type a number from " + str(low) + " to " + str(high) + ".") ^0

def new_game(game):
    ask_number("chips", 10, 1000) => chips
    if chips == -1:
        "Cancelled." ^0
        return game
    ask_number("seed", 0, 999999999) => seed
    if seed == -1:
        "Cancelled." ^0
        return game
    return start(chips, seed)

"== Blackjack ==" ^0
"Get closer to 21 than the dealer without going over. The dealer draws to 16 and stands on 17." ^0
start(100, 2026) => game
True => running
while running:
    "" ^0
    "1) play a hand  2) new game  3) quit" ^0
    trim(input("choice> ")) => choice
    if choice == "1":
        play_hand(game) => game
    elif choice == "2":
        new_game(game) => game
    elif choice == "3":
        False => running
    else:
        "Pick a number from 1 to 3." ^0
"Bye." ^0
```

Python projection of main.eml:

```python
import cards
import rng

def trim(s):
    i = 0
    j = len(s)
    while i < j and s[i] == " ":
        i = i + 1
    while j > i and s[j - 1] == " ":
        j = j - 1
    return s[i:j]

def number(s):
    if s == "" or len(s) > 9:
        return -1
    n = 0
    for c in s:
        if not c in "0123456789":
            return -1
        n = n * 10 + int(c)
    return n

def counted(n, word):
    if n == 1:
        return "1 " + word
    return str(n) + " " + word + "s"

def start(chips, seed):
    print("A new game: " + str(chips) + " chips, seed " + str(seed) + ".")
    return [chips, rng.Rng(seed), [], 0, 0, 0, 0, 0]

def shuffle(game):
    game[2] = cards.shuffled(cards.new_deck(), game[1])
    game[3] = 0

def draw(game):
    if game[3] == len(game[2]):
        print("The deck has run out - a new one is shuffled.")
        shuffle(game)
    card = game[2][game[3]]
    game[3] = game[3] + 1
    return card

def table(player, dealer, hide):
    print("Dealer: " + cards.shown(dealer, hide))
    if not hide:
        print("        " + cards.described(dealer))
    print("You:    " + cards.shown(player, False) + " - " + cards.described(player))

def settle(game, won):
    game[0] = game[0] + won
    game[4] = game[4] + 1
    if won > 0:
        game[5] = game[5] + 1
    elif won < 0:
        game[6] = game[6] + 1
    else:
        game[7] = game[7] + 1
    print("Chips " + str(game[0]) + " after " + counted(game[4], "hand") + ": " + str(game[5]) + " won, " + str(game[6]) + " lost, " + str(game[7]) + " pushed.")
    if game[0] == 0:
        print("You are out of chips - start a new game to play on.")

def ask_bet(game):
    while True:
        answer = trim(input("bet (1 to " + str(game[0]) + ")> "))
        if answer == "":
            return -1
        n = number(answer)
        if n >= 1 and n <= game[0]:
            return n
        print("Type a bet from 1 to " + str(game[0]) + ".")

def play_hand(game):
    if game[0] == 0:
        print("You have no chips left - start a new game.")
        return game
    if len(game[2]) - game[3] < 15:
        shuffle(game)
        print("The deck is shuffled.")
    bet = ask_bet(game)
    if bet == -1:
        print("Cancelled.")
        return game
    player = [draw(game)]
    dealer = [draw(game)]
    player = player + [draw(game)]
    dealer = dealer + [draw(game)]
    if cards.blackjack(player) or cards.blackjack(dealer):
        table(player, dealer, False)
        won = cards.natural_result(player, dealer, bet)
        if won == 0:
            print("Both have blackjack - a push, your bet comes back.")
        elif won > 0:
            print("Blackjack! You win " + str(won) + ".")
        else:
            print("The dealer has blackjack - you lose " + str(bet) + ".")
        settle(game, won)
        return game
    table(player, dealer, True)
    stake = bet
    asking = True
    while asking and cards.value(player)[0] < 21:
        can_double = len(player) == 2 and game[0] >= bet * 2
        if can_double:
            action = trim(input("h) hit  s) stand  d) double> "))
        else:
            action = trim(input("h) hit  s) stand> "))
        if action == "h":
            card = draw(game)
            player = player + [card]
            print("You draw " + cards.name(card) + ": " + cards.shown(player, False) + " - " + cards.described(player))
        elif action == "s":
            asking = False
        elif action == "d" and can_double:
            stake = bet * 2
            card = draw(game)
            player = player + [card]
            print("You double to " + str(stake) + " and draw " + cards.name(card) + ": " + cards.shown(player, False) + " - " + cards.described(player))
            asking = False
        elif can_double:
            print("Type h to take a card, s to stay, or d to double your bet for one card.")
        else:
            print("Type h to take a card or s to stay.")
    if cards.value(player)[0] > 21:
        print("Bust - you lose " + str(stake) + ".")
        settle(game, 0 - stake)
        return game
    print("The dealer turns over " + cards.name(dealer[1]) + ": " + cards.shown(dealer, False) + " - " + cards.described(dealer))
    while not cards.dealer_stands(dealer):
        card = draw(game)
        dealer = dealer + [card]
        print("The dealer draws " + cards.name(card) + ": " + cards.shown(dealer, False) + " - " + cards.described(dealer))
    won = cards.result(player, dealer, stake)
    p = cards.value(player)[0]
    d = cards.value(dealer)[0]
    if d > 21:
        print("The dealer busts - you win " + str(won) + ".")
    elif won > 0:
        print("You win " + str(won) + ", " + str(p) + " against " + str(d) + ".")
    elif won < 0:
        print("The dealer wins, " + str(d) + " against " + str(p) + " - you lose " + str(stake) + ".")
    else:
        print("A push at " + str(p) + " - your bet comes back.")
    settle(game, won)
    return game

def ask_number(prompt, low, high):
    while True:
        answer = trim(input(prompt + " (" + str(low) + " to " + str(high) + ")> "))
        if answer == "":
            return -1
        n = number(answer)
        if n >= low and n <= high:
            return n
        print("Type a number from " + str(low) + " to " + str(high) + ".")

def new_game(game):
    chips = ask_number("chips", 10, 1000)
    if chips == -1:
        print("Cancelled.")
        return game
    seed = ask_number("seed", 0, 999999999)
    if seed == -1:
        print("Cancelled.")
        return game
    return start(chips, seed)

print("== Blackjack ==")
print("Get closer to 21 than the dealer without going over. The dealer draws to 16 and stands on 17.")
game = start(100, 2026)
running = True
while running:
    print("")
    print("1) play a hand  2) new game  3) quit")
    choice = trim(input("choice> "))
    if choice == "1":
        game = play_hand(game)
    elif choice == "2":
        game = new_game(game)
    elif choice == "3":
        running = False
    else:
        print("Pick a number from 1 to 3.")
print("Bye.")
```

### cards.eml

```eml
# P049 blackjack - cards, the deck and the hands. A card is [rank, suit]:
# rank 1 (ace) to 13 (king), suit 0 to 3 (spades, hearts, diamonds, clubs).

def new_deck():
    [] => deck
    for s in [0:3]:
        for r in [1:13]:
            deck + [[r, s]] => deck
    return deck

def shuffled(deck, rng):
    # Fisher-Yates, from the last position down: the card for position i is
    # picked from positions 0 to i, so every order is equally likely - and a
    # card may stay where it was. A uniform shuffle leaves one card in place
    # on average, as the corpus case the-shuffle-left-one-in-place-as-expected
    # works out; forbidding that would make the shuffle less random, not more.
    deck[0:len(deck)] => d
    len(d) => n
    for k in [0:n - 2]:
        n - 1 - k => i
        rng.below(i + 1) => j
        d[i] => t
        d[j] => d[i]
        t => d[j]
    return d

def name(card):
    str(card[0]) => rank
    if card[0] == 1:
        "A" => rank
    elif card[0] == 11:
        "J" => rank
    elif card[0] == 12:
        "Q" => rank
    elif card[0] == 13:
        "K" => rank
    return rank + "SHDC"[card[1]]

def points(card):
    # An ace is 1 here; value() decides when it counts 11.
    if card[0] > 10:
        return 10
    return card[0]

def value(hand):
    # [total, soft]. Aces count 1, and one of them counts 11 when that does
    # not take the hand over 21 - the hand is then soft. Two aces at 11 would
    # be 22, so at most one ever counts 11.
    0 => total
    False => ace
    for card in hand:
        total + points(card) => total
        if card[0] == 1:
            True => ace
    if ace and total + 10 <= 21:
        return [total + 10, True]
    return [total, False]

def blackjack(hand):
    # 21 with the first two cards: an ace and a ten, jack, queen or king.
    return len(hand) == 2 and value(hand)[0] == 21

def shown(hand, hide_second):
    "" => s
    for k in [0:len(hand) - 1]:
        if k > 0:
            s + " " => s
        if k == 1 and hide_second:
            s + "??" => s
        else:
            s + name(hand[k]) => s
    return s

def described(hand):
    value(hand) => v
    if v[0] > 21:
        return str(v[0]) + ", bust"
    if blackjack(hand):
        return "blackjack"
    if v[1]:
        return "soft " + str(v[0])
    return str(v[0])

def dealer_stands(hand):
    # The dealer draws to 16 and stands on every 17, a soft 17 too.
    return value(hand)[0] >= 17

def natural_result(player, dealer, bet):
    # When either first hand is a blackjack the hand ends at once: both is a
    # push, the player's alone pays 3 to 2 (rounded down), the dealer's alone
    # takes the bet. Returns the chips won, negative for a loss.
    if blackjack(player) and blackjack(dealer):
        return 0
    if blackjack(player):
        return int(bet * 3 / 2)
    return 0 - bet

def result(player, dealer, stake):
    # Both hands finished, neither a blackjack: the chips won, negative for
    # a loss. A player who busts loses even if the dealer would bust too -
    # the dealer does not play then.
    value(player)[0] => p
    value(dealer)[0] => d
    if p > 21:
        return 0 - stake
    if d > 21 or p > d:
        return stake
    if p < d:
        return 0 - stake
    return 0
```

Python projection of cards.eml:

```python
def new_deck():
    deck = []
    for s in range(0, 4):
        for r in range(1, 14):
            deck = deck + [[r, s]]
    return deck

def shuffled(deck, rng):
    d = deck[0:len(deck)]
    n = len(d)
    for k in range(0, n - 2+1):
        i = n - 1 - k
        j = rng.below(i + 1)
        t = d[i]
        d[i] = d[j]
        d[j] = t
    return d

def name(card):
    rank = str(card[0])
    if card[0] == 1:
        rank = "A"
    elif card[0] == 11:
        rank = "J"
    elif card[0] == 12:
        rank = "Q"
    elif card[0] == 13:
        rank = "K"
    return rank + "SHDC"[card[1]]

def points(card):
    if card[0] > 10:
        return 10
    return card[0]

def value(hand):
    total = 0
    ace = False
    for card in hand:
        total = total + points(card)
        if card[0] == 1:
            ace = True
    if ace and total + 10 <= 21:
        return [total + 10, True]
    return [total, False]

def blackjack(hand):
    return len(hand) == 2 and value(hand)[0] == 21

def shown(hand, hide_second):
    s = ""
    for k in range(0, len(hand)):
        if k > 0:
            s = s + " "
        if k == 1 and hide_second:
            s = s + "??"
        else:
            s = s + name(hand[k])
    return s

def described(hand):
    v = value(hand)
    if v[0] > 21:
        return str(v[0]) + ", bust"
    if blackjack(hand):
        return "blackjack"
    if v[1]:
        return "soft " + str(v[0])
    return str(v[0])

def dealer_stands(hand):
    return value(hand)[0] >= 17

def natural_result(player, dealer, bet):
    if blackjack(player) and blackjack(dealer):
        return 0
    if blackjack(player):
        return int(bet * 3 / 2)
    return 0 - bet

def result(player, dealer, stake):
    p = value(player)[0]
    d = value(dealer)[0]
    if p > 21:
        return 0 - stake
    if d > 21 or p > d:
        return stake
    if p < d:
        return 0 - stake
    return 0
```

### rng.eml

```eml
# P049 blackjack - random numbers written in EML: the linear congruential
# generator of P008 (number guessing), with the constants of the C
# standard's example rand(). Python's random module is not used, so a seed
# gives the same shuffle on every machine, and the interpreter can check a
# whole session byte for byte.

class Rng:
    def __init__(self, seed):
        seed % 2147483648 => self.state

    def step(self):
        (1103515245 * self.state + 12345) % 2147483648 => self.state
        return self.state

    def below(self, n):
        # A number from 0 to n - 1, taken from the high bits of the state: the
        # low bits of this generator repeat with short periods. Dividing by
        # 65536 is exact in a float for a state below 2^31.
        return int(self.step() / 65536) % n
```

Python projection of rng.eml:

```python
class Rng:
    def __init__(self, seed):
        self.state = seed % 2147483648
    def step(self):
        self.state = (1103515245 * self.state + 12345) % 2147483648
        return self.state
    def below(self, n):
        return int(self.step() / 65536) % n
```

## README

# P049 - Blackjack

Blackjack against the dealer, with one deck and a stack of chips. Get closer
to 21 than the dealer without going over. Number cards count their number,
jacks, queens and kings count 10, and an ace counts 1 or 11, whichever
helps. You can hit, stand, or double your bet for one more card.

- `main.eml` - the menu, the bets, playing a hand in the order of a real
  table, and the chips
- `cards.eml` - the deck and its shuffle, card names, the value of a hand,
  and how a hand is settled
- `rng.eml` - random numbers written in EML, the generator of P008

How each part works:

- The deck is shuffled by Fisher-Yates from the last position down. The
  card for each position is picked from that position and every one before
  it, so every order is equally likely, and a card may stay where it was.
  The corpus case `the-shuffle-left-one-in-place-as-expected` works out
  that a uniform shuffle leaves one card in place on average; a shuffle
  that never did would be less random, not more. A new deck is shuffled
  before a hand when fewer than 15 cards are left. The random numbers come
  from EML code, so a seed deals the same cards every time.
- A hand's value counts every ace as 1, then one ace as 11 when that does
  not go over 21; the hand is then soft. Only one ace can ever count 11,
  since two would make 22.
- The cards are dealt you, dealer, you, dealer, with the dealer's second
  card face down. A blackjack - an ace and a ten-card as the first two -
  ends the hand at once: yours alone pays 3 to 2, rounded down; the
  dealer's alone takes your bet; both is a push.
- You may double on your first two cards when your chips cover twice the
  bet: the bet doubles and you get exactly one more card. A hand that
  reaches 21 stops asking.
- If you bust, you lose and the dealer does not play. Otherwise the dealer
  turns over the hidden card and draws to 16, standing on every 17,
  including a soft 17. There is no splitting and no insurance.

What is checked: menu choices 1 to 3; a bet from 1 to your chips; h, s and
d in a hand, where d is only accepted while doubling is allowed; a new game
with 10 to 1000 chips and a seed from 0 to 999999999; no hand without
chips. An empty answer cancels.

Sessions: `sessions/basic.in` plays nine hands of the default game (100
chips, seed 2026):
- a soft 13 hit to a soft 19, and a push when the dealer draws to 19;
- two doubles that lose;
- a blackjack on a bet of 5, which wins 7 (7.5 rounded down);
- a 9 hit twice to 20, winning against 18;
- a double that busts;
- a double that wins when the dealer busts after drawing twice;
- a new deck before the eighth hand, with 14 cards left, and a bust;
- the last 7 chips lost to the dealer's blackjack, and the game is out of
  chips.

`sessions/bad-input.in` gives:
- menu choices 0 and x;
- bets 0, 101 and abc, then an empty one;
- x in a hand, and d after a hit;
- d with all 100 chips bet, so doubling is not offered. That hand goes from
  a soft 17 to a hard 15 and busts, and the game is out of chips;
- a hand with no chips;
- a new game with chips 9, 1001 and x, cancelled at each question, and then
  10 chips with seed 7, where a blackjack on a bet of 10 wins 15.

Built on the verified corpus case `the-shuffle-left-one-in-place-as-expected`
(how often a uniform shuffle leaves an item where it was).
