|
| 1 | +(in-package #:tpd2.game.blackjack) |
| 2 | + |
| 3 | +(defparameter +one-deck+ |
| 4 | + (loop for s in +suits+ append (loop for i below +cards-per-suit+ collect (make-card :suit s :value i)))) |
| 5 | + |
| 6 | +(defparameter +decks-per-shoe+ 2) |
| 7 | +(defparameter +reshuffle-proportion+ 1/6) |
| 8 | + |
| 9 | +(defstruct hand |
| 10 | + cards |
| 11 | + (stake 0)) |
| 12 | + |
| 13 | +(defgame blackjack (coin-game) |
| 14 | + (shoe) |
| 15 | + (defplayer () |
| 16 | + ((hands nil))) |
| 17 | + (:game-name "Blackjack") |
| 18 | + (:advertised nil)) |
| 19 | + |
| 20 | +(my-defun blackjack reshuffle () |
| 21 | + (my announce :shuffle) |
| 22 | + (setf (my shoe) (random-shuffle (loop repeat +decks-per-shoe+ append +one-deck+)))) |
| 23 | + |
| 24 | +(my-defun blackjack take-card () |
| 25 | + (pop (my shoe))) |
| 26 | + |
| 27 | +(my-defun blackjack take-cards (&optional (num 2)) |
| 28 | + (loop repeat num collect (my take-card))) |
| 29 | + |
| 30 | +(defun cards-value (cards) |
| 31 | + (loop for card in cards |
| 32 | + for c = (card-value card) |
| 33 | + summing (min 10 (1+ c)))) |
| 34 | + |
| 35 | +(my-defun hand value () |
| 36 | + (cards-value (my cards))) |
| 37 | + |
| 38 | +(my-defun hand bust () |
| 39 | + (< 21 |
| 40 | + (my value))) |
| 41 | + |
| 42 | +(defun cards-best-value (cards) |
| 43 | + (let ((other 0) (aces 0)) |
| 44 | + (loop for card in cards |
| 45 | + for c = (card-value card) |
| 46 | + do |
| 47 | + (if (zerop c) |
| 48 | + (incf aces) |
| 49 | + (incf other (min 10 (1+ c))))) |
| 50 | + (loop for ac in |
| 51 | + (let ((min aces)) |
| 52 | + (list* min (loop repeat aces collect (incf min 9)))) |
| 53 | + for val = (+ ac other) |
| 54 | + when (>= 21 val) |
| 55 | + maximizing val))) |
| 56 | + |
| 57 | +(my-defun hand blackjack () |
| 58 | + (= 21 (cards-best-value (my cards)))) |
| 59 | + |
| 60 | +(my-defun blackjack 'play () |
| 61 | + (with-its-type (p blackjack-player) |
| 62 | + (with-game |
| 63 | + (loop |
| 64 | + |
| 65 | + (when (> (* +reshuffle-proportion+ +decks-per-shoe+ +cards-per-suit+ (length +suits+)) (length (my shoe))) |
| 66 | + (my reshuffle) |
| 67 | + (my new-state)) |
| 68 | + |
| 69 | + (with-join-spawn/cc () |
| 70 | + (loop for p in (my players) |
| 71 | + do |
| 72 | + (let-current-values (p) |
| 73 | + (spawn/cc () |
| 74 | + (setf (its hands p) (list (make-hand :stake (my move :select-new-stake p `(:integer 0 ,(max 1 (its coins p))))))))))) |
| 75 | + |
| 76 | + (loop for p in (my players) |
| 77 | + do (setf (its hands p) (delete-if (lambda (h) (zerop (hand-stake h))) (its hands p)))) |
| 78 | + |
| 79 | + (loop for p in (my players) |
| 80 | + do (loop for h in (its hands p) |
| 81 | + do (setf (hand-cards h) (my take-cards 2)))) |
| 82 | + |
| 83 | + (loop for p in (my players) |
| 84 | + do (loop for h in (its hands p) do |
| 85 | + (loop |
| 86 | + while (my move :hit p :boolean) |
| 87 | + do (push (my take-card) (hand-cards h)) |
| 88 | + (when (hand-blackjack h) |
| 89 | + (my announce :blackjack :player p :hand h) |
| 90 | + (return)) |
| 91 | + (when (hand-bust h) |
| 92 | + (my announce :bust :player p :hand h) |
| 93 | + (return))))) |
| 94 | + |
| 95 | + (let (hand) |
| 96 | + (loop while (> 17 (cards-value hand)) |
| 97 | + do (let ((c (my take-card))) |
| 98 | + (debug-assert c (hand me (my shoe))) |
| 99 | + (push c hand) |
| 100 | + (my announce :dealer :card c))) |
| 101 | + (let ((val (cards-best-value hand))) |
| 102 | + (loop for p in (my players) |
| 103 | + do (loop for h in (its hands p) |
| 104 | + for pval = (cards-best-value (hand-cards h)) |
| 105 | + do (its give-coins p |
| 106 | + (cond ((or (zerop pval) (> val pval)) |
| 107 | + (- (hand-stake h))) |
| 108 | + ((= pval val) |
| 109 | + 0) |
| 110 | + ((= 21 pval) |
| 111 | + (* 3/2 (hand-stake h))) |
| 112 | + (t |
| 113 | + (hand-stake h)))))))))))) |
| 114 | + |
| 115 | +(my-defun hand 'object-to-ml () |
| 116 | + (<div :class "blackjack-hand" |
| 117 | + (loop for c in (my cards) |
| 118 | + do (output-object-to-ml c)) |
| 119 | + (<p :class "stake" (my stake)) |
| 120 | + (when (my cards) |
| 121 | + (<p :class "value" "Value " (cards-best-value (my cards))) |
| 122 | + (cond ((my bust) |
| 123 | + (<p :class "bust" "Bust.")) |
| 124 | + ((my blackjack) |
| 125 | + (<p :class "blackjack" "Blackjack!")))))) |
| 126 | + |
| 127 | +(my-defun blackjack-player 'object-to-ml () |
| 128 | + (<div :class "blackjack-player" |
| 129 | + (call-next-method) |
| 130 | + (loop for h in (my hands) |
| 131 | + do (output-object-to-ml h)))) |
0 commit comments