Blackjack

Summary

Blackjack is a betting game often found in casinos. Players are dealt two cards from a 52-card deck. Players can incrementally ask for additional cards from a face-down deck, with the goal to obtain a hand of cards with the highest total value. However, they will lose all money bet if their total exceeds 21 points. Because of the risk involved in obtaining new cards, players can purchase insurance against losing based on their initial hand of cards. Players also need to exceed the hand total of the “Dealer”, a player that follows certain strict rules on when they will ask for new cards.

Rules of Play

These rules are summarized from https://www.pagat.com/banking/blackjack.html.

Variants Used

In Valet, players start with 10 chips, and can only bet even values. This means that any insurance purchased, which is half of their initial bet, will be an integer value. Players cannot split their hands when they are initially dealt two cards with the same rank.

Blackjack is a banking game. Other games in this genre include Baccarat and Faro.

Information Flow

Card Movement Graph

The diagram above visualizes information flow in Blackjack using a single random rollout in CardStock. Visibility is attached to card locations, so all cards in a location share the same visibility; information is revealed or concealed as cards move between locations and ownership changes.

Public locations are shown in green, hidden locations in yellow, private locations in blue, and memory locations in light gray. Locations containing cards with distinct backs are indicated with bold borders. Rectangles denote ownership (players or table), and directed edges show card movement between locations and resulting visibility changes. Taken information is shown with red dotted edges (public to private/hidden), and shared information with blue dashed edges (private cards transferred to another player).

RECYCLE Code

The rules for Blackjack are coded in RECYCLE, a card game description language, to encourage standardized implementations across different systems.

You can also Download the code.

;; BlackJack simple rules
;; https://www.pagat.com/banking/blackjack.html

