Skip to main content

freezeout_cards/deck/
mod.rs

1// Copyright (C) 2025 Vince Vasta
2// SPDX-License-Identifier: Apache-2.0
3
4//! Poker cards definitions.
5use rand::prelude::*;
6use serde::{Deserialize, Serialize};
7use std::fmt;
8
9#[cfg(feature = "parallel")]
10pub mod parallel;
11
12/// Primes used to encode a card rank.
13const PRIMES: [u32; 13] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41];
14
15/// A Poker card.
16#[derive(Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
17pub struct Card(u32);
18
19/// A Poker card.
20impl Card {
21    /// Create a card given a suit and rank.
22    pub fn new(rank: Rank, suit: Suit) -> Card {
23        // A card is represented using the encoding in the Cactus Kev's Poker
24        // hand evaluator with each card having the following format:
25        //
26        //  +--------+--------+--------+--------+
27        //  |xxxbbbbb|bbbbbbbb|cdhsrrrr|xxpppppp|
28        //  +--------+--------+--------+--------+
29        //  p = prime number of rank (deuce=2,trey=3,four=5,five=7,...,ace=41)
30        //  r = rank of card (deuce=0,trey=1,four=2,five=3,...,ace=12)
31        //  cdhs = suit of card
32        //  b = bit turned on depending on rank of card
33        //
34        //  See http://suffe.cool/poker/evaluator.html
35        let (rank, suit) = (rank as u32, suit as u32);
36        Self(PRIMES[rank as usize] | (rank << 8) | (suit << 12) | (1 << (rank + 16)))
37    }
38
39    /// This card unique id.
40    pub fn id(&self) -> u32 {
41        self.0
42    }
43
44    /// Returns the card suit.
45    pub fn suit(&self) -> Suit {
46        let suit_bits = self.suit_bits();
47        match suit_bits {
48            0x8 => Suit::Clubs,
49            0x4 => Suit::Diamonds,
50            0x2 => Suit::Hearts,
51            0x1 => Suit::Spades,
52            _ => panic!("Invalid suit value 0x{:x}", self.0),
53        }
54    }
55
56    /// Returns the card rank.
57    pub fn rank(&self) -> Rank {
58        let rank_bits = self.rank_bits();
59        match rank_bits {
60            0 => Rank::Deuce,
61            1 => Rank::Trey,
62            2 => Rank::Four,
63            3 => Rank::Five,
64            4 => Rank::Six,
65            5 => Rank::Seven,
66            6 => Rank::Eight,
67            7 => Rank::Nine,
68            8 => Rank::Ten,
69            9 => Rank::Jack,
70            10 => Rank::Queen,
71            11 => Rank::King,
72            12 => Rank::Ace,
73            _ => panic!("Invalid rank 0x{:x}", self.0),
74        }
75    }
76
77    /// Returns the rank bits.
78    #[inline]
79    pub fn rank_bits(&self) -> u8 {
80        ((self.0 >> 8) & 0xf) as u8
81    }
82
83    /// Returns the suit bits.
84    #[inline]
85    pub fn suit_bits(&self) -> u8 {
86        ((self.0 >> 12) & 0xf) as u8
87    }
88}
89
90impl Default for Card {
91    fn default() -> Self {
92        Card::new(Rank::Ace, Suit::Diamonds)
93    }
94}
95
96impl fmt::Display for Card {
97    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        write!(f, "{}{}", self.rank(), self.suit())
99    }
100}
101
102impl fmt::Debug for Card {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        write!(f, "Card({}{})", self.rank(), self.suit())
105    }
106}
107
108/// Card rank.
109#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
110pub enum Rank {
111    /// Deuce
112    Deuce = 0,
113    /// Trey
114    Trey,
115    /// Four
116    Four,
117    /// Five
118    Five,
119    /// Six
120    Six,
121    /// Seven
122    Seven,
123    /// Eight
124    Eight,
125    /// Nine
126    Nine,
127    /// Ten
128    Ten,
129    /// Jack
130    Jack,
131    /// Queen
132    Queen,
133    /// King
134    King,
135    /// Ace
136    Ace,
137}
138
139impl Rank {
140    /// Returns all ranks.
141    pub fn ranks() -> impl DoubleEndedIterator<Item = Rank> {
142        use Rank::*;
143        [
144            Deuce, Trey, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace,
145        ]
146        .into_iter()
147    }
148}
149
150impl fmt::Display for Rank {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        let rank = match self {
153            Rank::Deuce => '2',
154            Rank::Trey => '3',
155            Rank::Four => '4',
156            Rank::Five => '5',
157            Rank::Six => '6',
158            Rank::Seven => '7',
159            Rank::Eight => '8',
160            Rank::Nine => '9',
161            Rank::Ten => 'T',
162            Rank::Jack => 'J',
163            Rank::Queen => 'Q',
164            Rank::King => 'K',
165            Rank::Ace => 'A',
166        };
167
168        write!(f, "{rank}")
169    }
170}
171
172/// Card suit.
173#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
174pub enum Suit {
175    /// Clubs suit.
176    Clubs = 8,
177    /// Diamonds suit.
178    Diamonds = 4,
179    /// Hearts suit.
180    Hearts = 2,
181    /// Spades suit.
182    Spades = 1,
183}
184
185impl fmt::Display for Suit {
186    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187        let suit = match self {
188            Suit::Clubs => 'C',
189            Suit::Diamonds => 'D',
190            Suit::Hearts => 'H',
191            Suit::Spades => 'S',
192        };
193
194        write!(f, "{suit}")
195    }
196}
197
198impl Suit {
199    /// Returns all suits.
200    pub fn suits() -> impl DoubleEndedIterator<Item = Suit> {
201        [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades].into_iter()
202    }
203}
204
205/// A cards Deck
206#[derive(Debug)]
207pub struct Deck {
208    cards: Vec<Card>,
209}
210
211impl Deck {
212    /// The number of cards in the deck.
213    pub const SIZE: usize = 52;
214
215    /// Creates a new shuffled deck.
216    pub fn shuffled<R: Rng>(rng: &mut R) -> Self {
217        let mut deck = Self::default();
218        deck.cards.shuffle(rng);
219        deck
220    }
221
222    /// Deals a card from the deck.
223    pub fn deal(&mut self) -> Card {
224        self.cards.pop().unwrap()
225    }
226
227    /// Checks if the deck is empty.
228    pub fn is_empty(&self) -> bool {
229        self.cards.is_empty()
230    }
231
232    /// Number of cards in the deck.
233    pub fn count(&self) -> usize {
234        self.cards.len()
235    }
236
237    /// Removes a card from the deck.
238    pub fn remove(&mut self, card: Card) {
239        self.cards.retain(|c| c != &card);
240    }
241
242    /// Calls the given closure n times with a sample of k cards.
243    ///
244    /// Panics if k is not in the [1..Self::count()] range.
245    pub fn sample<F>(&self, n: usize, k: usize, mut f: F)
246    where
247        F: FnMut(&[Card]),
248    {
249        assert!(k > 0 && k < self.cards.len());
250
251        let mut h = vec![Card::new(Rank::Ace, Suit::Hearts); k];
252        let mut rng = SmallRng::from_os_rng();
253
254        for _ in 0..n {
255            for (pos, c) in self.cards.choose_multiple(&mut rng, k).enumerate() {
256                h[pos] = *c;
257            }
258
259            f(&h);
260        }
261    }
262
263    /// Calls the `f` closure for each k-cards hand.
264    ///
265    /// Panics if k is not in the range 2 <= k <= 7.
266    pub fn for_each<F>(&self, k: usize, mut f: F)
267    where
268        F: FnMut(&[Card]),
269    {
270        assert!(2 <= k && k <= 7, "2 <= k <= 7");
271
272        if k > self.cards.len() {
273            return;
274        }
275
276        let n = self.cards.len();
277        let mut h = vec![Card::new(Rank::Ace, Suit::Hearts); 7];
278
279        for c1 in 0..n {
280            h[0] = self.cards[c1];
281
282            for c2 in (c1 + 1)..n {
283                h[1] = self.cards[c2];
284
285                if k == 2 {
286                    f(&h[0..k]);
287                    continue;
288                }
289
290                for c3 in (c2 + 1)..n {
291                    h[2] = self.cards[c3];
292
293                    if k == 3 {
294                        f(&h[0..k]);
295                        continue;
296                    }
297
298                    for c4 in (c3 + 1)..n {
299                        h[3] = self.cards[c4];
300
301                        if k == 4 {
302                            f(&h[0..k]);
303                            continue;
304                        }
305
306                        for c5 in (c4 + 1)..n {
307                            h[4] = self.cards[c5];
308
309                            if k == 5 {
310                                f(&h[0..k]);
311                                continue;
312                            }
313
314                            for c6 in (c5 + 1)..n {
315                                h[5] = self.cards[c6];
316
317                                if k == 6 {
318                                    f(&h[0..k]);
319                                    continue;
320                                }
321
322                                for c7 in (c6 + 1)..n {
323                                    h[6] = self.cards[c7];
324                                    f(&h[0..k]);
325                                }
326                            }
327                        }
328                    }
329                }
330            }
331        }
332    }
333}
334
335impl Default for Deck {
336    fn default() -> Self {
337        let cards = Suit::suits()
338            .flat_map(|s| Rank::ranks().map(move |r| Card::new(r, s)))
339            .collect::<Vec<_>>();
340        Self { cards }
341    }
342}
343
344impl IntoIterator for Deck {
345    type Item = Card;
346    type IntoIter = std::vec::IntoIter<Card>;
347
348    fn into_iter(self) -> Self::IntoIter {
349        self.cards.into_iter()
350    }
351}
352
353#[cfg(test)]
354mod tests {
355    use super::*;
356    use ahash::HashSet;
357
358    #[test]
359    fn card_encoding() {
360        let mut cards = HashSet::default();
361        let mut deck = Deck::shuffled(&mut rand::rng());
362
363        while !deck.is_empty() {
364            let card = deck.deal();
365            assert_eq!(card.id() & 0xFF, PRIMES[card.rank() as usize]);
366            assert_eq!((card.id() >> 8) & 0xF, card.rank() as u32);
367            assert_eq!((card.id() >> 12) & 0xF, card.suit() as u32);
368            assert_eq!(card.id() >> 16, 1 << (card.rank() as usize));
369            cards.insert(card.id());
370        }
371
372        // Check uniquness.
373        assert_eq!(cards.len(), Deck::SIZE);
374
375        // From the Cactus Kev's website.
376        let kd = Card::new(Rank::King, Suit::Diamonds);
377        assert_eq!(kd.id(), 0x08004b25);
378
379        let fs = Card::new(Rank::Five, Suit::Spades);
380        assert_eq!(fs.id(), 0x00081307);
381
382        let jc = Card::new(Rank::Jack, Suit::Clubs);
383        assert_eq!(jc.id(), 0x0200891d);
384    }
385
386    #[test]
387    fn card_to_string() {
388        let c = Card::new(Rank::King, Suit::Diamonds);
389        assert_eq!(c.to_string(), "KD");
390
391        let c = Card::new(Rank::Five, Suit::Spades);
392        assert_eq!(c.to_string(), "5S");
393
394        let c = Card::new(Rank::Jack, Suit::Clubs);
395        assert_eq!(c.to_string(), "JC");
396
397        let c = Card::new(Rank::Ten, Suit::Hearts);
398        assert_eq!(c.to_string(), "TH");
399
400        let c = Card::new(Rank::Ace, Suit::Hearts);
401        assert_eq!(c.to_string(), "AH");
402    }
403
404    #[test]
405    fn deck_for_each() {
406        let deck = Deck::default();
407        assert_eq!(deck.count(), Deck::SIZE);
408
409        let mut hands = HashSet::default();
410        deck.for_each(5, |cards| {
411            assert_eq!(cards.len(), 5);
412            hands.insert(cards.to_owned());
413        });
414        assert_eq!(hands.len(), 2_598_960);
415
416        hands.clear();
417        deck.for_each(2, |cards| {
418            assert_eq!(cards.len(), 2);
419            hands.insert(cards.to_owned());
420        });
421        assert_eq!(hands.len(), 1_326);
422
423        hands.clear();
424        deck.for_each(3, |cards| {
425            assert_eq!(cards.len(), 3);
426            hands.insert(cards.to_owned());
427        });
428        assert_eq!(hands.len(), 22_100);
429    }
430
431    #[test]
432    fn deck_for_each_7cards() {
433        let deck = Deck::default();
434
435        let mut count = 0;
436        deck.for_each(7, |cards| {
437            assert_eq!(cards.len(), 7);
438            count += 1;
439        });
440        assert_eq!(count, 133_784_560);
441    }
442
443    #[test]
444    fn deck_for_each_remove() {
445        let mut deck = Deck::default();
446        deck.remove(Card::new(Rank::Ace, Suit::Diamonds));
447        deck.remove(Card::new(Rank::King, Suit::Diamonds));
448
449        let mut count = 0;
450        deck.for_each(7, |cards| {
451            assert_eq!(cards.len(), 7);
452            count += 1;
453        });
454        assert_eq!(count, 99_884_400);
455    }
456
457    #[test]
458    fn sample() {
459        let mut counter = 0;
460        Deck::default().sample(10, 1, |hand| {
461            assert_eq!(hand.len(), 1);
462            counter += 1;
463        });
464        assert_eq!(counter, 10);
465
466        let mut counter = 0;
467        Deck::default().sample(10, 7, |hand| {
468            assert_eq!(hand.len(), 7);
469            counter += 1;
470        });
471        assert_eq!(counter, 10);
472    }
473}