Skip to content

Instantly share code, notes, and snippets.

@m0smith
Last active December 14, 2015 22:29
Show Gist options
  • Save m0smith/5158595 to your computer and use it in GitHub Desktop.
Save m0smith/5158595 to your computer and use it in GitHub Desktop.
Basic Black Jack engine in Clojure
(def suites [:heart :club :diamond :spade])
(def ranks
{
:ace [1 11]
:two [2]
:three [3]
:four [4]
:five [5]
:six [6]
:seven [7]
:eight [8]
:nine [9]
:ten [10]
:jack [10]
:queen [10]
:king [10]})
(def deck (for [suite suites rank (keys ranks)] [rank suite]))
(defn multiple-decks [c]
(apply concat (repeat c deck)))
;; Stategies
;; Pass in a seq of cards [rank suit]
(defn card-values [[rank suit]]
(ranks rank))
(defn sum-cards*
[[card & more]]
(if-not (seq more)
(card-values card)
(for [vs (card-values card) ms (sum-cards more)]
(+ vs ms))))
(defn sum-cards
"Accept a seq of cards and return a seq of all possible values excluding values over 21"
[cards]
(filter #(< % 22) (sum-cards* cards)))
(defn hold-ai [ & args ]
:hold)
(defn under-17-ai [cards & args]
(if (some #(< % 17) (sum-cards cards))
:hit
:hold))
(def house-ai under-17-ai)
;; card = [rank suit]
;; player = [pot strategy]
(defn initial-state []
{
:deck (shuffle (multiple-decks 3))
:players [
[ 100 hold-ai]
;[ 100 under-17-ai]
]
:dealer [ 100 house-ai]
}
)
(defn play-hand
"accept a deck and a player [pot strategy]. Return a result [deck hand status]"
[start-deck [pot start-strategy]]
(when (seq deck)
(loop [deck (drop 2 start-deck)
hand (take 2 start-deck)
strategy start-strategy]
(let [vs (filter #(< % 22) (sum-cards hand))]
(if (seq vs)
(let [action (strategy hand)]
(if-not (= action :hit)
[deck [hand action 1]]
(if-not (seq deck)
nil
(recur (rest deck) (conj hand (first deck)) strategy))))
[deck [hand :bust 1]])))))
(defn gather-wagers [[deck wagers] player]
(when (seq deck)
(let [[new-deck wager] (play-hand deck player)]
[new-deck (conj wagers wager)])))
(defn play-all-players-hands [game-state]
(reduce gather-wagers [(:deck game-state) []] (:players game-state)))
(defn player-beat-dealer? [[dealer-hand dealer-status][player-hand player-status]]
(cond
(= :bust player-status) false
(= :bust dealer-status) true
:else (< (apply max (sum-cards dealer-hand))
(apply max (sum-cards player-hand)))))
(defn compute-winnings [dealer wagers]
(for [[hand status bet :as wager] wagers]
(if (player-beat-dealer? dealer wager)
(- bet)
bet)))
(defn apply-winnings [game-state winnings]
(reduce #(update-in %1 [:players (first %2) 0] + (second %2))
game-state
(map-indexed vector winnings)))
(defn game-iteration [game-state]
(when (seq (:deck game-state))
(let [[deck wagers] (play-all-players-hands game-state)
[deck dealer-hand] (play-hand deck (:dealer game-state))]
(when (and wagers dealer-hand)
(-> game-state
(apply-winnings (compute-winnings dealer-hand wagers))
(assoc :deck deck))))))
(defn iterate-while
[f x]
(when x
(cons x (lazy-seq (iterate-while f (f x))))))
(defn play-rounds [game-state rounds]
(let [states (take rounds (iterate-while game-iteration game-state))
pots (map first (:players (last states)))]
pots))
(defn play-entire-deck []
(let [final-state (last (iterate-while game-iteration (initial-state)))
final-pots (map first (:players final-state))]
final-pots))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment