1use rand::seq::SliceRandom;
2use rand::thread_rng;
3use std::fmt::Display;
4use std::hash::Hash;
5use strum::IntoEnumIterator;
6use strum_macros::EnumIter;
7
8use thiserror::Error;
9
10#[derive(Debug, PartialEq, Clone, Copy, EnumIter, Eq, Hash)]
13pub enum Suit {
14 Hearts,
16 Diamonds,
18 Spades,
20 Clubs,
22}
23
24#[derive(Debug, PartialEq, Clone, Copy, Eq, Hash)]
31pub struct Card {
32 pub val: u8,
33 pub suit: Suit,
34}
35
36impl Card {
37 pub fn new(val: u8, suit: Suit) -> Self {
38 Card { suit, val }
39 }
40}
41
42impl PartialOrd for Card {
43 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
44 self.val.partial_cmp(&other.val)
45 }
46}
47
48impl Ord for Card {
49 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
50 self.val.cmp(&other.val)
51 }
52}
53
54#[derive(Error, Debug)]
55pub enum CardError {
56 #[error("The card string is not the correct length")]
57 InvalidLength,
58
59 #[error("The card Value Rank is invalid")]
60 InvalidValue,
61
62 #[error("The card Suit is invalid")]
63 InvalidSuit,
64}
65
66impl TryFrom<&str> for Card {
67 type Error = CardError;
68
69 fn try_from(card: &str) -> Result<Self, Self::Error> {
70 if card.len() != 2 && card.len() != 3 {
71 return Err(CardError::InvalidLength);
72 }
73
74 let chars: Vec<char> = card.chars().collect();
75 let val = chars[0];
76
77 let val = match val {
78 'A' => 14,
79 'K' => 13,
80 'Q' => 12,
81 'J' => 11,
82 '1' => 10,
83 '2'..='9' => val.to_digit(10).unwrap() as u8,
84 _ => return Err(CardError::InvalidValue),
85 };
86
87 if val == 1 && card.len() == 2 {
88 return Err(CardError::InvalidValue);
89 }
90
91 let suit = chars[if val == 10 { 2 } else { 1 }];
92
93 let suit = match suit.to_ascii_lowercase() {
94 'h' => Suit::Hearts,
95 's' => Suit::Spades,
96 'd' => Suit::Diamonds,
97 'c' => Suit::Clubs,
98 _ => return Err(CardError::InvalidSuit),
99 };
100
101 Ok(Card::new(val, suit))
102 }
103}
104
105impl Display for Card {
106
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 write!(f, " ")?;
109 match self.val {
110 n @ 2..=10 => write!(f, "{n}"),
111 14 => write!(f, "A"),
112 13 => write!(f, "K"),
113 12 => write!(f, "Q"),
114 11 => write!(f, "J"),
115 _ => write!(f, "?"),
116 }?;
117 match self.suit {
118 Suit::Hearts => write!(f, "♥️"), Suit::Spades => write!(f, "♠️"), Suit::Diamonds => write!(f, "♦️"), Suit::Clubs => write!(f, "♣️"), }?;
124 write!(f, "{}", if self.val != 10 {" "} else {""})
125 }
126}
127
128#[derive(Debug, PartialEq, Clone, Eq)]
131pub struct Hand {
132 hand: [Card; 5],
133}
134
135impl Hand {
136 pub fn sort(&mut self) -> () {
139 self.hand.sort();
140 self.hand.reverse();
141 }
142
143 pub const fn get_hand_slice(&self) -> &[Card; 5] {
146 &self.hand
147 }
148
149 pub fn new(hand: [Card; 5]) -> Hand {
152 Hand { hand }
153 }
154}
155
156#[derive(Debug, PartialEq, Clone)]
159pub struct Deck {
160 deck: Vec<Card>,
161 it: usize,
162}
163
164impl Deck {
165 pub fn create_shuffled_deck() -> Deck {
168 let mut deck: Vec<Card> = Vec::new();
169
170 for suit in Suit::iter() {
171 for val in 2..=14 {
172 deck.push(Card::new(val, suit));
173 }
174 }
175 let mut rng = thread_rng();
176 deck.shuffle(&mut rng);
177 Deck { deck, it: 0 }
178 }
179
180 pub fn hand(&mut self) -> Option<Hand> {
183 if self.it == 52 {
184 return None; }
186 let hand: Hand = Hand {
187 hand: self.deck[self.it..=(self.it + 4)].try_into().ok()?,
188 };
189 self.it += 5; Some(hand)
191 }
192}
193
194impl Display for Hand {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 for i in 0..=4 {
197 write!(f, "{} ", self.hand[i])?;
198 }
199 writeln!(f, "")
200 }
201}
202
203impl Display for Deck {
204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205 for i in 0..52 {
206 write!(f, "{} ", self.deck[i])?;
207 if (i + 1) % 13 == 0 {
208 writeln!(f, "")?;
209 }
210 }
211 write!(f, "")
212 }
213}
214
215#[macro_export]
216macro_rules! newcard {
217 ($val:expr, $suit:tt) => {
218 Card::new($val, $suit)
219 };
220 ($val:expr) => {
221 Card::try_from($val).unwrap_or_else(|err| panic!("{}", err))
222 };
223}
224
225#[macro_export]
226macro_rules! hand {
227 ($c:expr,$c1:expr,$c2:expr,$c3:expr,$c4:expr) => {
228 Hand::new([
229 newcard![$c],
230 newcard![$c1],
231 newcard![$c2],
232 newcard![$c3],
233 newcard![$c4],
234 ])
235 };
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 #[test]
243 fn test_try_from_valid_card() {
244 let card = newcard!["Ad"];
245 assert_eq!(card.val, 14);
246 assert_eq!(card.suit, Suit::Diamonds);
247
248 assert_eq!(newcard!["Ah"], Card::new(14, Suit::Hearts));
249 assert_eq!(newcard!["Kh"], Card::new(13, Suit::Hearts));
250 assert_eq!(newcard!["Qh"], Card::new(12, Suit::Hearts));
251 assert_eq!(newcard!["Jh"], Card::new(11, Suit::Hearts));
252 assert_eq!(newcard!["10h"], Card::new(10, Suit::Hearts));
253 assert_eq!(newcard!["9h"], Card::new(9, Suit::Hearts));
254 }
255
256 #[test]
257 fn test_try_from_valid_hand() {
258 let hand = hand!["Ad", "Kd", "Qd", "Jd", "10d"];
259 assert_eq!(hand.hand[0], Card::new(14, Suit::Diamonds));
260 }
261}