1use super::{
2 Display, FromStr, Hash, ParseError, Rank, RankIdx, Suit, SuitIdx, fmt,
3};
4
5#[cfg(any(test, feature = "benchmark"))]
16#[macro_export]
17macro_rules! cards {
18 ($s:expr) => {
19 $crate::Card::new_vec($s)
20 };
21}
22
23#[cfg(any(test, feature = "benchmark"))]
33#[macro_export]
34macro_rules! card {
35 ($s:expr) => {
36 cards!($s)[0]
37 };
38}
39
40#[derive(Copy, Clone, Display, Hash, PartialEq, Eq, PartialOrd, Ord)]
56#[display("{rank}{suit}")]
57pub struct Card {
58 pub rank: Rank,
59 pub suit: Suit,
60}
61
62impl Card {
63 pub const ARR_ALL: [Self; 52] = [
66 Self::new(Rank::R2, Suit::S),
67 Self::new(Rank::R2, Suit::H),
68 Self::new(Rank::R2, Suit::D),
69 Self::new(Rank::R2, Suit::C),
70 Self::new(Rank::R3, Suit::S),
71 Self::new(Rank::R3, Suit::H),
72 Self::new(Rank::R3, Suit::D),
73 Self::new(Rank::R3, Suit::C),
74 Self::new(Rank::R4, Suit::S),
75 Self::new(Rank::R4, Suit::H),
76 Self::new(Rank::R4, Suit::D),
77 Self::new(Rank::R4, Suit::C),
78 Self::new(Rank::R5, Suit::S),
79 Self::new(Rank::R5, Suit::H),
80 Self::new(Rank::R5, Suit::D),
81 Self::new(Rank::R5, Suit::C),
82 Self::new(Rank::R6, Suit::S),
83 Self::new(Rank::R6, Suit::H),
84 Self::new(Rank::R6, Suit::D),
85 Self::new(Rank::R6, Suit::C),
86 Self::new(Rank::R7, Suit::S),
87 Self::new(Rank::R7, Suit::H),
88 Self::new(Rank::R7, Suit::D),
89 Self::new(Rank::R7, Suit::C),
90 Self::new(Rank::R8, Suit::S),
91 Self::new(Rank::R8, Suit::H),
92 Self::new(Rank::R8, Suit::D),
93 Self::new(Rank::R8, Suit::C),
94 Self::new(Rank::R9, Suit::S),
95 Self::new(Rank::R9, Suit::H),
96 Self::new(Rank::R9, Suit::D),
97 Self::new(Rank::R9, Suit::C),
98 Self::new(Rank::RT, Suit::S),
99 Self::new(Rank::RT, Suit::H),
100 Self::new(Rank::RT, Suit::D),
101 Self::new(Rank::RT, Suit::C),
102 Self::new(Rank::RJ, Suit::S),
103 Self::new(Rank::RJ, Suit::H),
104 Self::new(Rank::RJ, Suit::D),
105 Self::new(Rank::RJ, Suit::C),
106 Self::new(Rank::RQ, Suit::S),
107 Self::new(Rank::RQ, Suit::H),
108 Self::new(Rank::RQ, Suit::D),
109 Self::new(Rank::RQ, Suit::C),
110 Self::new(Rank::RK, Suit::S),
111 Self::new(Rank::RK, Suit::H),
112 Self::new(Rank::RK, Suit::D),
113 Self::new(Rank::RK, Suit::C),
114 Self::new(Rank::RA, Suit::S),
115 Self::new(Rank::RA, Suit::H),
116 Self::new(Rank::RA, Suit::D),
117 Self::new(Rank::RA, Suit::C),
118 ];
119
120 pub const ARR_ALL_SHORT: [Self; 36] = [
123 Self::new(Rank::R6, Suit::S),
124 Self::new(Rank::R6, Suit::H),
125 Self::new(Rank::R6, Suit::D),
126 Self::new(Rank::R6, Suit::C),
127 Self::new(Rank::R7, Suit::S),
128 Self::new(Rank::R7, Suit::H),
129 Self::new(Rank::R7, Suit::D),
130 Self::new(Rank::R7, Suit::C),
131 Self::new(Rank::R8, Suit::S),
132 Self::new(Rank::R8, Suit::H),
133 Self::new(Rank::R8, Suit::D),
134 Self::new(Rank::R8, Suit::C),
135 Self::new(Rank::R9, Suit::S),
136 Self::new(Rank::R9, Suit::H),
137 Self::new(Rank::R9, Suit::D),
138 Self::new(Rank::R9, Suit::C),
139 Self::new(Rank::RT, Suit::S),
140 Self::new(Rank::RT, Suit::H),
141 Self::new(Rank::RT, Suit::D),
142 Self::new(Rank::RT, Suit::C),
143 Self::new(Rank::RJ, Suit::S),
144 Self::new(Rank::RJ, Suit::H),
145 Self::new(Rank::RJ, Suit::D),
146 Self::new(Rank::RJ, Suit::C),
147 Self::new(Rank::RQ, Suit::S),
148 Self::new(Rank::RQ, Suit::H),
149 Self::new(Rank::RQ, Suit::D),
150 Self::new(Rank::RQ, Suit::C),
151 Self::new(Rank::RK, Suit::S),
152 Self::new(Rank::RK, Suit::H),
153 Self::new(Rank::RK, Suit::D),
154 Self::new(Rank::RK, Suit::C),
155 Self::new(Rank::RA, Suit::S),
156 Self::new(Rank::RA, Suit::H),
157 Self::new(Rank::RA, Suit::D),
158 Self::new(Rank::RA, Suit::C),
159 ];
160
161 #[must_use]
171 #[inline]
172 pub const fn new(r: Rank, s: Suit) -> Self {
173 Self { rank: r, suit: s }
174 }
175
176 #[must_use]
178 #[inline]
179 pub(crate) fn from_indices(r: RankIdx, s: SuitIdx) -> Self {
180 Self {
181 rank: r.to_rank(),
182 suit: s.to_suit(),
183 }
184 }
185
186 pub(crate) const fn to_u8(self) -> u8 {
188 const SHIFT_SUIT: u8 = 4;
189 (self.rank as u8) | ((self.suit as u8) << SHIFT_SUIT)
190 }
191
192 pub(crate) fn from_u8(v: u8) -> Self {
194 const SHIFT_SUIT: u8 = 4;
195 Self::from_indices(
196 RankIdx::new(v & 0b1111),
197 SuitIdx::new(v >> SHIFT_SUIT),
198 )
199 }
200}
201
202impl Default for Card {
206 fn default() -> Self {
207 Self::new(Rank::R2, Suit::S)
208 }
209}
210
211impl fmt::Debug for Card {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 write!(f, "{self}")
217 }
218}
219
220impl FromStr for Card {
238 type Err = ParseError;
239
240 fn from_str(s: &str) -> Result<Self, Self::Err> {
241 let mut cs = s.chars().filter(|c| !c.is_whitespace());
242
243 if let Some(c) = cs.next()
244 && let Ok(r) = Rank::try_from(c)
245 && let Some(c) = cs.next()
246 && let Ok(s) = Suit::try_from(c)
247 && cs.next().is_none()
248 {
249 return Ok(Self::new(r, s));
250 }
251
252 Err(ParseError::InvalidCard(s.into()))
253 }
254}
255
256impl<T> From<T> for Rank
257where
258 Card: From<T>,
259{
260 fn from(v: T) -> Self {
261 Card::from(v).rank
262 }
263}
264
265impl<T> From<T> for Suit
266where
267 Card: From<T>,
268{
269 fn from(v: T) -> Self {
270 Card::from(v).suit
271 }
272}
273
274#[cfg(any(test, feature = "benchmark"))]
275impl Card {
276 pub fn from_tuple((r, s): (char, char)) -> Self {
291 Self::new(r.try_into().unwrap(), s.try_into().unwrap())
292 }
293
294 pub fn new_vec(s: &str) -> Vec<Self> {
307 use itertools::Itertools;
308
309 s.chars()
310 .filter(|c| !c.is_whitespace())
311 .tuples()
312 .map(Self::from_tuple)
313 .collect()
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320 use crate::*;
321
322 impl Arbitrary for Card {
323 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
324 *g.choose(&Self::ARR_ALL).unwrap()
325 }
326 }
327
328 #[quickcheck]
329 fn test_rank_suit(r: Rank, s: Suit) {
330 let card = Card::new(r, s);
331
332 assert_eq!(r, card.rank);
333 assert_eq!(s, card.suit);
334 }
335
336 #[test]
337 fn test_default() {
338 assert_eq!(Card::new(Rank::R2, Suit::S), Card::default());
339 assert_eq!(card!("2s"), Card::default());
340 }
341
342 #[quickcheck]
343 fn test_hash(c1: Card) {
344 use std::hash::DefaultHasher;
345
346 let c2 = c1;
347
348 let mut h1 = DefaultHasher::new();
349 let mut h2 = DefaultHasher::new();
350
351 c1.hash(&mut h1);
352 c2.hash(&mut h2);
353
354 assert_eq!(h1.finish(), h2.finish());
355 }
356
357 #[quickcheck]
358 fn test_into_rank_and_suit(c: Card) {
359 let r: Rank = c.into();
360 let s: Suit = c.into();
361
362 assert_eq!(r, c.rank);
363 assert_eq!(s, c.suit);
364 }
365
366 #[test]
367 fn test_from_str() {
368 let c = |s| Ok(cards!(s)[0]);
369
370 assert_eq!(c("2s"), "2s".parse());
371 assert_eq!(c("2s"), " 2 S ".parse());
372 assert_eq!(
373 Err(ParseError::InvalidCard("2s?".to_owned())),
374 "2s?".parse::<Card>()
375 );
376 assert!("".parse::<Card>().is_err());
377 assert!("?".parse::<Card>().is_err());
378 assert!("2".parse::<Card>().is_err());
379 assert!("2k".parse::<Card>().is_err());
380 }
381
382 #[quickcheck]
383 fn test_to_string(c: Card) {
384 assert_eq!(format!("{}{}", c.rank, c.suit), c.to_string());
385 }
386
387 #[quickcheck]
388 fn test_to_u8(c: Card) {
389 assert_eq!(c, Card::from_u8(c.to_u8()));
390 }
391
392 #[test]
393 fn test_macro() {
394 let cards = cards!("As Kh");
395 assert_eq!(
396 cards,
397 vec![Card::new(Rank::RA, Suit::S), Card::new(Rank::RK, Suit::H),]
398 );
399
400 let flop = flop!("2s 3s 4s");
401 assert_eq!(
402 flop,
403 (
404 Card::new(Rank::R2, Suit::S),
405 Card::new(Rank::R3, Suit::S),
406 Card::new(Rank::R4, Suit::S),
407 )
408 .into()
409 );
410
411 let board = board!("As Kh 3s 5h 6c");
412 assert_eq!(
413 board,
414 (
415 Card::new(Rank::RA, Suit::S),
416 Card::new(Rank::RK, Suit::H),
417 Card::new(Rank::R3, Suit::S),
418 Card::new(Rank::R5, Suit::H),
419 Card::new(Rank::R6, Suit::C),
420 )
421 .into(),
422 );
423 }
424
425 #[test]
426 fn test_ord() {
427 let mut sorted = Card::ARR_ALL.to_vec();
428 sorted.reverse();
429 sorted.sort_unstable();
430
431 assert_eq!(sorted, Card::ARR_ALL);
432 }
433}