//! # Deck Module //! //! This module provides the `Deck` struct for managing a standard 52-card poker deck. //! The Deck struct supports shuffling, dealing cards, and various card manipulation //! operations required for poker game simulation and testing. //! //! ## Deck Structure //! //! A `Deck` contains: //! - A vector of remaining cards in the deck //! - A vector of cards that have been dealt //! - Methods for shuffling, dealing, and card management //! //! ## Key Features //! //! - **Standard Deck**: Creates a proper 52-card poker deck //! - **Shuffling**: Multiple shuffle methods including seeded randomization //! - **Dealing**: Deal cards sequentially or randomly //! - **Card Management**: Extract specific cards, check card availability //! - **Reset Functionality**: Return all cards to the deck //! - **Integration**: Works seamlessly with `Hand` and `Card` structs //! //! ## Examples //! //! ### Basic Deck Operations //! //! ```rust //! use poker_api::api::deck::Deck; //! use poker_api::api::card::Card; //! //! // Create and shuffle a new deck //! let mut deck = Deck::new(); //! deck.shuffle(); //! //! // Deal some cards //! if let Some(card) = deck.deal() { //! println!("Dealt: {}", card); //! } //! //! println!("Cards left: {}", deck.cards_left()); //! ``` //! //! ### Seeded Shuffling for Testing //! //! ```rust //! use poker_api::api::deck::Deck; //! //! // Create deck with reproducible shuffle for testing //! let mut deck = Deck::with_seed(12345); //! //! // Deal cards in predictable order //! let card1 = deck.deal().unwrap(); //! let card2 = deck.deal().unwrap(); //! ``` //! //! ### Card Management //! //! ```rust //! use poker_api::api::deck::Deck; //! use poker_api::api::hand::Hand; //! use poker_api::api::card::Card; //! //! let mut deck = Deck::new(); //! //! // Extract specific cards //! let ace_spades = Card::from_string("As").unwrap(); //! deck.extract_card(ace_spades); //! //! // Extract entire hand //! let mut hand = Hand::from_string("Kh Qh Jh").unwrap(); //! deck.extract_hand(&hand); //! //! // Reset deck to restore all cards //! deck.reset(); //! ``` //! //! ## Design Decisions //! //! - **Two-Vector Design**: Separate active and dealt cards for efficient operations //! - **Immutable Access**: Most getter methods take `&self` for safety //! - **Standard Ordering**: Cards ordered by suit (Spades, Hearts, Diamonds, Clubs) //! - **Error Handling**: Graceful handling of empty deck conditions //! - **Random Access**: Support for both sequential and random dealing //! //! ## Performance Characteristics //! //! - **Memory**: ~104 bytes overhead + 52 bytes for cards //! - **Shuffling**: O(n) where n is number of cards //! - **Dealing**: O(1) for sequential, O(n) for random //! - **Card Lookup**: O(n) linear search //! - **Reset**: O(1) vector extension use crate::api::card::Card; use crate::api::hand::Hand; use rand::seq::SliceRandom; use rand::SeedableRng; use std::fmt; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Deck { cards: Vec, dealt: Vec, // Cards that have been dealt } impl Deck { pub const NUM_CARDS: usize = 52; // Create a new, unshuffled deck pub fn new() -> Self { let mut cards = Vec::with_capacity(Self::NUM_CARDS); for suit in 1..=4 { for rank in Card::DEUCE..=Card::ACE { cards.push(Card::from_rank_suit(rank, suit).unwrap()); } } Self { cards, dealt: Vec::new(), } } // Create a new deck with a specific shuffle seed pub fn with_seed(seed: u64) -> Self { let mut deck = Self::new(); deck.shuffle_with_seed(seed); deck } // Shuffle the deck using a random seed pub fn shuffle(&mut self) { self.cards.shuffle(&mut rand::rng()); } // Shuffle the deck using a specific seed pub fn shuffle_with_seed(&mut self, seed: u64) { let mut rng = rand::rngs::StdRng::seed_from_u64(seed); self.cards.shuffle(&mut rng); } // Deal the next card from the deck pub fn deal(&mut self) -> Option { if self.cards.is_empty() { None } else { let card = self.cards.pop().unwrap(); self.dealt.push(card); Some(card) } } // Deal a random card from the deck pub fn deal_random(&mut self) -> Option { if self.cards.is_empty() { None } else { let index = rand::random::() as usize % self.cards.len(); let card = self.cards.remove(index); self.dealt.push(card); Some(card) } } // Reset the deck (put all cards back) pub fn reset(&mut self) { self.cards.extend(self.dealt.drain(..)); } // Check if a card is still in the deck pub fn in_deck(&self, card: Card) -> bool { self.cards.contains(&card) } // Remove a specific card from the deck pub fn extract_card(&mut self, card: Card) -> Option { if let Some(index) = self.cards.iter().position(|&c| c == card) { Some(self.cards.remove(index)) } else { None } } // Remove a specific card by index from the deck pub fn extract_card_by_index(&mut self, index: u8) -> Option { if let Some(pos) = self.cards.iter().position(|&c| c.index() == index) { Some(self.cards.remove(pos)) } else { None } } // Remove all cards in a hand from the deck pub fn extract_hand(&mut self, hand: &Hand) { for card in hand.get_card_array() { self.extract_card_by_index(card); } } // Get the number of cards left in the deck pub fn cards_left(&self) -> usize { self.cards.len() } // Get the top card (next card to be dealt) pub fn get_top_card(&self) -> Option { self.cards.last().copied() } // Get the index of the top card pub fn get_top_card_index(&self) -> Option { self.get_top_card().map(|card| card.index()) } // Get a card at a specific index in the deck pub fn get_card(&self, index: usize) -> Option { self.cards.get(index).copied() } } impl fmt::Display for Deck { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Deck ({} cards left)", self.cards_left()) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_deck_creation() { let deck = Deck::new(); assert_eq!(deck.cards_left(), Deck::NUM_CARDS); } #[test] fn test_deck_shuffle() { let mut deck = Deck::new(); deck.shuffle(); assert_eq!(deck.cards_left(), Deck::NUM_CARDS); } #[test] fn test_deck_deal() { let mut deck = Deck::new(); let card = deck.deal().unwrap(); assert_eq!(deck.cards_left(), Deck::NUM_CARDS - 1); assert!(!deck.in_deck(card)); } #[test] fn test_deck_reset() { let mut deck = Deck::new(); let card = deck.deal().unwrap(); deck.reset(); assert_eq!(deck.cards_left(), Deck::NUM_CARDS); assert!(deck.in_deck(card)); } #[test] fn test_deck_extract_card() { let mut deck = Deck::new(); let card = Card::from_string("As").unwrap(); deck.extract_card(card).unwrap(); assert_eq!(deck.cards_left(), Deck::NUM_CARDS - 1); assert!(!deck.in_deck(card)); } }