game:deck: new module for deck management
- construct empty or decks composed of n "decks" of cards - sort by standard ordering or suit ordering, and shuffle them using an RNG - get, add, remove - deal from the tail of a deck, or from any sequence of indices This API will be the cornerstone of how games are managed.
This commit is contained in:
145
src/game/deck.rs
Normal file
145
src/game/deck.rs
Normal file
@@ -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<Card>);
|
||||
|
||||
#[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<T: Rng>(&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<Vec<Card>, 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, "}}")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user