fudd/types/
playing_cards.rs1use crate::analysis::eval::Eval;
2use crate::types::arrays::five_card::FiveCard;
3use crate::types::arrays::seven_card::SevenCard;
4use crate::types::arrays::two_card::TwoCard;
5use crate::types::arrays::{Evaluable, Vectorable};
6use crate::types::playing_card::PlayingCard;
7use crate::types::poker_cards::PokerCards;
8use crate::types::poker_deck::PokerDeck;
9use crate::types::U32Card;
10use crate::util::random_ordering::RandomOrdering;
11use cardpack::Pile;
12use ckc_rs::{HandError, PokerCard};
13use core::fmt;
14use indexmap::set::Iter;
15use indexmap::IndexSet;
16use itertools::{Combinations, Itertools};
17use rayon::prelude::*;
18use std::fmt::Formatter;
19
20const NUMBER_OF_SHUFFLES: u8 = 5;
21
22#[derive(Clone, Debug, Default, PartialEq)]
23pub struct PlayingCards(IndexSet<PlayingCard>);
24
25impl PlayingCards {
26 #[must_use]
27 pub fn deck() -> PlayingCards {
28 let set: Vec<PlayingCard> = PokerDeck::par_iter().map(PlayingCard::from).collect();
29 PlayingCards::from(set)
30 }
31
32 #[must_use]
36 pub fn deck_minus(playing_cards: &PlayingCards) -> PlayingCards {
37 let mut cards = PlayingCards::default();
38 let deck = PlayingCards::deck();
39 for card in deck.iter() {
40 if playing_cards.get(card).is_none() {
41 cards.insert(*card);
42 }
43 }
44 cards
45 }
46
47 #[must_use]
48 pub fn deck_shuffled() -> PlayingCards {
49 let mut deck = PlayingCards(PokerDeck::iter().map(PlayingCard::from).collect());
50 deck.shuffle_in_place();
51 deck
52 }
53
54 pub fn append(&mut self, playing_cards: &PlayingCards) {
55 for card in playing_cards.iter() {
56 self.insert(*card);
57 }
58 }
59
60 pub fn combinations(&self, k: usize) -> Combinations<Iter<'_, PlayingCard>> {
61 self.iter().combinations(k)
62 }
63
64 #[must_use]
65 pub fn combine(&self, playing_cards: &PlayingCards) -> PlayingCards {
66 let mut cards = self.clone();
67 cards.append(playing_cards);
68 cards
69 }
70
71 #[must_use]
72 pub fn contains(&self, playing_card: &PlayingCard) -> bool {
73 self.0.contains(playing_card)
74 }
75
76 pub fn deal_from_the_bottom(&mut self) -> Option<PlayingCard> {
77 self.0.pop()
78 }
79
80 #[must_use]
81 pub fn draw(&mut self, number: usize) -> PlayingCards {
82 PlayingCards(self.0.drain(0..number).collect())
83 }
84
85 #[must_use]
86 pub fn draw_from_the_bottom(&mut self, number: usize) -> PlayingCards {
87 let l = self.len();
88 PlayingCards(self.0.drain(l - number..l).collect())
89 }
90
91 pub fn draw_one(&mut self) -> PlayingCard {
95 self.draw(1).deal_from_the_bottom().unwrap()
96 }
97
98 #[allow(clippy::unnecessary_unwrap)]
102 pub fn eval_7cards(&self) -> Result<Eval, HandError> {
103 match self.to_seven_array() {
104 Ok(array) => Ok(array.eval()),
105 Err(e) => Err(e),
106 }
107 }
108
109 #[must_use]
110 pub fn get(&self, playing_card: &PlayingCard) -> Option<&PlayingCard> {
111 self.0.get(playing_card)
112 }
113
114 #[must_use]
115 pub fn get_index(&self, index: usize) -> Option<&PlayingCard> {
116 self.0.get_index(index)
117 }
118
119 pub fn insert(&mut self, playing_card: PlayingCard) -> bool {
121 if playing_card.is_blank() {
122 false
123 } else {
124 self.0.insert(playing_card)
125 }
126 }
127
128 #[must_use]
129 pub fn is_disjoint(&self, other: &PlayingCards) -> bool {
130 self.0.is_disjoint(&other.0)
131 }
132
133 #[must_use]
134 pub fn is_empty(&self) -> bool {
135 self.len() == 0
136 }
137
138 #[must_use]
139 pub fn is_subset(&self, other: &PlayingCards) -> bool {
140 self.0.is_subset(&other.0)
141 }
142
143 #[must_use]
144 pub fn is_superset(&self, other: &PlayingCards) -> bool {
145 self.0.is_superset(&other.0)
146 }
147
148 #[must_use]
149 pub fn iter(&self) -> indexmap::set::Iter<'_, PlayingCard> {
150 self.0.iter()
151 }
152
153 #[must_use]
154 pub fn len(&self) -> usize {
155 self.0.len()
156 }
157
158 #[must_use]
159 pub fn peak(&self) -> Option<&PlayingCard> {
160 self.0.first()
161 }
162
163 pub fn reverse(&mut self) {
164 self.0.reverse();
165 }
166
167 #[must_use]
168 pub fn shuffle(&self) -> PlayingCards {
169 let mut shuffled = self.clone();
170 shuffled.shuffle_in_place();
171 shuffled
172 }
173
174 pub fn shuffle_in_place(&mut self) {
175 for _ in 0..NUMBER_OF_SHUFFLES {
176 self.0
177 .sort_by(|_, _| rand::random::<RandomOrdering>().into());
178 }
179 }
180
181 #[must_use]
182 pub fn sort(&self) -> PlayingCards {
183 let mut c = self.clone();
184 c.sort_in_place();
185 c
186 }
187
188 pub fn sort_in_place(&mut self) {
189 self.0.sort();
190 self.0.reverse();
191 }
192
193 #[allow(clippy::missing_panics_doc)]
197 pub fn to_five_array(&self) -> Result<[PlayingCard; 5], HandError> {
198 match self.len() {
199 0..=4 => Err(HandError::NotEnoughCards),
200 5 => Ok([
201 *self.get_index(0).unwrap(),
202 *self.get_index(1).unwrap(),
203 *self.get_index(2).unwrap(),
204 *self.get_index(3).unwrap(),
205 *self.get_index(4).unwrap(),
206 ]),
207 _ => Err(HandError::TooManyCards),
208 }
209 }
210
211 pub fn to_five_cards(&self) -> Result<FiveCard, HandError> {
215 match self.to_five_array() {
216 Ok(hand) => Ok(FiveCard::from(hand)),
217 Err(e) => Err(e),
218 }
219 }
220
221 #[allow(clippy::missing_panics_doc)]
225 pub fn to_seven_array(&self) -> Result<SevenCard, HandError> {
226 SevenCard::try_from(self)
227 }
228
229 #[must_use]
230 pub fn to_vec(&self) -> Vec<PlayingCard> {
231 self.iter().copied().collect::<Vec<PlayingCard>>()
232 }
233
234 #[must_use]
235 pub fn two_cards(&self) -> Vec<TwoCard> {
236 self.combinations(2)
237 .map(TwoCard::from)
238 .collect::<Vec<TwoCard>>()
239 }
240}
241
242impl fmt::Display for PlayingCards {
243 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
244 let s = self
245 .iter()
246 .map(PlayingCard::to_string)
247 .collect::<Vec<String>>()
248 .join(" ");
249
250 write!(f, "{s}")
251 }
252}
253
254impl indexmap::Equivalent<PlayingCard> for IndexSet<PlayingCard> {
255 fn equivalent(&self, key: &PlayingCard) -> bool {
256 self.get(key).is_some()
257 }
258}
259
260impl From<&Pile> for PlayingCards {
261 fn from(pile: &Pile) -> Self {
266 let filtered = pile.clone().into_iter().filter_map(|c| {
267 let pc = PlayingCard::from(&c);
268 if pc.is_blank() {
269 None
270 } else {
271 Some(pc)
272 }
273 });
274 PlayingCards(filtered.collect())
275 }
276}
277
278impl From<&PokerCards> for PlayingCards {
279 fn from(poker_cards: &PokerCards) -> Self {
280 PlayingCards::from(&poker_cards.to_vec())
281 }
282}
283
284impl From<&Vec<U32Card>> for PlayingCards {
285 fn from(v: &Vec<U32Card>) -> Self {
286 let filtered = v.iter().filter_map(|c| {
287 let pc = PlayingCard::from(*c);
288 if pc.is_blank() {
289 None
290 } else {
291 Some(pc)
292 }
293 });
294 PlayingCards(filtered.collect())
295 }
296}
297
298impl From<Vec<&PlayingCard>> for PlayingCards {
299 fn from(v: Vec<&PlayingCard>) -> Self {
300 let filtered = v.iter().filter_map(|c| {
301 let pc = **c;
302 if pc.is_blank() {
303 None
304 } else {
305 Some(pc)
306 }
307 });
308 PlayingCards(filtered.collect())
309 }
310}
311
312impl From<PlayingCard> for PlayingCards {
313 fn from(playing_card: PlayingCard) -> Self {
314 PlayingCards::from(vec![playing_card])
315 }
316}
317
318impl From<Vec<PlayingCard>> for PlayingCards {
319 fn from(value: Vec<PlayingCard>) -> Self {
320 PlayingCards(value.into_iter().collect::<IndexSet<_>>())
321 }
322}
323
324impl TryFrom<&'static str> for PlayingCards {
325 type Error = HandError;
326
327 #[allow(clippy::missing_panics_doc)]
331 fn try_from(value: &'static str) -> Result<Self, Self::Error> {
332 let mut cards = PlayingCards::default();
333
334 for s in value.split_whitespace() {
335 let card = PlayingCard::from(s);
336 if card.is_blank() {
337 return Err(HandError::InvalidCard);
338 }
339 cards.insert(card);
340 }
341 Ok(cards)
342 }
343}
344
345impl TryFrom<TwoCard> for PlayingCards {
346 type Error = HandError;
347
348 fn try_from(value: TwoCard) -> Result<Self, Self::Error> {
349 if value.is_dealt() {
350 let mut cards = PlayingCards::default();
351 cards.insert(PlayingCard::from(value.first()));
352 cards.insert(PlayingCard::from(value.second()));
353 Ok(cards)
354 } else {
355 Err(HandError::Incomplete)
356 }
357 }
358}
359
360#[cfg(test)]
361#[allow(non_snake_case)]
362mod playing_cards_tests {
363 use super::*;
364
365 fn royal_flush() -> PlayingCards {
366 PlayingCards::deck().draw(5)
367 }
368
369 fn is_royal_flush(cards: &PlayingCards) -> bool {
370 cards.contains(&PlayingCard::from("AS"))
371 && cards.contains(&PlayingCard::from("KS"))
372 && cards.contains(&PlayingCard::from("QS"))
373 && cards.contains(&PlayingCard::from("JS"))
374 && cards.contains(&PlayingCard::from("TS"))
375 && cards.len() == 5
376 }
377
378 #[test]
379 fn is_royal_flush__test() {
380 let mut cards = royal_flush();
381
382 assert!(is_royal_flush(&cards));
383
384 cards.insert(PlayingCard::from("KD"));
385
386 assert!(!is_royal_flush(&cards));
387 }
388
389 #[test]
390 fn combinations() {
391 let aces = PlayingCards::try_from("AS AH AD AC").unwrap();
392 assert_eq!(6, aces.combinations(2).count());
393 assert_eq!(2_598_960, PlayingCards::deck().combinations(5).count());
394 }
395
396 #[test]
397 fn two_cards() {
398 let aces = PlayingCards::try_from("AS AH AD AC").unwrap().two_cards();
399 assert_eq!(6, aces.len());
400 }
401
402 #[test]
403 fn contains() {
404 let cards = royal_flush();
405 assert!(cards.contains(&PlayingCard::from("AS")));
406 assert!(!cards.contains(&PlayingCard::from("AD")));
407 }
408
409 #[test]
410 fn deal_from_the_bottom() {
411 let mut cards = PlayingCards::deck();
412
413 let card = cards.deal_from_the_bottom().unwrap();
414
415 assert_eq!(card, PlayingCard::from("2C"));
416 }
417
418 #[test]
419 fn draw() {
420 let mut cards = PlayingCards::deck();
421
422 let drawn = cards.draw(5);
423
424 assert!(is_royal_flush(&drawn));
425 assert_eq!(cards.len(), 47);
426 }
427
428 #[test]
429 fn draw_one() {
430 let mut cards = PlayingCards::deck();
431
432 let drawn = cards.draw_one();
433
434 assert_eq!(drawn, PlayingCard::from("AS"));
435 assert_eq!(cards.len(), 51);
436 }
437
438 #[test]
439 fn get() {
440 let cards = royal_flush();
441 let ace_spades = PlayingCard::from("AS");
442
443 assert_eq!(cards.get(&ace_spades).unwrap(), &ace_spades);
444 assert!(cards.get(&PlayingCard::from("AD")).is_none());
445 }
446
447 #[test]
448 fn get_index() {
449 let cards = royal_flush();
450
451 assert_eq!(cards.get_index(0).unwrap(), &PlayingCard::from("AS"));
452 assert_eq!(cards.get_index(1).unwrap(), &PlayingCard::from("KS"));
453 assert_eq!(cards.get_index(2).unwrap(), &PlayingCard::from("QS"));
454 assert_eq!(cards.get_index(3).unwrap(), &PlayingCard::from("JS"));
455 assert_eq!(cards.get_index(4).unwrap(), &PlayingCard::from("TS"));
456 assert!(cards.get_index(5).is_none());
457 }
458
459 #[test]
460 fn insert() {
461 let mut cards = royal_flush();
462
463 let result = cards.insert(PlayingCard::from("AS"));
464
465 assert!(!result);
466 assert!(is_royal_flush(&cards));
467 }
468
469 #[test]
470 fn is_disjoint() {
471 let mut deck = PlayingCards::deck();
472 let royal_flush = deck.draw(5);
473 let straight_flush = deck.draw(5);
474
475 assert!(royal_flush.is_disjoint(&straight_flush));
476 assert!(straight_flush.is_disjoint(&royal_flush));
477 assert!(straight_flush.is_disjoint(&deck));
478 assert!(royal_flush.is_disjoint(&deck));
479 assert!(deck.is_disjoint(&royal_flush));
480 assert!(deck.is_disjoint(&straight_flush));
481 assert!(!royal_flush.is_disjoint(&PlayingCards::deck().draw(2)));
482 }
483
484 #[test]
485 fn is_empty() {
486 assert!(PlayingCards::default().is_empty())
487 }
488
489 #[test]
490 fn is_subset() {
491 let cards = royal_flush();
492 let other = PlayingCards::deck_shuffled();
493
494 assert!(cards.is_subset(&other));
495 assert!(!other.is_subset(&cards));
496 }
497
498 #[test]
499 fn is_superset() {
500 let cards = royal_flush();
501 let other = PlayingCards::deck_shuffled();
502
503 assert!(other.is_superset(&cards));
504 assert!(!cards.is_superset(&other));
505 }
506
507 #[test]
508 fn len() {
509 assert_eq!(5, royal_flush().len())
510 }
511
512 #[test]
513 fn reverse() {
514 let mut cards = royal_flush();
515 cards.reverse();
516
517 assert_eq!("T♠ J♠ Q♠ K♠ A♠", cards.to_string());
518 }
519
520 #[test]
521 fn shuffle_in_place() {
522 let mut cards = PlayingCards::deck().draw(5);
523
524 cards.shuffle_in_place();
525
526 assert!(is_royal_flush(&cards));
527 cards.sort_in_place();
528 assert_eq!("A♠ K♠ Q♠ J♠ T♠", cards.to_string());
529 }
530
531 #[test]
532 fn to_vec() {
533 let v = vec![
534 PlayingCard::from("AS"),
535 PlayingCard::from("KS"),
536 PlayingCard::from("QS"),
537 PlayingCard::from("JS"),
538 PlayingCard::from("TS"),
539 ];
540
541 assert_eq!(royal_flush().to_vec(), v);
542 }
543
544 #[test]
545 fn display() {
546 let cards = royal_flush();
547
548 assert_eq!("A♠ K♠ Q♠ J♠ T♠", cards.to_string());
549 }
550
551 #[test]
552 fn from__pile() {
553 let expected = PlayingCards::from(&Pile::french_deck());
554 let actual = PlayingCards::from(&Pile::french_deck_with_jokers());
555
556 assert_eq!(expected, actual);
557 assert_ne!(royal_flush(), actual);
558 assert!(PlayingCards::from(&Pile::skat_deck()).is_empty());
559 }
560
561 #[test]
562 fn try_from__static_str() {
563 let actual = PlayingCards::try_from("A♠ K♠ Q♠ J♠ T♠").unwrap();
564
565 assert_eq!(royal_flush(), actual);
566 }
567
568 #[test]
569 fn try_from__two_cards() {
570 let actual = PlayingCards::try_from(TwoCard::try_from("Q♠ J♠").unwrap()).unwrap();
571
572 assert_eq!("Q♠ J♠", actual.to_string());
573 }
574
575 #[test]
576 fn try_from__two_cards__invalid() {
577 let actual = PlayingCards::try_from(TwoCard::default());
578
579 assert!(actual.is_err());
580 }
581}