Last active
December 14, 2015 22:29
-
-
Save m0smith/5158595 to your computer and use it in GitHub Desktop.
Basic Black Jack engine in Clojure
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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