open_pql/base/
isomorphic.rs

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