1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
//! Game-related functions and structures, such as the dealer or hand value checking
use crate::cards;

/// Actions a player can perform
pub enum PlayerAction {
    Hit,
    Stand,
    DoubleDown,
    Split,
    /// Bet an amount of money
    Bet(i32),
    None,
}

/// Requests for the player from the dealer
pub enum DealerRequest {
    /// Request a bet from the player
    Bet,
    /// Request a player to play a hand
    ///
    /// # Arguments
    ///
    /// * `usize` - The index of the hand to play
    Play(usize),
    /// The dealer's up card
    UpCard([char; 2]),
    /// The dealer's hit card
    HitCard([char; 2]),
    /// The dealer's hand after they have finished playing
    DealerHand(Vec<[char; 2]>),
    /// The low card threshold was hit and a new shoe was created
    LowCards,
    /// An error with a returned PlayerAction
    ///
    /// # Arguments
    ///
    /// * `PlayerActionError` - More info on why the error occurred
    Error(PlayerActionError),
}

/// Reason for a dealer being unable to perform an action
pub enum PlayerActionError {
    /// Not enough money for the requested action
    ///
    /// # Arguments
    ///
    /// * `usize` - The index of the affected hand, if applicable
    /// * `PlayerAction` - The attempted action
    NotEnoughMoney(usize, PlayerAction),
    /// An unexpected action was returned
    ///
    /// # Arguments
    ///
    /// * `usize` - The index of the affected hand, if applicable
    /// * `PlayerAction` - The unexpected action
    UnexpectedAction(usize, PlayerAction),
}

/// Configure different aspects of the game
pub struct GameConfig {
    /// Whether the dealer should stand on soft 17 or hit
    pub stand_soft_17: bool,
    /// The multiplier for when a player gets a blackjack
    pub blackjack_payout: f32,
    /// Whether to allow splitting
    pub splitting: bool,
    /// Whether to allow doubling down
    pub doubling_down: bool,
    /// Whether to allow doubling down after splitting
    pub double_after_split: bool,
    /// The minimum player bet
    pub min_bet: i32,
    /// The maximum player bet
    pub max_bet: i32,
    /// How many decks to add to the new shoe if `auto_new_shoe` is enabled
    pub shoe_deck_count: u8,
    /// How many cards must be left in a deck before DealerRequest::LowCards is called.
    /// If `auto_new_shoe` is enabled, the new shoe will be created when this number is reached.
    pub low_cards_threshold: usize,
}

/// A default configuration for game settings.
///
/// Allows doubling down and splitting, stands on soft 17,
/// pays out blackjacks 3 to 2, and allows doubling after splitting.
///
/// Creates a new 6-deck shoe when 52 or less cards are remaining.
/// Minimum bet is 1 and maximum bet is `i32::MAX` (2,147,483,647)
pub const DEFAULT_CONFIG: GameConfig = GameConfig {
    stand_soft_17: true,
    blackjack_payout: 1.5,
    splitting: true,
    doubling_down: true,
    double_after_split: true,
    min_bet: 1,
    max_bet: i32::MAX,
    shoe_deck_count: 6,
    low_cards_threshold: 52,
};

/// Describes a blackjack dealer
pub struct Dealer<'a> {
    hand: Vec<[char; 2]>,
    shoe: Vec<[char; 2]>,
    players: Vec<Player>,
    config: GameConfig,
    callback: &'a dyn Fn(DealerRequest, Option<&Player>, &Dealer) -> PlayerAction,
}

/// Describes a blackjack player
pub struct Player {
    money: i32,
    hands: Vec<Vec<[char; 2]>>,
}