(game
 (setup  
  ;; Set up the players
  (create players 1)
  
  ;; Create the deck source
  (create deck (game iloc STOCK) 
   (deck (RANK (A, TWO, THREE, FOUR, FIVE, SIX, 
                SEVEN, EIGHT, NINE, TEN, J, Q, K))
         (COLOR (RED   (SUIT (HEARTS, DIAMONDS)))
                (BLACK (SUIT (SPADES, CLUBS)))))))          
 
 ;; Set up the game with 100 chips per person and 2 cards
 (do (
  (shuffle (game iloc STOCK))
  (set (game points HARD) 
       (((RANK : A)     1)))
  (set (game points SOFT) 
       (((RANK : A)    11)))
  (set (game points OTHER) 
       (((RANK : K)    10) ((RANK : Q)    10) ((RANK : J)    10)
        ((RANK : TEN)  10) ((RANK : NINE)  9) ((RANK : EIGHT) 8)
        ((RANK : SEVEN) 7) ((RANK : SIX)   6) ((RANK : FIVE)  5)
        ((RANK : FOUR)  4) ((RANK : THREE) 3) ((RANK : TWO)   2)))
  (set ((current player) sto CHIPS) 15)))

 ;; player makes a bet
 (choice (
  (any (range 2..10) 'B
   ((== (% 'B 2) 0)
    (do (
     (set ((current player) sto BET) 'B)
     (dec ((current player) sto CHIPS) 'B)))))))

 (do (
  ;; dealer hand dealt
  (move (top (game iloc STOCK)) (top (game iloc HAND)))
  (move (top (game iloc STOCK)) (top (game vloc HAND)))

  ;; player hand dealt
  (repeat 2 (move (top (game iloc STOCK))
                  (top ((current player) vloc HAND))))))
 
 (choice (
  ;; Buy insurance when total is 10 or 11? It is half your original bid  
  ((or (== (score (top (game vloc HAND)) using (game points OTHER)) 10)
       (== (score (top (game vloc HAND)) using (game points SOFT)) 11))
   (do (
    (set ((current player) sto INSURANCE) (// ((current player) sto BET) 2))
    (dec ((current player) sto CHIPS) (// ((current player) sto BET) 2)))))

  ;; decline the insurance
  (turn pass)))

 ;; determine what happens if the dealer has blackjack
 (do (
  ((== (+ (sum (union (game vloc HAND) (game iloc HAND)) using (game points OTHER)) 
          (sum (union (game iloc HAND) (game iloc HAND)) using (game points SOFT))) 21)
   (do (
    (set (game str STATE) END)

    ;; reveal the dealer's hand
    (move (top (game iloc HAND)) (top (game vloc HAND)))

    ;; if player also has blackjack, they regain their bid
    ((== (+ (sum ((current player) iloc HAND) using (game points OTHER)) 
            (sum ((current player) iloc HAND) using (game points SOFT))) 21)
     (inc ((current player) sto CHIPS) ((current player) sto BID)))

    ;; if the player bought insurance, they get the money back payout 2:1
    (inc ((current player) sto CHIPS) (* 3 ((current player) sto INSURANCE))))))))

 ;; player get cards until FINISHED or BUSTED 
 (stage player
  (end (or (== (game str STATE) END)
           (== ((current player) sto OVER) 1)))
        
  (choice (
   ;; HIT
   (move (top (game iloc STOCK)) 
         (top ((current player) vloc HAND)))
          
   ;; STAY
   (inc ((current player) sto OVER))))

  ;; Determine if busted
  (do (
   (let (size (filter ((current player) vloc HAND) 'HC 
                      (== (cardatt RANK 'HC) A))) 'ACECOUNT
    ((all (range 0..'ACECOUNT) 'R
      (> (+ (sum ((current player) vloc HAND) 
                 using (game points OTHER)) 'R 
            (* 11 (- 'ACECOUNT 'R))) 21))

     (inc ((current player) sto OVER)))))))
 
 (do (
  ;; if still playing, wrap up the hands
  ((!= (game str STATE) END)
   (do (

    ;; Determine best way to score player hand
    (set ((current player) sto SCORE) 0)
    (let (size (filter ((current player) vloc HAND) 'HC 
                       (== (cardatt RANK 'HC) A))) 'ACECOUNT
     (all (range 0..'ACECOUNT) 'R
      (let (+ (sum ((current player) vloc HAND) 
                   using (game points OTHER)) 'R 
              (* 11 (- 'ACECOUNT 'R))) 'TEMPSCORE
       ((and (<= 'TEMPSCORE 21)
             (> 'TEMPSCORE ((current player) sto SCORE)))
        (set ((current player) sto SCORE) 'TEMPSCORE))))) 

    ;; reveal the dealer's hand
    (move (top (game iloc HAND))
          (top (game vloc HAND))))))))
            
 ;; dealer flips until 17
 (stage player (end (or (== ((current player) sto SCORE) 0)
                        (== (game str STATE) END)
                        (== (game str DEALER) FINISHED)))
  (do (
   (set (game sto SCORE) 0)

   ;; calculate best dealer
   (let (size (filter (game vloc HAND) 'HC 
                      (== (cardatt RANK 'HC) A))) 'ACECOUNT
    (all (range 0..'ACECOUNT) 'R
     (let (+ (sum (game vloc HAND) using (game points OTHER)) 'R 
             (* 11 (- 'ACECOUNT 'R))) 'TEMPSCORE
      ((and (<= 'TEMPSCORE 21)
            (> 'TEMPSCORE (game sto SCORE)))
       (set (game sto SCORE) 'TEMPSCORE)))))

   ;; stop when equal or over 17
   ((or (>= (game sto SCORE) 17)
        (== (game sto SCORE) 0))
    (set (game str DEALER) FINISHED))

   ;; less than 17, flip one more card
   ((< (game sto SCORE) 17)
    (move (top (game iloc STOCK))
          (top (game vloc HAND)))))))

 ;; If player larger than dealer, player wins their bet
 (do (
  ((!= (game str STATE) END)
   (do (

   ;; equal and unbusted, player bet returned on a push
   ((and (!= ((current player) sto SCORE) 0)
         (== ((current player) sto SCORE) (game sto SCORE)))
    (inc ((current player) sto CHIPS) ((current player) sto BET)))

   ;; beat the dealer, player bet wins 2:1
   ((and (!= ((current player) sto SCORE) 0)
         (> ((current player) sto SCORE) (game sto SCORE)))
    (do (
     (inc ((current player) sto CHIPS) (* 2 ((current player) sto BET)))
     ((== ((current player) sto SCORE) 21)
      (inc ((current player) sto CHIPS) ((current player) sto BET)))))))))))
      
 (scoring max ((current player) sto CHIPS)))