]> git.scottworley.com Git - pluta-lesnura/blame - src/lib.rs
Hands
[pluta-lesnura] / src / lib.rs
CommitLineData
57f490a0
SW
1use rand::Rng;
2
95b540e1
SW
3pub const NUM_RANKS: usize = 13;
4pub const NUM_SUITS: usize = 4;
5pub const NUM_JOKERS: usize = 2;
6pub const NUM_CARDS: usize = NUM_RANKS * NUM_SUITS + NUM_JOKERS;
57f490a0
SW
7
8#[derive(Clone, Copy, Eq, PartialEq)]
9pub struct Rank(u8);
b8e315ac
SW
10impl Rank {
11 #[must_use]
12 pub fn value(&self) -> u8 {
13 self.0 + 1
14 }
15}
57f490a0 16
09822a98
SW
17#[derive(Clone, Copy, Eq, PartialEq)]
18pub struct Suit(u8);
19
644e6c7a 20#[derive(Clone, Copy, Debug, Eq, PartialEq)]
09822a98
SW
21pub struct Card(u8);
22impl Card {
23 #[must_use]
10e7da7b 24 pub fn is_joker(&self) -> bool {
95b540e1 25 usize::from(self.0) >= NUM_RANKS * NUM_SUITS
09822a98
SW
26 }
27 #[must_use]
10e7da7b
SW
28 pub fn rank(&self) -> Option<Rank> {
29 (!self.is_joker()).then_some(Rank(self.0 >> 2))
09822a98 30 }
10e7da7b
SW
31 #[must_use]
32 pub fn suit(&self) -> Option<Suit> {
33 (!self.is_joker()).then_some(Suit(self.0 & 3))
34 }
35}
36
37#[derive(Clone, Copy)]
38pub enum WithOrWithoutJokers {
39 WithJokers,
40 WithoutJokers,
09822a98
SW
41}
42
43#[must_use]
10e7da7b 44pub fn deck(j: WithOrWithoutJokers) -> Vec<Card> {
95b540e1 45 let limit = u8::try_from(match j {
10e7da7b
SW
46 WithOrWithoutJokers::WithJokers => NUM_CARDS,
47 WithOrWithoutJokers::WithoutJokers => NUM_SUITS * NUM_RANKS,
95b540e1
SW
48 })
49 .expect("Too many cards?");
10e7da7b 50 (0..limit).map(Card).collect()
09822a98
SW
51}
52
57f490a0
SW
53#[derive(Clone, Copy)]
54pub struct PathLength(Rank);
55
56#[derive(Clone, Copy, Default)]
57pub struct PathLengthInfo(u16);
58impl PathLengthInfo {
59 #[must_use]
60 pub fn is_showing(&self, i: Rank) -> bool {
61 (self.0 >> i.0) & 1 == 1
62 }
63 fn reveal(&mut self, i: Rank) {
64 self.0 |= 1 << i.0;
65 }
66 pub fn reveal_random(&mut self, true_length: PathLength) -> Option<Rank> {
95b540e1 67 let showing = usize::try_from(self.0.count_ones()).expect("There aren't that many bits");
57f490a0
SW
68 let not_showing = NUM_RANKS - showing;
69 if not_showing <= 1 {
70 return None;
71 }
72
73 let mut show = rand::thread_rng().gen_range(0..not_showing - 1);
74 for i in 0..NUM_RANKS {
95b540e1 75 let r = Rank(u8::try_from(i).expect("Too many cards?"));
57f490a0
SW
76 if !self.is_showing(r) && r != true_length.0 {
77 if show == 0 {
78 self.reveal(r);
79 return Some(r);
80 }
81 show -= 1;
82 }
83 }
84 unreachable!()
85 }
754e9730
SW
86}
87
644e6c7a
SW
88#[derive(Default)]
89pub struct Discard {
90 cards: Vec<Card>,
91}
92impl Discard {
93 pub fn discard(&mut self, card: Card) {
94 self.cards.push(card);
95 }
96}
97
98pub struct Library {
99 cards: Vec<Card>,
100}
101impl Library {
102 #[must_use]
103 pub fn new(cards: Vec<Card>) -> Self {
104 Self { cards }
105 }
106 pub fn draw(&mut self, discard: &mut Discard) -> Option<Card> {
107 if self.cards.is_empty() {
108 if let Some(top_discard) = discard.cards.pop() {
109 std::mem::swap(&mut self.cards, &mut discard.cards);
110 discard.discard(top_discard);
111 // TODO: Shuffle
112 }
113 }
114 self.cards.pop()
115 }
116}
117
b255b7c6
SW
118#[cfg(test)]
119#[derive(Default)]
120struct Hand {
121 cards: Vec<Card>,
122}
123#[cfg(test)]
124impl Hand {
125 fn add(&mut self, card: Card) {
126 self.cards.push(card);
127 }
128 fn remove(&mut self, card: Card) -> Result<(), &'static str> {
129 let i = self
130 .cards
131 .iter()
132 .position(|&e| e == card)
133 .ok_or("That card is not in your hand")?;
134 self.cards.swap_remove(i);
135 Ok(())
136 }
137}
138
754e9730
SW
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
57f490a0
SW
144 fn path_length_info_random_reveal() {
145 let length = PathLength(Rank(7));
146 let mut pli = PathLengthInfo::default();
147 for _ in 0..12 {
148 let old_pli = PathLengthInfo::clone(&pli);
149 match pli.reveal_random(length) {
150 None => panic!("Nothing revealed?"),
151 Some(r) => {
152 assert!(!old_pli.is_showing(r));
153 assert!(pli.is_showing(r));
154 }
155 }
156 assert_eq!(pli.0.count_ones(), 1 + old_pli.0.count_ones());
157 }
158 assert!(pli.reveal_random(length).is_none());
754e9730 159 }
09822a98
SW
160
161 #[test]
162 fn test_deck() {
10e7da7b 163 use WithOrWithoutJokers::*;
b8e315ac
SW
164 let d = deck(WithoutJokers);
165 let rank_sum: u32 = d
166 .iter()
167 .map(Card::rank)
168 .flatten()
169 .map(|r| u32::from(r.value()))
170 .sum();
171 assert_eq!(rank_sum, 364);
10e7da7b 172 let _dj = deck(WithJokers);
09822a98 173 }
644e6c7a
SW
174
175 #[test]
176 fn test_library() {
177 let mut lib = Library::new(vec![Card(7)]);
178 let mut dis = Discard::default();
179 dis.discard(Card(8));
180 dis.discard(Card(9));
181 assert_eq!(lib.draw(&mut dis), Some(Card(7)));
182 assert_eq!(lib.draw(&mut dis), Some(Card(8)));
183 assert_eq!(lib.draw(&mut dis), None);
184 }
b255b7c6
SW
185
186 #[test]
187 fn test_hand() {
188 let mut h = Hand::default();
189 assert!(h.remove(Card(4)).is_err());
190 h.add(Card(4));
191 assert!(h.remove(Card(3)).is_err());
192 assert!(h.remove(Card(4)).is_ok());
193 assert!(h.remove(Card(4)).is_err());
194 }
754e9730 195}