ckc_rs/
lib.rs

1#![cfg_attr(not(test), no_std)]
2#![warn(clippy::pedantic)]
3#![allow(clippy::unreadable_literal)]
4
5extern crate alloc;
6
7use crate::cards::binary_card::{BinaryCard, BC64};
8use crate::parse::get_rank_and_suit;
9use strum::EnumIter;
10
11pub mod cards;
12pub mod deck;
13pub mod hand_rank;
14mod lookups;
15pub mod parse;
16
17/// A `PokerCard` is a u32 representation of a variant of Cactus Kev's binary
18/// representation of a poker card as designed for rapid hand evaluation as
19/// documented [here](https://suffe.cool/poker/evaluator.html).
20///
21/// The variation being that the `Suit` bits order is inverted for easier sorting.
22/// ```txt
23/// +--------+--------+--------+--------+
24/// |mmmbbbbb|bbbbbbbb|SHDCrrrr|xxpppppp|
25/// +--------+--------+--------+--------+
26///
27/// p = prime number of rank (deuce=2,trey=3,four=5,...,ace=41)
28/// r = rank of card (deuce=0,trey=1,four=2,five=3,...,ace=12)
29/// SHDC = suit of card (bit turned on based on suit of card)
30/// b = bit turned on depending on rank of card
31/// m = Flags reserved for multiples of the same rank. Stripped for evals.
32/// ```
33pub type CKCNumber = u32;
34
35/// u32 constants for all 52 cards in a standard poker deck.
36pub struct CardNumber;
37
38#[rustfmt::skip]
39impl CardNumber {
40    pub const RANK_FLAG_FILTER: u32 = 0x1FFF0000; // 536805376 aka 0b00011111_11111111_00000000_00000000
41    pub const RANK_FLAG_SHIFT: u32 = 16;
42    pub const RANK_PRIME_FILTER: u32 = 0b00111111;
43
44    /// Binary filter for `CardNumber` `Suit` flags.
45    /// 00000000 00000000 11110000 00000000
46    pub const SUIT_FILTER: u32 = 0xF000; // 61440 aka 0b11110000_00000000
47    pub const SUIT_SHORT_MASK: u32 = 0b1111;
48    pub const SUIT_SHIFT: u32 = 12;
49
50    //region multiples
51
52    /// These flags are used to give sorting priority when more than one card
53    /// of a specific rank is present.
54    pub const PAIR: u32 = 536_870_912;
55    pub const TRIPS: u32 = 1_073_741_824;
56    pub const QUADS: u32 = 2_147_483_648;
57    pub const MULTIPLES_FILTER: u32 = 536_870_911;
58
59    //endregion
60
61    //region cardnumbers
62    pub const ACE_SPADES: CKCNumber     = 0b010000000000001000110000101001;
63    pub const KING_SPADES: CKCNumber    = 0b001000000000001000101100100101;
64    pub const QUEEN_SPADES: CKCNumber   = 0b000100000000001000101000011111;
65    pub const JACK_SPADES: CKCNumber    = 0b000010000000001000100100011101;
66    pub const TEN_SPADES: CKCNumber     = 0b000001000000001000100000010111;
67    pub const NINE_SPADES: CKCNumber    = 0b000000100000001000011100010011;
68    pub const EIGHT_SPADES: CKCNumber   = 0b000000010000001000011000010001;
69    pub const SEVEN_SPADES: CKCNumber   = 0b000000001000001000010100001101;
70    pub const SIX_SPADES: CKCNumber     = 0b000000000100001000010000001011;
71    pub const FIVE_SPADES: CKCNumber    = 0b000000000010001000001100000111;
72    pub const FOUR_SPADES: CKCNumber    = 0b000000000001001000001000000101;
73    pub const TREY_SPADES: CKCNumber    = 0b000000000000101000000100000011;
74    pub const DEUCE_SPADES: CKCNumber   = 0b000000000000011000000000000010;
75    pub const ACE_HEARTS: CKCNumber     = 0b010000000000000100110000101001;
76    pub const KING_HEARTS: CKCNumber    = 0b001000000000000100101100100101;
77    pub const QUEEN_HEARTS: CKCNumber   = 0b000100000000000100101000011111;
78    pub const JACK_HEARTS: CKCNumber    = 0b000010000000000100100100011101;
79    pub const TEN_HEARTS: CKCNumber     = 0b000001000000000100100000010111;
80    pub const NINE_HEARTS: CKCNumber    = 0b000000100000000100011100010011;
81    pub const EIGHT_HEARTS: CKCNumber   = 0b000000010000000100011000010001;
82    pub const SEVEN_HEARTS: CKCNumber   = 0b000000001000000100010100001101;
83    pub const SIX_HEARTS: CKCNumber     = 0b000000000100000100010000001011;
84    pub const FIVE_HEARTS: CKCNumber    = 0b000000000010000100001100000111;
85    pub const FOUR_HEARTS: CKCNumber    = 0b000000000001000100001000000101;
86    pub const TREY_HEARTS: CKCNumber    = 0b000000000000100100000100000011;
87    pub const DEUCE_HEARTS: CKCNumber   = 0b000000000000010100000000000010;
88    pub const ACE_DIAMONDS: CKCNumber   = 0b010000000000000010110000101001;
89    pub const KING_DIAMONDS: CKCNumber  = 0b001000000000000010101100100101;
90    pub const QUEEN_DIAMONDS: CKCNumber = 0b000100000000000010101000011111;
91    pub const JACK_DIAMONDS: CKCNumber  = 0b000010000000000010100100011101;
92    pub const TEN_DIAMONDS: CKCNumber   = 0b000001000000000010100000010111;
93    pub const NINE_DIAMONDS: CKCNumber  = 0b000000100000000010011100010011;
94    pub const EIGHT_DIAMONDS: CKCNumber = 0b000000010000000010011000010001;
95    pub const SEVEN_DIAMONDS: CKCNumber = 0b000000001000000010010100001101;
96    pub const SIX_DIAMONDS: CKCNumber   = 0b000000000100000010010000001011;
97    pub const FIVE_DIAMONDS: CKCNumber  = 0b000000000010000010001100000111;
98    pub const FOUR_DIAMONDS: CKCNumber  = 0b000000000001000010001000000101;
99    pub const TREY_DIAMONDS: CKCNumber  = 0b000000000000100010000100000011;
100    pub const DEUCE_DIAMONDS: CKCNumber = 0b000000000000010010000000000010;
101    pub const ACE_CLUBS: CKCNumber      = 0b010000000000000001110000101001;
102    pub const KING_CLUBS: CKCNumber     = 0b001000000000000001101100100101;
103    pub const QUEEN_CLUBS: CKCNumber    = 0b000100000000000001101000011111;
104    pub const JACK_CLUBS: CKCNumber     = 0b000010000000000001100100011101;
105    pub const TEN_CLUBS: CKCNumber      = 0b000001000000000001100000010111;
106    pub const NINE_CLUBS: CKCNumber     = 0b000000100000000001011100010011;
107    pub const EIGHT_CLUBS: CKCNumber    = 0b000000010000000001011000010001;
108    pub const SEVEN_CLUBS: CKCNumber    = 0b000000001000000001010100001101;
109    pub const SIX_CLUBS: CKCNumber      = 0b000000000100000001010000001011;
110    pub const FIVE_CLUBS: CKCNumber     = 0b000000000010000001001100000111;
111    pub const FOUR_CLUBS: CKCNumber     = 0b000000000001000001001000000101;
112    pub const TREY_CLUBS: CKCNumber     = 0b000000000000100001000100000011;
113    pub const DEUCE_CLUBS: CKCNumber    = 0b000000000000010001000000000010;
114    pub const BLANK: CKCNumber = 0;
115    //endregion
116
117    #[must_use]
118    pub fn filter(number: CKCNumber) -> CKCNumber {
119        <CKCNumber as PokerCard>::filter(number)
120    }
121}
122
123#[cfg(test)]
124mod card_number_tests {
125    use super::*;
126
127    #[test]
128    fn filter() {
129        assert_eq!(CardNumber::filter(2), CardNumber::BLANK);
130        assert_eq!(CardNumber::filter(CardNumber::NINE_CLUBS), CardNumber::NINE_CLUBS);
131    }
132}
133
134#[derive(Clone, Copy, Debug, EnumIter, Eq, Hash, PartialEq)]
135pub enum CardRank {
136    ACE = 14,
137    KING = 13,
138    QUEEN = 12,
139    JACK = 11,
140    TEN = 10,
141    NINE = 9,
142    EIGHT = 8,
143    SEVEN = 7,
144    SIX = 6,
145    FIVE = 5,
146    FOUR = 4,
147    THREE = 3,
148    TWO = 2,
149    BLANK = 0,
150}
151
152impl CardRank {
153    #[must_use]
154    pub fn from_char(index: char) -> CardRank {
155        match index {
156            'A' | 'a' => CardRank::ACE,
157            'K' | 'k' => CardRank::KING,
158            'Q' | 'q' => CardRank::QUEEN,
159            'J' | 'j' => CardRank::JACK,
160            'T' | 't' | '0' => CardRank::TEN,
161            '9' => CardRank::NINE,
162            '8' => CardRank::EIGHT,
163            '7' => CardRank::SEVEN,
164            '6' => CardRank::SIX,
165            '5' => CardRank::FIVE,
166            '4' => CardRank::FOUR,
167            '3' => CardRank::THREE,
168            '2' => CardRank::TWO,
169            _ => CardRank::BLANK,
170        }
171    }
172
173    fn bits(self) -> u32 {
174        1 << (16 + self.number())
175    }
176
177    fn number(self) -> u32 {
178        match self {
179            CardRank::ACE => 12,
180            CardRank::KING => 11,
181            CardRank::QUEEN => 10,
182            CardRank::JACK => 9,
183            CardRank::TEN => 8,
184            CardRank::NINE => 7,
185            CardRank::EIGHT => 6,
186            CardRank::SEVEN => 5,
187            CardRank::SIX => 4,
188            CardRank::FIVE => 3,
189            CardRank::FOUR => 2,
190            CardRank::THREE => 1,
191            _ => 0,
192        }
193    }
194
195    fn prime(self) -> u32 {
196        match self {
197            CardRank::ACE => 41,
198            CardRank::KING => 37,
199            CardRank::QUEEN => 31,
200            CardRank::JACK => 29,
201            CardRank::TEN => 23,
202            CardRank::NINE => 19,
203            CardRank::EIGHT => 17,
204            CardRank::SEVEN => 13,
205            CardRank::SIX => 11,
206            CardRank::FIVE => 7,
207            CardRank::FOUR => 5,
208            CardRank::THREE => 3,
209            CardRank::TWO => 2,
210            CardRank::BLANK => 0,
211        }
212    }
213
214    fn shift8(self) -> u32 {
215        self.number() << 8
216    }
217}
218
219#[cfg(test)]
220mod card_rank_tests {
221    use super::*;
222    use rstest::rstest;
223
224    #[rstest]
225    #[case('A', CardRank::ACE)]
226    #[case('a', CardRank::ACE)]
227    #[case('K', CardRank::KING)]
228    #[case('k', CardRank::KING)]
229    #[case('Q', CardRank::QUEEN)]
230    #[case('q', CardRank::QUEEN)]
231    #[case('J', CardRank::JACK)]
232    #[case('j', CardRank::JACK)]
233    #[case('T', CardRank::TEN)]
234    #[case('t', CardRank::TEN)]
235    #[case('0', CardRank::TEN)]
236    #[case('9', CardRank::NINE)]
237    #[case('8', CardRank::EIGHT)]
238    #[case('7', CardRank::SEVEN)]
239    #[case('6', CardRank::SIX)]
240    #[case('5', CardRank::FIVE)]
241    #[case('4', CardRank::FOUR)]
242    #[case('3', CardRank::THREE)]
243    #[case('2', CardRank::TWO)]
244    #[case('_', CardRank::BLANK)]
245    #[case(' ', CardRank::BLANK)]
246    fn from_char(#[case] input: char, #[case] expected: CardRank) {
247        assert_eq!(expected, CardRank::from_char(input));
248    }
249}
250
251#[derive(Clone, Copy, Debug, EnumIter, Eq, Hash, PartialEq)]
252pub enum CardSuit {
253    SPADES = 4,
254    HEARTS = 3,
255    DIAMONDS = 2,
256    CLUBS = 1,
257    BLANK = 0,
258}
259
260impl CardSuit {
261    #[must_use]
262    pub fn binary_signature(&self) -> u32 {
263        match self {
264            CardSuit::SPADES => 0x8000,
265            CardSuit::HEARTS => 0x4000,
266            CardSuit::DIAMONDS => 0x2000,
267            CardSuit::CLUBS => 0x1000,
268            CardSuit::BLANK => 0,
269        }
270    }
271
272    #[must_use]
273    pub fn from_char(symbol: char) -> CardSuit {
274        match symbol {
275            '♤' | '♠' | 'S' | 's' => CardSuit::SPADES,
276            '♡' | '♥' | 'H' | 'h' => CardSuit::HEARTS,
277            '♢' | '♦' | 'D' | 'd' => CardSuit::DIAMONDS,
278            '♧' | '♣' | 'C' | 'c' => CardSuit::CLUBS,
279            _ => CardSuit::BLANK,
280        }
281    }
282}
283
284#[cfg(test)]
285mod card_suit_tests {
286    use super::*;
287    use rstest::rstest;
288
289    #[test]
290    fn binary_signature() {
291        assert_eq!(32768, CardSuit::SPADES.binary_signature());
292        assert_eq!(16384, CardSuit::HEARTS.binary_signature());
293        assert_eq!(8192, CardSuit::DIAMONDS.binary_signature());
294        assert_eq!(4096, CardSuit::CLUBS.binary_signature());
295        assert_eq!(0, CardSuit::BLANK.binary_signature());
296    }
297
298    #[rstest]
299    #[case('♠', CardSuit::SPADES)]
300    #[case('♤', CardSuit::SPADES)]
301    #[case('S', CardSuit::SPADES)]
302    #[case('s', CardSuit::SPADES)]
303    #[case('♥', CardSuit::HEARTS)]
304    #[case('♡', CardSuit::HEARTS)]
305    #[case('H', CardSuit::HEARTS)]
306    #[case('h', CardSuit::HEARTS)]
307    #[case('♦', CardSuit::DIAMONDS)]
308    #[case('♢', CardSuit::DIAMONDS)]
309    #[case('D', CardSuit::DIAMONDS)]
310    #[case('d', CardSuit::DIAMONDS)]
311    #[case('♣', CardSuit::CLUBS)]
312    #[case('♧', CardSuit::CLUBS)]
313    #[case('C', CardSuit::CLUBS)]
314    #[case('c', CardSuit::CLUBS)]
315    #[case(' ', CardSuit::BLANK)]
316    #[case('F', CardSuit::BLANK)]
317    fn from_char(#[case] input: char, #[case] expected: CardSuit) {
318        assert_eq!(expected, CardSuit::from_char(input));
319    }
320}
321
322pub mod evaluate {
323    use crate::cards::five::Five;
324    use crate::cards::HandRanker;
325    use crate::hand_rank::HandRankValue;
326    use crate::{CKCNumber, CardNumber};
327
328    pub const POSSIBLE_COMBINATIONS: usize = 7937;
329
330    #[must_use]
331    #[allow(clippy::cast_possible_truncation)]
332    pub fn five_cards(five_cards: [CKCNumber; 5]) -> HandRankValue {
333        Five::from(five_cards).hand_rank_value_validated()
334    }
335
336    #[must_use]
337    #[deprecated(since = "0.1.9", note = "use Five.is_flush()")]
338    pub fn is_flush(five_cards: [CKCNumber; 5]) -> bool {
339        (five_cards[0] & five_cards[1] & five_cards[2] & five_cards[3] & five_cards[4] & CardNumber::SUIT_FILTER) != 0
340    }
341
342    /// Returns a value that is made up of performing an or operation on all of the
343    /// rank bit flags of the `PokerCard`.
344    #[must_use]
345    #[deprecated(since = "0.1.9", note = "use Five.or_rank_bits()")]
346    pub fn or_rank_bits(five_cards: [CKCNumber; 5]) -> usize {
347        Five::from(five_cards).or_rank_bits() as usize
348    }
349}
350
351#[cfg(test)]
352mod evaluate_tests {
353    use super::*;
354
355    #[test]
356    fn five_cards_royal_flush() {
357        let cards = [
358            CardNumber::ACE_SPADES,
359            CardNumber::KING_SPADES,
360            CardNumber::QUEEN_SPADES,
361            CardNumber::JACK_SPADES,
362            CardNumber::TEN_SPADES,
363        ];
364        assert_eq!(evaluate::five_cards(cards), 1);
365    }
366
367    #[test]
368    fn five_cards_straight() {
369        let first = [
370            CardNumber::NINE_CLUBS,
371            CardNumber::KING_SPADES,
372            CardNumber::QUEEN_SPADES,
373            CardNumber::JACK_SPADES,
374            CardNumber::TEN_SPADES,
375        ];
376        let second = [
377            CardNumber::NINE_CLUBS,
378            CardNumber::QUEEN_SPADES,
379            CardNumber::JACK_SPADES,
380            CardNumber::TEN_SPADES,
381            CardNumber::EIGHT_CLUBS,
382        ];
383        assert_eq!(evaluate::five_cards(first), 1601);
384        assert_eq!(evaluate::five_cards(second), 1602);
385    }
386
387    #[test]
388    fn five_cards_two_pair() {
389        let cards = [
390            CardNumber::JACK_CLUBS,
391            CardNumber::DEUCE_CLUBS,
392            CardNumber::DEUCE_DIAMONDS,
393            CardNumber::JACK_SPADES,
394            CardNumber::TEN_SPADES,
395        ];
396        assert_eq!(evaluate::five_cards(cards), 2922);
397    }
398
399    #[test]
400    fn five_cards_king_high() {
401        let first = [
402            CardNumber::JACK_CLUBS,
403            CardNumber::DEUCE_CLUBS,
404            CardNumber::TREY_CLUBS,
405            CardNumber::KING_SPADES,
406            CardNumber::TEN_SPADES,
407        ];
408        let second = [
409            CardNumber::JACK_CLUBS,
410            CardNumber::QUEEN_DIAMONDS,
411            CardNumber::TREY_CLUBS,
412            CardNumber::KING_SPADES,
413            CardNumber::TEN_SPADES,
414        ];
415        assert_eq!(evaluate::five_cards(first), 6825);
416        assert_eq!(evaluate::five_cards(second), 6684);
417    }
418
419    #[test]
420    fn check_dupes() {
421        let hand = [
422            CardNumber::JACK_CLUBS,
423            CardNumber::DEUCE_CLUBS,
424            CardNumber::TREY_CLUBS,
425            CardNumber::KING_SPADES,
426            CardNumber::JACK_CLUBS,
427        ];
428        assert_eq!(evaluate::five_cards(hand), 0);
429    }
430
431    #[test]
432    fn check_corrupt() {
433        let first = [
434            CardNumber::JACK_CLUBS,
435            CardNumber::DEUCE_CLUBS,
436            23,
437            CardNumber::KING_SPADES,
438            CardNumber::TEN_SPADES,
439        ];
440        let second = [
441            CardNumber::JACK_CLUBS,
442            CardNumber::QUEEN_DIAMONDS,
443            CardNumber::TREY_CLUBS,
444            CardNumber::KING_SPADES,
445            CardNumber::BLANK,
446        ];
447        assert_eq!(evaluate::five_cards(first), 0);
448        assert_eq!(evaluate::five_cards(second), 0);
449    }
450}
451
452#[derive(Debug, PartialEq)]
453pub enum HandError {
454    BlankCard,
455    DuplicateCard,
456    Incomplete,
457    InvalidBinaryFormat,
458    InvalidCard,
459    InvalidCardCount,
460    InvalidIndex,
461    NotEnoughCards,
462    TooManyCards,
463}
464
465pub trait PokerCard {
466    //region static
467
468    #[must_use]
469    fn create(rank: CardRank, suit: CardSuit) -> CKCNumber {
470        CKCNumber::filter(rank.bits() | rank.prime() | rank.shift8() | suit.binary_signature())
471    }
472
473    /// Only allows you to create a `CKCNumber` that is valid.
474    #[must_use]
475    fn filter(number: CKCNumber) -> CKCNumber {
476        match number {
477            CardNumber::ACE_SPADES
478            | CardNumber::KING_SPADES
479            | CardNumber::QUEEN_SPADES
480            | CardNumber::JACK_SPADES
481            | CardNumber::TEN_SPADES
482            | CardNumber::NINE_SPADES
483            | CardNumber::EIGHT_SPADES
484            | CardNumber::SEVEN_SPADES
485            | CardNumber::SIX_SPADES
486            | CardNumber::FIVE_SPADES
487            | CardNumber::FOUR_SPADES
488            | CardNumber::TREY_SPADES
489            | CardNumber::DEUCE_SPADES
490            | CardNumber::ACE_HEARTS
491            | CardNumber::KING_HEARTS
492            | CardNumber::QUEEN_HEARTS
493            | CardNumber::JACK_HEARTS
494            | CardNumber::TEN_HEARTS
495            | CardNumber::NINE_HEARTS
496            | CardNumber::EIGHT_HEARTS
497            | CardNumber::SEVEN_HEARTS
498            | CardNumber::SIX_HEARTS
499            | CardNumber::FIVE_HEARTS
500            | CardNumber::FOUR_HEARTS
501            | CardNumber::TREY_HEARTS
502            | CardNumber::DEUCE_HEARTS
503            | CardNumber::ACE_DIAMONDS
504            | CardNumber::KING_DIAMONDS
505            | CardNumber::QUEEN_DIAMONDS
506            | CardNumber::JACK_DIAMONDS
507            | CardNumber::TEN_DIAMONDS
508            | CardNumber::NINE_DIAMONDS
509            | CardNumber::EIGHT_DIAMONDS
510            | CardNumber::SEVEN_DIAMONDS
511            | CardNumber::SIX_DIAMONDS
512            | CardNumber::FIVE_DIAMONDS
513            | CardNumber::FOUR_DIAMONDS
514            | CardNumber::TREY_DIAMONDS
515            | CardNumber::DEUCE_DIAMONDS
516            | CardNumber::ACE_CLUBS
517            | CardNumber::KING_CLUBS
518            | CardNumber::QUEEN_CLUBS
519            | CardNumber::JACK_CLUBS
520            | CardNumber::TEN_CLUBS
521            | CardNumber::NINE_CLUBS
522            | CardNumber::EIGHT_CLUBS
523            | CardNumber::SEVEN_CLUBS
524            | CardNumber::SIX_CLUBS
525            | CardNumber::FIVE_CLUBS
526            | CardNumber::FOUR_CLUBS
527            | CardNumber::TREY_CLUBS
528            | CardNumber::DEUCE_CLUBS => number,
529            _ => CardNumber::BLANK,
530        }
531    }
532
533    #[must_use]
534    fn from_binary_card(bc: BinaryCard) -> CKCNumber {
535        match bc {
536            BinaryCard::ACE_SPADES => CardNumber::ACE_SPADES,
537            BinaryCard::KING_SPADES => CardNumber::KING_SPADES,
538            BinaryCard::QUEEN_SPADES => CardNumber::QUEEN_SPADES,
539            BinaryCard::JACK_SPADES => CardNumber::JACK_SPADES,
540            BinaryCard::TEN_SPADES => CardNumber::TEN_SPADES,
541            BinaryCard::NINE_SPADES => CardNumber::NINE_SPADES,
542            BinaryCard::EIGHT_SPADES => CardNumber::EIGHT_SPADES,
543            BinaryCard::SEVEN_SPADES => CardNumber::SEVEN_SPADES,
544            BinaryCard::SIX_SPADES => CardNumber::SIX_SPADES,
545            BinaryCard::FIVE_SPADES => CardNumber::FIVE_SPADES,
546            BinaryCard::FOUR_SPADES => CardNumber::FOUR_SPADES,
547            BinaryCard::TREY_SPADES => CardNumber::TREY_SPADES,
548            BinaryCard::DEUCE_SPADES => CardNumber::DEUCE_SPADES,
549            BinaryCard::ACE_HEARTS => CardNumber::ACE_HEARTS,
550            BinaryCard::KING_HEARTS => CardNumber::KING_HEARTS,
551            BinaryCard::QUEEN_HEARTS => CardNumber::QUEEN_HEARTS,
552            BinaryCard::JACK_HEARTS => CardNumber::JACK_HEARTS,
553            BinaryCard::TEN_HEARTS => CardNumber::TEN_HEARTS,
554            BinaryCard::NINE_HEARTS => CardNumber::NINE_HEARTS,
555            BinaryCard::EIGHT_HEARTS => CardNumber::EIGHT_HEARTS,
556            BinaryCard::SEVEN_HEARTS => CardNumber::SEVEN_HEARTS,
557            BinaryCard::SIX_HEARTS => CardNumber::SIX_HEARTS,
558            BinaryCard::FIVE_HEARTS => CardNumber::FIVE_HEARTS,
559            BinaryCard::FOUR_HEARTS => CardNumber::FOUR_HEARTS,
560            BinaryCard::TREY_HEARTS => CardNumber::TREY_HEARTS,
561            BinaryCard::DEUCE_HEARTS => CardNumber::DEUCE_HEARTS,
562            BinaryCard::ACE_DIAMONDS => CardNumber::ACE_DIAMONDS,
563            BinaryCard::KING_DIAMONDS => CardNumber::KING_DIAMONDS,
564            BinaryCard::QUEEN_DIAMONDS => CardNumber::QUEEN_DIAMONDS,
565            BinaryCard::JACK_DIAMONDS => CardNumber::JACK_DIAMONDS,
566            BinaryCard::TEN_DIAMONDS => CardNumber::TEN_DIAMONDS,
567            BinaryCard::NINE_DIAMONDS => CardNumber::NINE_DIAMONDS,
568            BinaryCard::EIGHT_DIAMONDS => CardNumber::EIGHT_DIAMONDS,
569            BinaryCard::SEVEN_DIAMONDS => CardNumber::SEVEN_DIAMONDS,
570            BinaryCard::SIX_DIAMONDS => CardNumber::SIX_DIAMONDS,
571            BinaryCard::FIVE_DIAMONDS => CardNumber::FIVE_DIAMONDS,
572            BinaryCard::FOUR_DIAMONDS => CardNumber::FOUR_DIAMONDS,
573            BinaryCard::TREY_DIAMONDS => CardNumber::TREY_DIAMONDS,
574            BinaryCard::DEUCE_DIAMONDS => CardNumber::DEUCE_DIAMONDS,
575            BinaryCard::ACE_CLUBS => CardNumber::ACE_CLUBS,
576            BinaryCard::KING_CLUBS => CardNumber::KING_CLUBS,
577            BinaryCard::QUEEN_CLUBS => CardNumber::QUEEN_CLUBS,
578            BinaryCard::JACK_CLUBS => CardNumber::JACK_CLUBS,
579            BinaryCard::TEN_CLUBS => CardNumber::TEN_CLUBS,
580            BinaryCard::NINE_CLUBS => CardNumber::NINE_CLUBS,
581            BinaryCard::EIGHT_CLUBS => CardNumber::EIGHT_CLUBS,
582            BinaryCard::SEVEN_CLUBS => CardNumber::SEVEN_CLUBS,
583            BinaryCard::SIX_CLUBS => CardNumber::SIX_CLUBS,
584            BinaryCard::FIVE_CLUBS => CardNumber::FIVE_CLUBS,
585            BinaryCard::FOUR_CLUBS => CardNumber::FOUR_CLUBS,
586            BinaryCard::TREY_CLUBS => CardNumber::TREY_CLUBS,
587            BinaryCard::DEUCE_CLUBS => CardNumber::DEUCE_CLUBS,
588            _ => CardNumber::BLANK,
589        }
590    }
591
592    #[must_use]
593    fn from_index(index: &str) -> CKCNumber {
594        let (rank, suit) = get_rank_and_suit(index);
595        CKCNumber::create(rank, suit)
596    }
597
598    //endregion
599
600    fn as_u32(&self) -> u32;
601
602    fn get_card_rank(&self) -> CardRank {
603        match self.get_rank_bit() {
604            4096 => CardRank::ACE,
605            2048 => CardRank::KING,
606            1024 => CardRank::QUEEN,
607            512 => CardRank::JACK,
608            256 => CardRank::TEN,
609            128 => CardRank::NINE,
610            64 => CardRank::EIGHT,
611            32 => CardRank::SEVEN,
612            16 => CardRank::SIX,
613            8 => CardRank::FIVE,
614            4 => CardRank::FOUR,
615            2 => CardRank::THREE,
616            1 => CardRank::TWO,
617            _ => CardRank::BLANK,
618        }
619    }
620
621    fn get_card_suit(&self) -> CardSuit {
622        match self.get_suit_bit() {
623            8 => CardSuit::SPADES,
624            4 => CardSuit::HEARTS,
625            2 => CardSuit::DIAMONDS,
626            1 => CardSuit::CLUBS,
627            _ => CardSuit::BLANK,
628        }
629    }
630
631    fn get_chen_points(&self) -> f32 {
632        match self.get_card_rank() {
633            CardRank::ACE => 10.0,
634            CardRank::KING => 8.0,
635            CardRank::QUEEN => 7.0,
636            CardRank::JACK => 6.0,
637            CardRank::BLANK => 0.0,
638            _ => f32::from(self.get_card_rank() as u8) / 2.0,
639        }
640    }
641
642    fn get_rank_bit(&self) -> u32 {
643        self.get_rank_flag() >> CardNumber::RANK_FLAG_SHIFT
644    }
645
646    fn get_rank_char(&self) -> char {
647        match self.get_rank_bit() {
648            4096 => 'A',
649            2048 => 'K',
650            1024 => 'Q',
651            512 => 'J',
652            256 => 'T',
653            128 => '9',
654            64 => '8',
655            32 => '7',
656            16 => '6',
657            8 => '5',
658            4 => '4',
659            2 => '3',
660            1 => '2',
661            _ => '_',
662        }
663    }
664
665    fn get_rank_flag(&self) -> u32 {
666        self.as_u32() & CardNumber::RANK_FLAG_FILTER
667    }
668
669    fn get_rank_prime(&self) -> u32 {
670        self.as_u32() & CardNumber::RANK_PRIME_FILTER
671    }
672
673    fn get_suit_bit(&self) -> u32 {
674        self.get_suit_flag() >> CardNumber::SUIT_SHIFT
675    }
676
677    fn get_suit_char(&self) -> char {
678        match self.get_suit_bit() {
679            8 => '♠',
680            4 => '♥',
681            2 => '♦',
682            1 => '♣',
683            _ => '_',
684        }
685    }
686
687    fn get_suit_letter(&self) -> char {
688        match self.get_suit_bit() {
689            8 => 'S',
690            4 => 'H',
691            2 => 'D',
692            1 => 'C',
693            _ => '_',
694        }
695    }
696
697    fn get_suit_flag(&self) -> u32 {
698        self.as_u32() & CardNumber::SUIT_FILTER
699    }
700
701    fn is_blank(&self) -> bool;
702
703    //region multiples
704
705    fn flag_as_pair(&self) -> CKCNumber {
706        self.as_u32() | CardNumber::PAIR
707    }
708
709    fn flag_as_trips(&self) -> CKCNumber {
710        self.as_u32() | CardNumber::TRIPS
711    }
712
713    fn flag_as_quads(&self) -> CKCNumber {
714        self.as_u32() | CardNumber::QUADS
715    }
716
717    fn next_suit(&self) -> CardSuit {
718        match self.get_card_suit() {
719            CardSuit::SPADES => CardSuit::HEARTS,
720            CardSuit::HEARTS => CardSuit::DIAMONDS,
721            CardSuit::DIAMONDS => CardSuit::CLUBS,
722            CardSuit::CLUBS => CardSuit::SPADES,
723            CardSuit::BLANK => CardSuit::BLANK,
724        }
725    }
726
727    fn strip_multiples_flags(&self) -> CKCNumber {
728        CardNumber::MULTIPLES_FILTER & self.as_u32()
729    }
730
731    //endregion
732}
733
734impl PokerCard for CKCNumber {
735    fn as_u32(&self) -> u32 {
736        *self
737    }
738
739    fn is_blank(&self) -> bool {
740        *self == CardNumber::BLANK
741    }
742}
743
744/// Trait that shifts the suit of a card to the next one down. Spades to hearts;
745/// hearts to diamonds, diamonds to clubs, and clubs back to spades.
746///
747/// This is used for analysis. Since no suit is better than another from an evaluation
748/// perspective, the odds calculated for cards of any suit should be the same if all
749/// the cards are shifted.
750pub trait Shifty {
751    #[must_use]
752    fn shift_suit(&self) -> Self;
753}
754
755impl Shifty for CKCNumber {
756    fn shift_suit(&self) -> Self {
757        CKCNumber::create(self.get_card_rank(), self.next_suit())
758    }
759}
760
761#[cfg(test)]
762mod poker_card_tests {
763    use super::*;
764    use rstest::rstest;
765
766    #[test]
767    fn filter() {
768        assert_eq!(
769            <CKCNumber as PokerCard>::filter(CardNumber::ACE_SPADES),
770            CardNumber::ACE_SPADES
771        );
772        assert_eq!(
773            <CKCNumber as PokerCard>::filter(CardNumber::KING_CLUBS),
774            CardNumber::filter(CardNumber::KING_CLUBS)
775        );
776        assert_eq!(<CKCNumber as PokerCard>::filter(2), CardNumber::BLANK);
777    }
778
779    #[rstest]
780    #[case("A♠", CardNumber::ACE_SPADES)]
781    #[case("ks", CardNumber::KING_SPADES)]
782    #[case("QS", CardNumber::QUEEN_SPADES)]
783    #[case("J♠", CardNumber::JACK_SPADES)]
784    #[case("TS", CardNumber::TEN_SPADES)]
785    #[case("9s", CardNumber::NINE_SPADES)]
786    #[case("8♠", CardNumber::EIGHT_SPADES)]
787    #[case("7S", CardNumber::SEVEN_SPADES)]
788    #[case("6♠", CardNumber::SIX_SPADES)]
789    #[case("5S", CardNumber::FIVE_SPADES)]
790    #[case("4♠", CardNumber::FOUR_SPADES)]
791    #[case("3s", CardNumber::TREY_SPADES)]
792    #[case("2S", CardNumber::DEUCE_SPADES)]
793    #[case("A♥", CardNumber::ACE_HEARTS)]
794    #[case("k♥", CardNumber::KING_HEARTS)]
795    #[case("QH", CardNumber::QUEEN_HEARTS)]
796    #[case("jh", CardNumber::JACK_HEARTS)]
797    #[case("T♥", CardNumber::TEN_HEARTS)]
798    #[case("9♥", CardNumber::NINE_HEARTS)]
799    #[case("8h", CardNumber::EIGHT_HEARTS)]
800    #[case("7H", CardNumber::SEVEN_HEARTS)]
801    #[case("6h", CardNumber::SIX_HEARTS)]
802    #[case("5H", CardNumber::FIVE_HEARTS)]
803    #[case("4♥", CardNumber::FOUR_HEARTS)]
804    #[case("3♥", CardNumber::TREY_HEARTS)]
805    #[case("2h", CardNumber::DEUCE_HEARTS)]
806    #[case("A♦", CardNumber::ACE_DIAMONDS)]
807    #[case("k♦", CardNumber::KING_DIAMONDS)]
808    #[case("Q♦", CardNumber::QUEEN_DIAMONDS)]
809    #[case("Jd", CardNumber::JACK_DIAMONDS)]
810    #[case("tD", CardNumber::TEN_DIAMONDS)]
811    #[case("9♦", CardNumber::NINE_DIAMONDS)]
812    #[case("8D", CardNumber::EIGHT_DIAMONDS)]
813    #[case("7♦", CardNumber::SEVEN_DIAMONDS)]
814    #[case("6D", CardNumber::SIX_DIAMONDS)]
815    #[case("5D", CardNumber::FIVE_DIAMONDS)]
816    #[case("4♦", CardNumber::FOUR_DIAMONDS)]
817    #[case("3♦", CardNumber::TREY_DIAMONDS)]
818    #[case("2d", CardNumber::DEUCE_DIAMONDS)]
819    #[case("a♣", CardNumber::ACE_CLUBS)]
820    #[case("k♣", CardNumber::KING_CLUBS)]
821    #[case("QC", CardNumber::QUEEN_CLUBS)]
822    #[case("jc", CardNumber::JACK_CLUBS)]
823    #[case("tC", CardNumber::TEN_CLUBS)]
824    #[case("9♣", CardNumber::NINE_CLUBS)]
825    #[case("8♣", CardNumber::EIGHT_CLUBS)]
826    #[case("7c", CardNumber::SEVEN_CLUBS)]
827    #[case("6♣", CardNumber::SIX_CLUBS)]
828    #[case("5C", CardNumber::FIVE_CLUBS)]
829    #[case("4c", CardNumber::FOUR_CLUBS)]
830    #[case("3C", CardNumber::TREY_CLUBS)]
831    #[case("2C", CardNumber::DEUCE_CLUBS)]
832    fn from_index(#[case] index: &str, #[case] expected: CKCNumber) {
833        assert_eq!(CKCNumber::from_index(index), expected);
834    }
835
836    #[rstest]
837    #[case("A♠", 10.0)]
838    #[case("ks", 8.0)]
839    #[case("QS", 7.0)]
840    #[case("J♠", 6.0)]
841    #[case("TS", 5.0)]
842    #[case("9s", 4.5)]
843    #[case("8♠", 4.0)]
844    #[case("7S", 3.5)]
845    #[case("6♠", 3.0)]
846    #[case("5S", 2.5)]
847    #[case("4♠", 2.0)]
848    #[case("3s", 1.5)]
849    #[case("2S", 1.0)]
850    fn get_chen_points(#[case] index: &str, #[case] expected: f32) {
851        assert_eq!(CKCNumber::from_index(index).get_chen_points(), expected);
852    }
853
854    #[test]
855    fn get_rank() {
856        let card = CardNumber::ACE_CLUBS as CKCNumber;
857        assert_eq!(0b00010000_00000000, card.get_rank_bit());
858        assert_eq!(0b00101001, card.get_rank_prime());
859        assert_eq!(CardRank::ACE, card.get_card_rank());
860
861        let card = CardNumber::KING_DIAMONDS as CKCNumber;
862        assert_eq!(0b00001000_00000000, card.get_rank_bit());
863        assert_eq!(0b00100101, card.get_rank_prime());
864        assert_eq!(CardRank::KING, card.get_card_rank());
865
866        let card = CardNumber::QUEEN_SPADES as CKCNumber;
867        assert_eq!(0b00000100_00000000, card.get_rank_bit());
868        assert_eq!(0b00011111, card.get_rank_prime());
869        assert_eq!(CardRank::QUEEN, card.get_card_rank());
870
871        let card = CardNumber::JACK_HEARTS as CKCNumber;
872        assert_eq!(0b00000010_00000000, card.get_rank_bit());
873        assert_eq!(0b00011101, card.get_rank_prime());
874        assert_eq!(CardRank::JACK, card.get_card_rank());
875
876        let card = CardNumber::TEN_SPADES as CKCNumber;
877        assert_eq!(0b00000001_00000000, card.get_rank_bit());
878        assert_eq!(0b00010111, card.get_rank_prime());
879        assert_eq!(CardRank::TEN, card.get_card_rank());
880
881        let card = CardNumber::NINE_HEARTS as CKCNumber;
882        assert_eq!(0b00000000_10000000, card.get_rank_bit());
883        assert_eq!(0b00010011, card.get_rank_prime());
884        assert_eq!(CardRank::NINE, card.get_card_rank());
885
886        let card = CardNumber::EIGHT_DIAMONDS as CKCNumber;
887        assert_eq!(0b00000000_01000000, card.get_rank_bit());
888        assert_eq!(0b00010001, card.get_rank_prime());
889        assert_eq!(CardRank::EIGHT, card.get_card_rank());
890
891        let card = CardNumber::SEVEN_CLUBS as CKCNumber;
892        assert_eq!(0b00000000_00100000, card.get_rank_bit());
893        assert_eq!(0b00001101, card.get_rank_prime());
894        assert_eq!(CardRank::SEVEN, card.get_card_rank());
895
896        let card = CardNumber::SIX_SPADES as CKCNumber;
897        assert_eq!(0b00000000_00010000, card.get_rank_bit());
898        assert_eq!(0b00001011, card.get_rank_prime());
899        assert_eq!(CardRank::SIX, card.get_card_rank());
900
901        let card = CardNumber::FIVE_HEARTS as CKCNumber;
902        assert_eq!(0b00000000_00001000, card.get_rank_bit());
903        assert_eq!(0b00000111, card.get_rank_prime());
904        assert_eq!(CardRank::FIVE, card.get_card_rank());
905
906        let card = CardNumber::FOUR_DIAMONDS as CKCNumber;
907        assert_eq!(0b00000000_00000100, card.get_rank_bit());
908        assert_eq!(0b00000101, card.get_rank_prime());
909        assert_eq!(CardRank::FOUR, card.get_card_rank());
910
911        let card = CardNumber::TREY_CLUBS as CKCNumber;
912        assert_eq!(0b00000000_00000010, card.get_rank_bit());
913        assert_eq!(0b00000011, card.get_rank_prime());
914        assert_eq!(CardRank::THREE, card.get_card_rank());
915
916        let card = CardNumber::DEUCE_SPADES as CKCNumber;
917        assert_eq!(0b00000000_00000001, card.get_rank_bit());
918        assert_eq!(0b00000010, card.get_rank_prime());
919        assert_eq!(CardRank::TWO, card.get_card_rank());
920
921        let card = CardNumber::BLANK as CKCNumber;
922        assert_eq!(0b00000000_00000000, card.get_rank_bit());
923        assert_eq!(0b00000000, card.get_rank_prime());
924        assert_eq!(CardRank::BLANK, card.get_card_rank());
925        assert_eq!(CardSuit::BLANK, card.get_card_suit());
926    }
927
928    #[test]
929    fn get_rank_flag() {
930        let card = CardNumber::ACE_CLUBS as CKCNumber;
931        assert_eq!(0b00010000_00000000_00000000_00000000, card.get_rank_flag());
932        let card = CardNumber::KING_DIAMONDS as CKCNumber;
933        assert_eq!(0b00001000_00000000_00000000_00000000, card.get_rank_flag());
934        let card = CardNumber::QUEEN_SPADES as CKCNumber;
935        assert_eq!(0b00000100_00000000_00000000_00000000, card.get_rank_flag());
936        let card = CardNumber::JACK_HEARTS as CKCNumber;
937        assert_eq!(0b00000010_00000000_00000000_00000000, card.get_rank_flag());
938        let card = CardNumber::TEN_SPADES as CKCNumber;
939        assert_eq!(0b00000001_00000000_00000000_00000000, card.get_rank_flag());
940        let card = CardNumber::NINE_HEARTS as CKCNumber;
941        assert_eq!(0b00000000_10000000_00000000_00000000, card.get_rank_flag());
942        let card = CardNumber::EIGHT_DIAMONDS as CKCNumber;
943        assert_eq!(0b00000000_01000000_00000000_00000000, card.get_rank_flag());
944        let card = CardNumber::SEVEN_CLUBS as CKCNumber;
945        assert_eq!(0b00000000_00100000_00000000_00000000, card.get_rank_flag());
946        let card = CardNumber::SIX_SPADES as CKCNumber;
947        assert_eq!(0b00000000_00010000_00000000_00000000, card.get_rank_flag());
948        let card = CardNumber::FIVE_HEARTS as CKCNumber;
949        assert_eq!(0b00000000_00001000_00000000_00000000, card.get_rank_flag());
950        let card = CardNumber::FOUR_DIAMONDS as CKCNumber;
951        assert_eq!(0b00000000_00000100_00000000_00000000, card.get_rank_flag());
952        let card = CardNumber::TREY_CLUBS as CKCNumber;
953        assert_eq!(0b00000000_00000010_00000000_00000000, card.get_rank_flag());
954        let card = CardNumber::DEUCE_SPADES as CKCNumber;
955        assert_eq!(0b00000000_00000001_00000000_00000000, card.get_rank_flag());
956    }
957
958    #[test]
959    fn get_suit_bit() {
960        let card = CardNumber::SEVEN_SPADES as CKCNumber;
961        assert_eq!(0b1000, card.get_suit_bit());
962        assert_eq!(CardSuit::SPADES, card.get_card_suit());
963
964        let card = CardNumber::SEVEN_HEARTS as CKCNumber;
965        assert_eq!(0b0100, card.get_suit_bit());
966        assert_eq!(CardSuit::HEARTS, card.get_card_suit());
967
968        let card = CardNumber::SEVEN_DIAMONDS as CKCNumber;
969        assert_eq!(0b0010, card.get_suit_bit());
970        assert_eq!(CardSuit::DIAMONDS, card.get_card_suit());
971
972        let card = CardNumber::SEVEN_CLUBS as CKCNumber;
973        assert_eq!(0b0001, card.get_suit_bit());
974        assert_eq!(CardSuit::CLUBS, card.get_card_suit());
975    }
976
977    #[test]
978    fn get_suit_flag() {
979        let card = CardNumber::SEVEN_SPADES as CKCNumber;
980        assert_eq!(0b10000000_00000000, card.get_suit_flag());
981
982        let card = CardNumber::SEVEN_HEARTS as CKCNumber;
983        assert_eq!(0b01000000_00000000, card.get_suit_flag());
984
985        let card = CardNumber::SEVEN_DIAMONDS as CKCNumber;
986        assert_eq!(0b00100000_00000000, card.get_suit_flag());
987
988        let card = CardNumber::SEVEN_CLUBS as CKCNumber;
989        assert_eq!(0b00010000_00000000, card.get_suit_flag());
990    }
991
992    #[test]
993    fn is_blank() {
994        let card = CardNumber::BLANK;
995
996        assert!(card.is_blank());
997        assert!(0_u32.is_blank());
998        assert!(0.is_blank());
999    }
1000
1001    #[test]
1002    fn flag_as_pair() {
1003        assert_eq!(805_342_249, CardNumber::ACE_SPADES.flag_as_pair());
1004    }
1005
1006    #[test]
1007    fn flag_as_trips() {
1008        assert_eq!(1_342_213_161, CardNumber::ACE_SPADES.flag_as_trips());
1009    }
1010
1011    #[test]
1012    fn flag_as_quads() {
1013        assert_eq!(2_415_954_985, CardNumber::ACE_SPADES.flag_as_quads());
1014    }
1015
1016    #[test]
1017    fn next_suit() {
1018        assert_eq!(CardNumber::TEN_SPADES.next_suit(), CardSuit::HEARTS);
1019        assert_eq!(CardNumber::TEN_HEARTS.next_suit(), CardSuit::DIAMONDS);
1020        assert_eq!(CardNumber::TEN_DIAMONDS.next_suit(), CardSuit::CLUBS);
1021        assert_eq!(CardNumber::TEN_CLUBS.next_suit(), CardSuit::SPADES);
1022        assert_eq!(CardNumber::BLANK.next_suit(), CardSuit::BLANK);
1023    }
1024
1025    #[test]
1026    fn shift_suit() {
1027        assert_eq!(CardNumber::ACE_SPADES.shift_suit(), CardNumber::ACE_HEARTS);
1028        assert_eq!(CardNumber::ACE_HEARTS.shift_suit(), CardNumber::ACE_DIAMONDS);
1029        assert_eq!(CardNumber::ACE_DIAMONDS.shift_suit(), CardNumber::ACE_CLUBS);
1030        assert_eq!(CardNumber::ACE_CLUBS.shift_suit(), CardNumber::ACE_SPADES);
1031        assert_eq!(CardNumber::BLANK.shift_suit(), CardNumber::BLANK);
1032    }
1033
1034    #[test]
1035    fn strip_multiples_flags() {
1036        assert_eq!(
1037            CardNumber::ACE_SPADES,
1038            CardNumber::ACE_SPADES.flag_as_pair().strip_multiples_flags()
1039        );
1040        assert_eq!(
1041            CardNumber::ACE_SPADES,
1042            CardNumber::ACE_SPADES.flag_as_trips().strip_multiples_flags()
1043        );
1044        assert_eq!(
1045            CardNumber::ACE_SPADES,
1046            CardNumber::ACE_SPADES.flag_as_quads().strip_multiples_flags()
1047        );
1048        assert_eq!(
1049            CardNumber::ACE_SPADES,
1050            CardNumber::ACE_SPADES
1051                .flag_as_pair()
1052                .flag_as_trips()
1053                .flag_as_quads()
1054                .strip_multiples_flags()
1055        );
1056    }
1057
1058    #[test]
1059    fn scratch() {
1060        // let paired = 0b10010000000000001000110000101001
1061    }
1062}