open_pql/base/isomorphic/
mod.rs

1use super::{Board, Card, Flop, HandN, Suit};
2
3mod suit_mapping;
4pub use suit_mapping::*;
5
6/// Index of the turn card in a board array
7pub const IDX_TURN: usize = 3;
8/// Index of the river card in a board array
9pub const IDX_RIVER: usize = 4;
10/// Number of cards in a flop
11pub const N_FLOP: usize = 3;
12
13impl<const N: usize> HandN<N> {
14    pub fn new_iso_with_mapping(
15        cards: &[Card],
16        mapping: &mut SuitMapping,
17    ) -> Self {
18        Self::new(create_iso_array(cards, mapping))
19    }
20
21    pub fn new_iso(cards: &[Card]) -> (Self, SuitMapping) {
22        let mut mapping = SuitMapping::default();
23        let iso = Self::new_iso_with_mapping(cards, &mut mapping);
24        (iso, mapping)
25    }
26}
27
28impl Board {
29    pub fn new_iso_with_mapping(
30        cards: &[Card],
31        mapping: &mut SuitMapping,
32    ) -> Self {
33        create_iso_board(cards, mapping)
34    }
35}
36
37fn create_iso_array<const N: usize>(
38    cards: &[Card],
39    mapping: &mut SuitMapping,
40) -> [Card; N] {
41    let mut sorted = cards[..N]
42        .iter()
43        .map(|c| {
44            (
45                cards[..N].iter().filter(|&el| el.suit == c.suit).count(),
46                c.rank,
47                c,
48            )
49        })
50        .collect::<Vec<_>>();
51
52    sorted.sort_unstable();
53
54    let mut res = [Card::default(); N];
55
56    for (i, (_, _, card)) in sorted.into_iter().enumerate() {
57        res[i] = Card::new(card.rank, mapping.map_suit(card.suit));
58    }
59
60    res.sort_unstable();
61
62    res
63}
64
65fn create_iso_board(board_cards: &[Card], mapping: &mut SuitMapping) -> Board {
66    let n = board_cards.len();
67    let mut board = Board::default();
68
69    if n >= N_FLOP {
70        board.flop =
71            Some(Flop::new(create_iso_array(&board_cards[..N_FLOP], mapping)));
72    }
73
74    if n > IDX_TURN {
75        let card = board_cards[IDX_TURN];
76        board.turn = Some(Card::new(card.rank, mapping.map_suit(card.suit)));
77    }
78
79    if n > IDX_RIVER {
80        let card = board_cards[IDX_RIVER];
81        board.river = Some(Card::new(card.rank, mapping.map_suit(card.suit)));
82    }
83
84    board
85}
86
87/// Converts a suit to its isomorphic character representation
88pub const fn to_suitvar_char(s: Suit) -> char {
89    match s {
90        Suit::S => 'w',
91        Suit::H => 'x',
92        Suit::D => 'y',
93        Suit::C => 'z',
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use std::collections::{HashMap, HashSet};
100
101    use super::*;
102    use crate::*;
103
104    #[test]
105    fn test_to_suitvar_char() {
106        // Test each suit mapping to its expected character
107        assert_eq!(to_suitvar_char(Suit::S), 'w');
108        assert_eq!(to_suitvar_char(Suit::H), 'x');
109        assert_eq!(to_suitvar_char(Suit::D), 'y');
110        assert_eq!(to_suitvar_char(Suit::C), 'z');
111        //Hand<6♠8♥9♦Q♦Q♣>,
112        //Hand<6♠8♥9♦Q♣Q♦>,
113    }
114
115    #[test]
116    fn test_iso() {
117        assert_eq!(
118            HandN::<5>::new_iso(&cards!("6s8h9dQdQc")).0,
119            HandN::<5>::new_iso(&cards!("6s8h9dQcQd")).0
120        );
121    }
122
123    #[test]
124    fn test_iso_flop() {
125        let mut res: HashMap<HandN<3>, HandN<3>> = HashMap::new();
126        let mut iso_set: HashSet<HandN<3>> = HashSet::default();
127        for hand in HandN::<3>::iter_all_short() {
128            let (iso, _) = HandN::new_iso(&hand.0);
129            res.insert(hand, iso);
130            iso_set.insert(iso);
131        }
132
133        assert_eq!(res.len(), 7140);
134        assert_eq!(iso_set.len(), 573);
135    }
136}