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, 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}