use crate::card::Card; use itertools::Itertools; use std::fmt; #[derive(Clone, Debug)] pub struct Hand { cards: Vec, } impl Hand { pub fn new() -> Self { Self { cards: Vec::new() } } /// Returns the value of the hand. If there are any aces, this is the highest value without /// busting. pub fn value(&self) -> u8 { let mut num_aces = 0; let mut sum = 0; // count up the value of everything that's not an ace for card in &self.cards { if card.value == "A" { num_aces += 1; } else { sum += u8::from(card); } } if num_aces == 0 { return sum; } // Figure out the possible ace sums based on how many aces we have let mut values: Vec = [1, 11] .into_iter() .combinations_with_replacement(num_aces) .map(|a| a.iter().sum()) .collect(); // Find the highest value without busting, adding in the rest of the cards. If they all // bust, then take the lowest value. values.sort(); values.reverse(); let mut total = 0; for v in values.iter() { total = v + sum; if total <= 21 { break; } } total } /// Returns true if the hand is a blackjack, meaning we have an ace and a 10-valued card pub fn is_blackjack(&self) -> bool { self.cards.len() == 2 && self.value() == 21 } /// Returns true if hand has an ace pub fn has_ace(&self) -> bool { for card in &self.cards { if card.value == "A" { return true; } } false } /// Returns true if the hand has a pair (can be split) pub fn is_pair(&self) -> bool { self.cards.len() == 2 && u8::from(&self.cards[0]) == u8::from(&self.cards[1]) } /// Returns a read-only copy of the cards in the hand pub fn cards(&self) -> Vec { self.cards.clone() } /// Add a card to the hand pub fn push(&mut self, card: Card) { self.cards.push(card) } /// Split the hand into two pub fn split(&mut self) -> Hand { let other_cards: Vec = Vec::from(self.cards.pop().as_slice()); Hand { cards: other_cards } } } impl Default for Hand { fn default() -> Self { Hand::new() } } impl From> for Hand { fn from(cards: Vec) -> Self { Self { cards } } } impl From> for Hand { fn from(str_cards: Vec<(&str, &str)>) -> Self { Self { cards: str_cards.into_iter().map(|c| (c).into()).collect(), } } } impl fmt::Display for Hand { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for (i, c) in self.cards.iter().enumerate() { if i != 0 { write!(f, " ")?; } write!(f, "{c}")?; } Ok(()) } } #[cfg(test)] mod tests { use super::*; use crate::card::{Card, Suit}; #[test] fn no_aces() { let mut h = Hand::new(); h.cards.push(Card::new(Suit::Club, "K")); h.cards.push(Card::new(Suit::Club, "7")); assert_eq!(h.value(), 17); } #[test] fn one_ace() { let mut h = Hand::new(); h.cards.push(Card::new(Suit::Club, "K")); h.cards.push(Card::new(Suit::Club, "A")); assert_eq!(h.value(), 21); } #[test] fn three_aces() { let mut h = Hand::new(); h.cards.push(Card::new(Suit::Club, "3")); h.cards.push(Card::new(Suit::Club, "A")); h.cards.push(Card::new(Suit::Club, "A")); h.cards.push(Card::new(Suit::Club, "A")); assert_eq!(h.value(), 16); } }