use rand::prelude::SliceRandom; use rand::Rng; use std::fmt; use strum::IntoEnumIterator; use strum_macros::EnumIter; #[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, EnumIter)] pub enum Suit { Club, Diamond, Heart, Spade, } #[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, EnumIter)] pub enum Rank { Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace, } impl fmt::Display for Suit { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { f.write_str(match self { Suit::Club => "♣", Suit::Diamond => "♢", Suit::Heart => "♡", Suit::Spade => "♠", }) } } #[derive(Debug, PartialEq, Eq)] pub enum ParseError { InvalidSuit(String), InvalidRank(String), MissingParts(String), } impl std::str::FromStr for Suit { type Err = ParseError; fn from_str(s: &str) -> std::result::Result { match s.trim() { "♣" => Ok(Suit::Club), "C" => Ok(Suit::Club), "♢" => Ok(Suit::Diamond), "♦" => Ok(Suit::Diamond), "D" => Ok(Suit::Diamond), "♡" => Ok(Suit::Heart), "♥" => Ok(Suit::Heart), "H" => Ok(Suit::Heart), "♠" => Ok(Suit::Spade), "S" => Ok(Suit::Spade), _ => Err(ParseError::InvalidSuit(s.to_string())), } } } impl fmt::Debug for Suit { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { write!(f, "{}", self) } } impl fmt::Display for Rank { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { f.write_str(match self { Rank::Ace => "A", Rank::King => "K", Rank::Queen => "Q", Rank::Jack => "J", Rank::Ten => "10", Rank::Nine => "9", Rank::Eight => "8", Rank::Seven => "7", Rank::Six => "6", Rank::Five => "5", Rank::Four => "4", Rank::Three => "3", Rank::Two => "2", }) } } impl fmt::Debug for Rank { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { write!(f, "{}", self) } } impl std::str::FromStr for Rank { type Err = ParseError; fn from_str(s: &str) -> std::result::Result { match s.trim().to_ascii_uppercase().as_str() { "A" => Ok(Rank::Ace), "K" => Ok(Rank::King), "Q" => Ok(Rank::Queen), "J" => Ok(Rank::Jack), "10" => Ok(Rank::Ten), "T" => Ok(Rank::Ten), "9" => Ok(Rank::Nine), "8" => Ok(Rank::Eight), "7" => Ok(Rank::Seven), "6" => Ok(Rank::Six), "5" => Ok(Rank::Five), "4" => Ok(Rank::Four), "3" => Ok(Rank::Three), "2" => Ok(Rank::Two), _ => Err(ParseError::InvalidRank(s.to_string())), } } } #[derive(PartialEq, Eq, Clone, Copy)] pub struct Card(pub Suit, pub Rank); impl fmt::Display for Card { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { let Card(suit, rank) = self; write!(f, "{}{}", suit, rank) } } impl fmt::Debug for Card { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { write!(f, "{}", self) } } impl std::str::FromStr for Card { type Err = ParseError; fn from_str(s: &str) -> std::result::Result { let stripped = s.replace(" ", ""); let mut chars = stripped.chars(); let suit = chars .next() .ok_or(ParseError::MissingParts(s.to_string()))? .to_string() .parse()?; let rank = chars.collect::().parse()?; Ok(Card(suit, rank)) } } fn make_deck() -> Vec { let mut result = vec![]; for suit in Suit::iter() { for rank in Rank::iter() { result.push(Card(suit, rank)); } } result } #[cfg(test)] mod tests { use super::*; use log::info; #[test] fn string_conversion() { crate::tests::test_setup(); info!("deck: {:?}", make_deck()); assert_eq!( make_deck(), make_deck() .iter() .map(|card| format!("{}", card).parse().unwrap()) .collect::>(), ); } } pub fn shuffle_deck(rng: &mut R) -> (Vec, Vec, Vec, Vec) where R: Rng, { let mut deck = make_deck(); deck.shuffle(rng); let mut deck = deck.iter(); let n = deck.by_ref().take(13).cloned().collect(); let w = deck.by_ref().take(13).cloned().collect(); let s = deck.by_ref().take(13).cloned().collect(); let e = deck.by_ref().take(13).cloned().collect(); (n, w, s, e) }