impl Dealer<'_> {
    /// Returns a new Dealer
    ///
    /// # Arguments
    ///
    /// * `shoe` - The shoe (or deck) to draw from
    /// * `callback` - A function to handle player turns
    ///
    /// `callback` is passed a `DealerRequest` and an `Option<&Player>`.
    /// The option will always have a player if it applies to the event (eg. betting),
    /// but will not have a player for dealer updates (eg. up card, dealer hits).
    ///
    /// # Callback
    ///
    /// The callback function will always return a `PlayerAction`,
    /// but it should return different things based on the `DealerRequest`:
    ///
    /// | `DealerRequest`                             | `PlayerAction`                                                                                       |
    /// |---------------------------------------------|------------------------------------------------------------------------------------------------------|
    /// | `DealerRequest::Bet`                        | `PlayerAction::Bet(i32)`                                                                             |
    /// | `DealerRequest::Play`                       | One of `PlayerAction::Hit`, `PlayerAction::Stand`, `PlayerAction::DoubleDown`, `PlayerAction::Split` |
    /// | `DealerRequest::Error(PlayerActionError)`   | `PlayerAction::None` and handle the returned error                                                   |
    /// | `DealerRequest::UpCard([char; 2])`          | `PlayerAction::None`                                                                                 |
    /// | `DealerRequest::HitCard([char; 2])`         | `PlayerAction::None`                                                                                 |
    /// | `DealerRequest::DealerHand(Vec<[char; 2]>)` | `PlayerAction::None`                                                                                 |
    ///
    /// If an unexpected return value is given, the callback will be called
    ///  again with a request of `DealerAction::Error(PlayerActionError::UnexpectedAction)`
    /// along with the index of the affected hand (if applicable)
    /// and the request that was invalid.
    ///
    /// After an error is provided, the dealer will request the same action that
    /// caused the error. If nothing changes, the dealer will infinitely loop.
    ///
    /// # Examples
    ///
    /// Example code is available in the [Quick Start](../index.html#quick-start) from the main page.
    pub fn new<'a>(
        shoe: Vec<[char; 2]>,
        game_config: GameConfig,
        callback: &'a dyn Fn(DealerRequest, Option<&Player>, &Dealer) -> PlayerAction,
    ) -> Dealer {
        Dealer {
            hand: Vec::new(),
            shoe: shoe,
            players: Vec::new(),
            config: game_config,
            callback: callback,
        }
    }

    /// Returns a reference to the dealer's hand
    pub fn hand(&self) -> &Vec<[char; 2]> {
        &self.hand
    }

    /// Returns a reference to the dealer's shoe
    pub fn shoe(&self) -> &Vec<[char; 2]> {
        &self.shoe
    }

    /// Returns a reference to the dealer's players
    pub fn players(&self) -> &Vec<Player> {
        &self.players
    }
    /// Returns a mutable reference to the dealer's hand
    pub fn hand_mut(&mut self) -> &mut Vec<[char; 2]> {
        &mut self.hand
    }

    /// Returns a mutable reference to the dealer's shoe
    pub fn shoe_mut(&mut self) -> &mut Vec<[char; 2]> {
        &mut self.shoe
    }

    /// Returns a mutable reference to the dealer's players
    pub fn players_mut(&mut self) -> &mut Vec<Player> {
        &mut self.players
    }

    /// Clear the dealer's and all players' hands
    pub fn clear_table(&mut self) {
        self.hand.clear();
        for player in self.players.iter_mut() {
            player.hands_mut().clear();
            player.hands_mut().push(Vec::new());
        }
    }

    /// Deal a hand to all players
    pub fn deal_hands(&mut self) {
        for _ in 0..2 {
            cards::hit_card(&mut self.shoe, &mut self.hand);
            for player in self.players.iter_mut() {
                cards::hit_card(&mut self.shoe, &mut player.hands_mut()[0]);
            }
        }
    }

    /// Hit a card to a player
    ///
    /// # Arguments
    ///
    /// * `player` - The index of the player to hit
    /// * `hand` - The index of the player's hand (used for split hands)
    pub fn hit_card(&mut self, player: usize, hand: usize) {
        cards::hit_card(&mut self.shoe, &mut self.players[player].hands[hand]);
    }

    /// Play a round of blackjack
    ///
    /// Calls `callback` to get player bets/actions.
    ///
    /// # Arguments
    ///
    /// * `clear_table` - Clear the table at the beginning of the round
    pub fn play_round(&mut self, clear_table: bool) {
        if clear_table {
            self.clear_table();
        }

        let mut player_bets: Vec<i32> = Vec::new();

        // Get bets
        for i in 0..self.players.len() {
            loop {
                let bet = (self.callback)(DealerRequest::Bet, Some(&self.players[i]), &self);
                if let PlayerAction::Bet(amount) = bet {
                    // Check if player can afford bet and if it is within limits
                    if self.players[i].money() >= &amount {
                        if self.config.min_bet <= amount && amount <= self.config.max_bet {
                            player_bets.push(amount);
                            *self.players[i].money_mut() -= amount;
                            break;
                        } else {
                            let error = PlayerActionError::UnexpectedAction(0, bet);
                            (self.callback)(
                                DealerRequest::Error(error),
                                Some(&self.players[i]),
                                &self,
                            );
                        }
                    } else {
                        let error = PlayerActionError::NotEnoughMoney(0, bet);
                        (self.callback)(DealerRequest::Error(error), Some(&self.players[i]), &self);
                    }
                } else {
                    let error = PlayerActionError::UnexpectedAction(0, bet);
                    (self.callback)(DealerRequest::Error(error), Some(&self.players[i]), &self);
                }
            }
        }

        // Deal hands
        self.deal_hands();

        // Send dealer up card
        (self.callback)(DealerRequest::UpCard(self.hand[1]), None, &self);

        // Get player actions
        for i in 0..self.players.len() {
            let mut can_double: Vec<bool>;
            let mut can_split: bool;
            if self.config.doubling_down {
                // Check if player has enough money to double down
                can_double = vec![self.players[i].money() >= &player_bets[i]];
            } else {
                can_double = vec![false];
            }
            if self.config.splitting {
                // Check if player cards are valid for a split and if player has enough money
                can_split = crate::game::can_split(&self.players[i].hands()[0]) && can_double[0];
            } else {
                can_split = false;
            }

            // Keep track of stood hands
            let mut stood = vec![false];

            // Keep track of original bet
            // Used when doubling after splitting
            let original_bet = player_bets[i];

            // Active hand
            let mut hand_count = 1;
            let mut j = 0;
            // Get actions from each hand, one at a time
            // Using a loop and incrementing j manually because for loops would not recheck
            // length of player.hands() after a split
            loop {
                // Check for low cards
                if self.shoe.len() <= self.config.low_cards_threshold {
                    (self.callback)(DealerRequest::LowCards, None, &self);
                    // Create a new shoe if the option is enabled
                    self.shoe = cards::create_shoe(self.config.shoe_deck_count);
                    cards::shuffle_deck(&mut self.shoe);
                }

                if j >= hand_count {
                    break;
                }
                while !stood[j] {
                    let action =
                        (self.callback)(DealerRequest::Play(j), Some(&self.players[i]), &self);
                    match action {
                        PlayerAction::Hit => {
                            self.hit_card(i, j);
                            can_double[j] = false;
                        }
                        PlayerAction::Stand => stood[j] = true,
                        PlayerAction::DoubleDown => {
                            if can_double[j] {
                                *self.players[i].money_mut() -= original_bet;
                                player_bets[i] += original_bet;
                                stood[j] = true;
                                self.hit_card(i, j);
                                can_double[j] = false;
                            } else {
                                (self.callback)(
                                    DealerRequest::Error(PlayerActionError::UnexpectedAction(
                                        j, action,
                                    )),
                                    Some(&self.players[i]),
                                    &self,
                                );
                            }
                        }
                        PlayerAction::Split => {
                            if can_split {
                                *self.players[i].money_mut() -= original_bet;
                                player_bets[i] += original_bet;
                                self.players[i].hands_mut().push(Vec::new());
                                stood.push(false);
                                if self.config.double_after_split && self.config.doubling_down {
                                    can_double.push(true);
                                } else {
                                    can_double[0] = false;
                                    can_double.push(false);
                                }
                                // "Draw" card from first hand and place it into second
                                let card = cards::draw_card(
                                    self.players[i].hands_mut().get_mut(0).unwrap(),
                                );
                                self.players[i].hands_mut()[1].push(card.unwrap());
                                // Hit another card to each hand
                                self.hit_card(i, 0);
                                self.hit_card(i, 1);
                                hand_count = 2;
                                can_split = false;
                            } else {
                                (self.callback)(
                                    DealerRequest::Error(PlayerActionError::UnexpectedAction(
                                        j, action,
                                    )),
                                    Some(&self.players[i]),
                                    &self,
                                );
                            }
                        }
                        _ => {
                            let error = PlayerActionError::UnexpectedAction(j, action);
                            (self.callback)(
                                DealerRequest::Error(error),
                                Some(&self.players[i]),
                                &self,
                            );
                        }
                    }

                    // Check if the hand is busted
                    if get_hand_value(&self.players[i].hands()[j], true) > 21 {
                        stood[j] = true;
                    }
                }
                j += 1;
            }
        }

        // Dealer play
        let mut busted = false;
        loop {
            let hand_value = get_hand_value(&self.hand, true);
            if hand_value > 21 {
                busted = true;
                break;
            } else if hand_value >= 17 {
                if self.config.stand_soft_17 {
                    break;
                // Check if hand is exactly 17 contains an ace
                } else if hand_value == 17 && self.hand.iter().any(|&i| i[1] == 'A') {
                    // Check if ace is acting as an 11 or a 1
                    if hand_value == get_hand_value(&self.hand, false) {
                        let card = cards::draw_card(&mut self.shoe).unwrap();
                        self.hand.push(card);
                        (self.callback)(DealerRequest::HitCard(card), None, &self);
                    } else {
                        break;
                    }
                }
                break;
            } else {
                let card = cards::draw_card(&mut self.shoe).unwrap();
                self.hand.push(card);
                (self.callback)(DealerRequest::HitCard(card), None, &self);
            }
        }

        // Pay out winners
        let dealer_hand_value = get_hand_value(&self.hand, true);
        for i in 0..self.players.len() {
            for j in 0..self.players[i].hands().len() {
                let hand_value = get_hand_value(&self.players[i].hands()[j], true);
                // Check if player busted
                if hand_value > 21 {
                    continue;
                }

                // Pay out normal amount if player did not bust, did not have blackjack,
                // and beat dealer/dealer busted
                if hand_value < 21 && (busted || hand_value > dealer_hand_value) {
                    self.players[i].money +=
                        player_bets[i] * 2 / self.players[i].hands().len() as i32;
                } else if hand_value == 21 && (busted || hand_value > dealer_hand_value) {
                    // Check if player had blackjack
                    if self.players[i].hands()[j].len() == 2 {
                        // Make sure dealer didn't have blackjack
                        if dealer_hand_value == 21 && self.hand.len() == 2 {
                            // Push, refund player
                            self.players[i].money += player_bets[i];
                        } else {
                            // Pay out 3 to 2
                            self.players[i].money += player_bets[i]
                                + (player_bets[i] as f32 * self.config.blackjack_payout) as i32;
                        }
                    } else {
                        self.players[i].money +=
                            player_bets[i] * 2 / self.players[i].hands().len() as i32;
                    }
                // Push, refund player
                } else if hand_value == dealer_hand_value {
                    self.players[i].money += player_bets[i];
                }
            }
        }

        (self.callback)(DealerRequest::DealerHand(self.hand.clone()), None, &self);
    }
}

