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! flop {
14 ($s:expr) => {
15 $crate::Flop::from(
16 <[$crate::Card; 3]>::try_from($crate::Card::new_vec($s)).unwrap(),
17 )
18 };
19}
20
21#[cfg(any(test, feature = "benchmark"))]
22#[macro_export]
23macro_rules! board {
24 ($s:expr) => {
25 $crate::Board::from(
26 $crate::Card::new_vec($s).as_ref() as &[$crate::Card]
27 )
28 };
29}
30
31#[derive(Copy, Clone, PartialEq, Eq, Display)]
33#[display("{r}{s}")]
34pub struct Card {
35 pub r: Rank,
36 pub s: Suit,
37}
38
39impl Hash for Card {
40 fn hash<H: Hasher>(&self, state: &mut H) {
41 ((self.s as u8) * N_RANKS + (self.r as u8)).hash(state);
42 }
43}
44
45impl Card {
46 pub(crate) const C_2S: Self = Self::new(Rank::R2, Suit::S);
47 pub(crate) const C_3S: Self = Self::new(Rank::R3, Suit::S);
48 pub(crate) const C_4S: Self = Self::new(Rank::R4, Suit::S);
49 pub(crate) const C_5S: Self = Self::new(Rank::R5, Suit::S);
50 pub(crate) const C_6S: Self = Self::new(Rank::R6, Suit::S);
51 pub(crate) const C_7S: Self = Self::new(Rank::R7, Suit::S);
52 pub(crate) const C_8S: Self = Self::new(Rank::R8, Suit::S);
53 pub(crate) const C_9S: Self = Self::new(Rank::R9, Suit::S);
54 pub(crate) const C_TS: Self = Self::new(Rank::RT, Suit::S);
55 pub(crate) const C_JS: Self = Self::new(Rank::RJ, Suit::S);
56 pub(crate) const C_QS: Self = Self::new(Rank::RQ, Suit::S);
57 pub(crate) const C_KS: Self = Self::new(Rank::RK, Suit::S);
58 pub(crate) const C_AS: Self = Self::new(Rank::RA, Suit::S);
59
60 pub(crate) const C_2H: Self = Self::new(Rank::R2, Suit::H);
61 pub(crate) const C_3H: Self = Self::new(Rank::R3, Suit::H);
62 pub(crate) const C_4H: Self = Self::new(Rank::R4, Suit::H);
63 pub(crate) const C_5H: Self = Self::new(Rank::R5, Suit::H);
64 pub(crate) const C_6H: Self = Self::new(Rank::R6, Suit::H);
65 pub(crate) const C_7H: Self = Self::new(Rank::R7, Suit::H);
66 pub(crate) const C_8H: Self = Self::new(Rank::R8, Suit::H);
67 pub(crate) const C_9H: Self = Self::new(Rank::R9, Suit::H);
68 pub(crate) const C_TH: Self = Self::new(Rank::RT, Suit::H);
69 pub(crate) const C_JH: Self = Self::new(Rank::RJ, Suit::H);
70 pub(crate) const C_QH: Self = Self::new(Rank::RQ, Suit::H);
71 pub(crate) const C_KH: Self = Self::new(Rank::RK, Suit::H);
72 pub(crate) const C_AH: Self = Self::new(Rank::RA, Suit::H);
73
74 pub(crate) const C_2D: Self = Self::new(Rank::R2, Suit::D);
75 pub(crate) const C_3D: Self = Self::new(Rank::R3, Suit::D);
76 pub(crate) const C_4D: Self = Self::new(Rank::R4, Suit::D);
77 pub(crate) const C_5D: Self = Self::new(Rank::R5, Suit::D);
78 pub(crate) const C_6D: Self = Self::new(Rank::R6, Suit::D);
79 pub(crate) const C_7D: Self = Self::new(Rank::R7, Suit::D);
80 pub(crate) const C_8D: Self = Self::new(Rank::R8, Suit::D);
81 pub(crate) const C_9D: Self = Self::new(Rank::R9, Suit::D);
82 pub(crate) const C_TD: Self = Self::new(Rank::RT, Suit::D);
83 pub(crate) const C_JD: Self = Self::new(Rank::RJ, Suit::D);
84 pub(crate) const C_QD: Self = Self::new(Rank::RQ, Suit::D);
85 pub(crate) const C_KD: Self = Self::new(Rank::RK, Suit::D);
86 pub(crate) const C_AD: Self = Self::new(Rank::RA, Suit::D);
87
88 pub(crate) const C_2C: Self = Self::new(Rank::R2, Suit::C);
89 pub(crate) const C_3C: Self = Self::new(Rank::R3, Suit::C);
90 pub(crate) const C_4C: Self = Self::new(Rank::R4, Suit::C);
91 pub(crate) const C_5C: Self = Self::new(Rank::R5, Suit::C);
92 pub(crate) const C_6C: Self = Self::new(Rank::R6, Suit::C);
93 pub(crate) const C_7C: Self = Self::new(Rank::R7, Suit::C);
94 pub(crate) const C_8C: Self = Self::new(Rank::R8, Suit::C);
95 pub(crate) const C_9C: Self = Self::new(Rank::R9, Suit::C);
96 pub(crate) const C_TC: Self = Self::new(Rank::RT, Suit::C);
97 pub(crate) const C_JC: Self = Self::new(Rank::RJ, Suit::C);
98 pub(crate) const C_QC: Self = Self::new(Rank::RQ, Suit::C);
99 pub(crate) const C_KC: Self = Self::new(Rank::RK, Suit::C);
100 pub(crate) const C_AC: Self = Self::new(Rank::RA, Suit::C);
101
102 pub const ARR_ALL: [Self; 52] = [
104 Self::C_2S,
105 Self::C_3S,
106 Self::C_4S,
107 Self::C_5S,
108 Self::C_6S,
109 Self::C_7S,
110 Self::C_8S,
111 Self::C_9S,
112 Self::C_TS,
113 Self::C_JS,
114 Self::C_QS,
115 Self::C_KS,
116 Self::C_AS,
117 Self::C_2H,
118 Self::C_3H,
119 Self::C_4H,
120 Self::C_5H,
121 Self::C_6H,
122 Self::C_7H,
123 Self::C_8H,
124 Self::C_9H,
125 Self::C_TH,
126 Self::C_JH,
127 Self::C_QH,
128 Self::C_KH,
129 Self::C_AH,
130 Self::C_2D,
131 Self::C_3D,
132 Self::C_4D,
133 Self::C_5D,
134 Self::C_6D,
135 Self::C_7D,
136 Self::C_8D,
137 Self::C_9D,
138 Self::C_TD,
139 Self::C_JD,
140 Self::C_QD,
141 Self::C_KD,
142 Self::C_AD,
143 Self::C_2C,
144 Self::C_3C,
145 Self::C_4C,
146 Self::C_5C,
147 Self::C_6C,
148 Self::C_7C,
149 Self::C_8C,
150 Self::C_9C,
151 Self::C_TC,
152 Self::C_JC,
153 Self::C_QC,
154 Self::C_KC,
155 Self::C_AC,
156 ];
157
158 pub const ARR_ALL_SHORT: [Self; 32] = [
160 Self::C_7S,
161 Self::C_8S,
162 Self::C_9S,
163 Self::C_TS,
164 Self::C_JS,
165 Self::C_QS,
166 Self::C_KS,
167 Self::C_AS,
168 Self::C_7H,
169 Self::C_8H,
170 Self::C_9H,
171 Self::C_TH,
172 Self::C_JH,
173 Self::C_QH,
174 Self::C_KH,
175 Self::C_AH,
176 Self::C_7D,
177 Self::C_8D,
178 Self::C_9D,
179 Self::C_TD,
180 Self::C_JD,
181 Self::C_QD,
182 Self::C_KD,
183 Self::C_AD,
184 Self::C_7C,
185 Self::C_8C,
186 Self::C_9C,
187 Self::C_TC,
188 Self::C_JC,
189 Self::C_QC,
190 Self::C_KC,
191 Self::C_AC,
192 ];
193
194 #[must_use]
210 #[inline]
211 pub const fn new(r: Rank, s: Suit) -> Self {
212 Self { r, s }
213 }
214}
215
216impl Default for Card {
217 fn default() -> Self {
218 Self::C_2S
219 }
220}
221
222impl PartialOrd for Card {
223 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
224 if self == other {
225 Some(Ordering::Equal)
226 } else {
227 None
228 }
229 }
230}
231
232impl fmt::Debug for Card {
233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234 let r = match self.r {
235 Rank::R2 => '2',
236 Rank::R3 => '3',
237 Rank::R4 => '4',
238 Rank::R5 => '5',
239 Rank::R6 => '6',
240 Rank::R7 => '7',
241 Rank::R8 => '8',
242 Rank::R9 => '9',
243 Rank::RT => 'T',
244 Rank::RJ => 'J',
245 Rank::RQ => 'Q',
246 Rank::RK => 'K',
247 Rank::RA => 'A',
248 };
249
250 let s = match self.s {
251 Suit::S => 's',
252 Suit::H => 'h',
253 Suit::D => 'd',
254 Suit::C => 'c',
255 };
256
257 f.write_str(&format!("{r}{s}"))
258 }
259}
260
261impl FromStr for Card {
262 type Err = ParseError;
263
264 fn from_str(s: &str) -> Result<Self, Self::Err> {
265 let mut cs = s.chars().filter(|c| !c.is_whitespace());
266
267 if let Some(c) = cs.next() {
268 if let Ok(r) = Rank::try_from(c) {
269 if let Some(c) = cs.next() {
270 if let Ok(s) = Suit::try_from(c) {
271 if cs.next().is_none() {
272 return Ok(Self::new(r, s));
273 }
274 }
275 }
276 }
277 }
278
279 Err(ParseError::InvalidCard(s.into()))
280 }
281}
282
283impl<T> From<T> for Rank
284where
285 Card: From<T>,
286{
287 fn from(v: T) -> Self {
288 Card::from(v).r
289 }
290}
291
292impl<T> From<T> for Suit
293where
294 Card: From<T>,
295{
296 fn from(v: T) -> Self {
297 Card::from(v).s
298 }
299}
300
301#[cfg(any(test, feature = "benchmark"))]
302impl Card {
303 pub fn from_tuple((r, s): (char, char)) -> Self {
306 Self::new(r.try_into().unwrap(), s.try_into().unwrap())
307 }
308
309 pub fn new_vec(s: &str) -> Vec<Self> {
310 use itertools::Itertools;
311
312 s.chars()
313 .filter(|c| !c.is_whitespace())
314 .tuples()
315 .map(Self::from_tuple)
316 .collect()
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323
324 impl Arbitrary for Card {
325 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
326 *g.choose(&Self::ARR_ALL).unwrap()
327 }
328 }
329
330 #[quickcheck]
331 fn test_rank_suit(r: Rank, s: Suit) {
332 let card = Card::new(r, s);
333
334 assert_eq!(r, card.r);
335 assert_eq!(s, card.s);
336 }
337
338 #[test]
339 fn test_default() {
340 assert_eq!(Card::C_2S, Card::default());
341 }
342
343 #[quickcheck]
344 fn test_hash(c1: Card) {
345 use std::hash::DefaultHasher;
346
347 let c2 = c1;
348
349 let mut h1 = DefaultHasher::new();
350 let mut h2 = DefaultHasher::new();
351
352 c1.hash(&mut h1);
353 c2.hash(&mut h2);
354
355 assert_eq!(h1.finish(), h2.finish());
356 }
357
358 #[quickcheck]
359 fn test_into_rank_and_suit(c: Card) {
360 let r: Rank = c.into();
361 let s: Suit = c.into();
362
363 assert_eq!(r, c.r);
364 assert_eq!(s, c.s);
365 }
366
367 #[test]
368 fn test_from_str() {
369 let c = |s| Ok(cards!(s)[0]);
370
371 assert_eq!(c("2s"), "2s".parse());
372 assert_eq!(c("2s"), " 2 S ".parse());
373 assert_eq!(
374 Err(ParseError::InvalidCard("2s?".to_owned())),
375 "2s?".parse::<Card>()
376 );
377 assert!("".parse::<Card>().is_err());
378 assert!("?".parse::<Card>().is_err());
379 assert!("2".parse::<Card>().is_err());
380 assert!("2k".parse::<Card>().is_err());
381 }
382
383 #[quickcheck]
384 fn test_to_string(c: Card) {
385 assert_eq!(format!("{}{}", c.r, c.s), c.to_string());
386 }
387
388 #[test]
389 fn test_macro() {
390 let cards = cards!("As Kh");
391 assert_eq!(cards, vec![Card::C_AS, Card::C_KH]);
392
393 let flop = flop!("2s 3s 4s");
394 assert_eq!(flop, (Card::C_2S, Card::C_3S, Card::C_4S).into());
395
396 let board = board!("As Kh 3s 5h 6c");
397 assert_eq!(
398 board,
399 (Card::C_AS, Card::C_KH, Card::C_3S, Card::C_5H, Card::C_6C).into(),
400 );
401 }
402
403 #[test]
404 fn test_partial_ord() {
405 assert!(Card::C_2S == Card::C_2S);
406 assert_eq!(Card::C_2S.partial_cmp(&Card::C_2S), Some(Ordering::Equal));
407
408 assert!(!(Card::C_2S <= Card::C_3S || Card::C_2S >= Card::C_3S));
409 assert!(Card::C_2S <= Card::C_2S);
410 }
411}