Last active
February 19, 2020 14:05
-
-
Save Tempate/2a05c584e1b29c9c609c90856c566128 to your computer and use it in GitHub Desktop.
This file contains 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
;;;; 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