ckc_rs/cards/
two.rs

1use crate::cards::HandValidator;
2use crate::{BinaryCard, CKCNumber, CardNumber, HandError, PokerCard, Shifty, BC64};
3use core::cmp;
4use core::slice::Iter;
5use serde::{Deserialize, Serialize};
6
7#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
8pub struct Two([CKCNumber; 2]);
9
10#[allow(non_upper_case_globals)]
11impl Two {
12    //region hands
13    pub const AA: [Two; 6] = [
14        Two([CardNumber::ACE_SPADES, CardNumber::ACE_HEARTS]),
15        Two([CardNumber::ACE_SPADES, CardNumber::ACE_DIAMONDS]),
16        Two([CardNumber::ACE_SPADES, CardNumber::ACE_CLUBS]),
17        Two([CardNumber::ACE_HEARTS, CardNumber::ACE_DIAMONDS]),
18        Two([CardNumber::ACE_HEARTS, CardNumber::ACE_CLUBS]),
19        Two([CardNumber::ACE_DIAMONDS, CardNumber::ACE_CLUBS]),
20    ];
21    pub const AK: [Two; 16] = [
22        Two([CardNumber::ACE_SPADES, CardNumber::KING_SPADES]),
23        Two([CardNumber::ACE_HEARTS, CardNumber::KING_HEARTS]),
24        Two([CardNumber::ACE_DIAMONDS, CardNumber::KING_DIAMONDS]),
25        Two([CardNumber::ACE_CLUBS, CardNumber::KING_CLUBS]),
26        Two([CardNumber::ACE_SPADES, CardNumber::KING_HEARTS]),
27        Two([CardNumber::ACE_SPADES, CardNumber::KING_DIAMONDS]),
28        Two([CardNumber::ACE_SPADES, CardNumber::KING_CLUBS]),
29        Two([CardNumber::ACE_HEARTS, CardNumber::KING_SPADES]),
30        Two([CardNumber::ACE_HEARTS, CardNumber::KING_DIAMONDS]),
31        Two([CardNumber::ACE_HEARTS, CardNumber::KING_CLUBS]),
32        Two([CardNumber::ACE_DIAMONDS, CardNumber::KING_SPADES]),
33        Two([CardNumber::ACE_DIAMONDS, CardNumber::KING_HEARTS]),
34        Two([CardNumber::ACE_DIAMONDS, CardNumber::KING_CLUBS]),
35        Two([CardNumber::ACE_CLUBS, CardNumber::KING_SPADES]),
36        Two([CardNumber::ACE_CLUBS, CardNumber::KING_HEARTS]),
37        Two([CardNumber::ACE_CLUBS, CardNumber::KING_DIAMONDS]),
38    ];
39
40    // pub const aaa: [Two; 4] = &Two::AK[0..3].clone();
41
42    pub const AKs: [Two; 4] = [Two::AK[0], Two::AK[1], Two::AK[2], Two::AK[3]];
43    pub const AKo: [Two; 12] = [
44        Two::AK[4],
45        Two::AK[5],
46        Two::AK[6],
47        Two::AK[7],
48        Two::AK[8],
49        Two::AK[9],
50        Two::AK[10],
51        Two::AK[11],
52        Two::AK[12],
53        Two::AK[13],
54        Two::AK[14],
55        Two::AK[15],
56    ];
57
58    pub const AQs: [Two; 4] = [
59        Two([CardNumber::ACE_SPADES, CardNumber::QUEEN_SPADES]),
60        Two([CardNumber::ACE_HEARTS, CardNumber::QUEEN_HEARTS]),
61        Two([CardNumber::ACE_DIAMONDS, CardNumber::QUEEN_DIAMONDS]),
62        Two([CardNumber::ACE_CLUBS, CardNumber::QUEEN_CLUBS]),
63    ];
64    pub const AQo: [Two; 12] = [
65        Two([CardNumber::ACE_SPADES, CardNumber::QUEEN_HEARTS]),
66        Two([CardNumber::ACE_SPADES, CardNumber::QUEEN_DIAMONDS]),
67        Two([CardNumber::ACE_SPADES, CardNumber::QUEEN_CLUBS]),
68        Two([CardNumber::ACE_HEARTS, CardNumber::QUEEN_SPADES]),
69        Two([CardNumber::ACE_HEARTS, CardNumber::QUEEN_DIAMONDS]),
70        Two([CardNumber::ACE_HEARTS, CardNumber::QUEEN_CLUBS]),
71        Two([CardNumber::ACE_DIAMONDS, CardNumber::QUEEN_SPADES]),
72        Two([CardNumber::ACE_DIAMONDS, CardNumber::QUEEN_HEARTS]),
73        Two([CardNumber::ACE_DIAMONDS, CardNumber::QUEEN_CLUBS]),
74        Two([CardNumber::ACE_CLUBS, CardNumber::QUEEN_SPADES]),
75        Two([CardNumber::ACE_CLUBS, CardNumber::QUEEN_HEARTS]),
76        Two([CardNumber::ACE_CLUBS, CardNumber::QUEEN_DIAMONDS]),
77    ];
78
79    //endregion
80    #[must_use]
81    pub fn new(first: CKCNumber, second: CKCNumber) -> Self {
82        Self([first, second])
83    }
84
85    fn from_index(index: &str) -> Option<[CKCNumber; 2]> {
86        let mut esses = index.split_whitespace();
87
88        let first = CKCNumber::from_index(esses.next()?);
89        let second = CKCNumber::from_index(esses.next()?);
90        let hand: [CKCNumber; 2] = [first, second];
91        Some(hand)
92    }
93
94    //region accessors
95
96    #[must_use]
97    pub fn second(&self) -> CKCNumber {
98        self.0[1]
99    }
100
101    pub fn set_first(&mut self, card_number: CKCNumber) {
102        self.0[0] = card_number;
103    }
104
105    pub fn set_second(&mut self, card_number: CKCNumber) {
106        self.0[1] = card_number;
107    }
108
109    #[must_use]
110    pub fn to_arr(&self) -> [CKCNumber; 2] {
111        self.0
112    }
113
114    //endregion
115
116    #[must_use]
117    #[allow(clippy::cast_possible_truncation)]
118    pub fn chen_formula(&self) -> i8 {
119        let high_card = self.high_card();
120        let mut points = high_card.get_chen_points();
121
122        if self.is_pocket_pair() {
123            points = f32::max(points * 2.0, 5.0);
124        } else {
125            let gap = self.get_gap();
126            points -= match gap {
127                1 => 1.0,
128                2 => 2.0,
129                3 => 4.0,
130                0 => 0.0,
131                _ => 5.0,
132            };
133
134            let top_rank = high_card.get_card_rank() as u8;
135            if (gap < 2) && (top_rank < 12u8) {
136                points += 1.0;
137            }
138        }
139
140        if self.is_suited() {
141            points += 2.0;
142        }
143
144        points.ceil() as i8
145    }
146
147    #[must_use]
148    pub fn get_gap(&self) -> u8 {
149        let s = self.sort();
150        let distance_between = s.first().get_card_rank() as u8 - s.second().get_card_rank() as u8;
151        distance_between.saturating_sub(1)
152    }
153
154    #[must_use]
155    pub fn high_card(&self) -> CKCNumber {
156        cmp::max(self.first(), self.second())
157    }
158
159    #[must_use]
160    pub fn is_connector(&self) -> bool {
161        self.get_gap() == 0
162    }
163
164    #[must_use]
165    pub fn is_pocket_pair(&self) -> bool {
166        self.first().get_card_rank() == self.second().get_card_rank()
167    }
168
169    #[must_use]
170    pub fn is_suited(&self) -> bool {
171        self.first().get_card_suit() == self.second().get_card_suit()
172    }
173
174    #[must_use]
175    pub fn is_suited_connector(&self) -> bool {
176        self.is_suited() && self.is_connector()
177    }
178
179    //region vs
180    //endregion -> Result Preflop <-
181
182    // pub fn types() -> Vec<&str> {
183    //     vec![
184    //         "A♠ A♥ A♦ A♣",  // EQUALS
185    //         "A♠ A♥ A♦ K♦",  // Dominated / Connector / Suited
186    //         "A♠ A♥ A♦ K♠",  // Dominated / Partially Covered / Connector / Off
187    //         "A♠ A♥ K♠ K♥",  // Dominated / Covered / Connector / Off
188    //         "A♠ A♥ K♠ K♥",  // Dominated / Covered / Connector / Off
189    //     ]
190    // }
191
192    //endregion
193}
194
195impl From<&[CKCNumber; 2]> for Two {
196    fn from(array: &[CKCNumber; 2]) -> Self {
197        Two(*array)
198    }
199}
200
201impl From<[CKCNumber; 2]> for Two {
202    fn from(array: [CKCNumber; 2]) -> Self {
203        Two(array)
204    }
205}
206
207impl TryFrom<&'static str> for Two {
208    type Error = HandError;
209
210    /// # Errors
211    ///
212    /// Will return `CardError::InvalidIndex` for an invalid index.
213    fn try_from(index: &'static str) -> Result<Self, Self::Error> {
214        match Two::from_index(index) {
215            None => Err(HandError::InvalidIndex),
216            Some(five) => Ok(Two::from(five)),
217        }
218    }
219}
220
221impl TryFrom<BinaryCard> for Two {
222    type Error = HandError;
223
224    fn try_from(binary_card: BinaryCard) -> Result<Self, Self::Error> {
225        match binary_card.number_of_cards() {
226            0..=1 => Err(HandError::NotEnoughCards),
227            2 => {
228                let mut bc = binary_card;
229                let two = Two::new(
230                    CKCNumber::from_binary_card(bc.peel()),
231                    CKCNumber::from_binary_card(bc.peel()),
232                );
233                if two.is_valid() {
234                    Ok(two)
235                } else {
236                    Err(HandError::InvalidBinaryFormat)
237                }
238            },
239            _ => Err(HandError::TooManyCards),
240        }
241    }
242}
243
244impl HandValidator for Two {
245    fn are_unique(&self) -> bool {
246        self.first() != self.second()
247    }
248
249    fn first(&self) -> CKCNumber {
250        self.0[0]
251    }
252
253    fn sort(&self) -> Self {
254        let mut array = *self;
255        array.sort_in_place();
256        array
257    }
258
259    fn sort_in_place(&mut self) {
260        self.0.sort_unstable();
261        self.0.reverse();
262    }
263
264    fn iter(&self) -> Iter<'_, CKCNumber> {
265        self.0.iter()
266    }
267}
268
269impl Shifty for Two {
270    fn shift_suit(&self) -> Self {
271        Two::new(self.first().shift_suit(), self.second().shift_suit())
272    }
273}
274
275#[cfg(test)]
276#[allow(non_snake_case)]
277mod cards_two_tests {
278    use super::*;
279    use crate::CardNumber;
280    use rstest::rstest;
281
282    #[test]
283    fn ak() {
284        let aks = &Two::AK[..4];
285
286        assert_eq!(aks, Two::AKs);
287    }
288
289    #[test]
290    fn are_unique() {
291        assert!(!Two::new(CardNumber::ACE_CLUBS, CardNumber::ACE_CLUBS).are_unique());
292        assert!(!Two::new(CardNumber::BLANK, CardNumber::BLANK).are_unique());
293        assert!(Two::new(CardNumber::ACE_SPADES, CardNumber::ACE_CLUBS).are_unique());
294    }
295
296    #[test]
297    fn contain_blank() {
298        assert!(!Two::new(CardNumber::ACE_SPADES, CardNumber::ACE_CLUBS).contain_blank());
299        assert!(!Two::new(CardNumber::ACE_CLUBS, CardNumber::ACE_CLUBS).contain_blank());
300        assert!(Two::new(CardNumber::BLANK, CardNumber::BLANK).contain_blank());
301        assert!(Two::new(CardNumber::BLANK, CardNumber::ACE_CLUBS).contain_blank());
302        assert!(Two::new(CardNumber::ACE_CLUBS, CardNumber::BLANK).contain_blank());
303    }
304
305    #[test]
306    fn is_valid() {
307        assert!(!Two::new(CardNumber::ACE_CLUBS, CardNumber::ACE_CLUBS).is_valid());
308        assert!(!Two::new(CardNumber::BLANK, CardNumber::BLANK).is_valid());
309        assert!(!Two::new(CardNumber::BLANK, CardNumber::ACE_CLUBS).is_valid());
310        assert!(!Two::new(CardNumber::ACE_CLUBS, CardNumber::BLANK).is_valid());
311        assert!(Two::new(CardNumber::ACE_SPADES, CardNumber::ACE_CLUBS).is_valid());
312    }
313
314    #[rstest]
315    #[case(20, Two::new(CardNumber::ACE_SPADES, CardNumber::ACE_CLUBS))]
316    #[case(12, Two::new(CardNumber::ACE_SPADES, CardNumber::KING_SPADES))]
317    #[case(10, Two::new(CardNumber::ACE_SPADES, CardNumber::KING_CLUBS))]
318    #[case(16, Two::new(CardNumber::KING_SPADES, CardNumber::KING_CLUBS))]
319    #[case(14, Two::new(CardNumber::QUEEN_SPADES, CardNumber::QUEEN_CLUBS))]
320    #[case(12, Two::new(CardNumber::JACK_SPADES, CardNumber::JACK_CLUBS))]
321    #[case(10, Two::new(CardNumber::TEN_SPADES, CardNumber::TEN_CLUBS))]
322    #[case(9, Two::new(CardNumber::JACK_SPADES, CardNumber::TEN_SPADES))]
323    #[case(5, Two::new(CardNumber::FIVE_SPADES, CardNumber::ACE_CLUBS))]
324    #[case(7, Two::new(CardNumber::FIVE_SPADES, CardNumber::ACE_SPADES))]
325    #[case(6, Two::new(CardNumber::FIVE_SPADES, CardNumber::SIX_SPADES))]
326    #[case(5, Two::new(CardNumber::TREY_SPADES, CardNumber::TREY_CLUBS))]
327    #[case(5, Two::new(CardNumber::DEUCE_SPADES, CardNumber::DEUCE_CLUBS))]
328    #[case(-1, Two::new(CardNumber::DEUCE_SPADES, CardNumber::SEVEN_CLUBS))]
329    fn chen_formula(#[case] chen_number: i8, #[case] hand: Two) {
330        assert_eq!(chen_number, hand.chen_formula());
331    }
332
333    #[test]
334    fn get_gap() {
335        assert_eq!(11, Two::new(CardNumber::DEUCE_CLUBS, CardNumber::ACE_CLUBS).get_gap());
336        assert_eq!(11, Two::new(CardNumber::ACE_SPADES, CardNumber::DEUCE_CLUBS).get_gap());
337        assert_eq!(0, Two::new(CardNumber::ACE_SPADES, CardNumber::ACE_CLUBS).get_gap());
338    }
339
340    #[test]
341    fn high_card() {
342        let hand = Two::new(CardNumber::ACE_CLUBS, CardNumber::KING_SPADES);
343
344        assert_eq!(hand.high_card(), CardNumber::ACE_CLUBS);
345    }
346
347    #[test]
348    fn is_connector() {
349        assert!(Two::new(CardNumber::ACE_CLUBS, CardNumber::KING_SPADES).is_connector());
350        assert!(!Two::new(CardNumber::ACE_CLUBS, CardNumber::DEUCE_CLUBS).is_connector());
351    }
352
353    #[test]
354    fn is_pocket_pair() {
355        assert!(Two::new(CardNumber::ACE_CLUBS, CardNumber::ACE_SPADES).is_pocket_pair());
356        assert!(!Two::new(CardNumber::ACE_CLUBS, CardNumber::KING_SPADES).is_pocket_pair());
357    }
358
359    #[test]
360    fn is_suited() {
361        assert!(Two::new(CardNumber::ACE_CLUBS, CardNumber::KING_CLUBS).is_suited());
362        assert!(!Two::new(CardNumber::ACE_CLUBS, CardNumber::KING_SPADES).is_suited());
363    }
364
365    #[test]
366    fn is_suited_connector() {
367        assert!(Two::new(CardNumber::ACE_CLUBS, CardNumber::KING_CLUBS).is_suited_connector());
368        assert!(Two::new(CardNumber::NINE_CLUBS, CardNumber::EIGHT_CLUBS).is_suited_connector());
369        assert!(!Two::new(CardNumber::NINE_CLUBS, CardNumber::EIGHT_DIAMONDS).is_suited_connector());
370        assert!(!Two::new(CardNumber::NINE_CLUBS, CardNumber::SEVEN_CLUBS).is_suited_connector());
371        assert!(!Two::new(CardNumber::ACE_CLUBS, CardNumber::KING_SPADES).is_suited_connector());
372    }
373
374    #[test]
375    fn shifty__shift_suit() {
376        assert_eq!(
377            Two::try_from("AS AD").unwrap().shift_suit(),
378            Two::try_from("AH AC").unwrap()
379        )
380    }
381
382    #[test]
383    fn try_from__binary_card() {
384        let t = Two::try_from(BinaryCard::ACE_SPADES.fold_in(BinaryCard::ACE_DIAMONDS));
385        assert!(t.is_ok());
386        assert!(!t.is_err());
387        assert_eq!(Two::new(CardNumber::ACE_SPADES, CardNumber::ACE_DIAMONDS), t.unwrap());
388    }
389
390    #[test]
391    fn try_from__binary_card__not_enough() {
392        let t = Two::try_from(BinaryCard::ACE_SPADES);
393        assert!(t.is_err());
394        assert_eq!(t.unwrap_err(), HandError::NotEnoughCards);
395        assert_eq!(Two::try_from(BinaryCard::BLANK).unwrap_err(), HandError::NotEnoughCards);
396    }
397
398    #[test]
399    fn try_from__binary_card__too_many() {
400        let t = Two::try_from(
401            BinaryCard::ACE_SPADES
402                .fold_in(BinaryCard::ACE_DIAMONDS)
403                .fold_in(BinaryCard::ACE_CLUBS),
404        );
405        assert!(t.is_err());
406        assert_eq!(t.unwrap_err(), HandError::TooManyCards);
407    }
408
409    #[test]
410    fn try_from__binary_card__invalid_binary_format() {
411        let bc = 0b1_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000
412            .fold_in(0b10_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000);
413        let t = Two::try_from(bc);
414        assert!(t.is_err());
415        assert_eq!(t.unwrap_err(), HandError::InvalidBinaryFormat);
416    }
417
418    #[test]
419    fn try_from__index() {
420        let two = Two::try_from("J♠ T♠");
421
422        assert!(two.is_ok());
423        let two = two.unwrap();
424        assert_eq!(two.first(), CardNumber::JACK_SPADES);
425        assert_eq!(two.second(), CardNumber::TEN_SPADES);
426    }
427
428    #[test]
429    fn try_from__index__blank() {
430        let two = Two::try_from("A♠ XX");
431
432        assert!(two.is_ok());
433        let two = two.unwrap();
434        assert_eq!(two.first(), CardNumber::ACE_SPADES);
435        assert_eq!(two.second(), CardNumber::BLANK);
436    }
437
438    #[test]
439    fn try_from__index__too_short() {
440        let two = Two::try_from("A♠");
441
442        assert!(two.is_err());
443    }
444}