soliterm-model 0.1.0

Shared model types for soliterm
Documentation
use std::mem::take;

use super::{Pile, PileMut, Split};
use crate::card::Card;
use crate::enums::EnumSequence as _;
use crate::shuffle::Shuffle;

#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Cards {
    cards: Vec<Card>,
}

impl Cards {
    pub const fn empty() -> Self {
        Self { cards: Vec::new() }
    }

    pub fn deck() -> Self {
        Self {
            cards: Card::values_iter().collect(),
        }
    }

    pub fn shuffled_deck<S>(shuffle: S) -> Self
    where
        S: Shuffle,
    {
        let cards = Card::values_iter().collect::<Vec<_>>();
        let permutation = shuffle.permutation(cards.len());
        let cards = permutation.permute(&cards);

        Self { cards }
    }

    pub fn into_split(self, left_len: usize) -> Split {
        Split::from_cards(self, left_len)
    }

    pub fn into_left_split(self) -> Split {
        Split::from_left(self)
    }

    pub fn into_right_split(self) -> Split {
        Split::from_right(self)
    }

    #[must_use]
    pub fn flipped(mut self) -> Self {
        self.cards.reverse();

        self
    }
}

impl From<Vec<Card>> for Cards {
    fn from(cards: Vec<Card>) -> Self {
        Self { cards }
    }
}

impl<const N: usize> From<[Card; N]> for Cards {
    fn from(cards: [Card; N]) -> Self {
        Self {
            cards: cards.into(),
        }
    }
}

impl FromIterator<Card> for Cards {
    fn from_iter<I>(cards: I) -> Self
    where
        I: IntoIterator<Item = Card>,
    {
        Self {
            cards: cards.into_iter().collect(),
        }
    }
}

impl IntoIterator for Cards {
    type Item = Card;
    type IntoIter = impl Iterator<Item = Self::Item>
        + DoubleEndedIterator<Item = Self::Item>
        + ExactSizeIterator<Item = Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        self.cards.into_iter()
    }
}

impl<'a> IntoIterator for &'a Cards {
    type Item = Card;
    type IntoIter = impl Iterator<Item = Self::Item>
        + DoubleEndedIterator<Item = Self::Item>
        + ExactSizeIterator<Item = Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        self.cards.iter().copied()
    }
}

impl Extend<Card> for Cards {
    fn extend<I>(&mut self, cards: I)
    where
        I: IntoIterator<Item = Card>,
    {
        self.place(cards);
    }
}

impl Pile for Cards {
    fn cards(&self) -> &[Card] {
        &self.cards
    }

    fn len(&self) -> usize {
        self.cards.len()
    }

    fn is_empty(&self) -> bool {
        self.cards.is_empty()
    }
}

impl PileMut for Cards {
    fn take_at_most(&mut self, count: usize) -> Cards {
        let index = self.len().saturating_sub(count);

        self.cards.split_off(index).into()
    }

    fn take_at_most_one(&mut self) -> Option<Card> {
        self.cards.pop()
    }

    fn take_or_none(&mut self, count: usize) -> Option<Cards> {
        self.len()
            .checked_sub(count)
            .map(|index| self.cards.split_off(index).into())
    }

    fn take_exactly(&mut self, count: usize) -> Cards {
        assert!(count <= self.len());
        let index = self.len() - count;

        self.cards.split_off(index).into()
    }

    fn take_exactly_one(&mut self) -> Card {
        assert!(!self.is_empty());

        self.take_at_most_one().unwrap()
    }

    fn take_all(&mut self) -> Cards {
        take(self)
    }

    fn place<I>(&mut self, cards: I)
    where
        I: IntoIterator<Item = Card>,
    {
        self.cards.extend(cards);
    }

    fn place_one(&mut self, card: Card) {
        self.cards.push(card);
    }
}