;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Author: Steve Lombardi ; http://www.stephenlombardi.com/ ; Date: August 13th, 2008 ; Description: ; Generates the probability of winning ; a game of blackjack ; ; MIT license ; I make no garuntees that this software ; doesn't become self aware and take over ; the world. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The hand is stored as the value of the cards added together ; (with aces counting as 11), as well as the number of aces ; in the hand. It is important to keep track of the number of ; aces so that you can count the appropriate aces as having a ; value of one if the hand goes over 21. ; Special note: don't use this function to determine if a hand goes ; over 21; if it's not possible to reduce a hand to under 21, the ; function simply returns 21. Use bust? to test if a hand is over. (defun max-hand-value (value aces) "converts the appropriate aces to have a value of one if the hand would otherwise bust" (- value (* 10 (min (max (truncate (/ (- value 12) 10)) 0) aces ) )) ) (defun bust? (value aces) "returns true if the value is over 21 (accounting for aces)" (if (> (- value (* aces 10)) 21) t nil ) ) (defun outcome (dealervalue dealeraces playervalue playeraces) "returns the winner of the blackjack game - 1.0 if the player wins, 0.0 if the dealer wins, 0.5 for a push" (let ( (pval (max-hand-value playervalue playeraces)) (dval (max-hand-value dealervalue dealeraces)) ) (if (= pval dval) 0.5 (if (> pval dval) 1.0 0.0 ) ) ) ) (defun dealer-turn (dealervalue dealeraces playervalue playeraces) "checks to see if dealer has less than 17 or 'soft-17' (A,6), if so hit, otherwise stay" (cond ((and (= dealeraces 0) (<= dealervalue 16)) (dealer-hit dealervalue dealeraces playervalue playeraces) ) ((and (> dealeraces 0) (<= (max-hand-value dealervalue dealeraces) 17)) (dealer-hit dealervalue dealeraces playervalue playeraces) ) (t (dealer-stand dealervalue dealeraces playervalue playeraces) ) ) ) (defun dealer-hit (dealervalue dealeraces playervalue playeraces) "takes the average of the possiblities of winning for each card that the dealer could get" (/ (loop for cardval in '(2 3 4 5 6 7 8 9 10 10 10 11) as isace in '(0 0 0 0 0 0 0 0 0 0 0 1) sum (dealer-turn (+ dealervalue cardval) (+ dealeraces isace) playervalue playeraces) ) 13) ) (defun dealer-stand (dealervalue dealeraces playervalue playeraces) "returns the probability of the player winning if the dealer accepts no more cards" (if (bust? dealervalue dealeraces) 1.0 (outcome dealervalue dealeraces playervalue playeraces) ) ) (defun player-turn (dealervalue dealeraces playervalue playeraces) "returns the probability of the player winning with the current hand" (cond ((bust? playervalue playeraces) 0.0 ) (t (max (player-hit dealervalue dealeraces playervalue playeraces) (player-stand dealervalue dealeraces playervalue playeraces) ) ) ) ) (defun player-hit (dealervalue dealeraces playervalue playeraces) "returns the probability if the player accepts an additional card to their hand" (/ (loop for cardval in '(2 3 4 5 6 7 8 9 10 10 10 11) as isace in '(0 0 0 0 0 0 0 0 0 0 0 1) sum (player-turn dealervalue dealeraces (+ playervalue cardval) (+ playeraces isace)) ) 13) ) (defun player-stand (dealervalue dealeraces playervalue playeraces) "returns the probability of the player winning if the player does not accept additional cards" (dealer-turn dealervalue dealeraces playervalue playeraces) ) (defun probability-table () "produces a table which gives the optimal move for any given initial player hand value and dealer card" (format t " ~0,8@T 2 3 4 5 6 7 8 9 J A~%") (loop for player in '(17 16 15 14 13 12 20 19 18 17 16) as playeraces in '(0 0 0 0 0 0 1 1 1 1 1) do (format t "~a~0,8@T" player) (loop for dealer in '(2 3 4 5 6 7 8 9 10 11) as dealeraces in '(0 0 0 0 0 0 0 0 0 1) do (if (> (player-hit dealer dealeraces player playeraces) (player-stand dealer dealeraces player playeraces)) (format t "H ") (format t "S ") ) ) (format t "~%") ) ) (defun probability-table2 () "produces a table which gives the probability of winning for a hit or stand for any given initial player hand value and dealer card" (format t "player~0,8@Tdealer~%") (loop for player in '(17 16 15 14 13 12 20 19 18 17 16) as playeraces in '(0 0 0 0 0 0 1 1 1 1 1) do (loop for dealer in '(2 3 4 5 6 7 8 9 10 11) as dealeraces in '(0 0 0 0 0 0 0 0 0 1) do (format t "~a~0,8@T" player) (format t "~a~0,8@T" dealer) (format t "H: ~0,5f S: ~0,5f" (player-hit dealer dealeraces player playeraces) (player-stand dealer dealeraces player playeraces)) (format t "~%") ) ) ) ; Example of use: ; >(player-hit 10 0 16 0) ; 0.19261277031149104 ; >(player-stand 10 0 16 0) ; 0.18343373756546313 ; >(probability-table) ; 2 3 4 5 6 7 8 9 J A ; 17 S S S S S S S S S H ; 16 S S S S S H H H H H ; 15 S S S S S H H H H H ; 14 S S S S S H H H H H ; 13 H S S S S H H H H H ; 12 H H H S S H H H H H ; 20 S S S S S S S S S S ; 19 S S S S S S S S S S ; 18 S S S S ... ; >(probability-table2) ; player dealer ; 17 2 H: .17970 S: .30711 ; 17 3 H: .18349 S: .32520 ; 17 4 H: .18737 S: .34428 ; 17 5 H: .19161 S: .36357 ; 17 6 H: .19302 S: .38865 ; 17 7 H: .21852 S: .36688 ; 17 8 H: .21233 S: .27054 ; 17 9 H: .19594 S: .25250 ; 17 10 H: .17458 S: .23441 ; 17 11 H: .13365 S: .12780 ; ...