diff --git a/src/game/deck.rs b/src/game/deck.rs new file mode 100644 index 0000000..f32ce7e --- /dev/null +++ b/src/game/deck.rs @@ -0,0 +1,145 @@ +use std::fmt::{Display, Formatter}; + +use crate::card::Card; +use rand::{seq::SliceRandom, Rng}; + +#[derive(Debug)] +/// A Deck of Cards - essentially a container of cards. +pub struct Deck(Vec); + +#[derive(Debug, Clone, PartialEq, Eq)] +/// Reasons for why an operation may fail. +pub enum Reason { + NotSorted, + OutOfBounds(usize), +} + +impl Deck { + pub fn new_empty() -> Self { + Deck(Vec::new()) + } + + pub fn new_full(n: usize) -> Self { + assert!(n > 0); + Self(Card::iter_all(n as i64).collect()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn shuffle(&mut self, rng: &mut T) { + self.0.shuffle(rng); + } + + pub fn sort(&mut self) { + self.0.sort(); + } + + pub fn sort_by_suit(&mut self) { + // Sort by suit then rank + self.0.sort_by(|x, y| { + x.suit() + .cmp(&y.suit()) + .then_with(|| x.rank().cmp(&y.rank())) + }) + } + + /// Add a set of cards to the end of `self`. + pub fn add(&mut self, cards: &[Card]) { + self.0.extend_from_slice(cards); + } + + /// Get a set of cards at indices `indices` as a vector. + /// Returns Err if any index is out of bounds. + pub fn get(&self, indices: &[usize]) -> Result, Reason> { + let mut collector = Vec::with_capacity(indices.len()); + for &ind in indices { + if ind >= self.len() { + return Err(Reason::OutOfBounds(ind)); + } + collector.push(self.0[ind]); + } + Ok(collector) + } + + /// Remove cards at indices `indices` from `self`. Order is not preserved. + /// Returns Err if `indices` is not sorted in ascending order or if any + /// index is out of bounds. + pub fn remove(&mut self, indices: &[usize]) -> Result<(), Reason> { + if !indices.is_sorted() { + Err(Reason::NotSorted) + } else if let Some(index) = indices.iter().find(|&&x| x >= self.len()) { + Err(Reason::OutOfBounds(*index)) + } else { + for &index in indices.iter().rev() { + self.0.swap_remove(index); + } + Ok(()) + } + } + + /// Remove `n` cards from the end of `self`, and append them to the deck + /// `other`. + /// Returns Err if the number of cards requested exceed the size of the + /// current deck. + pub fn deal_tail( + &mut self, + other: &mut Self, + n: usize, + ) -> Result<(), Reason> { + if n > self.0.len() { + Err(Reason::OutOfBounds(n)) + } else { + let mut tail = self.0.split_off(self.len() - n); + tail.sort(); + other.0.append(&mut tail); + Ok(()) + } + } + + /// Remove cards at indices `indices` from `self` and append them onto the + /// deck `other`. Order is not preserved. + /// Returns Err if `indices` are not sorted in ascending order or if any + /// indices are out of bounds. + pub fn deal_any( + &mut self, + other: &mut Self, + indices: &[usize], + ) -> Result<(), Reason> { + let mut removed_cards = self.get(indices)?; + self.remove(indices)?; + other.0.append(&mut removed_cards); + Ok(()) + } + + /// Given two indices (`a` and `b`) in the deck, swap the cards in `self`. + /// Returns Err if either `a` or `b` are out of bounds. + pub fn swap(&mut self, a: usize, b: usize) -> Result<(), Reason> { + if a >= self.len() { + Err(Reason::OutOfBounds(a)) + } else if b >= self.len() { + Err(Reason::OutOfBounds(b)) + } else { + self.0.swap(a, b); + Ok(()) + } + } +} + +impl Display for Deck { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{{")?; + for (i, c) in self.0.iter().enumerate() { + write!(f, "{}", c)?; + if i < self.0.len() - 1 { + write!(f, ", ")? + } + } + write!(f, "}}") + } +} diff --git a/src/game/mod.rs b/src/game/mod.rs index e69de29..2d8ad78 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -0,0 +1 @@ +pub mod deck;