open_pql/base/
card.rs

1use super::*;
2
3#[cfg(any(test, feature = "benchmark"))]
4#[macro_export]
5macro_rules! cards {
6    ($s:expr) => {
7        $crate::Card::new_vec($s)
8    };
9}
10
11#[cfg(any(test, feature = "benchmark"))]
12#[macro_export]
13macro_rules! card {
14    ($s:expr) => {
15        cards!($s)[0]
16    };
17}
18
19#[cfg(any(test, feature = "benchmark"))]
20#[macro_export]
21macro_rules! flop {
22    ($s:expr) => {
23        $crate::Flop::from(
24            <[$crate::Card; 3]>::try_from($crate::Card::new_vec($s)).unwrap(),
25        )
26    };
27}
28
29#[cfg(any(test, feature = "benchmark"))]
30#[macro_export]
31macro_rules! board {
32    ($s:expr) => {
33        $crate::Board::from(
34            $crate::Card::new_vec($s).as_ref() as &[$crate::Card]
35        )
36    };
37}
38
39/// Single Card
40#[derive(Copy, Clone, Display, Hash, PartialEq, Eq, PartialOrd, Ord)]
41#[display("{rank}{suit}")]
42pub struct Card {
43    pub rank: Rank,
44    pub suit: Suit,
45}
46
47impl Card {
48    pub const ARR_ALL: [Self; 52] = [
49        Self::new(Rank::R2, Suit::S),
50        Self::new(Rank::R2, Suit::H),
51        Self::new(Rank::R2, Suit::D),
52        Self::new(Rank::R2, Suit::C),
53        Self::new(Rank::R3, Suit::S),
54        Self::new(Rank::R3, Suit::H),
55        Self::new(Rank::R3, Suit::D),
56        Self::new(Rank::R3, Suit::C),
57        Self::new(Rank::R4, Suit::S),
58        Self::new(Rank::R4, Suit::H),
59        Self::new(Rank::R4, Suit::D),
60        Self::new(Rank::R4, Suit::C),
61        Self::new(Rank::R5, Suit::S),
62        Self::new(Rank::R5, Suit::H),
63        Self::new(Rank::R5, Suit::D),
64        Self::new(Rank::R5, Suit::C),
65        Self::new(Rank::R6, Suit::S),
66        Self::new(Rank::R6, Suit::H),
67        Self::new(Rank::R6, Suit::D),
68        Self::new(Rank::R6, Suit::C),
69        Self::new(Rank::R7, Suit::S),
70        Self::new(Rank::R7, Suit::H),
71        Self::new(Rank::R7, Suit::D),
72        Self::new(Rank::R7, Suit::C),
73        Self::new(Rank::R8, Suit::S),
74        Self::new(Rank::R8, Suit::H),
75        Self::new(Rank::R8, Suit::D),
76        Self::new(Rank::R8, Suit::C),
77        Self::new(Rank::R9, Suit::S),
78        Self::new(Rank::R9, Suit::H),
79        Self::new(Rank::R9, Suit::D),
80        Self::new(Rank::R9, Suit::C),
81        Self::new(Rank::RT, Suit::S),
82        Self::new(Rank::RT, Suit::H),
83        Self::new(Rank::RT, Suit::D),
84        Self::new(Rank::RT, Suit::C),
85        Self::new(Rank::RJ, Suit::S),
86        Self::new(Rank::RJ, Suit::H),
87        Self::new(Rank::RJ, Suit::D),
88        Self::new(Rank::RJ, Suit::C),
89        Self::new(Rank::RQ, Suit::S),
90        Self::new(Rank::RQ, Suit::H),
91        Self::new(Rank::RQ, Suit::D),
92        Self::new(Rank::RQ, Suit::C),
93        Self::new(Rank::RK, Suit::S),
94        Self::new(Rank::RK, Suit::H),
95        Self::new(Rank::RK, Suit::D),
96        Self::new(Rank::RK, Suit::C),
97        Self::new(Rank::RA, Suit::S),
98        Self::new(Rank::RA, Suit::H),
99        Self::new(Rank::RA, Suit::D),
100        Self::new(Rank::RA, Suit::C),
101    ];
102
103    pub const ARR_ALL_SHORT: [Self; 36] = [
104        Self::new(Rank::R6, Suit::S),
105        Self::new(Rank::R6, Suit::H),
106        Self::new(Rank::R6, Suit::D),
107        Self::new(Rank::R6, Suit::C),
108        Self::new(Rank::R7, Suit::S),
109        Self::new(Rank::R7, Suit::H),
110        Self::new(Rank::R7, Suit::D),
111        Self::new(Rank::R7, Suit::C),
112        Self::new(Rank::R8, Suit::S),
113        Self::new(Rank::R8, Suit::H),
114        Self::new(Rank::R8, Suit::D),
115        Self::new(Rank::R8, Suit::C),
116        Self::new(Rank::R9, Suit::S),
117        Self::new(Rank::R9, Suit::H),
118        Self::new(Rank::R9, Suit::D),
119        Self::new(Rank::R9, Suit::C),
120        Self::new(Rank::RT, Suit::S),
121        Self::new(Rank::RT, Suit::H),
122        Self::new(Rank::RT, Suit::D),
123        Self::new(Rank::RT, Suit::C),
124        Self::new(Rank::RJ, Suit::S),
125        Self::new(Rank::RJ, Suit::H),
126        Self::new(Rank::RJ, Suit::D),
127        Self::new(Rank::RJ, Suit::C),
128        Self::new(Rank::RQ, Suit::S),
129        Self::new(Rank::RQ, Suit::H),
130        Self::new(Rank::RQ, Suit::D),
131        Self::new(Rank::RQ, Suit::C),
132        Self::new(Rank::RK, Suit::S),
133        Self::new(Rank::RK, Suit::H),
134        Self::new(Rank::RK, Suit::D),
135        Self::new(Rank::RK, Suit::C),
136        Self::new(Rank::RA, Suit::S),
137        Self::new(Rank::RA, Suit::H),
138        Self::new(Rank::RA, Suit::D),
139        Self::new(Rank::RA, Suit::C),
140    ];
141
142    #[must_use]
143    #[inline]
144    pub const fn new(r: Rank, s: Suit) -> Self {
145        Self { rank: r, suit: s }
146    }
147
148    #[must_use]
149    #[inline]
150    pub fn from_indices(r: u8, s: u8) -> Self {
151        Self {
152            rank: Rank::from_u8(r),
153            suit: Suit::from_u8(s),
154        }
155    }
156
157    pub const fn to_u8(self) -> u8 {
158        const SHIFT_SUIT: u8 = 4;
159        (self.rank as u8) | ((self.suit as u8) << SHIFT_SUIT)
160    }
161
162    pub fn from_u8(v: u8) -> Self {
163        const SHIFT_SUIT: u8 = 4;
164        Self::from_indices(v & 0b1111, v >> SHIFT_SUIT)
165    }
166}
167
168impl Default for Card {
169    fn default() -> Self {
170        Self::new(Rank::R2, Suit::S)
171    }
172}
173
174impl fmt::Debug for Card {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        write!(f, "{self}")
177    }
178}
179
180impl FromStr for Card {
181    type Err = ParseError;
182
183    fn from_str(s: &str) -> Result<Self, Self::Err> {
184        let mut cs = s.chars().filter(|c| !c.is_whitespace());
185
186        if let Some(c) = cs.next()
187            && let Ok(r) = Rank::try_from(c)
188            && let Some(c) = cs.next()
189            && let Ok(s) = Suit::try_from(c)
190            && cs.next().is_none()
191        {
192            return Ok(Self::new(r, s));
193        }
194
195        Err(ParseError::InvalidCard(s.into()))
196    }
197}
198
199impl<T> From<T> for Rank
200where
201    Card: From<T>,
202{
203    fn from(v: T) -> Self {
204        Card::from(v).rank
205    }
206}
207
208impl<T> From<T> for Suit
209where
210    Card: From<T>,
211{
212    fn from(v: T) -> Self {
213        Card::from(v).suit
214    }
215}
216
217#[cfg(any(test, feature = "benchmark"))]
218impl Card {
219    /// # Panics
220    /// this is intended to be used in tests and benchmarks only.
221    pub fn from_tuple((r, s): (char, char)) -> Self {
222        Self::new(r.try_into().unwrap(), s.try_into().unwrap())
223    }
224
225    pub fn new_vec(s: &str) -> Vec<Self> {
226        use itertools::Itertools;
227
228        s.chars()
229            .filter(|c| !c.is_whitespace())
230            .tuples()
231            .map(Self::from_tuple)
232            .collect()
233    }
234}
235
236#[cfg(test)]
237mod tests {
238    use super::*;
239
240    impl Arbitrary for Card {
241        fn arbitrary(g: &mut quickcheck::Gen) -> Self {
242            *g.choose(&Self::ARR_ALL).unwrap()
243        }
244    }
245
246    #[quickcheck]
247    fn test_rank_suit(r: Rank, s: Suit) {
248        let card = Card::new(r, s);
249
250        assert_eq!(r, card.rank);
251        assert_eq!(s, card.suit);
252    }
253
254    #[test]
255    fn test_default() {
256        assert_eq!(Card::new(Rank::R2, Suit::S), Card::default());
257        assert_eq!(card!("2s"), Card::default());
258    }
259
260    #[quickcheck]
261    fn test_hash(c1: Card) {
262        use std::hash::DefaultHasher;
263
264        let c2 = c1;
265
266        let mut h1 = DefaultHasher::new();
267        let mut h2 = DefaultHasher::new();
268
269        c1.hash(&mut h1);
270        c2.hash(&mut h2);
271
272        assert_eq!(h1.finish(), h2.finish());
273    }
274
275    #[quickcheck]
276    fn test_into_rank_and_suit(c: Card) {
277        let r: Rank = c.into();
278        let s: Suit = c.into();
279
280        assert_eq!(r, c.rank);
281        assert_eq!(s, c.suit);
282    }
283
284    #[test]
285    fn test_from_str() {
286        let c = |s| Ok(cards!(s)[0]);
287
288        assert_eq!(c("2s"), "2s".parse());
289        assert_eq!(c("2s"), " 2 S ".parse());
290        assert_eq!(
291            Err(ParseError::InvalidCard("2s?".to_owned())),
292            "2s?".parse::<Card>()
293        );
294        assert!("".parse::<Card>().is_err());
295        assert!("?".parse::<Card>().is_err());
296        assert!("2".parse::<Card>().is_err());
297        assert!("2k".parse::<Card>().is_err());
298    }
299
300    #[quickcheck]
301    fn test_to_string(c: Card) {
302        assert_eq!(format!("{}{}", c.rank, c.suit), c.to_string());
303    }
304
305    #[quickcheck]
306    fn test_to_u8(c: Card) {
307        assert_eq!(c, Card::from_u8(c.to_u8()));
308    }
309
310    #[test]
311    fn test_macro() {
312        let cards = cards!("As Kh");
313        assert_eq!(
314            cards,
315            vec![Card::new(Rank::RA, Suit::S), Card::new(Rank::RK, Suit::H),]
316        );
317
318        let flop = flop!("2s 3s 4s");
319        assert_eq!(
320            flop,
321            (
322                Card::new(Rank::R2, Suit::S),
323                Card::new(Rank::R3, Suit::S),
324                Card::new(Rank::R4, Suit::S),
325            )
326                .into()
327        );
328
329        let board = board!("As Kh 3s 5h 6c");
330        assert_eq!(
331            board,
332            (
333                Card::new(Rank::RA, Suit::S),
334                Card::new(Rank::RK, Suit::H),
335                Card::new(Rank::R3, Suit::S),
336                Card::new(Rank::R5, Suit::H),
337                Card::new(Rank::R6, Suit::C),
338            )
339                .into(),
340        );
341    }
342
343    #[test]
344    fn test_ord() {
345        let mut sorted = Card::ARR_ALL.to_vec();
346        sorted.reverse();
347        sorted.sort_unstable();
348
349        assert_eq!(sorted, Card::ARR_ALL);
350    }
351}