cliard24 0.1.2

cliard24 is a command-line 24-point card game. It provides two main functions: the game mode allows you to play the classic 24-point game interactively in the terminal, where you randomly draw 4 cards and use addition, subtraction, multiplication, division, and parentheses to reach 24; the solve mode lets you input any combination of numbers and a target value to calculate all possible expression solutions. The tool supports customizable attempt limits, endless mode, solution count limits, and mixed input of card letters (A/J/Q/K) and numbers.
Documentation
//! # Card Generator Module
//!
//! This module implements a deck of playing cards with shuffle and draw functionality
//! for the 24-point card game. It provides random sampling without replacement
//! using the Fisher-Yates shuffle algorithm.
//!
//! The generator maintains an internal deck state and automatically reshuffles
//! when all cards have been drawn, ensuring continuous gameplay.
//!
//! # Features
//! - Standard 52-card deck simulation
//! - Random shuffling using thread-local RNG
//! - Automatic reshuffle on deck exhaustion
//! - Efficient card indexing with minimal memory allocation

use crate::playing_cards::card::{Card, CardIdx, DECK_SIZE};
use rand::{rng, seq::SliceRandom};

/// Card generator that simulates a standard deck of playing cards
///
/// The generator maintains a virtual deck of 52 cards and provides methods
/// for drawing cards with automatic reshuffling when the deck is exhausted.
///
/// # Implementation Details
/// - Uses Fisher-Yates shuffle algorithm via `SliceRandom::shuffle()`
/// - Tracks current position in the deck for efficient drawing
/// - Automatically reshuffles when all cards are drawn
/// - Uses thread-local RNG for simplicity and good performance
///
/// # Examples
/// ```
/// use cliard24::playing_cards::generator::CardGenerator;
///
/// let mut generator = CardGenerator::new();
/// let hand = generator.draw(4); // Draw 4 cards for 24-point game
/// assert_eq!(hand.len(), 4);
/// ```
pub struct CardGenerator {
    /// Card indices representing the current deck order
    deck: Vec<CardIdx>,
    /// Current position in the deck (index of next card to draw)
    cur_idx: usize,
}

impl CardGenerator {
    /// Creates a new card generator ready for drawing
    ///
    /// The generator starts with a full deck of 52 cards, ready for drawing.
    /// Shuffling occurs lazily on first draw.
    ///
    /// # Returns
    /// New `CardGenerator` instance
    ///
    /// # Examples
    /// ```
    /// use cliard24::playing_cards::generator::CardGenerator;
    ///
    /// let mut generator = CardGenerator::new();
    /// let first_card = generator.draw_a_card();
    /// ```
    pub fn new() -> Self {
        CardGenerator {
            // Initialize deck with sequential card indices (0-51)
            deck: (0..DECK_SIZE as CardIdx).collect(),
            // Start with deck "exhausted" to trigger initial shuffle on first draw
            cur_idx: DECK_SIZE,
        }
    }

    /// Draws a single card from the deck with automatic reshuffling
    ///
    /// When the deck is exhausted (all cards drawn), the method automatically
    /// reshuffles the entire deck before drawing, ensuring continuous operation.
    ///
    /// # Returns
    /// `Card` instance representing the drawn playing card
    ///
    /// # Examples
    /// ```
    /// use cliard24::playing_cards::generator::CardGenerator;
    ///
    /// let mut generator = CardGenerator::new();
    /// let card = generator.draw_a_card();
    /// println!("Drew: {}", card.rank_to_str());
    /// ```
    #[inline]
    pub fn draw_a_card(&mut self) -> Card {
        // Auto-reshuffle when deck is exhausted (all cards drawn)
        if self.cur_idx >= DECK_SIZE {
            self.deck.shuffle(&mut rng());
            self.cur_idx = 0;
        }

        // Draw card from current position and advance index
        let card_idx = self.deck[self.cur_idx];
        self.cur_idx += 1;

        Card::from_index(card_idx)
    }

    /// Draws multiple cards from the deck in a single operation
    ///
    /// This is a convenience method that repeatedly calls `draw_a_card()`
    /// to obtain the requested number of cards. Automatic reshuffling occurs
    /// seamlessly if the deck is exhausted during the drawing process.
    ///
    /// # Arguments
    /// * `count` - Number of cards to draw from the deck
    ///
    /// # Returns
    /// `Vec<Card>` containing the drawn cards in order of drawing
    ///
    /// # Examples
    /// ```
    /// use cliard24::playing_cards::generator::CardGenerator;
    ///
    /// let mut generator = CardGenerator::new();
    /// // Draw 4 cards for a 24-point game
    /// let hand = generator.draw(4);
    /// assert_eq!(hand.len(), 4);
    /// ```
    pub fn draw(&mut self, count: usize) -> Vec<Card> {
        // Pre-allocate vector for efficiency
        let mut cards = Vec::with_capacity(count);

        for _ in 0..count {
            cards.push(self.draw_a_card());
        }

        cards
    }

    /// Manually forces a reshuffle of the entire deck
    ///
    /// This method immediately shuffles all cards in the deck and resets the
    /// draw position to the beginning. Useful for starting a new game round
    /// or when explicit reshuffling is desired.
    ///
    /// # Examples
    /// ```
    /// use cliard24::playing_cards::generator::CardGenerator;
    ///
    /// let mut generator = CardGenerator::new();
    /// generator.draw_a_card(); // Draw one card
    /// generator.reshuffle();   // Manual reshuffle
    /// let card = generator.draw_a_card(); // Now drawing from freshly shuffled deck
    /// ```
    #[inline]
    pub fn reshuffle(&mut self) {
        self.deck.shuffle(&mut rng());
        self.cur_idx = 0;
    }
}

impl Default for CardGenerator {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashSet;

    #[test]
    fn test_card_generate() {
        let mut generator = CardGenerator::new();
        // 将整个牌堆都抽完
        let cards = generator.draw(52);
        assert_eq!(cards.len(), 52);
        // 检查是否满足无放回
        let mut set = HashSet::new();
        assert!(cards.iter().all(|c| set.insert(c)));
    }

    #[test]
    fn test_generate_exhausted() {
        let mut generator = CardGenerator::new();
        // 进行多次抽取检查是否能够继承上次的牌堆继续抽取
        let mut cards = generator.draw(10);
        assert_eq!(cards.len(), 10);
        let mut set = HashSet::new();
        assert!(cards.iter().all(|c| set.insert(c)));

        // 新增10张牌
        cards.append(&mut generator.draw(10));
        assert_eq!(cards.len(), 20);
        let mut set = HashSet::new();
        assert!(cards.iter().all(|c| set.insert(c)));

        // 再新增10张牌
        cards.append(&mut generator.draw(10));
        assert_eq!(cards.len(), 30);
        let mut set = HashSet::new();
        assert!(cards.iter().all(|c| set.insert(c)));
    }

    #[test]
    fn test_shuffle() {
        let mut generator = CardGenerator::new();
        // 先抽1张再打乱通过检查是否重复来检查是否成功打乱
        let mut cards = generator.draw(1);
        generator.reshuffle();
        // 新增51张牌
        cards.append(&mut generator.draw(51));
        assert_eq!(cards.len(), 52);
        let mut set = HashSet::new();
        // 如果打乱前的这张牌重复了就证明有成功打乱,但有概率失败
        assert!(!cards.iter().all(|c| set.insert(c)));
    }
}