impl Player {
    /// Returns a new Player
    ///
    /// # Arguments
    ///
    /// * `money` - The amount of money to give the player
    /// * `hands` - A Vector of hands (`Vec<[char; 2]>`)
    ///
    /// # Examples
    ///
    /// ```
    /// use twentyone::game::Player;
    /// let player = Player::new(100);
    /// ```
    pub fn new(money: i32) -> Player {
        Player {
            money: money,
            hands: vec![Vec::new()],
        }
    }

    /// Returns a reference to the player's money
    pub fn money(&self) -> &i32 {
        &self.money
    }

    /// Returns a reference to the player's hands
    pub fn hands(&self) -> &Vec<Vec<[char; 2]>> {
        &self.hands
    }

    /// Returns a mutable reference to the player's money
    pub fn money_mut(&mut self) -> &mut i32 {
        &mut self.money
    }

    /// Returns a mutable reference to the player's hands
    pub fn hands_mut(&mut self) -> &mut Vec<Vec<[char; 2]>> {
        &mut self.hands
    }
}

/// Returns the value of a hand
///
/// # Arguments
///
/// * `hand` - The hand to get the value of
///
/// # Examples
///
/// ```
/// use twentyone::{cards, game};
/// let mut deck = cards::create_deck();
/// cards::shuffle_deck(&mut deck);
/// let mut hand = Vec::new();
/// cards::hit_card(&mut deck, &mut hand);
/// cards::hit_card(&mut deck, &mut hand);
/// println!("{}", game::get_hand_value(&hand, true));
/// ```
pub fn get_hand_value(hand: &Vec<[char; 2]>, auto_aces: bool) -> u8 {
    let mut value = 0;
    let mut aces = 0;
    for i in hand.iter() {
        value += match i[1] {
            '2' => 2,
            '3' => 3,
            '4' => 4,
            '5' => 5,
            '6' => 6,
            '7' => 7,
            '8' => 8,
            '9' => 9,
            'T' | 'J' | 'Q' | 'K' => 10,
            'A' => {
                aces += 1;
                0
            }
            _ => 0,
        }
    }
    // Add aces
    if auto_aces {
        // Check if an ace being 11 would bust the hand
        for _ in 0..aces {
            if value + 11 > 21 {
                value += 1;
            } else {
                value += 11;
            }
        }
    } else {
        value += 11 * aces;
    }
    value
}

/// Returns whether a hand is able to split
///
/// # Arguments
///
/// * `hand` - The hand to be split
///
/// # Examples
///
/// ```
/// use twentyone::{cards, game};
/// let mut deck = cards::create_deck();
/// cards::shuffle_deck(&mut deck);
/// let mut hand = Vec::new();
/// cards::hit_card(&mut deck, &mut hand);
/// cards::hit_card(&mut deck, &mut hand);
/// println!("{}", game::can_split(&hand));
/// ```
pub fn can_split(hand: &Vec<[char; 2]>) -> bool {
    if hand.len() != 2 {
        return false;
    }

    hand[0][1] == hand[1][1]
}