Skip to content

Instantly share code, notes, and snippets.

@Tempate
Last active February 19, 2020 14:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Tempate/2a05c584e1b29c9c609c90856c566128 to your computer and use it in GitHub Desktop.
Save Tempate/2a05c584e1b29c9c609c90856c566128 to your computer and use it in GitHub Desktop.
;;;; Black Jack
(defclass deck ()
((card-count :initarg :card-count :initform 0 :reader deck-count)
(cards :initarg :cards :initform '() :reader deck-cards)))
(defclass hand ()
((cards :initarg :cards :initform '() :reader hand-cards)))
(defclass card ()
((suit :initarg :suit :reader card-suit)
(rank :initarg :rank :reader card-rank)
(value :initarg :value :reader card-value)))
(defmethod make-52-card-deck ()
(make-instance 'deck
:card-count 52
:cards (loop :for suit :in '(:clubs :diamonds :hearts :spades)
:nconc (make-13-card-list suit))))
(defmethod make-13-card-list (suit)
(loop :for rank :in '(1 2 3 4 5 6 7 8 9 10 :jack :queen :king)
:for i :from 1
:collect (make-instance 'card :suit suit :rank rank
:value (min 10 (mod i 14)))))
(defmethod make-2-card-hand (deck)
(let ((hand (make-instance 'hand)))
(deal deck hand 2)
hand))
(defmethod print-deck (deck)
(loop :for card :in (deck-cards deck) :do (print-card card)))
(defmethod print-hand (hand)
(loop :for card :in (hand-cards hand) :do (print card)))
(defmethod print-object ((self card) stream)
(print-unreadable-object (self stream :identity t :type t)
(format stream "~A of ~A" (card-rank self) (card-suit self)))
self)
;; Adds n cards from the deck to a hand
(defmethod deal (deck hand n)
(with-slots ((deck-cards cards) (deck-count card-count)) deck
(loop :for i :from 1 :to n :do
(let ((card (nth (random deck-count) deck-cards)))
(with-slots ((hand-cards cards)) hand
(push card hand-cards)
(setf deck-cards (delete card deck-cards))
(decf deck-count))))))
;; Adds 1 card from the deck to a hand
(defmethod hit (deck hand)
(deal deck hand 1)
hand)
;; Returns a new hand with a the given card added
(defmethod add-card (hand card)
(make-instance 'hand :cards (cons card (hand-cards hand))))
;; Generate all possible hands from a given deck and a hand
(defmethod possible-hands (deck hand)
(loop :for card :in (deck-cards deck)
:collect (add-card hand card)))
;; Returns a hand's value giving aces a value of 1
(defmethod min-value (hand)
(loop :for card :in (hand-cards hand)
:sum (card-value card)))
;; Returns a hand's real value
;; Aces are valued 11 if the hand doesn't bust
(defmethod value (hand)
(let ((value 0) (found-ace NIL))
(loop :for card :in (hand-cards hand) :do
(when (= (card-value card) 1)
(setf found-ace t))
(incf value (card-value card)))
(when (and found-ace (<= value 11))
(incf value 10))
value))
;; Returns the probability of busting
(defmethod bust-prob (hand)
(let ((value (min-value hand)))
(cond ((<= value 11) 0)
((>= value 21) 1)
(t (/ (loop :for v :from 1 :to 10
:sum (if (> (+ value v) 21)
(- (if (= v 10) 16 4)
(loop :for card :in (hand-cards hand)
:sum (if (= (card-value card) v) 1 0)))
0))
(- 52 (length (hand-cards hand))))))))
;; Returns the probability of losing to the dealer
(defmethod lose-prob (pvalue dealer)
(let ((dvalue (value dealer)))
(cond ((> dvalue 21) 0)
((>= dvalue 17) (if (>= dvalue pvalue) 1 0))
(t (/ (loop :for card :in (make-13-card-list :no-suit)
:sum (lose-prob pvalue (add-card dealer card)))
13)))))
;; Play n games between the dealer and the player
(defun match (n)
(let ((dwins 0))
(loop :for i :from 1 :to n :do
(when (eq (game #'dealer-strategy #'player-strategy) :dealer-wins)
(incf dwins)))
(print (cons "Dealer wins" dwins))
(print (cons "Player wins" (- n dwins)))))
;; Play a game between a dealer and a player
(defun game (dealer-strategy player-strategy)
(let* ((deck (make-52-card-deck))
(dealer (play deck (make-2-card-hand deck) dealer-strategy))
(player (play deck (make-2-card-hand deck) player-strategy))
(pvalue (value player))
(dvalue (value dealer)))
(cond ((> pvalue 21) :dealer-wins)
((> dvalue 21) :player-wins)
((= pvalue dvalue) :dealer-wins)
((> pvalue dvalue) :player-wins)
(t :dealer-wins))))
;; Play a hand following a given strategy
(defun play (deck hand strategy)
(loop :while (and (<= (min-value hand) 21) (funcall strategy hand)) :do
(hit deck hand))
hand)
;; The default strategy used by the dealer in most casinos
(defun dealer-strategy (hand)
(< (value hand) 17))
;; The player asks for a card when the odds of busting are < 0.5
(defun player-strategy (hand)
(< (bust-prob hand) 0.5))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment