openpql_prelude/card/
rank16.rs1use super::{
2 BitAnd, BitAndAssign, BitOr, BitOrAssign, Card64, CardCount, Hash, Idx,
3 N_STRAIGHT, N_STRAIGHT_SD, Not, Rank, Rank16Inner, RankIdx, Suit, fmt, ops,
4};
5
6#[macro_export]
7macro_rules! r16 {
8 ($s:expr) => {
9 $crate::Rank16::from(
10 $s.chars()
11 .filter(|c| !c.is_whitespace())
12 .map(|c| $crate::Rank::from_char(c).unwrap())
13 .collect::<Vec<_>>()
14 .as_slice(),
15 )
16 };
17}
18
19#[derive(
29 Copy,
30 Clone,
31 derive_more::Debug,
32 PartialEq,
33 Eq,
34 BitAnd,
35 BitOr,
36 PartialOrd,
37 Ord,
38 Hash,
39 Default,
40 BitOrAssign,
41 BitAndAssign,
42)]
43#[debug("Rank16({})", self)]
44pub struct Rank16(pub(crate) Rank16Inner);
45
46impl Rank16 {
47 pub(crate) const ALL: Self = Self(0b0001_1111_1111_1111);
49 pub(crate) const ALL_SD: Self = Self(0b0001_1111_1111_0000);
51
52 pub const STRAIGHT_A6789: Self = Self(0b0001_0000_1111_0000);
53 pub const STRAIGHT_A2345: Self = Self(0b0001_0000_0000_1111);
54 pub const STRAIGHT_23456: Self = Self(0b0000_0000_0001_1111);
55 pub const STRAIGHT_34567: Self = Self(0b0000_0000_0011_1110);
56 pub const STRAIGHT_45678: Self = Self(0b0000_0000_0111_1100);
57 pub const STRAIGHT_56789: Self = Self(0b0000_0000_1111_1000);
58 pub const STRAIGHT_6789T: Self = Self(0b0000_0001_1111_0000);
59 pub const STRAIGHT_789TJ: Self = Self(0b0000_0011_1110_0000);
60 pub const STRAIGHT_89TJQ: Self = Self(0b0000_0111_1100_0000);
61 pub const STRAIGHT_9TJQK: Self = Self(0b0000_1111_1000_0000);
62 pub const STRAIGHT_TJQKA: Self = Self(0b0001_1111_0000_0000);
63
64 const ALL_STRAIGHT: [Self; N_STRAIGHT] = [
65 Self::STRAIGHT_A2345,
66 Self::STRAIGHT_23456,
67 Self::STRAIGHT_34567,
68 Self::STRAIGHT_45678,
69 Self::STRAIGHT_56789,
70 Self::STRAIGHT_6789T,
71 Self::STRAIGHT_789TJ,
72 Self::STRAIGHT_89TJQ,
73 Self::STRAIGHT_9TJQK,
74 Self::STRAIGHT_TJQKA,
75 ];
76
77 const ALL_STRAIGHT_SD: [Self; N_STRAIGHT_SD] = [
78 Self::STRAIGHT_A6789,
79 Self::STRAIGHT_6789T,
80 Self::STRAIGHT_789TJ,
81 Self::STRAIGHT_89TJQ,
82 Self::STRAIGHT_9TJQK,
83 Self::STRAIGHT_TJQKA,
84 ];
85
86 #[inline]
87 pub const fn all<const SD: bool>() -> Self {
88 const { if SD { Self::ALL_SD } else { Self::ALL } }
89 }
90
91 #[inline]
92 pub const fn all_straights<const SD: bool>() -> &'static [Self] {
93 const {
94 if SD {
95 &Self::ALL_STRAIGHT_SD
96 } else {
97 &Self::ALL_STRAIGHT
98 }
99 }
100 }
101
102 #[must_use]
104 #[inline]
105 pub const fn is_empty(self) -> bool {
106 self.0 == 0
107 }
108
109 #[inline]
111 pub const fn set(&mut self, r: Rank) {
112 self.0 |= 1 << r as Idx;
113 }
114
115 #[inline]
117 pub const fn unset(&mut self, r: Rank) {
118 self.0 &= !(1 << r as Idx);
119 }
120
121 #[must_use]
123 #[inline]
124 pub const fn contains_rank(self, r: Rank) -> bool {
125 let v = 1 << (r as Idx);
126 v == v & self.0
127 }
128
129 #[must_use]
131 #[inline]
132 pub const fn count(self) -> CardCount {
133 self.0.count_ones().to_le_bytes()[0]
134 }
135
136 #[must_use]
138 #[inline]
139 #[allow(clippy::cast_possible_truncation)]
140 pub const fn min_rank(self) -> Option<Rank> {
141 RankIdx(self.0.trailing_zeros() as Idx).to_rank()
142 }
143
144 #[must_use]
146 #[inline]
147 #[allow(clippy::cast_possible_truncation)]
148 pub const fn max_rank(self) -> Option<Rank> {
149 const N_ZEROS_R2: Idx = 15;
150
151 RankIdx(N_ZEROS_R2 - (Self::ALL.0 & self.0).leading_zeros() as Idx)
152 .to_rank()
153 }
154
155 #[must_use]
157 #[inline]
158 pub const fn nth_rank(self, mut n: CardCount) -> Option<Rank> {
159 let ranks = Rank::all::<false>();
160 let mut i = ranks.len();
161
162 while i > 0 {
163 i -= 1;
164 let rank = ranks[i];
165
166 if self.contains_rank(rank) {
167 if n == 1 {
168 return Some(rank);
169 } else if n == 0 {
170 return None;
171 }
172
173 n -= 1;
174 }
175 }
176
177 None
178 }
179
180 #[inline]
181 pub(crate) const fn from_rank(rank: Rank) -> Self {
182 Self(1 << rank as Idx)
183 }
184}
185
186impl fmt::Display for Rank16 {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 let ranks: String = Rank::all::<false>()
189 .iter()
190 .filter(|&r| self.contains_rank(*r))
191 .map(|r| r.to_char())
192 .collect();
193
194 write!(f, "{ranks}")
195 }
196}
197
198impl Not for Rank16 {
199 type Output = Self;
200
201 fn not(self) -> Self::Output {
202 Self(!self.0 & Self::ALL.0)
203 }
204}
205
206impl From<Rank> for Rank16 {
207 fn from(r: Rank) -> Self {
208 Self(1 << r as Idx)
209 }
210}
211
212impl FromIterator<Rank> for Rank16 {
213 fn from_iter<T: IntoIterator<Item = Rank>>(iter: T) -> Self {
214 let mut res = Self::default();
215
216 for rank in iter {
217 res.set(rank);
218 }
219
220 res
221 }
222}
223
224impl From<&[Rank]> for Rank16 {
225 fn from(ranks: &[Rank]) -> Self {
226 ranks.iter().copied().collect()
227 }
228}
229
230impl From<Card64> for Rank16 {
231 fn from(c: Card64) -> Self {
232 c.ranks_by_suit(Suit::S)
233 | c.ranks_by_suit(Suit::H)
234 | c.ranks_by_suit(Suit::D)
235 | c.ranks_by_suit(Suit::C)
236 }
237}
238
239impl From<Rank16Inner> for Rank16 {
240 fn from(i: Rank16Inner) -> Self {
241 Self(i & Self::ALL.0)
242 }
243}
244
245impl From<Rank16> for Rank16Inner {
246 fn from(v: Rank16) -> Self {
247 v.0
248 }
249}
250
251impl ops::BitOrAssign<Rank> for Rank16 {
252 fn bitor_assign(&mut self, rhs: Rank) {
253 self.set(rhs);
254 }
255}
256
257#[cfg(any(test, feature = "quickcheck"))]
258impl quickcheck::Arbitrary for Rank16 {
259 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
260 let inner = Rank16Inner::arbitrary(g);
261
262 Self(Self::ALL.0 & inner)
263 }
264}
265
266#[cfg(test)]
267#[cfg_attr(coverage_nightly, coverage(off))]
268mod tests {
269 use super::*;
270 use crate::*;
271
272 #[quickcheck]
273 fn test_all(rank: Rank) {
274 if rank >= Rank::R6 {
275 assert!(Rank16::all::<true>().contains_rank(rank));
276 }
277 assert!(Rank16::all::<false>().contains_rank(rank));
278 }
279
280 #[test]
281 fn test_all_straights() {
282 for s in ["23456", "34567", "45678", "56789"] {
283 assert!(Rank16::all_straights::<false>().contains(&r16!(s)));
284 }
285 for s in ["6789T", "789TJ", "89TJQ", "9TJQK", "TJQKA"] {
286 assert!(Rank16::all_straights::<false>().contains(&r16!(s)));
287 assert!(Rank16::all_straights::<true>().contains(&r16!(s)));
288 }
289
290 assert!(Rank16::all_straights::<false>().contains(&r16!("A2345")));
291 assert!(Rank16::all_straights::<true>().contains(&r16!("A6789")));
292 }
293
294 #[test]
295 fn test_empty() {
296 assert!(Rank16::default().is_empty());
297 assert!(!Rank16::all::<false>().is_empty());
298 }
299
300 #[quickcheck]
301 fn test_set(rank: Rank) {
302 let mut res = Rank16::default();
303 res.set(rank);
304
305 assert!(res.contains_rank(rank));
306 }
307
308 #[quickcheck]
309 fn test_unset(rank: Rank) {
310 let mut res = Rank16::all::<false>();
311 res.unset(rank);
312
313 assert!(!res.contains_rank(rank));
314 }
315
316 #[quickcheck]
317 #[allow(clippy::cast_possible_truncation)]
318 fn test_count(r16: Rank16) {
319 assert_eq!(
320 r16.count(),
321 Rank::all::<false>()
322 .iter()
323 .filter(|&r| r16.contains_rank(*r))
324 .count() as CardCount
325 );
326 }
327
328 #[quickcheck]
329 fn test_min_rank_and_max_rank(ranks: Distinct<5, Rank>) {
330 let r16 = Rank16::from(ranks.as_slice());
331
332 let max = ranks.iter().max().copied();
333 let min = ranks.iter().min().copied();
334
335 assert_eq!(r16.max_rank(), max);
336 assert_eq!(r16.min_rank(), min);
337 }
338
339 #[test]
340 fn test_nth_rank() {
341 let ranks = r16!("26K");
342
343 assert_eq!(ranks.nth_rank(0), None);
344 assert_eq!(ranks.nth_rank(1), Some(Rank::RK));
345 assert_eq!(ranks.nth_rank(2), Some(Rank::R6));
346 assert_eq!(ranks.nth_rank(3), Some(Rank::R2));
347 assert_eq!(ranks.nth_rank(4), None);
348 }
349
350 #[test]
351 fn test_display() {
352 assert_eq!(format!("{:?}", r16!("ATJ")), "Rank16(TJA)");
353 }
354
355 #[quickcheck]
356 fn test_bit_not(r16: Rank16) {
357 assert_eq!((!r16).0, Rank16::ALL.0 & !(r16.0));
358 }
359
360 #[quickcheck]
361 fn test_bit_and(r1: Rank, r2: Rank) {
362 let a = Rank16::from(r1);
363 let b = Rank16::from(r2);
364
365 assert_eq!((a & b).is_empty(), r1 != r2);
366 }
367
368 #[quickcheck]
369 fn test_bit_or(r1: Rank, r2: Rank) {
370 let a = Rank16::from(r1);
371 let b = Rank16::from(r2);
372
373 assert!((a | b).contains_rank(r1));
374 assert!((a | b).contains_rank(r2));
375
376 let mut ab = Rank16::default();
377 ab |= r1;
378 ab |= r2;
379 assert_eq!(ab, a | b);
380 }
381
382 #[quickcheck]
383 fn test_from_cards_and_card64(cards: Vec<Card>) {
384 let mut ranks = Rank16::default();
385
386 for i in 0..cards.len() {
387 ranks.set(cards[i].rank);
388
389 let c64 = Card64::from(&cards[0..=i]);
390
391 assert_eq!(Rank16::from(c64), ranks);
392 }
393 }
394
395 #[quickcheck]
396 fn test_from_and_to_int(i: Rank16Inner) {
397 let obj = Rank16::from(i);
398 let mask = Rank16::ALL.0;
399
400 assert_eq!(obj.0, i & mask);
401 assert_eq!(i & mask, Rank16Inner::from(obj));
402 }
403}