1use super::*;
2
3#[cfg(any(test, feature = "benchmark"))]
4#[macro_export]
5macro_rules! cards {
6 ($s:expr) => {
7 $crate::Card::new_vec($s)
8 };
9}
10
11#[cfg(any(test, feature = "benchmark"))]
12#[macro_export]
13macro_rules! card {
14 ($s:expr) => {
15 cards!($s)[0]
16 };
17}
18
19#[cfg(any(test, feature = "benchmark"))]
20#[macro_export]
21macro_rules! flop {
22 ($s:expr) => {
23 $crate::Flop::from(
24 <[$crate::Card; 3]>::try_from($crate::Card::new_vec($s)).unwrap(),
25 )
26 };
27}
28
29#[cfg(any(test, feature = "benchmark"))]
30#[macro_export]
31macro_rules! board {
32 ($s:expr) => {
33 $crate::Board::from(
34 $crate::Card::new_vec($s).as_ref() as &[$crate::Card]
35 )
36 };
37}
38
39#[derive(Copy, Clone, Display, Hash, PartialEq, Eq, PartialOrd, Ord)]
41#[display("{rank}{suit}")]
42pub struct Card {
43 pub rank: Rank,
44 pub suit: Suit,
45}
46
47impl Card {
48 pub const ARR_ALL: [Self; 52] = [
49 Self::new(Rank::R2, Suit::S),
50 Self::new(Rank::R2, Suit::H),
51 Self::new(Rank::R2, Suit::D),
52 Self::new(Rank::R2, Suit::C),
53 Self::new(Rank::R3, Suit::S),
54 Self::new(Rank::R3, Suit::H),
55 Self::new(Rank::R3, Suit::D),
56 Self::new(Rank::R3, Suit::C),
57 Self::new(Rank::R4, Suit::S),
58 Self::new(Rank::R4, Suit::H),
59 Self::new(Rank::R4, Suit::D),
60 Self::new(Rank::R4, Suit::C),
61 Self::new(Rank::R5, Suit::S),
62 Self::new(Rank::R5, Suit::H),
63 Self::new(Rank::R5, Suit::D),
64 Self::new(Rank::R5, Suit::C),
65 Self::new(Rank::R6, Suit::S),
66 Self::new(Rank::R6, Suit::H),
67 Self::new(Rank::R6, Suit::D),
68 Self::new(Rank::R6, Suit::C),
69 Self::new(Rank::R7, Suit::S),
70 Self::new(Rank::R7, Suit::H),
71 Self::new(Rank::R7, Suit::D),
72 Self::new(Rank::R7, Suit::C),
73 Self::new(Rank::R8, Suit::S),
74 Self::new(Rank::R8, Suit::H),
75 Self::new(Rank::R8, Suit::D),
76 Self::new(Rank::R8, Suit::C),
77 Self::new(Rank::R9, Suit::S),
78 Self::new(Rank::R9, Suit::H),
79 Self::new(Rank::R9, Suit::D),
80 Self::new(Rank::R9, Suit::C),
81 Self::new(Rank::RT, Suit::S),
82 Self::new(Rank::RT, Suit::H),
83 Self::new(Rank::RT, Suit::D),
84 Self::new(Rank::RT, Suit::C),
85 Self::new(Rank::RJ, Suit::S),
86 Self::new(Rank::RJ, Suit::H),
87 Self::new(Rank::RJ, Suit::D),
88 Self::new(Rank::RJ, Suit::C),
89 Self::new(Rank::RQ, Suit::S),
90 Self::new(Rank::RQ, Suit::H),
91 Self::new(Rank::RQ, Suit::D),
92 Self::new(Rank::RQ, Suit::C),
93 Self::new(Rank::RK, Suit::S),
94 Self::new(Rank::RK, Suit::H),
95 Self::new(Rank::RK, Suit::D),
96 Self::new(Rank::RK, Suit::C),
97 Self::new(Rank::RA, Suit::S),
98 Self::new(Rank::RA, Suit::H),
99 Self::new(Rank::RA, Suit::D),
100 Self::new(Rank::RA, Suit::C),
101 ];
102
103 pub const ARR_ALL_SHORT: [Self; 36] = [
104 Self::new(Rank::R6, Suit::S),
105 Self::new(Rank::R6, Suit::H),
106 Self::new(Rank::R6, Suit::D),
107 Self::new(Rank::R6, Suit::C),
108 Self::new(Rank::R7, Suit::S),
109 Self::new(Rank::R7, Suit::H),
110 Self::new(Rank::R7, Suit::D),
111 Self::new(Rank::R7, Suit::C),
112 Self::new(Rank::R8, Suit::S),
113 Self::new(Rank::R8, Suit::H),
114 Self::new(Rank::R8, Suit::D),
115 Self::new(Rank::R8, Suit::C),
116 Self::new(Rank::R9, Suit::S),
117 Self::new(Rank::R9, Suit::H),
118 Self::new(Rank::R9, Suit::D),
119 Self::new(Rank::R9, Suit::C),
120 Self::new(Rank::RT, Suit::S),
121 Self::new(Rank::RT, Suit::H),
122 Self::new(Rank::RT, Suit::D),
123 Self::new(Rank::RT, Suit::C),
124 Self::new(Rank::RJ, Suit::S),
125 Self::new(Rank::RJ, Suit::H),
126 Self::new(Rank::RJ, Suit::D),
127 Self::new(Rank::RJ, Suit::C),
128 Self::new(Rank::RQ, Suit::S),
129 Self::new(Rank::RQ, Suit::H),
130 Self::new(Rank::RQ, Suit::D),
131 Self::new(Rank::RQ, Suit::C),
132 Self::new(Rank::RK, Suit::S),
133 Self::new(Rank::RK, Suit::H),
134 Self::new(Rank::RK, Suit::D),
135 Self::new(Rank::RK, Suit::C),
136 Self::new(Rank::RA, Suit::S),
137 Self::new(Rank::RA, Suit::H),
138 Self::new(Rank::RA, Suit::D),
139 Self::new(Rank::RA, Suit::C),
140 ];
141
142 #[must_use]
143 #[inline]
144 pub const fn new(r: Rank, s: Suit) -> Self {
145 Self { rank: r, suit: s }
146 }
147
148 #[must_use]
149 #[inline]
150 pub fn from_indices(r: u8, s: u8) -> Self {
151 Self {
152 rank: Rank::from_u8(r),
153 suit: Suit::from_u8(s),
154 }
155 }
156
157 pub const fn to_u8(self) -> u8 {
158 const SHIFT_SUIT: u8 = 4;
159 (self.rank as u8) | ((self.suit as u8) << SHIFT_SUIT)
160 }
161
162 pub fn from_u8(v: u8) -> Self {
163 const SHIFT_SUIT: u8 = 4;
164 Self::from_indices(v & 0b1111, v >> SHIFT_SUIT)
165 }
166}
167
168impl Default for Card {
169 fn default() -> Self {
170 Self::new(Rank::R2, Suit::S)
171 }
172}
173
174impl fmt::Debug for Card {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 write!(f, "{self}")
177 }
178}
179
180impl FromStr for Card {
181 type Err = ParseError;
182
183 fn from_str(s: &str) -> Result<Self, Self::Err> {
184 let mut cs = s.chars().filter(|c| !c.is_whitespace());
185
186 if let Some(c) = cs.next()
187 && let Ok(r) = Rank::try_from(c)
188 && let Some(c) = cs.next()
189 && let Ok(s) = Suit::try_from(c)
190 && cs.next().is_none()
191 {
192 return Ok(Self::new(r, s));
193 }
194
195 Err(ParseError::InvalidCard(s.into()))
196 }
197}
198
199impl<T> From<T> for Rank
200where
201 Card: From<T>,
202{
203 fn from(v: T) -> Self {
204 Card::from(v).rank
205 }
206}
207
208impl<T> From<T> for Suit
209where
210 Card: From<T>,
211{
212 fn from(v: T) -> Self {
213 Card::from(v).suit
214 }
215}
216
217#[cfg(any(test, feature = "benchmark"))]
218impl Card {
219 pub fn from_tuple((r, s): (char, char)) -> Self {
222 Self::new(r.try_into().unwrap(), s.try_into().unwrap())
223 }
224
225 pub fn new_vec(s: &str) -> Vec<Self> {
226 use itertools::Itertools;
227
228 s.chars()
229 .filter(|c| !c.is_whitespace())
230 .tuples()
231 .map(Self::from_tuple)
232 .collect()
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239
240 impl Arbitrary for Card {
241 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
242 *g.choose(&Self::ARR_ALL).unwrap()
243 }
244 }
245
246 #[quickcheck]
247 fn test_rank_suit(r: Rank, s: Suit) {
248 let card = Card::new(r, s);
249
250 assert_eq!(r, card.rank);
251 assert_eq!(s, card.suit);
252 }
253
254 #[test]
255 fn test_default() {
256 assert_eq!(Card::new(Rank::R2, Suit::S), Card::default());
257 assert_eq!(card!("2s"), Card::default());
258 }
259
260 #[quickcheck]
261 fn test_hash(c1: Card) {
262 use std::hash::DefaultHasher;
263
264 let c2 = c1;
265
266 let mut h1 = DefaultHasher::new();
267 let mut h2 = DefaultHasher::new();
268
269 c1.hash(&mut h1);
270 c2.hash(&mut h2);
271
272 assert_eq!(h1.finish(), h2.finish());
273 }
274
275 #[quickcheck]
276 fn test_into_rank_and_suit(c: Card) {
277 let r: Rank = c.into();
278 let s: Suit = c.into();
279
280 assert_eq!(r, c.rank);
281 assert_eq!(s, c.suit);
282 }
283
284 #[test]
285 fn test_from_str() {
286 let c = |s| Ok(cards!(s)[0]);
287
288 assert_eq!(c("2s"), "2s".parse());
289 assert_eq!(c("2s"), " 2 S ".parse());
290 assert_eq!(
291 Err(ParseError::InvalidCard("2s?".to_owned())),
292 "2s?".parse::<Card>()
293 );
294 assert!("".parse::<Card>().is_err());
295 assert!("?".parse::<Card>().is_err());
296 assert!("2".parse::<Card>().is_err());
297 assert!("2k".parse::<Card>().is_err());
298 }
299
300 #[quickcheck]
301 fn test_to_string(c: Card) {
302 assert_eq!(format!("{}{}", c.rank, c.suit), c.to_string());
303 }
304
305 #[quickcheck]
306 fn test_to_u8(c: Card) {
307 assert_eq!(c, Card::from_u8(c.to_u8()));
308 }
309
310 #[test]
311 fn test_macro() {
312 let cards = cards!("As Kh");
313 assert_eq!(
314 cards,
315 vec![Card::new(Rank::RA, Suit::S), Card::new(Rank::RK, Suit::H),]
316 );
317
318 let flop = flop!("2s 3s 4s");
319 assert_eq!(
320 flop,
321 (
322 Card::new(Rank::R2, Suit::S),
323 Card::new(Rank::R3, Suit::S),
324 Card::new(Rank::R4, Suit::S),
325 )
326 .into()
327 );
328
329 let board = board!("As Kh 3s 5h 6c");
330 assert_eq!(
331 board,
332 (
333 Card::new(Rank::RA, Suit::S),
334 Card::new(Rank::RK, Suit::H),
335 Card::new(Rank::R3, Suit::S),
336 Card::new(Rank::R5, Suit::H),
337 Card::new(Rank::R6, Suit::C),
338 )
339 .into(),
340 );
341 }
342
343 #[test]
344 fn test_ord() {
345 let mut sorted = Card::ARR_ALL.to_vec();
346 sorted.reverse();
347 sorted.sort_unstable();
348
349 assert_eq!(sorted, Card::ARR_ALL);
350 }
351}