openpql_prelude/rating/
flop_hand_category.rs

1use super::{Display, FromStr, N_FLOP_CATEGORY, ParseError, cmp};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Display)]
4pub enum FlopHandCategory {
5    #[default]
6    #[display("FLOPNOTHING")]
7    Nothing,
8    #[display("FLOPUNDERPAIR")]
9    UnderPair,
10    #[display("FLOPTHIRDPAIR")]
11    ThirdPair,
12    #[display("FLOPPOCKET23")]
13    Pocket23,
14    #[display("FLOPSECONDPAIR")]
15    SecondPair,
16    #[display("FLOPPOCKET12")]
17    Pocket12,
18    #[display("FLOPTOPPAIR")]
19    TopPair,
20    #[display("FLOPOVERPAIR")]
21    Overpair,
22    #[display("FLOPBOTTOMTWO")]
23    BottomTwo,
24    #[display("FLOPTOPANDBOTTOM")]
25    TopAndBottom,
26    #[display("FLOPTOPTWO")]
27    TopTwo,
28    #[display("FLOPTRIPS")]
29    Trips,
30    #[display("FLOPSET")]
31    Set,
32    #[display("FLOPSTRAIGHT")]
33    Straight,
34    #[display("FLOPFLUSH")]
35    Flush,
36    #[display("FLOPFULLHOUSE")]
37    FullHouse,
38    #[display("FLOPQUADS")]
39    Quads,
40    #[display("FLOPSTRAIGHTFLUSH")]
41    StraightFlush,
42}
43
44type Idx = u8;
45
46impl FlopHandCategory {
47    pub const MAX: Self = Self::StraightFlush;
48    pub const MIN: Self = Self::Nothing;
49
50    pub const ARR_ALL: [Self; N_FLOP_CATEGORY] = [
51        Self::Nothing,
52        Self::UnderPair,
53        Self::ThirdPair,
54        Self::Pocket23,
55        Self::SecondPair,
56        Self::Pocket12,
57        Self::TopPair,
58        Self::Overpair,
59        Self::BottomTwo,
60        Self::TopAndBottom,
61        Self::TopTwo,
62        Self::Trips,
63        Self::Set,
64        Self::Straight,
65        Self::Flush,
66        Self::FullHouse,
67        Self::Quads,
68        Self::StraightFlush,
69    ];
70
71    const fn to_idx<const SD: bool>(self) -> Idx {
72        match self {
73            Self::Nothing => 0,
74            Self::UnderPair => 1,
75            Self::ThirdPair => 2,
76            Self::Pocket23 => 3,
77            Self::SecondPair => 4,
78            Self::Pocket12 => 5,
79            Self::TopPair => 6,
80            Self::Overpair => 7,
81            Self::BottomTwo => 8,
82            Self::TopAndBottom => 9,
83            Self::TopTwo => 10,
84            Self::Trips => 11,
85            Self::Set => 12,
86            Self::Straight => 13,
87            Self::Flush => 14 + (SD as Idx) * 2, // shortdeck: 16
88            Self::FullHouse => 15,
89            Self::Quads => 17,
90            Self::StraightFlush => 18,
91        }
92    }
93
94    pub fn compare<const SD: bool>(self, other: Self) -> cmp::Ordering {
95        self.to_idx::<SD>().cmp(&other.to_idx::<SD>())
96    }
97}
98
99impl FromStr for FlopHandCategory {
100    type Err = ParseError;
101
102    fn from_str(s: &str) -> Result<Self, Self::Err> {
103        match s.to_ascii_lowercase().trim() {
104            "flopnothing" => Ok(Self::Nothing),
105            "flopunderpair" => Ok(Self::UnderPair),
106            "flopthirdpair" => Ok(Self::ThirdPair),
107            "floppocket23" => Ok(Self::Pocket23),
108            "flopsecondpair" => Ok(Self::SecondPair),
109            "floppocket12" => Ok(Self::Pocket12),
110            "floptoppair" => Ok(Self::TopPair),
111            "flopoverpair" => Ok(Self::Overpair),
112            "flopbottomtwo" => Ok(Self::BottomTwo),
113            "floptopandbottom" => Ok(Self::TopAndBottom),
114            "floptoptwo" => Ok(Self::TopTwo),
115            "floptrips" => Ok(Self::Trips),
116            "flopset" => Ok(Self::Set),
117            "flopstraight" => Ok(Self::Straight),
118            "flopflush" => Ok(Self::Flush),
119            "flopfullhouse" => Ok(Self::FullHouse),
120            "flopquads" => Ok(Self::Quads),
121            "flopstraightflush" => Ok(Self::StraightFlush),
122            _ => Err(ParseError::InvalidFlopHandCategory(s.into())),
123        }
124    }
125}
126
127#[cfg(any(test, feature = "quickcheck"))]
128#[cfg_attr(coverage_nightly, coverage(off))]
129impl quickcheck::Arbitrary for FlopHandCategory {
130    fn arbitrary(g: &mut quickcheck::Gen) -> Self {
131        *g.choose(&Self::ARR_ALL).unwrap()
132    }
133}
134
135#[cfg(test)]
136#[cfg_attr(coverage_nightly, coverage(off))]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn test_from_str() {
142        use FlopHandCategory as Cat;
143
144        assert_eq!(Ok(Cat::Nothing), "flopNothing".parse());
145        assert_eq!(Ok(Cat::UnderPair), "flopuNderpair".parse());
146        assert_eq!(Ok(Cat::ThirdPair), "flopthIrdpair".parse());
147        assert_eq!(Ok(Cat::Pocket23), "floppocKet23".parse());
148        assert_eq!(Ok(Cat::SecondPair), "flopsecoNdpair".parse());
149        assert_eq!(Ok(Cat::Pocket12), "floppockeT12".parse());
150        assert_eq!(Ok(Cat::TopPair), "floptoppaiR".parse());
151        assert_eq!(Ok(Cat::Overpair), "flopOverpair".parse());
152        assert_eq!(Ok(Cat::BottomTwo), "flopbOttomtwo".parse());
153        assert_eq!(Ok(Cat::TopAndBottom), "floptoPandbottom".parse());
154        assert_eq!(Ok(Cat::TopTwo), "floptopTwo".parse());
155        assert_eq!(Ok(Cat::Trips), "floptripS".parse());
156        assert_eq!(Ok(Cat::Set), "flopSet".parse());
157        assert_eq!(Ok(Cat::Straight), "flopsTraight".parse());
158        assert_eq!(Ok(Cat::Flush), "flopflUsh".parse());
159        assert_eq!(Ok(Cat::FullHouse), "flopfulLhouse".parse());
160        assert_eq!(Ok(Cat::Quads), "flopquadS".parse());
161        assert_eq!(Ok(Cat::StraightFlush), "flopStraightFlush".parse());
162
163        assert_eq!(Ok(Cat::Nothing), " flopnothing ".parse(), "should trim");
164
165        assert!("invalid".parse::<Cat>().is_err());
166    }
167
168    #[test]
169    fn test_ord_holdem() {
170        let mut sorted = FlopHandCategory::ARR_ALL.to_vec();
171        sorted.sort_unstable_by(|l, r| l.compare::<false>(*r));
172
173        assert_eq!(sorted, FlopHandCategory::ARR_ALL);
174    }
175
176    #[test]
177    fn test_ord_shortdeck() {
178        let mut sorted = FlopHandCategory::ARR_ALL.to_vec();
179        sorted.sort_unstable_by(|l, r| l.compare::<true>(*r));
180
181        let mut sorted_sd = FlopHandCategory::ARR_ALL.to_vec();
182        let i_flush = sorted_sd
183            .iter()
184            .position(|c| *c == FlopHandCategory::Flush)
185            .unwrap();
186        sorted_sd.swap(i_flush, i_flush + 1);
187
188        assert_eq!(
189            sorted_sd[i_flush],
190            FlopHandCategory::FullHouse,
191            "Fullhouse should be where Flush was"
192        );
193        assert_eq!(sorted, sorted_sd);
194    }
195}