openpql_prelude/card/
card.rs1use super::{
2 Card64, CardCount, Display, FromStr, Hash, ParseError, Rank, Suit,
3};
4
5#[macro_export]
6macro_rules! card {
7 ($s:expr) => {
8 $s.parse::<$crate::Card>().unwrap()
9 };
10}
11
12#[macro_export]
13macro_rules! cards {
14 ($s:expr) => {{
15 let s: &str = $s;
16 let mut cards = Vec::new();
17 let mut chars = s.chars().filter(|c| !c.is_whitespace());
18 while let (Some(r), Some(s)) = (chars.next(), chars.next()) {
19 cards.push($crate::card![format!("{r}{s}")]);
20 }
21 cards
22 }};
23}
24
25#[derive(Clone, Copy, Debug, Display, Hash, PartialEq, Eq, PartialOrd, Ord)]
29#[display("{rank}{suit}")]
30pub struct Card {
31 pub rank: Rank,
32 pub suit: Suit,
33}
34
35impl Card {
36 pub const N_CARDS: CardCount = Suit::N_SUITS * Rank::N_RANKS;
38 pub const N_CARDS_SD: CardCount = Suit::N_SUITS * Rank::N_RANKS_SD;
40
41 const ARR_ALL: [Self; Self::N_CARDS as usize] = sealed::all_cards();
42 const ARR_ALL_SD: [Self; Self::N_CARDS_SD as usize] =
43 sealed::all_cards_sd();
44
45 #[must_use]
47 #[inline]
48 pub const fn new(r: Rank, s: Suit) -> Self {
49 Self { rank: r, suit: s }
50 }
51
52 #[inline]
54 pub const fn all<const SD: bool>() -> &'static [Self] {
55 const {
56 if SD {
57 &Self::ARR_ALL_SD
58 } else {
59 &Self::ARR_ALL
60 }
61 }
62 }
63
64 #[inline]
65 pub(crate) const fn to_c64(self) -> Card64 {
66 let mut res = Card64::EMPTY;
67 res.set(self);
68
69 res
70 }
71
72 #[inline]
73 pub(crate) const fn eq(self, other: Self) -> bool {
74 self.rank.eq(other.rank) && self.suit.eq(other.suit)
75 }
76}
77
78#[cfg_attr(coverage_nightly, coverage(off))]
80mod sealed {
81 use super::{Card, CardCount, Rank, Suit};
82
83 const fn mk_card(r: CardCount, s: CardCount) -> Card {
84 Card::new(Rank::all::<false>()[r as usize], Suit::ARR_ALL[s as usize])
85 }
86
87 pub(super) const fn all_cards() -> [Card; Card::N_CARDS as usize] {
88 let mut res = [mk_card(0, 0); Card::N_CARDS as usize];
89 let mut i = 0;
90
91 while i < Card::N_CARDS {
92 res[i as usize] = mk_card(i / Suit::N_SUITS, i % Suit::N_SUITS);
93
94 i += 1;
95 }
96
97 res
98 }
99
100 pub(super) const fn all_cards_sd() -> [Card; Card::N_CARDS_SD as usize] {
101 const SHIFT: CardCount = 4;
102 let mut res = [mk_card(0, 0); Card::N_CARDS_SD as usize];
103 let mut i = 0;
104
105 while i < Card::N_CARDS_SD {
106 res[i as usize] =
107 mk_card(i / Suit::N_SUITS + SHIFT, i % Suit::N_SUITS);
108
109 i += 1;
110 }
111
112 res
113 }
114}
115
116impl Default for Card {
117 fn default() -> Self {
118 Self::new(Rank::R2, Suit::S)
119 }
120}
121
122impl FromStr for Card {
123 type Err = ParseError;
124
125 fn from_str(s: &str) -> Result<Self, Self::Err> {
126 let mut cs = s.chars().filter(|c| !c.is_whitespace());
127
128 if let Some(c) = cs.next()
129 && let Ok(r) = Rank::try_from(c)
130 && let Some(c) = cs.next()
131 && let Ok(s) = Suit::try_from(c)
132 && cs.next().is_none()
133 {
134 return Ok(Self::new(r, s));
135 }
136
137 Err(ParseError::InvalidCard(s.into()))
138 }
139}
140
141#[cfg(any(test, feature = "quickcheck"))]
142impl quickcheck::Arbitrary for Card {
143 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
144 *g.choose(&Self::ARR_ALL).unwrap()
145 }
146}
147
148#[cfg(test)]
149#[cfg_attr(coverage_nightly, coverage(off))]
150mod tests {
151 use super::*;
152 use crate::*;
153
154 #[quickcheck]
155 fn test_all(cards: CardN<3>) {
156 for c in cards {
157 if c.rank >= Rank::R6 {
158 assert!(Card::all::<true>().contains(&c));
159 }
160
161 assert!(Card::all::<false>().contains(&c));
162 }
163 }
164
165 #[test]
166 fn test_default() {
167 assert_eq!(card!("2s"), Card::default());
168 }
169
170 #[test]
171 fn test_from_str() {
172 let c = |s| Ok(cards!(s)[0]);
173
174 assert_eq!(c("2s"), "2s".parse());
175 assert_eq!(c("2s"), " 2 S ".parse());
176 assert_eq!(
177 Err(ParseError::InvalidCard("2s?".to_owned())),
178 "2s?".parse::<Card>()
179 );
180 assert!("".parse::<Card>().is_err());
181 assert!("?".parse::<Card>().is_err());
182 assert!("2".parse::<Card>().is_err());
183 assert!("2k".parse::<Card>().is_err());
184 }
185
186 #[quickcheck]
187 fn test_to_string(c: Card) {
188 assert_eq!(format!("{}{}", c.rank, c.suit), c.to_string());
189 }
190
191 #[quickcheck]
192 fn test_macro(cs: CardN<3>) {
193 assert_eq!(cs[0], card!(&cs[0].to_string()));
194 let v = cs.as_slice().to_vec();
195 let s: String = v.iter().map(ToString::to_string).collect();
196 assert_eq!(v, cards!(&s));
197 }
198
199 #[test]
200 fn test_ord() {
201 let mut sorted = Card::ARR_ALL.to_vec();
202 sorted.sort_unstable();
203
204 assert_eq!(sorted, Card::ARR_ALL);
205 }
206}