fudd/types/slots/
hole_cards.rs1use crate::types::arrays::two_card::TwoCard;
2use crate::types::card_slot::CardSlot;
3use crate::types::playing_card::PlayingCard;
4use crate::types::playing_cards::PlayingCards;
5use crate::types::U32Card;
6use ckc_rs::PokerCard;
7use log::warn;
8use serde::{Deserialize, Serialize};
9use std::cell::Cell;
10use std::fmt;
11
12#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
13pub struct HoleCards(Cell<PlayingCard>, Cell<PlayingCard>);
14
15impl HoleCards {
16 #[must_use]
17 pub fn new(first: PlayingCard, second: PlayingCard) -> HoleCards {
18 HoleCards(Cell::new(first), Cell::new(second))
19 }
20
21 #[must_use]
28 pub fn from_index(index: &'static str) -> HoleCards {
29 HoleCards::from(index)
30 }
31
32 pub fn get_first_card(&self) -> PlayingCard {
33 self.0.get()
34 }
35
36 pub fn get_second_card(&self) -> PlayingCard {
37 self.1.get()
38 }
39
40 pub fn take_first_card(&self, card: PlayingCard) {
41 self.0.set(card);
42 }
43
44 pub fn take_second_card(&self, card: PlayingCard) {
45 self.1.set(card);
46 }
47
48 #[must_use]
49 pub fn simple_index(&self) -> String {
50 format!(
51 "{} {}",
52 self.get_first_card().simple_index(),
53 self.get_second_card().simple_index()
54 )
55 }
56
57 #[must_use]
58 pub fn simple_index_short(&self) -> String {
59 format!(
60 "{}{}",
61 self.get_first_card().simple_index(),
62 self.get_second_card().simple_index()
63 )
64 }
65
66 pub fn to_array(&self) -> [U32Card; 2] {
67 [
68 self.get_first_card().as_u32(),
69 self.get_second_card().as_u32(),
70 ]
71 }
72}
73
74impl CardSlot for HoleCards {
75 fn take(&self, card: PlayingCard) -> bool {
76 if self.get_first_card().is_blank() {
77 self.take_first_card(card);
78 return true;
79 }
80 if self.get_second_card().is_blank() {
81 self.take_second_card(card);
82 return true;
83 }
84 false
85 }
86
87 fn fold(&self) -> PlayingCards {
88 let folded = self.to_playing_cards();
89 self.0.set(PlayingCard::default());
90 self.1.set(PlayingCard::default());
91 folded
92 }
93
94 fn is_dealt(&self) -> bool {
95 !self.get_first_card().is_blank() && !self.get_second_card().is_blank()
96 }
97
98 fn to_playing_cards(&self) -> PlayingCards {
99 let mut playing_cards = PlayingCards::default();
100 playing_cards.insert(self.0.get());
101 playing_cards.insert(self.1.get());
102 playing_cards
103 }
104}
105
106impl Default for HoleCards {
107 fn default() -> HoleCards {
108 HoleCards::new(PlayingCard::default(), PlayingCard::default())
109 }
110}
111
112impl fmt::Display for HoleCards {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 write!(f, "{} {}", self.get_first_card(), self.get_second_card())
115 }
116}
117
118impl From<&'static str> for HoleCards {
119 fn from(value: &'static str) -> HoleCards {
120 let hole_cards = HoleCards::default();
121 if !hole_cards.take_from_index(value) {
122 warn!("Invalid index: {}", value);
123 }
124 hole_cards
125 }
126}
127
128impl From<TwoCard> for HoleCards {
129 fn from(two_cards: TwoCard) -> Self {
130 HoleCards::new(
131 PlayingCard::from(two_cards.first()),
132 PlayingCard::from(two_cards.second()),
133 )
134 }
135}
136
137#[cfg(test)]
138#[allow(non_snake_case)]
139mod holdem_hole_cards_tests {
140 use super::*;
141 use ckc_rs::CardNumber;
142
143 #[test]
144 fn new() {
145 let first = PlayingCard::from("A♦");
146 let second = PlayingCard::from("K♦");
147
148 let hole = HoleCards::new(first, second);
149
150 assert_eq!("A♦ K♦", format!("{}", hole));
151 assert_eq!("A♦ K♦", format!("{}", hole.to_playing_cards()));
152 assert_eq!(2, hole.dealt().len());
153 assert!(hole.is_dealt());
154 }
155
156 #[test]
157 fn from_index() {
158 let slot = HoleCards::from("AS AD");
159
160 assert_eq!("A♠ A♦", format!("{}", slot));
161 assert!(slot.is_dealt());
162 }
163
164 #[test]
165 fn from_index__invalid() {
166 let slot = HoleCards::from("FF");
167
168 assert_eq!("__ __", format!("{}", slot));
169 assert!(!slot.is_dealt());
170 }
171
172 #[test]
173 fn is_dealt() {
174 let hole = HoleCards::default();
175
176 assert!(!hole.is_dealt());
177 }
178
179 #[test]
180 fn is_dealt__first_card_dealt__false() {
181 let hole = HoleCards::default();
182 hole.take_from_index("AS");
183
184 assert!(!hole.is_dealt());
185 }
186
187 #[test]
188 fn is_dealt__second_card_dealt__false() {
189 let hole = HoleCards::default();
190 hole.take_second_card(PlayingCard::from("A♦"));
191
192 assert!(!hole.is_dealt());
193 }
194
195 #[test]
196 fn is_dealt__both_cards_dealt__true() {
197 let hole = HoleCards::default();
198 hole.take_from_index("AS AD");
199
200 assert!(hole.is_dealt());
201 }
202
203 #[test]
204 fn to_array() {
205 let slot = HoleCards::from("AS AD");
206
207 assert_eq!(
208 [CardNumber::ACE_SPADES, CardNumber::ACE_DIAMONDS],
209 slot.to_array()
210 );
211 }
212
213 #[test]
214 fn to_array__default() {
215 assert_eq!(
216 [CardNumber::BLANK, CardNumber::BLANK],
217 HoleCards::default().to_array()
218 );
219 }
220
221 #[test]
222 fn default() {
223 let hole = HoleCards::default();
224
225 assert_eq!("__ __", format!("{}", hole));
226 assert_eq!("", format!("{}", hole.to_playing_cards()));
227 assert_eq!(0, hole.dealt().len());
228 assert!(!hole.is_dealt());
229 }
230
231 #[test]
232 fn display() {
233 let hole = HoleCards::default();
234 hole.take_from_index("AS");
235 hole.take_from_index("AD");
236
237 assert_eq!("A♠ A♦", format!("{}", hole));
238 }
239
240 #[test]
241 fn display__default() {
242 let hole = HoleCards::default();
243
244 assert_eq!("__ __", format!("{}", hole));
245 }
246
247 #[test]
248 fn display__one_card() {
249 let hole = HoleCards::default();
250 hole.take_from_index("AS");
251
252 assert_eq!("A♠ __", format!("{}", hole));
253 }
254
255 #[test]
256 fn display__only_second_card() {
257 let hole = HoleCards::default();
258 hole.take_second_card(PlayingCard::from("A♦"));
259
260 assert_eq!("__ A♦", format!("{}", hole));
261 }
262
263 #[test]
264 fn to_string() {
265 let cards = HoleCards::from("AS AD");
266
267 assert_eq!("A♠ A♦", cards.to_string());
268 }
269
270 #[test]
271 fn from__two_cards() {
272 let two_cards = TwoCard::try_from("AD AS").unwrap();
273 let expected = HoleCards::from("AS AD");
274
275 let actual = HoleCards::from(two_cards);
276
277 assert_eq!(actual, expected);
278 }
279}