]> git.scottworley.com Git - pluta-lesnura/blobdiff - src/lib.rs
Library, drawing, & recycling discard pile
[pluta-lesnura] / src / lib.rs
index b7dc954b6d430de16bfc2f0ccccdd3e027f41c24..4496f80bdb898a5448f6184821a4ad1bc937836b 100644 (file)
@@ -1,31 +1,53 @@
 use rand::Rng;
 
-pub const NUM_RANKS: u8 = 13;
-pub const NUM_SUITS: u8 = 4;
-pub const NUM_CARDS: u8 = NUM_RANKS * NUM_SUITS;
+pub const NUM_RANKS: usize = 13;
+pub const NUM_SUITS: usize = 4;
+pub const NUM_JOKERS: usize = 2;
+pub const NUM_CARDS: usize = NUM_RANKS * NUM_SUITS + NUM_JOKERS;
 
 #[derive(Clone, Copy, Eq, PartialEq)]
 pub struct Rank(u8);
+impl Rank {
+    #[must_use]
+    pub fn value(&self) -> u8 {
+        self.0 + 1
+    }
+}
 
 #[derive(Clone, Copy, Eq, PartialEq)]
 pub struct Suit(u8);
 
-#[derive(Clone, Copy, Eq, PartialEq)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
 pub struct Card(u8);
 impl Card {
     #[must_use]
-    pub fn rank(&self) -> Rank {
-        Rank(self.0 >> 2)
+    pub fn is_joker(&self) -> bool {
+        usize::from(self.0) >= NUM_RANKS * NUM_SUITS
+    }
+    #[must_use]
+    pub fn rank(&self) -> Option<Rank> {
+        (!self.is_joker()).then_some(Rank(self.0 >> 2))
     }
     #[must_use]
-    pub fn suit(&self) -> Suit {
-        Suit(self.0 & 3)
+    pub fn suit(&self) -> Option<Suit> {
+        (!self.is_joker()).then_some(Suit(self.0 & 3))
     }
 }
 
+#[derive(Clone, Copy)]
+pub enum WithOrWithoutJokers {
+    WithJokers,
+    WithoutJokers,
+}
+
 #[must_use]
-pub fn deck() -> Vec<Card> {
-    (0..NUM_CARDS).map(Card).collect()
+pub fn deck(j: WithOrWithoutJokers) -> Vec<Card> {
+    let limit = u8::try_from(match j {
+        WithOrWithoutJokers::WithJokers => NUM_CARDS,
+        WithOrWithoutJokers::WithoutJokers => NUM_SUITS * NUM_RANKS,
+    })
+    .expect("Too many cards?");
+    (0..limit).map(Card).collect()
 }
 
 #[derive(Clone, Copy)]
@@ -42,7 +64,7 @@ impl PathLengthInfo {
         self.0 |= 1 << i.0;
     }
     pub fn reveal_random(&mut self, true_length: PathLength) -> Option<Rank> {
-        let showing = u8::try_from(self.0.count_ones()).expect("There aren't that many bits");
+        let showing = usize::try_from(self.0.count_ones()).expect("There aren't that many bits");
         let not_showing = NUM_RANKS - showing;
         if not_showing <= 1 {
             return None;
@@ -50,7 +72,7 @@ impl PathLengthInfo {
 
         let mut show = rand::thread_rng().gen_range(0..not_showing - 1);
         for i in 0..NUM_RANKS {
-            let r = Rank(i);
+            let r = Rank(u8::try_from(i).expect("Too many cards?"));
             if !self.is_showing(r) && r != true_length.0 {
                 if show == 0 {
                     self.reveal(r);
@@ -63,6 +85,36 @@ impl PathLengthInfo {
     }
 }
 
+#[derive(Default)]
+pub struct Discard {
+    cards: Vec<Card>,
+}
+impl Discard {
+    pub fn discard(&mut self, card: Card) {
+        self.cards.push(card);
+    }
+}
+
+pub struct Library {
+    cards: Vec<Card>,
+}
+impl Library {
+    #[must_use]
+    pub fn new(cards: Vec<Card>) -> Self {
+        Self { cards }
+    }
+    pub fn draw(&mut self, discard: &mut Discard) -> Option<Card> {
+        if self.cards.is_empty() {
+            if let Some(top_discard) = discard.cards.pop() {
+                std::mem::swap(&mut self.cards, &mut discard.cards);
+                discard.discard(top_discard);
+                // TODO: Shuffle
+            }
+        }
+        self.cards.pop()
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -87,6 +139,26 @@ mod tests {
 
     #[test]
     fn test_deck() {
-        let _d = deck();
+        use WithOrWithoutJokers::*;
+        let d = deck(WithoutJokers);
+        let rank_sum: u32 = d
+            .iter()
+            .map(Card::rank)
+            .flatten()
+            .map(|r| u32::from(r.value()))
+            .sum();
+        assert_eq!(rank_sum, 364);
+        let _dj = deck(WithJokers);
+    }
+
+    #[test]
+    fn test_library() {
+        let mut lib = Library::new(vec![Card(7)]);
+        let mut dis = Discard::default();
+        dis.discard(Card(8));
+        dis.discard(Card(9));
+        assert_eq!(lib.draw(&mut dis), Some(Card(7)));
+        assert_eq!(lib.draw(&mut dis), Some(Card(8)));
+        assert_eq!(lib.draw(&mut dis), None);
     }
 }