cribbage-core 0.1.5

A (work-in-progress) library providing the core functionality of the game Cribbage.
Documentation
use rand::prelude::SliceRandom;
use rand::thread_rng;

use crate::card::{Card, Rank, Suit};
use crate::CribbageCoreError;

pub struct Deck {
    cards_drawn: usize,
    cards: Vec<Card>,
}

impl Deck {
    pub fn new() -> Deck {
        Deck::default()
    }

    pub fn draw(&mut self) -> Result<Card, CribbageCoreError> {
        let card = match self.cards.get(self.cards_drawn) {
            Some(card) => {
                self.cards_drawn += 1;
                card
            }
            None => return Err(CribbageCoreError::NotEnoughCards),
        };

        Ok(*card)
    }

    pub fn draw_n(&mut self, n: usize) -> Result<Vec<Card>, CribbageCoreError> {
        let start = self.cards_drawn;
        let end = self.cards_drawn + n;
        if end > self.cards.len() {
            return Err(CribbageCoreError::NotEnoughCards);
        }

        self.cards_drawn = end;
        Ok(self.cards[start..end].to_vec())
    }

    pub fn shuffle(&mut self) {
        self.cards_drawn = 0;
        self.cards.shuffle(&mut thread_rng());
    }
}

impl Default for Deck {
    fn default() -> Deck {
        let cards = vec![
            Card::new(Rank::Ace, Suit::Hearts),
            Card::new(Rank::Two, Suit::Hearts),
            Card::new(Rank::Three, Suit::Hearts),
            Card::new(Rank::Four, Suit::Hearts),
            Card::new(Rank::Five, Suit::Hearts),
            Card::new(Rank::Six, Suit::Hearts),
            Card::new(Rank::Seven, Suit::Hearts),
            Card::new(Rank::Eight, Suit::Hearts),
            Card::new(Rank::Nine, Suit::Hearts),
            Card::new(Rank::Ten, Suit::Hearts),
            Card::new(Rank::Jack, Suit::Hearts),
            Card::new(Rank::Queen, Suit::Hearts),
            Card::new(Rank::King, Suit::Hearts),
            Card::new(Rank::Ace, Suit::Clubs),
            Card::new(Rank::Two, Suit::Clubs),
            Card::new(Rank::Three, Suit::Clubs),
            Card::new(Rank::Four, Suit::Clubs),
            Card::new(Rank::Five, Suit::Clubs),
            Card::new(Rank::Six, Suit::Clubs),
            Card::new(Rank::Seven, Suit::Clubs),
            Card::new(Rank::Eight, Suit::Clubs),
            Card::new(Rank::Nine, Suit::Clubs),
            Card::new(Rank::Ten, Suit::Clubs),
            Card::new(Rank::Jack, Suit::Clubs),
            Card::new(Rank::Queen, Suit::Clubs),
            Card::new(Rank::King, Suit::Clubs),
            Card::new(Rank::King, Suit::Diamonds),
            Card::new(Rank::Queen, Suit::Diamonds),
            Card::new(Rank::Jack, Suit::Diamonds),
            Card::new(Rank::Ten, Suit::Diamonds),
            Card::new(Rank::Nine, Suit::Diamonds),
            Card::new(Rank::Eight, Suit::Diamonds),
            Card::new(Rank::Seven, Suit::Diamonds),
            Card::new(Rank::Six, Suit::Diamonds),
            Card::new(Rank::Five, Suit::Diamonds),
            Card::new(Rank::Four, Suit::Diamonds),
            Card::new(Rank::Three, Suit::Diamonds),
            Card::new(Rank::Two, Suit::Diamonds),
            Card::new(Rank::Ace, Suit::Diamonds),
            Card::new(Rank::King, Suit::Spades),
            Card::new(Rank::Queen, Suit::Spades),
            Card::new(Rank::Jack, Suit::Spades),
            Card::new(Rank::Ten, Suit::Spades),
            Card::new(Rank::Nine, Suit::Spades),
            Card::new(Rank::Eight, Suit::Spades),
            Card::new(Rank::Seven, Suit::Spades),
            Card::new(Rank::Six, Suit::Spades),
            Card::new(Rank::Five, Suit::Spades),
            Card::new(Rank::Four, Suit::Spades),
            Card::new(Rank::Three, Suit::Spades),
            Card::new(Rank::Two, Suit::Spades),
            Card::new(Rank::Ace, Suit::Spades),
        ];

        Deck {
            cards_drawn: 0,
            cards,
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::card::{Card, Rank, Suit};
    use crate::deck::Deck;
    use crate::CribbageCoreError;

    #[test]
    fn test_new() {
        let deck = Deck::new();
        assert_eq!(deck.cards_drawn, 0);
        assert_eq!(deck.cards.len(), 52);
    }

    #[test]
    fn test_new_shuffle() {
        let mut deck = Deck::new();
        deck.shuffle();
        assert_ne!(deck.cards, Deck::new().cards);
    }

    #[test]
    pub fn test_draw() {
        let mut deck = Deck::new();
        assert_eq!(deck.cards_drawn, 0);

        assert_eq!(deck.draw().unwrap(), Card::new(Rank::Ace, Suit::Hearts));
        assert_eq!(deck.cards_drawn, 1);

        for _ in 0..51 {
            deck.draw().unwrap();
        }

        assert_eq!(deck.cards_drawn, 52);

        assert_eq!(deck.draw(), Err(CribbageCoreError::NotEnoughCards));
        assert_eq!(deck.cards_drawn, 52);
    }

    #[test]
    pub fn test_draw_n() {
        let mut deck = Deck::new();
        assert_eq!(deck.draw_n(0).unwrap(), Vec::new());
        assert_eq!(deck.cards_drawn, 0);

        assert_eq!(
            deck.draw_n(1).unwrap(),
            vec![Card::new(Rank::Ace, Suit::Hearts)]
        );
        assert_eq!(deck.cards_drawn, 1);

        deck = Deck::new();
        assert_eq!(deck.draw_n(52).unwrap(), Deck::new().cards);
        assert_eq!(deck.cards_drawn, 52);

        assert_eq!(deck.draw_n(0).unwrap(), Vec::new());
        assert_eq!(deck.cards_drawn, 52);

        assert_eq!(deck.draw_n(1), Err(CribbageCoreError::NotEnoughCards));
        assert_eq!(deck.cards_drawn, 52);

        assert_eq!(deck.draw_n(52), Err(CribbageCoreError::NotEnoughCards));
        assert_eq!(deck.cards_drawn, 52);
    }

    #[test]
    pub fn test_shuffle() {
        let mut deck = Deck::new();
        deck.shuffle();
        assert_ne!(deck.cards, Deck::new().cards);

        deck.draw().unwrap();
        assert_eq!(deck.cards_drawn, 1);

        deck.shuffle();
        assert_eq!(deck.cards_drawn, 0);

        deck.draw_n(52).unwrap();
        assert_eq!(deck.cards_drawn, 52);

        deck.shuffle();
        assert_eq!(deck.cards_drawn, 0);
    }
}