//! # Deck Module //! //! This module provides the `Deck` struct for representing a deck of playing cards //! in the poker AI system. Supports shuffling, dealing, and serialization. //! //! ## Deck Representation //! //! A Deck contains a vector of cards not yet dealt, starting with all 52 cards. //! //! ## Examples //! //! ### Creating and Using a Deck //! //! ```rust //! use holdem_core::{Deck, Card}; //! //! let mut deck = Deck::new(); //! assert_eq!(deck.remaining(), 52); //! //! // Shuffle the deck //! use rand::SeedableRng; //! let mut rng = rand::rngs::StdRng::from_seed([1; 32]); //! deck.shuffle(&mut rng); //! //! // Deal some cards //! let hole_cards = deck.deal(2); //! let flop = deck.deal(3); //! let turn = deck.deal(1); //! let river = deck.deal(1); //! //! assert_eq!(hole_cards.len(), 2); //! assert_eq!(flop.len(), 3); //! assert_eq!(turn.len(), 1); //! assert_eq!(river.len(), 1); //! assert_eq!(deck.remaining(), 52 - 7); //! ``` //! //! ## Design Decisions //! //! - **Full Deck**: Always starts with 52 cards in standard poker order //! - **Efficient Dealing**: Pop from end for O(1) deal operations //! - **Serialization Ready**: Full serde support for persistence //! - **Flexible Shuffling**: Uses rand crate for high-quality randomization use crate::card::Card; use serde::{Deserialize, Serialize}; /// Represents a deck of cards not yet dealt #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Deck { cards: Vec, } impl Deck { /// Creates a new full deck of 52 cards /// /// # Examples /// /// ```rust /// use holdem_core::Deck; /// /// let deck = Deck::new(); /// assert_eq!(deck.remaining(), 52); /// ``` /// /// # Panics /// /// This method does not panic. pub fn new() -> Self { let mut cards = Vec::with_capacity(52); for suit in 0..4 { for rank in 0..13 { cards.push(Card::new(rank, suit).unwrap()); } } Self { cards } } /// Shuffles the deck using the provided random number generator /// /// # Examples /// /// ```rust /// use holdem_core::Deck; /// use rand::SeedableRng; /// /// let mut deck = Deck::new(); /// let mut rng = rand::rngs::StdRng::from_seed([1; 32]); /// deck.shuffle(&mut rng); /// ``` /// /// # Panics /// /// This method does not panic. pub fn shuffle(&mut self, rng: &mut R) { use rand::seq::SliceRandom; self.cards.shuffle(rng); } /// Deals a single card from the top of the deck /// /// Returns `None` if the deck is empty. /// /// # Examples /// /// ```rust /// use holdem_core::Deck; /// /// let mut deck = Deck::new(); /// let card = deck.deal_one().unwrap(); /// assert_eq!(deck.remaining(), 51); /// ``` /// /// # Panics /// /// This method does not panic, but returns `None` if the deck is empty. pub fn deal_one(&mut self) -> Option { self.cards.pop() } /// Deals multiple cards from the top of the deck /// /// Returns a vector containing up to `count` cards. If fewer than `count` cards /// remain in the deck, returns all remaining cards. /// /// # Examples /// /// ```rust /// use holdem_core::Deck; /// /// let mut deck = Deck::new(); /// let cards = deck.deal(5); /// assert_eq!(cards.len(), 5); /// assert_eq!(deck.remaining(), 47); /// ``` /// /// # Panics /// /// This method does not panic. pub fn deal(&mut self, count: usize) -> Vec { let mut dealt = Vec::new(); for _ in 0..count { if let Some(card) = self.deal_one() { dealt.push(card); } else { break; } } dealt } /// Returns the number of cards remaining in the deck /// /// # Examples /// /// ```rust /// use holdem_core::Deck; /// /// let mut deck = Deck::new(); /// assert_eq!(deck.remaining(), 52); /// /// deck.deal(5); /// assert_eq!(deck.remaining(), 47); /// ``` /// /// # Panics /// /// This method does not panic. pub fn remaining(&self) -> usize { self.cards.len() } /// Returns true if the deck is empty /// /// # Examples /// /// ```rust /// use holdem_core::Deck; /// /// let mut deck = Deck::new(); /// assert!(!deck.is_empty()); /// /// // Deal all cards /// deck.deal(52); /// assert!(deck.is_empty()); /// ``` /// /// # Panics /// /// This method does not panic. pub fn is_empty(&self) -> bool { self.cards.is_empty() } /// Returns a reference to the remaining cards /// /// # Examples /// /// ```rust /// use holdem_core::Deck; /// /// let deck = Deck::new(); /// let cards = deck.cards(); /// assert_eq!(cards.len(), 52); /// ``` /// /// # Panics /// /// This method does not panic. pub fn cards(&self) -> &[Card] { &self.cards } /// Creates a new deck excluding the specified cards. /// /// Useful for constructing a "remaining deck" that omits known cards /// (hero's hole cards, board cards) — typically resulting in a 45–47 card /// deck for enumeration-based equity calculations. /// /// # Examples /// /// ```rust /// use holdem_core::{Deck, Card}; /// use std::str::FromStr; /// /// let deck = Deck::new(); /// let excluded = vec![ /// Card::from_str("As").unwrap(), /// Card::from_str("Kh").unwrap(), /// ]; /// let remaining = deck.without_cards(&excluded); /// assert_eq!(remaining.remaining(), 50); /// ``` /// /// # Panics /// /// This method does not panic. pub fn without_cards(&self, excluded: &[Card]) -> Deck { use std::collections::HashSet; let exclude_set: HashSet = excluded.iter().copied().collect(); let cards: Vec = self .cards .iter() .copied() .filter(|c| !exclude_set.contains(c)) .collect(); Deck { cards } } } impl Default for Deck { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { use super::*; use std::collections::HashSet; #[test] fn test_deck_new() { let deck = Deck::new(); assert_eq!(deck.remaining(), 52); assert!(!deck.is_empty()); // Check all cards are present let cards = deck.cards(); let mut card_set = HashSet::new(); for &card in cards { assert!(card_set.insert(card), "Duplicate card in deck: {}", card); } assert_eq!(card_set.len(), 52); } #[test] fn test_deck_shuffle() { let mut deck1 = Deck::new(); let deck2 = Deck::new(); // Decks should be identical initially assert_eq!(deck1.cards(), deck2.cards()); // Shuffle one deck use rand::SeedableRng; let mut rng = rand::rngs::StdRng::from_seed([1; 32]); deck1.shuffle(&mut rng); // After shuffle, order should be different (with very high probability) assert_ne!(deck1.cards(), deck2.cards()); assert_eq!(deck1.remaining(), 52); assert_eq!(deck2.remaining(), 52); } #[test] fn test_deck_deal_one() { let mut deck = Deck::new(); // Deal one card let card = deck.deal_one().unwrap(); assert_eq!(deck.remaining(), 51); // Deal another let card2 = deck.deal_one().unwrap(); assert_eq!(deck.remaining(), 50); // Cards should be different assert_ne!(card, card2); } #[test] fn test_deck_deal_multiple() { let mut deck = Deck::new(); // Deal 5 cards let cards = deck.deal(5); assert_eq!(cards.len(), 5); assert_eq!(deck.remaining(), 47); // All cards should be unique let mut card_set = HashSet::new(); for &card in &cards { assert!(card_set.insert(card), "Duplicate card dealt: {}", card); } } #[test] fn test_deck_deal_empty() { let mut deck = Deck::new(); // Deal all cards let all_cards = deck.deal(52); assert_eq!(all_cards.len(), 52); assert_eq!(deck.remaining(), 0); assert!(deck.is_empty()); // Try to deal one more assert!(deck.deal_one().is_none()); // Try to deal multiple more let empty_deal = deck.deal(5); assert!(empty_deal.is_empty()); } #[test] fn test_deck_serialization() { let deck = Deck::new(); // Test JSON serialization let json = serde_json::to_string(&deck).unwrap(); let deserialized: Deck = serde_json::from_str(&json).unwrap(); assert_eq!(deck.cards(), deserialized.cards()); // Test TOML serialization let toml = toml::to_string(&deck).unwrap(); let deserialized: Deck = toml::from_str(&toml).unwrap(); assert_eq!(deck.cards(), deserialized.cards()); } #[test] fn test_deck_shuffle_randomness() { use rand::SeedableRng; let mut deck = Deck::new(); let original_order: Vec = deck.cards().to_vec(); // Shuffle with different seeds and verify randomness let mut rng1 = rand::rngs::StdRng::from_seed([1; 32]); deck.shuffle(&mut rng1); let shuffled1 = deck.cards().to_vec(); // Reset deck deck = Deck::new(); let mut rng2 = rand::rngs::StdRng::from_seed([2; 32]); deck.shuffle(&mut rng2); let shuffled2 = deck.cards().to_vec(); // Different seeds should produce different shuffles assert_ne!(shuffled1, shuffled2); assert_ne!(shuffled1, original_order); assert_ne!(shuffled2, original_order); // All cards should still be present let mut set1 = HashSet::new(); let mut set2 = HashSet::new(); for &card in &shuffled1 { set1.insert(card); } for &card in &shuffled2 { set2.insert(card); } assert_eq!(set1.len(), 52); assert_eq!(set2.len(), 52); } #[test] fn test_deck_deal_edge_cases() { let mut deck = Deck::new(); // Deal more cards than available let cards = deck.deal(60); assert_eq!(cards.len(), 52); assert_eq!(deck.remaining(), 0); assert!(deck.is_empty()); // Try dealing from empty deck let empty_deal = deck.deal(5); assert!(empty_deal.is_empty()); // Deal one from empty deck assert!(deck.deal_one().is_none()); } #[test] fn test_deck_remaining_cards() { let mut deck = Deck::new(); assert_eq!(deck.remaining(), 52); // Deal various amounts and check remaining deck.deal(5); assert_eq!(deck.remaining(), 47); deck.deal_one(); assert_eq!(deck.remaining(), 46); deck.deal(10); assert_eq!(deck.remaining(), 36); // Deal all remaining deck.deal(36); assert_eq!(deck.remaining(), 0); assert!(deck.is_empty()); } #[test] fn test_deck_deal_zero() { let mut deck = Deck::new(); // Deal zero cards let empty_deal = deck.deal(0); assert!(empty_deal.is_empty()); assert_eq!(deck.remaining(), 52); } #[test] fn test_deck_default() { let deck = Deck::default(); assert_eq!(deck.remaining(), 52); assert!(!deck.is_empty()); } #[test] fn test_deck_clone() { let mut deck1 = Deck::new(); deck1.deal(5); let deck2 = deck1.clone(); assert_eq!(deck1.remaining(), deck2.remaining()); assert_eq!(deck1.cards(), deck2.cards()); } #[test] fn test_deck_serialization_partial() { let mut deck = Deck::new(); deck.deal(10); // Remove 10 cards // Serialize partial deck let json = serde_json::to_string(&deck).unwrap(); let deserialized: Deck = serde_json::from_str(&json).unwrap(); assert_eq!(deck.remaining(), deserialized.remaining()); assert_eq!(deck.cards(), deserialized.cards()); // Should be able to continue dealing from deserialized deck let mut deserialized = deserialized; let more_cards = deserialized.deal(5); assert_eq!(more_cards.len(), 5); } #[test] fn test_deck_deal_uniqueness() { let mut deck = Deck::new(); // Deal all cards and ensure uniqueness let mut all_cards = Vec::new(); while let Some(card) = deck.deal_one() { all_cards.push(card); } assert_eq!(all_cards.len(), 52); let mut card_set = HashSet::new(); for card in all_cards { assert!(card_set.insert(card), "Duplicate card found: {}", card); } assert_eq!(card_set.len(), 52); } #[test] fn test_deck_performance() { use std::time::Instant; // Test creation performance let start = Instant::now(); for _ in 0..1000 { let _deck = Deck::new(); } let creation_time = start.elapsed(); // Test dealing performance let mut deck = Deck::new(); let start = Instant::now(); for _ in 0..52 { deck.deal_one(); } let dealing_time = start.elapsed(); // Should be very fast assert!( creation_time.as_millis() < 100, "Deck creation too slow: {:?}", creation_time ); assert!( dealing_time.as_millis() < 10, "Dealing too slow: {:?}", dealing_time ); } }