Project 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.

3 modules · 2 recorded sessionstext-menu UI in the terminalupdated 2026-10-10

Every screen below was recorded under CPython. When this page was built, the EML interpreter replayed each session from the same input and printed the same bytes.

About

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).

Recorded sessions

What the screen shows while someone uses the program. Each typed line appears after its prompt, the way a terminal shows it.

bad-input

interpreter: byte-equal
== 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.
What was typed (34 lines)
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

basic

interpreter: byte-equal
== 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.
What was typed (30 lines)
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

Modules

The program as written, entry module first. Each module transpiles to its own Python file, which is what eml project run executes.

main.eml(entry)

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 (main.py)
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 (cards.py)
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 (rng.py)
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

Built on these corpus cases