twentyone/
game.rs

1//! Game-related functions and structures, such as the dealer or hand value checking
2use crate::cards;
3
4/// Actions a player can perform
5pub enum PlayerAction {
6    Hit,
7    Stand,
8    DoubleDown,
9    Split,
10    /// Bet an amount of money
11    Bet(i32),
12    None,
13}
14
15/// Requests for the player from the dealer
16pub enum DealerRequest {
17    /// Request a bet from the player
18    Bet,
19    /// Request a player to play a hand
20    ///
21    /// # Arguments
22    ///
23    /// * `usize` - The index of the hand to play
24    Play(usize),
25    /// The dealer's up card
26    UpCard([char; 2]),
27    /// The dealer's hit card
28    HitCard([char; 2]),
29    /// The dealer's hand after they have finished playing
30    DealerHand(Vec<[char; 2]>),
31    /// The low card threshold was hit and a new shoe was created
32    LowCards,
33    /// An error with a returned PlayerAction
34    ///
35    /// # Arguments
36    ///
37    /// * `PlayerActionError` - More info on why the error occurred
38    Error(PlayerActionError),
39}
40
41/// Reason for a dealer being unable to perform an action
42pub enum PlayerActionError {
43    /// Not enough money for the requested action
44    ///
45    /// # Arguments
46    ///
47    /// * `usize` - The index of the affected hand, if applicable
48    /// * `PlayerAction` - The attempted action
49    NotEnoughMoney(usize, PlayerAction),
50    /// An unexpected action was returned
51    ///
52    /// # Arguments
53    ///
54    /// * `usize` - The index of the affected hand, if applicable
55    /// * `PlayerAction` - The unexpected action
56    UnexpectedAction(usize, PlayerAction),
57}
58
59/// Configure different aspects of the game
60pub struct GameConfig {
61    /// Whether the dealer should stand on soft 17 or hit
62    pub stand_soft_17: bool,
63    /// The multiplier for when a player gets a blackjack
64    pub blackjack_payout: f32,
65    /// Whether to allow splitting
66    pub splitting: bool,
67    /// Whether to allow doubling down
68    pub doubling_down: bool,
69    /// Whether to allow doubling down after splitting
70    pub double_after_split: bool,
71    /// The minimum player bet
72    pub min_bet: i32,
73    /// The maximum player bet
74    pub max_bet: i32,
75    /// How many decks to add to the new shoe if `auto_new_shoe` is enabled
76    pub shoe_deck_count: u8,
77    /// How many cards must be left in a deck before DealerRequest::LowCards is called.
78    /// If `auto_new_shoe` is enabled, the new shoe will be created when this number is reached.
79    pub low_cards_threshold: usize,
80}
81
82/// A default configuration for game settings.
83///
84/// Allows doubling down and splitting, stands on soft 17,
85/// pays out blackjacks 3 to 2, and allows doubling after splitting.
86///
87/// Creates a new 6-deck shoe when 52 or less cards are remaining.
88/// Minimum bet is 1 and maximum bet is `i32::MAX` (2,147,483,647)
89pub const DEFAULT_CONFIG: GameConfig = GameConfig {
90    stand_soft_17: true,
91    blackjack_payout: 1.5,
92    splitting: true,
93    doubling_down: true,
94    double_after_split: true,
95    min_bet: 1,
96    max_bet: i32::MAX,
97    shoe_deck_count: 6,
98    low_cards_threshold: 52,
99};
100
101/// Describes a blackjack dealer
102pub struct Dealer<'a> {
103    hand: Vec<[char; 2]>,
104    shoe: Vec<[char; 2]>,
105    players: Vec<Player>,
106    config: GameConfig,
107    callback: &'a dyn Fn(DealerRequest, Option<&Player>, &Dealer) -> PlayerAction,
108}
109
110/// Describes a blackjack player
111pub struct Player {
112    money: i32,
113    hands: Vec<Vec<[char; 2]>>,
114}
115
116impl Dealer<'_> {
117    /// Returns a new Dealer
118    ///
119    /// # Arguments
120    ///
121    /// * `shoe` - The shoe (or deck) to draw from
122    /// * `callback` - A function to handle player turns
123    ///
124    /// `callback` is passed a `DealerRequest` and an `Option<&Player>`.
125    /// The option will always have a player if it applies to the event (eg. betting),
126    /// but will not have a player for dealer updates (eg. up card, dealer hits).
127    ///
128    /// # Callback
129    ///
130    /// The callback function will always return a `PlayerAction`,
131    /// but it should return different things based on the `DealerRequest`:
132    ///
133    /// | `DealerRequest`                             | `PlayerAction`                                                                                       |
134    /// |---------------------------------------------|------------------------------------------------------------------------------------------------------|
135    /// | `DealerRequest::Bet`                        | `PlayerAction::Bet(i32)`                                                                             |
136    /// | `DealerRequest::Play`                       | One of `PlayerAction::Hit`, `PlayerAction::Stand`, `PlayerAction::DoubleDown`, `PlayerAction::Split` |
137    /// | `DealerRequest::Error(PlayerActionError)`   | `PlayerAction::None` and handle the returned error                                                   |
138    /// | `DealerRequest::UpCard([char; 2])`          | `PlayerAction::None`                                                                                 |
139    /// | `DealerRequest::HitCard([char; 2])`         | `PlayerAction::None`                                                                                 |
140    /// | `DealerRequest::DealerHand(Vec<[char; 2]>)` | `PlayerAction::None`                                                                                 |
141    ///
142    /// If an unexpected return value is given, the callback will be called
143    ///  again with a request of `DealerAction::Error(PlayerActionError::UnexpectedAction)`
144    /// along with the index of the affected hand (if applicable)
145    /// and the request that was invalid.
146    ///
147    /// After an error is provided, the dealer will request the same action that
148    /// caused the error. If nothing changes, the dealer will infinitely loop.
149    ///
150    /// # Examples
151    ///
152    /// Example code is available in the [Quick Start](../index.html#quick-start) from the main page.
153    pub fn new<'a>(
154        shoe: Vec<[char; 2]>,
155        game_config: GameConfig,
156        callback: &'a dyn Fn(DealerRequest, Option<&Player>, &Dealer) -> PlayerAction,
157    ) -> Dealer {
158        Dealer {
159            hand: Vec::new(),
160            shoe: shoe,
161            players: Vec::new(),
162            config: game_config,
163            callback: callback,
164        }
165    }
166
167    /// Returns a reference to the dealer's hand
168    pub fn hand(&self) -> &Vec<[char; 2]> {
169        &self.hand
170    }
171
172    /// Returns a reference to the dealer's shoe
173    pub fn shoe(&self) -> &Vec<[char; 2]> {
174        &self.shoe
175    }
176
177    /// Returns a reference to the dealer's players
178    pub fn players(&self) -> &Vec<Player> {
179        &self.players
180    }
181    /// Returns a mutable reference to the dealer's hand
182    pub fn hand_mut(&mut self) -> &mut Vec<[char; 2]> {
183        &mut self.hand
184    }
185
186    /// Returns a mutable reference to the dealer's shoe
187    pub fn shoe_mut(&mut self) -> &mut Vec<[char; 2]> {
188        &mut self.shoe
189    }
190
191    /// Returns a mutable reference to the dealer's players
192    pub fn players_mut(&mut self) -> &mut Vec<Player> {
193        &mut self.players
194    }
195
196    /// Clear the dealer's and all players' hands
197    pub fn clear_table(&mut self) {
198        self.hand.clear();
199        for player in self.players.iter_mut() {
200            player.hands_mut().clear();
201            player.hands_mut().push(Vec::new());
202        }
203    }
204
205    /// Deal a hand to all players
206    pub fn deal_hands(&mut self) {
207        for _ in 0..2 {
208            cards::hit_card(&mut self.shoe, &mut self.hand);
209            for player in self.players.iter_mut() {
210                cards::hit_card(&mut self.shoe, &mut player.hands_mut()[0]);
211            }
212        }
213    }
214
215    /// Hit a card to a player
216    ///
217    /// # Arguments
218    ///
219    /// * `player` - The index of the player to hit
220    /// * `hand` - The index of the player's hand (used for split hands)
221    pub fn hit_card(&mut self, player: usize, hand: usize) {
222        cards::hit_card(&mut self.shoe, &mut self.players[player].hands[hand]);
223    }
224
225    /// Play a round of blackjack
226    ///
227    /// Calls `callback` to get player bets/actions.
228    ///
229    /// # Arguments
230    ///
231    /// * `clear_table` - Clear the table at the beginning of the round
232    pub fn play_round(&mut self, clear_table: bool) {
233        if clear_table {
234            self.clear_table();
235        }
236
237        let mut player_bets: Vec<i32> = Vec::new();
238
239        // Get bets
240        for i in 0..self.players.len() {
241            loop {
242                let bet = (self.callback)(DealerRequest::Bet, Some(&self.players[i]), &self);
243                if let PlayerAction::Bet(amount) = bet {
244                    // Check if player can afford bet and if it is within limits
245                    if self.players[i].money() >= &amount {
246                        if self.config.min_bet <= amount && amount <= self.config.max_bet {
247                            player_bets.push(amount);
248                            *self.players[i].money_mut() -= amount;
249                            break;
250                        } else {
251                            let error = PlayerActionError::UnexpectedAction(0, bet);
252                            (self.callback)(
253                                DealerRequest::Error(error),
254                                Some(&self.players[i]),
255                                &self,
256                            );
257                        }
258                    } else {
259                        let error = PlayerActionError::NotEnoughMoney(0, bet);
260                        (self.callback)(DealerRequest::Error(error), Some(&self.players[i]), &self);
261                    }
262                } else {
263                    let error = PlayerActionError::UnexpectedAction(0, bet);
264                    (self.callback)(DealerRequest::Error(error), Some(&self.players[i]), &self);
265                }
266            }
267        }
268
269        // Deal hands
270        self.deal_hands();
271
272        // Send dealer up card
273        (self.callback)(DealerRequest::UpCard(self.hand[1]), None, &self);
274
275        // Get player actions
276        for i in 0..self.players.len() {
277            let mut can_double: Vec<bool>;
278            let mut can_split: bool;
279            if self.config.doubling_down {
280                // Check if player has enough money to double down
281                can_double = vec![self.players[i].money() >= &player_bets[i]];
282            } else {
283                can_double = vec![false];
284            }
285            if self.config.splitting {
286                // Check if player cards are valid for a split and if player has enough money
287                can_split = crate::game::can_split(&self.players[i].hands()[0]) && can_double[0];
288            } else {
289                can_split = false;
290            }
291
292            // Keep track of stood hands
293            let mut stood = vec![false];
294
295            // Keep track of original bet
296            // Used when doubling after splitting
297            let original_bet = player_bets[i];
298
299            // Active hand
300            let mut hand_count = 1;
301            let mut j = 0;
302            // Get actions from each hand, one at a time
303            // Using a loop and incrementing j manually because for loops would not recheck
304            // length of player.hands() after a split
305            loop {
306                // Check for low cards
307                if self.shoe.len() <= self.config.low_cards_threshold {
308                    (self.callback)(DealerRequest::LowCards, None, &self);
309                    // Create a new shoe if the option is enabled
310                    self.shoe = cards::create_shoe(self.config.shoe_deck_count);
311                    cards::shuffle_deck(&mut self.shoe);
312                }
313
314                if j >= hand_count {
315                    break;
316                }
317                while !stood[j] {
318                    let action =
319                        (self.callback)(DealerRequest::Play(j), Some(&self.players[i]), &self);
320                    match action {
321                        PlayerAction::Hit => {
322                            self.hit_card(i, j);
323                            can_double[j] = false;
324                        }
325                        PlayerAction::Stand => stood[j] = true,
326                        PlayerAction::DoubleDown => {
327                            if can_double[j] {
328                                *self.players[i].money_mut() -= original_bet;
329                                player_bets[i] += original_bet;
330                                stood[j] = true;
331                                self.hit_card(i, j);
332                                can_double[j] = false;
333                            } else {
334                                (self.callback)(
335                                    DealerRequest::Error(PlayerActionError::UnexpectedAction(
336                                        j, action,
337                                    )),
338                                    Some(&self.players[i]),
339                                    &self,
340                                );
341                            }
342                        }
343                        PlayerAction::Split => {
344                            if can_split {
345                                *self.players[i].money_mut() -= original_bet;
346                                player_bets[i] += original_bet;
347                                self.players[i].hands_mut().push(Vec::new());
348                                stood.push(false);
349                                if self.config.double_after_split && self.config.doubling_down {
350                                    can_double.push(true);
351                                } else {
352                                    can_double[0] = false;
353                                    can_double.push(false);
354                                }
355                                // "Draw" card from first hand and place it into second
356                                let card = cards::draw_card(
357                                    self.players[i].hands_mut().get_mut(0).unwrap(),
358                                );
359                                self.players[i].hands_mut()[1].push(card.unwrap());
360                                // Hit another card to each hand
361                                self.hit_card(i, 0);
362                                self.hit_card(i, 1);
363                                hand_count = 2;
364                                can_split = false;
365                            } else {
366                                (self.callback)(
367                                    DealerRequest::Error(PlayerActionError::UnexpectedAction(
368                                        j, action,
369                                    )),
370                                    Some(&self.players[i]),
371                                    &self,
372                                );
373                            }
374                        }
375                        _ => {
376                            let error = PlayerActionError::UnexpectedAction(j, action);
377                            (self.callback)(
378                                DealerRequest::Error(error),
379                                Some(&self.players[i]),
380                                &self,
381                            );
382                        }
383                    }
384
385                    // Check if the hand is busted
386                    if get_hand_value(&self.players[i].hands()[j], true) > 21 {
387                        stood[j] = true;
388                    }
389                }
390                j += 1;
391            }
392        }
393
394        // Dealer play
395        let mut busted = false;
396        loop {
397            let hand_value = get_hand_value(&self.hand, true);
398            if hand_value > 21 {
399                busted = true;
400                break;
401            } else if hand_value >= 17 {
402                if self.config.stand_soft_17 {
403                    break;
404                // Check if hand is exactly 17 contains an ace
405                } else if hand_value == 17 && self.hand.iter().any(|&i| i[1] == 'A') {
406                    // Check if ace is acting as an 11 or a 1
407                    if hand_value == get_hand_value(&self.hand, false) {
408                        let card = cards::draw_card(&mut self.shoe).unwrap();
409                        self.hand.push(card);
410                        (self.callback)(DealerRequest::HitCard(card), None, &self);
411                    } else {
412                        break;
413                    }
414                }
415                break;
416            } else {
417                let card = cards::draw_card(&mut self.shoe).unwrap();
418                self.hand.push(card);
419                (self.callback)(DealerRequest::HitCard(card), None, &self);
420            }
421        }
422
423        // Pay out winners
424        let dealer_hand_value = get_hand_value(&self.hand, true);
425        for i in 0..self.players.len() {
426            for j in 0..self.players[i].hands().len() {
427                let hand_value = get_hand_value(&self.players[i].hands()[j], true);
428                // Check if player busted
429                if hand_value > 21 {
430                    continue;
431                }
432
433                // Pay out normal amount if player did not bust, did not have blackjack,
434                // and beat dealer/dealer busted
435                if hand_value < 21 && (busted || hand_value > dealer_hand_value) {
436                    self.players[i].money +=
437                        player_bets[i] * 2 / self.players[i].hands().len() as i32;
438                } else if hand_value == 21 && (busted || hand_value > dealer_hand_value) {
439                    // Check if player had blackjack
440                    if self.players[i].hands()[j].len() == 2 {
441                        // Make sure dealer didn't have blackjack
442                        if dealer_hand_value == 21 && self.hand.len() == 2 {
443                            // Push, refund player
444                            self.players[i].money += player_bets[i];
445                        } else {
446                            // Pay out 3 to 2
447                            self.players[i].money += player_bets[i]
448                                + (player_bets[i] as f32 * self.config.blackjack_payout) as i32;
449                        }
450                    } else {
451                        self.players[i].money +=
452                            player_bets[i] * 2 / self.players[i].hands().len() as i32;
453                    }
454                // Push, refund player
455                } else if hand_value == dealer_hand_value {
456                    self.players[i].money += player_bets[i];
457                }
458            }
459        }
460
461        (self.callback)(DealerRequest::DealerHand(self.hand.clone()), None, &self);
462    }
463}
464
465impl Player {
466    /// Returns a new Player
467    ///
468    /// # Arguments
469    ///
470    /// * `money` - The amount of money to give the player
471    /// * `hands` - A Vector of hands (`Vec<[char; 2]>`)
472    ///
473    /// # Examples
474    ///
475    /// ```
476    /// use twentyone::game::Player;
477    /// let player = Player::new(100);
478    /// ```
479    pub fn new(money: i32) -> Player {
480        Player {
481            money: money,
482            hands: vec![Vec::new()],
483        }
484    }
485
486    /// Returns a reference to the player's money
487    pub fn money(&self) -> &i32 {
488        &self.money
489    }
490
491    /// Returns a reference to the player's hands
492    pub fn hands(&self) -> &Vec<Vec<[char; 2]>> {
493        &self.hands
494    }
495
496    /// Returns a mutable reference to the player's money
497    pub fn money_mut(&mut self) -> &mut i32 {
498        &mut self.money
499    }
500
501    /// Returns a mutable reference to the player's hands
502    pub fn hands_mut(&mut self) -> &mut Vec<Vec<[char; 2]>> {
503        &mut self.hands
504    }
505}
506
507/// Returns the value of a hand
508///
509/// # Arguments
510///
511/// * `hand` - The hand to get the value of
512///
513/// # Examples
514///
515/// ```
516/// use twentyone::{cards, game};
517/// let mut deck = cards::create_deck();
518/// cards::shuffle_deck(&mut deck);
519/// let mut hand = Vec::new();
520/// cards::hit_card(&mut deck, &mut hand);
521/// cards::hit_card(&mut deck, &mut hand);
522/// println!("{}", game::get_hand_value(&hand, true));
523/// ```
524pub fn get_hand_value(hand: &Vec<[char; 2]>, auto_aces: bool) -> u8 {
525    let mut value = 0;
526    let mut aces = 0;
527    for i in hand.iter() {
528        value += match i[1] {
529            '2' => 2,
530            '3' => 3,
531            '4' => 4,
532            '5' => 5,
533            '6' => 6,
534            '7' => 7,
535            '8' => 8,
536            '9' => 9,
537            'T' | 'J' | 'Q' | 'K' => 10,
538            'A' => {
539                aces += 1;
540                0
541            }
542            _ => 0,
543        }
544    }
545    // Add aces
546    if auto_aces {
547        // Check if an ace being 11 would bust the hand
548        for _ in 0..aces {
549            if value + 11 > 21 {
550                value += 1;
551            } else {
552                value += 11;
553            }
554        }
555    } else {
556        value += 11 * aces;
557    }
558    value
559}
560
561/// Returns whether a hand is able to split
562///
563/// # Arguments
564///
565/// * `hand` - The hand to be split
566///
567/// # Examples
568///
569/// ```
570/// use twentyone::{cards, game};
571/// let mut deck = cards::create_deck();
572/// cards::shuffle_deck(&mut deck);
573/// let mut hand = Vec::new();
574/// cards::hit_card(&mut deck, &mut hand);
575/// cards::hit_card(&mut deck, &mut hand);
576/// println!("{}", game::can_split(&hand));
577/// ```
578pub fn can_split(hand: &Vec<[char; 2]>) -> bool {
579    if hand.len() != 2 {
580        return false;
581    }
582
583    hand[0][1] == hand[1][1]
584}