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! flop {
14    ($s:expr) => {
15        $crate::Flop::from(
16            <[$crate::Card; 3]>::try_from($crate::Card::new_vec($s)).unwrap(),
17        )
18    };
19}
20
21#[cfg(any(test, feature = "benchmark"))]
22#[macro_export]
23macro_rules! board {
24    ($s:expr) => {
25        $crate::Board::from(
26            $crate::Card::new_vec($s).as_ref() as &[$crate::Card]
27        )
28    };
29}
30
31/// Single Card
32#[derive(Copy, Clone, PartialEq, Eq, Display)]
33#[display("{r}{s}")]
34pub struct Card {
35    pub r: Rank,
36    pub s: Suit,
37}
38
39impl Hash for Card {
40    fn hash<H: Hasher>(&self, state: &mut H) {
41        ((self.s as u8) * N_RANKS + (self.r as u8)).hash(state);
42    }
43}
44
45impl Card {
46    pub(crate) const C_2S: Self = Self::new(Rank::R2, Suit::S);
47    pub(crate) const C_3S: Self = Self::new(Rank::R3, Suit::S);
48    pub(crate) const C_4S: Self = Self::new(Rank::R4, Suit::S);
49    pub(crate) const C_5S: Self = Self::new(Rank::R5, Suit::S);
50    pub(crate) const C_6S: Self = Self::new(Rank::R6, Suit::S);
51    pub(crate) const C_7S: Self = Self::new(Rank::R7, Suit::S);
52    pub(crate) const C_8S: Self = Self::new(Rank::R8, Suit::S);
53    pub(crate) const C_9S: Self = Self::new(Rank::R9, Suit::S);
54    pub(crate) const C_TS: Self = Self::new(Rank::RT, Suit::S);
55    pub(crate) const C_JS: Self = Self::new(Rank::RJ, Suit::S);
56    pub(crate) const C_QS: Self = Self::new(Rank::RQ, Suit::S);
57    pub(crate) const C_KS: Self = Self::new(Rank::RK, Suit::S);
58    pub(crate) const C_AS: Self = Self::new(Rank::RA, Suit::S);
59
60    pub(crate) const C_2H: Self = Self::new(Rank::R2, Suit::H);
61    pub(crate) const C_3H: Self = Self::new(Rank::R3, Suit::H);
62    pub(crate) const C_4H: Self = Self::new(Rank::R4, Suit::H);
63    pub(crate) const C_5H: Self = Self::new(Rank::R5, Suit::H);
64    pub(crate) const C_6H: Self = Self::new(Rank::R6, Suit::H);
65    pub(crate) const C_7H: Self = Self::new(Rank::R7, Suit::H);
66    pub(crate) const C_8H: Self = Self::new(Rank::R8, Suit::H);
67    pub(crate) const C_9H: Self = Self::new(Rank::R9, Suit::H);
68    pub(crate) const C_TH: Self = Self::new(Rank::RT, Suit::H);
69    pub(crate) const C_JH: Self = Self::new(Rank::RJ, Suit::H);
70    pub(crate) const C_QH: Self = Self::new(Rank::RQ, Suit::H);
71    pub(crate) const C_KH: Self = Self::new(Rank::RK, Suit::H);
72    pub(crate) const C_AH: Self = Self::new(Rank::RA, Suit::H);
73
74    pub(crate) const C_2D: Self = Self::new(Rank::R2, Suit::D);
75    pub(crate) const C_3D: Self = Self::new(Rank::R3, Suit::D);
76    pub(crate) const C_4D: Self = Self::new(Rank::R4, Suit::D);
77    pub(crate) const C_5D: Self = Self::new(Rank::R5, Suit::D);
78    pub(crate) const C_6D: Self = Self::new(Rank::R6, Suit::D);
79    pub(crate) const C_7D: Self = Self::new(Rank::R7, Suit::D);
80    pub(crate) const C_8D: Self = Self::new(Rank::R8, Suit::D);
81    pub(crate) const C_9D: Self = Self::new(Rank::R9, Suit::D);
82    pub(crate) const C_TD: Self = Self::new(Rank::RT, Suit::D);
83    pub(crate) const C_JD: Self = Self::new(Rank::RJ, Suit::D);
84    pub(crate) const C_QD: Self = Self::new(Rank::RQ, Suit::D);
85    pub(crate) const C_KD: Self = Self::new(Rank::RK, Suit::D);
86    pub(crate) const C_AD: Self = Self::new(Rank::RA, Suit::D);
87
88    pub(crate) const C_2C: Self = Self::new(Rank::R2, Suit::C);
89    pub(crate) const C_3C: Self = Self::new(Rank::R3, Suit::C);
90    pub(crate) const C_4C: Self = Self::new(Rank::R4, Suit::C);
91    pub(crate) const C_5C: Self = Self::new(Rank::R5, Suit::C);
92    pub(crate) const C_6C: Self = Self::new(Rank::R6, Suit::C);
93    pub(crate) const C_7C: Self = Self::new(Rank::R7, Suit::C);
94    pub(crate) const C_8C: Self = Self::new(Rank::R8, Suit::C);
95    pub(crate) const C_9C: Self = Self::new(Rank::R9, Suit::C);
96    pub(crate) const C_TC: Self = Self::new(Rank::RT, Suit::C);
97    pub(crate) const C_JC: Self = Self::new(Rank::RJ, Suit::C);
98    pub(crate) const C_QC: Self = Self::new(Rank::RQ, Suit::C);
99    pub(crate) const C_KC: Self = Self::new(Rank::RK, Suit::C);
100    pub(crate) const C_AC: Self = Self::new(Rank::RA, Suit::C);
101
102    /// [ 2s, 3s, ... As, ..., Ah, ..., Ad, ..., Ac ]
103    pub const ARR_ALL: [Self; 52] = [
104        Self::C_2S,
105        Self::C_3S,
106        Self::C_4S,
107        Self::C_5S,
108        Self::C_6S,
109        Self::C_7S,
110        Self::C_8S,
111        Self::C_9S,
112        Self::C_TS,
113        Self::C_JS,
114        Self::C_QS,
115        Self::C_KS,
116        Self::C_AS,
117        Self::C_2H,
118        Self::C_3H,
119        Self::C_4H,
120        Self::C_5H,
121        Self::C_6H,
122        Self::C_7H,
123        Self::C_8H,
124        Self::C_9H,
125        Self::C_TH,
126        Self::C_JH,
127        Self::C_QH,
128        Self::C_KH,
129        Self::C_AH,
130        Self::C_2D,
131        Self::C_3D,
132        Self::C_4D,
133        Self::C_5D,
134        Self::C_6D,
135        Self::C_7D,
136        Self::C_8D,
137        Self::C_9D,
138        Self::C_TD,
139        Self::C_JD,
140        Self::C_QD,
141        Self::C_KD,
142        Self::C_AD,
143        Self::C_2C,
144        Self::C_3C,
145        Self::C_4C,
146        Self::C_5C,
147        Self::C_6C,
148        Self::C_7C,
149        Self::C_8C,
150        Self::C_9C,
151        Self::C_TC,
152        Self::C_JC,
153        Self::C_QC,
154        Self::C_KC,
155        Self::C_AC,
156    ];
157
158    /// [ 7s, 8s, ... As, ..., Ah, ..., Ad, ..., Ac ]
159    pub const ARR_ALL_SHORT: [Self; 32] = [
160        Self::C_7S,
161        Self::C_8S,
162        Self::C_9S,
163        Self::C_TS,
164        Self::C_JS,
165        Self::C_QS,
166        Self::C_KS,
167        Self::C_AS,
168        Self::C_7H,
169        Self::C_8H,
170        Self::C_9H,
171        Self::C_TH,
172        Self::C_JH,
173        Self::C_QH,
174        Self::C_KH,
175        Self::C_AH,
176        Self::C_7D,
177        Self::C_8D,
178        Self::C_9D,
179        Self::C_TD,
180        Self::C_JD,
181        Self::C_QD,
182        Self::C_KD,
183        Self::C_AD,
184        Self::C_7C,
185        Self::C_8C,
186        Self::C_9C,
187        Self::C_TC,
188        Self::C_JC,
189        Self::C_QC,
190        Self::C_KC,
191        Self::C_AC,
192    ];
193
194    /// Constructs [Card] from [Rank] and [Suit]
195    ///
196    /// # Examples
197    ///
198    /// ```
199    /// use open_pql::{Card, Rank, Suit};
200    ///
201    /// let rank: Rank = Rank::RA;
202    /// let suit: Suit = Suit::H;
203    ///
204    /// let card = Card::new(rank, suit);
205    ///
206    /// assert_eq!(card.r, rank); // Ace
207    /// assert_eq!(card.s, suit); // Heart
208    /// ```
209    #[must_use]
210    #[inline]
211    pub const fn new(r: Rank, s: Suit) -> Self {
212        Self { r, s }
213    }
214}
215
216impl Default for Card {
217    fn default() -> Self {
218        Self::C_2S
219    }
220}
221
222impl PartialOrd for Card {
223    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
224        if self == other {
225            Some(Ordering::Equal)
226        } else {
227            None
228        }
229    }
230}
231
232impl fmt::Debug for Card {
233    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234        let r = match self.r {
235            Rank::R2 => '2',
236            Rank::R3 => '3',
237            Rank::R4 => '4',
238            Rank::R5 => '5',
239            Rank::R6 => '6',
240            Rank::R7 => '7',
241            Rank::R8 => '8',
242            Rank::R9 => '9',
243            Rank::RT => 'T',
244            Rank::RJ => 'J',
245            Rank::RQ => 'Q',
246            Rank::RK => 'K',
247            Rank::RA => 'A',
248        };
249
250        let s = match self.s {
251            Suit::S => 's',
252            Suit::H => 'h',
253            Suit::D => 'd',
254            Suit::C => 'c',
255        };
256
257        f.write_str(&format!("{r}{s}"))
258    }
259}
260
261impl FromStr for Card {
262    type Err = ParseError;
263
264    fn from_str(s: &str) -> Result<Self, Self::Err> {
265        let mut cs = s.chars().filter(|c| !c.is_whitespace());
266
267        if let Some(c) = cs.next() {
268            if let Ok(r) = Rank::try_from(c) {
269                if let Some(c) = cs.next() {
270                    if let Ok(s) = Suit::try_from(c) {
271                        if cs.next().is_none() {
272                            return Ok(Self::new(r, s));
273                        }
274                    }
275                }
276            }
277        }
278
279        Err(ParseError::InvalidCard(s.into()))
280    }
281}
282
283impl<T> From<T> for Rank
284where
285    Card: From<T>,
286{
287    fn from(v: T) -> Self {
288        Card::from(v).r
289    }
290}
291
292impl<T> From<T> for Suit
293where
294    Card: From<T>,
295{
296    fn from(v: T) -> Self {
297        Card::from(v).s
298    }
299}
300
301#[cfg(any(test, feature = "benchmark"))]
302impl Card {
303    /// # Panics
304    /// this is intended to be used in tests and benchmarks only.
305    pub fn from_tuple((r, s): (char, char)) -> Self {
306        Self::new(r.try_into().unwrap(), s.try_into().unwrap())
307    }
308
309    pub fn new_vec(s: &str) -> Vec<Self> {
310        use itertools::Itertools;
311
312        s.chars()
313            .filter(|c| !c.is_whitespace())
314            .tuples()
315            .map(Self::from_tuple)
316            .collect()
317    }
318}
319
320#[cfg(test)]
321mod tests {
322    use super::*;
323
324    impl Arbitrary for Card {
325        fn arbitrary(g: &mut quickcheck::Gen) -> Self {
326            *g.choose(&Self::ARR_ALL).unwrap()
327        }
328    }
329
330    #[quickcheck]
331    fn test_rank_suit(r: Rank, s: Suit) {
332        let card = Card::new(r, s);
333
334        assert_eq!(r, card.r);
335        assert_eq!(s, card.s);
336    }
337
338    #[test]
339    fn test_default() {
340        assert_eq!(Card::C_2S, Card::default());
341    }
342
343    #[quickcheck]
344    fn test_hash(c1: Card) {
345        use std::hash::DefaultHasher;
346
347        let c2 = c1;
348
349        let mut h1 = DefaultHasher::new();
350        let mut h2 = DefaultHasher::new();
351
352        c1.hash(&mut h1);
353        c2.hash(&mut h2);
354
355        assert_eq!(h1.finish(), h2.finish());
356    }
357
358    #[quickcheck]
359    fn test_into_rank_and_suit(c: Card) {
360        let r: Rank = c.into();
361        let s: Suit = c.into();
362
363        assert_eq!(r, c.r);
364        assert_eq!(s, c.s);
365    }
366
367    #[test]
368    fn test_from_str() {
369        let c = |s| Ok(cards!(s)[0]);
370
371        assert_eq!(c("2s"), "2s".parse());
372        assert_eq!(c("2s"), " 2 S ".parse());
373        assert_eq!(
374            Err(ParseError::InvalidCard("2s?".to_owned())),
375            "2s?".parse::<Card>()
376        );
377        assert!("".parse::<Card>().is_err());
378        assert!("?".parse::<Card>().is_err());
379        assert!("2".parse::<Card>().is_err());
380        assert!("2k".parse::<Card>().is_err());
381    }
382
383    #[quickcheck]
384    fn test_to_string(c: Card) {
385        assert_eq!(format!("{}{}", c.r, c.s), c.to_string());
386    }
387
388    #[test]
389    fn test_macro() {
390        let cards = cards!("As Kh");
391        assert_eq!(cards, vec![Card::C_AS, Card::C_KH]);
392
393        let flop = flop!("2s 3s 4s");
394        assert_eq!(flop, (Card::C_2S, Card::C_3S, Card::C_4S).into());
395
396        let board = board!("As Kh 3s 5h 6c");
397        assert_eq!(
398            board,
399            (Card::C_AS, Card::C_KH, Card::C_3S, Card::C_5H, Card::C_6C).into(),
400        );
401    }
402
403    #[test]
404    fn test_partial_ord() {
405        assert!(Card::C_2S == Card::C_2S);
406        assert_eq!(Card::C_2S.partial_cmp(&Card::C_2S), Some(Ordering::Equal));
407
408        assert!(!(Card::C_2S <= Card::C_3S || Card::C_2S >= Card::C_3S));
409        assert!(Card::C_2S <= Card::C_2S);
410    }
411}