openpql_prelude/card/
isomorphic.rs1use super::{Board, Card, CardCount, Flop, HandN, Suit, SuitMapping};
2
3impl<const N: usize> HandN<N> {
4 pub fn new_iso_with_mapping(
6 cards: &[Card],
7 mapping: &mut SuitMapping,
8 ) -> Self {
9 Self::new(create_iso_array(cards, mapping))
10 }
11
12 pub fn new_iso(cards: &[Card]) -> (Self, SuitMapping) {
14 let mut mapping = SuitMapping::default();
15 let iso = Self::new_iso_with_mapping(cards, &mut mapping);
16 (iso, mapping)
17 }
18}
19
20impl Board {
21 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) {
31 let mut mapping = SuitMapping::default();
32 let iso = Self::new_iso_with_mapping(cards, &mut mapping);
33 (iso, mapping)
34 }
35}
36
37type SuitCount = [CardCount; Suit::N_SUITS as usize];
38
39fn count_suits(cards: &[Card]) -> SuitCount {
40 let mut res = SuitCount::default();
41
42 for card in cards {
43 res[card.suit as usize] += 1;
44 }
45
46 res
47}
48
49fn create_iso_array<const N: usize>(
52 slice: &[Card],
53 mapping: &mut SuitMapping,
54) -> [Card; N] {
55 let mut cards: [_; N] = slice[..N].try_into().unwrap();
56 let suit_count = count_suits(cards.as_slice());
57
58 cards.sort_unstable_by_key(|&card| {
59 (Suit::N_SUITS - suit_count[card.suit as usize], card)
60 });
61
62 for card in &mut cards {
63 card.suit = mapping.map_suit(card.suit);
64 }
65
66 cards.sort_unstable();
67
68 cards
69}
70
71fn create_iso_board(board_cards: &[Card], mapping: &mut SuitMapping) -> Board {
72 #[inline]
73 const fn map_card(card: Card, mapping: &mut SuitMapping) -> Card {
74 Card::new(card.rank, mapping.map_suit(card.suit))
75 }
76
77 let n = board_cards.len();
78 let mut board = Board::default();
79
80 if n >= Board::N_FLOP {
81 board.flop = Some(Flop::new_iso_with_mapping(board_cards, mapping));
82 }
83
84 if n >= Board::N_TURN {
85 board.turn = Some(map_card(board_cards[Board::IDX_TURN], mapping));
86 }
87
88 if n >= Board::N_RIVER {
89 board.river = Some(map_card(board_cards[Board::IDX_RIVER], mapping));
90 }
91
92 board
93}
94
95#[cfg(test)]
96#[cfg_attr(coverage_nightly, coverage(off))]
97mod tests {
98 use super::*;
99 use crate::*;
100
101 #[test]
102 fn test_iso() {
103 assert_eq!(
104 HandN::<5>::new_iso(&cards!("6s8h9dQdQc")).0,
105 HandN::<5>::new_iso(&cards!("6s8h9dQcQd")).0
106 );
107 }
108
109 #[test]
110 fn test_iso_flop() {
111 let mut res = FxHashMap::default();
112 let mut iso_set = FxHashSet::default();
113
114 for hand in Flop::iter_all::<true>() {
115 let (iso, _) = HandN::<3>::new_iso(&hand.0);
116 res.insert(hand, iso);
117 iso_set.insert(iso);
118 }
119
120 assert_eq!(res.len(), 7140);
121 assert_eq!(iso_set.len(), 573);
122 }
123
124 #[test]
125 fn test_iso_board() {
126 fn assert_iso_eq(lhs: &str, rhs: &str) {
127 assert_eq!(
128 Board::new_iso(&cards!(lhs)).0,
129 Board::new_iso(&cards!(rhs)).0
130 );
131 }
132
133 assert_iso_eq("", "");
134 assert_iso_eq("AsKhQd", "AhKsQc");
135 assert_iso_eq("AsKhQdJdTd", "AhKsQcJcTc"); }
137}