156 lines
3.8 KiB
Rust
156 lines
3.8 KiB
Rust
use crate::card::Card;
|
|
use itertools::Itertools;
|
|
use std::fmt;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct Hand {
|
|
cards: Vec<Card>,
|
|
}
|
|
|
|
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<u8> = [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<Card> {
|
|
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<Card> = Vec::from(self.cards.pop().as_slice());
|
|
|
|
Hand { cards: other_cards }
|
|
}
|
|
}
|
|
|
|
impl Default for Hand {
|
|
fn default() -> Self {
|
|
Hand::new()
|
|
}
|
|
}
|
|
|
|
impl From<Vec<Card>> for Hand {
|
|
fn from(cards: Vec<Card>) -> Self {
|
|
Self { cards }
|
|
}
|
|
}
|
|
|
|
impl From<Vec<(&str, &str)>> 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);
|
|
}
|
|
}
|