1use crate::cards::HandValidator;
2use crate::{BinaryCard, CKCNumber, CardNumber, HandError, PokerCard, Shifty, BC64};
3use core::cmp;
4use core::slice::Iter;
5use serde::{Deserialize, Serialize};
6
7#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
8pub struct Two([CKCNumber; 2]);
9
10#[allow(non_upper_case_globals)]
11impl Two {
12 pub const AA: [Two; 6] = [
14 Two([CardNumber::ACE_SPADES, CardNumber::ACE_HEARTS]),
15 Two([CardNumber::ACE_SPADES, CardNumber::ACE_DIAMONDS]),
16 Two([CardNumber::ACE_SPADES, CardNumber::ACE_CLUBS]),
17 Two([CardNumber::ACE_HEARTS, CardNumber::ACE_DIAMONDS]),
18 Two([CardNumber::ACE_HEARTS, CardNumber::ACE_CLUBS]),
19 Two([CardNumber::ACE_DIAMONDS, CardNumber::ACE_CLUBS]),
20 ];
21 pub const AK: [Two; 16] = [
22 Two([CardNumber::ACE_SPADES, CardNumber::KING_SPADES]),
23 Two([CardNumber::ACE_HEARTS, CardNumber::KING_HEARTS]),
24 Two([CardNumber::ACE_DIAMONDS, CardNumber::KING_DIAMONDS]),
25 Two([CardNumber::ACE_CLUBS, CardNumber::KING_CLUBS]),
26 Two([CardNumber::ACE_SPADES, CardNumber::KING_HEARTS]),
27 Two([CardNumber::ACE_SPADES, CardNumber::KING_DIAMONDS]),
28 Two([CardNumber::ACE_SPADES, CardNumber::KING_CLUBS]),
29 Two([CardNumber::ACE_HEARTS, CardNumber::KING_SPADES]),
30 Two([CardNumber::ACE_HEARTS, CardNumber::KING_DIAMONDS]),
31 Two([CardNumber::ACE_HEARTS, CardNumber::KING_CLUBS]),
32 Two([CardNumber::ACE_DIAMONDS, CardNumber::KING_SPADES]),
33 Two([CardNumber::ACE_DIAMONDS, CardNumber::KING_HEARTS]),
34 Two([CardNumber::ACE_DIAMONDS, CardNumber::KING_CLUBS]),
35 Two([CardNumber::ACE_CLUBS, CardNumber::KING_SPADES]),
36 Two([CardNumber::ACE_CLUBS, CardNumber::KING_HEARTS]),
37 Two([CardNumber::ACE_CLUBS, CardNumber::KING_DIAMONDS]),
38 ];
39
40 pub const AKs: [Two; 4] = [Two::AK[0], Two::AK[1], Two::AK[2], Two::AK[3]];
43 pub const AKo: [Two; 12] = [
44 Two::AK[4],
45 Two::AK[5],
46 Two::AK[6],
47 Two::AK[7],
48 Two::AK[8],
49 Two::AK[9],
50 Two::AK[10],
51 Two::AK[11],
52 Two::AK[12],
53 Two::AK[13],
54 Two::AK[14],
55 Two::AK[15],
56 ];
57
58 pub const AQs: [Two; 4] = [
59 Two([CardNumber::ACE_SPADES, CardNumber::QUEEN_SPADES]),
60 Two([CardNumber::ACE_HEARTS, CardNumber::QUEEN_HEARTS]),
61 Two([CardNumber::ACE_DIAMONDS, CardNumber::QUEEN_DIAMONDS]),
62 Two([CardNumber::ACE_CLUBS, CardNumber::QUEEN_CLUBS]),
63 ];
64 pub const AQo: [Two; 12] = [
65 Two([CardNumber::ACE_SPADES, CardNumber::QUEEN_HEARTS]),
66 Two([CardNumber::ACE_SPADES, CardNumber::QUEEN_DIAMONDS]),
67 Two([CardNumber::ACE_SPADES, CardNumber::QUEEN_CLUBS]),
68 Two([CardNumber::ACE_HEARTS, CardNumber::QUEEN_SPADES]),
69 Two([CardNumber::ACE_HEARTS, CardNumber::QUEEN_DIAMONDS]),
70 Two([CardNumber::ACE_HEARTS, CardNumber::QUEEN_CLUBS]),
71 Two([CardNumber::ACE_DIAMONDS, CardNumber::QUEEN_SPADES]),
72 Two([CardNumber::ACE_DIAMONDS, CardNumber::QUEEN_HEARTS]),
73 Two([CardNumber::ACE_DIAMONDS, CardNumber::QUEEN_CLUBS]),
74 Two([CardNumber::ACE_CLUBS, CardNumber::QUEEN_SPADES]),
75 Two([CardNumber::ACE_CLUBS, CardNumber::QUEEN_HEARTS]),
76 Two([CardNumber::ACE_CLUBS, CardNumber::QUEEN_DIAMONDS]),
77 ];
78
79 #[must_use]
81 pub fn new(first: CKCNumber, second: CKCNumber) -> Self {
82 Self([first, second])
83 }
84
85 fn from_index(index: &str) -> Option<[CKCNumber; 2]> {
86 let mut esses = index.split_whitespace();
87
88 let first = CKCNumber::from_index(esses.next()?);
89 let second = CKCNumber::from_index(esses.next()?);
90 let hand: [CKCNumber; 2] = [first, second];
91 Some(hand)
92 }
93
94 #[must_use]
97 pub fn second(&self) -> CKCNumber {
98 self.0[1]
99 }
100
101 pub fn set_first(&mut self, card_number: CKCNumber) {
102 self.0[0] = card_number;
103 }
104
105 pub fn set_second(&mut self, card_number: CKCNumber) {
106 self.0[1] = card_number;
107 }
108
109 #[must_use]
110 pub fn to_arr(&self) -> [CKCNumber; 2] {
111 self.0
112 }
113
114 #[must_use]
117 #[allow(clippy::cast_possible_truncation)]
118 pub fn chen_formula(&self) -> i8 {
119 let high_card = self.high_card();
120 let mut points = high_card.get_chen_points();
121
122 if self.is_pocket_pair() {
123 points = f32::max(points * 2.0, 5.0);
124 } else {
125 let gap = self.get_gap();
126 points -= match gap {
127 1 => 1.0,
128 2 => 2.0,
129 3 => 4.0,
130 0 => 0.0,
131 _ => 5.0,
132 };
133
134 let top_rank = high_card.get_card_rank() as u8;
135 if (gap < 2) && (top_rank < 12u8) {
136 points += 1.0;
137 }
138 }
139
140 if self.is_suited() {
141 points += 2.0;
142 }
143
144 points.ceil() as i8
145 }
146
147 #[must_use]
148 pub fn get_gap(&self) -> u8 {
149 let s = self.sort();
150 let distance_between = s.first().get_card_rank() as u8 - s.second().get_card_rank() as u8;
151 distance_between.saturating_sub(1)
152 }
153
154 #[must_use]
155 pub fn high_card(&self) -> CKCNumber {
156 cmp::max(self.first(), self.second())
157 }
158
159 #[must_use]
160 pub fn is_connector(&self) -> bool {
161 self.get_gap() == 0
162 }
163
164 #[must_use]
165 pub fn is_pocket_pair(&self) -> bool {
166 self.first().get_card_rank() == self.second().get_card_rank()
167 }
168
169 #[must_use]
170 pub fn is_suited(&self) -> bool {
171 self.first().get_card_suit() == self.second().get_card_suit()
172 }
173
174 #[must_use]
175 pub fn is_suited_connector(&self) -> bool {
176 self.is_suited() && self.is_connector()
177 }
178
179 }
194
195impl From<&[CKCNumber; 2]> for Two {
196 fn from(array: &[CKCNumber; 2]) -> Self {
197 Two(*array)
198 }
199}
200
201impl From<[CKCNumber; 2]> for Two {
202 fn from(array: [CKCNumber; 2]) -> Self {
203 Two(array)
204 }
205}
206
207impl TryFrom<&'static str> for Two {
208 type Error = HandError;
209
210 fn try_from(index: &'static str) -> Result<Self, Self::Error> {
214 match Two::from_index(index) {
215 None => Err(HandError::InvalidIndex),
216 Some(five) => Ok(Two::from(five)),
217 }
218 }
219}
220
221impl TryFrom<BinaryCard> for Two {
222 type Error = HandError;
223
224 fn try_from(binary_card: BinaryCard) -> Result<Self, Self::Error> {
225 match binary_card.number_of_cards() {
226 0..=1 => Err(HandError::NotEnoughCards),
227 2 => {
228 let mut bc = binary_card;
229 let two = Two::new(
230 CKCNumber::from_binary_card(bc.peel()),
231 CKCNumber::from_binary_card(bc.peel()),
232 );
233 if two.is_valid() {
234 Ok(two)
235 } else {
236 Err(HandError::InvalidBinaryFormat)
237 }
238 },
239 _ => Err(HandError::TooManyCards),
240 }
241 }
242}
243
244impl HandValidator for Two {
245 fn are_unique(&self) -> bool {
246 self.first() != self.second()
247 }
248
249 fn first(&self) -> CKCNumber {
250 self.0[0]
251 }
252
253 fn sort(&self) -> Self {
254 let mut array = *self;
255 array.sort_in_place();
256 array
257 }
258
259 fn sort_in_place(&mut self) {
260 self.0.sort_unstable();
261 self.0.reverse();
262 }
263
264 fn iter(&self) -> Iter<'_, CKCNumber> {
265 self.0.iter()
266 }
267}
268
269impl Shifty for Two {
270 fn shift_suit(&self) -> Self {
271 Two::new(self.first().shift_suit(), self.second().shift_suit())
272 }
273}
274
275#[cfg(test)]
276#[allow(non_snake_case)]
277mod cards_two_tests {
278 use super::*;
279 use crate::CardNumber;
280 use rstest::rstest;
281
282 #[test]
283 fn ak() {
284 let aks = &Two::AK[..4];
285
286 assert_eq!(aks, Two::AKs);
287 }
288
289 #[test]
290 fn are_unique() {
291 assert!(!Two::new(CardNumber::ACE_CLUBS, CardNumber::ACE_CLUBS).are_unique());
292 assert!(!Two::new(CardNumber::BLANK, CardNumber::BLANK).are_unique());
293 assert!(Two::new(CardNumber::ACE_SPADES, CardNumber::ACE_CLUBS).are_unique());
294 }
295
296 #[test]
297 fn contain_blank() {
298 assert!(!Two::new(CardNumber::ACE_SPADES, CardNumber::ACE_CLUBS).contain_blank());
299 assert!(!Two::new(CardNumber::ACE_CLUBS, CardNumber::ACE_CLUBS).contain_blank());
300 assert!(Two::new(CardNumber::BLANK, CardNumber::BLANK).contain_blank());
301 assert!(Two::new(CardNumber::BLANK, CardNumber::ACE_CLUBS).contain_blank());
302 assert!(Two::new(CardNumber::ACE_CLUBS, CardNumber::BLANK).contain_blank());
303 }
304
305 #[test]
306 fn is_valid() {
307 assert!(!Two::new(CardNumber::ACE_CLUBS, CardNumber::ACE_CLUBS).is_valid());
308 assert!(!Two::new(CardNumber::BLANK, CardNumber::BLANK).is_valid());
309 assert!(!Two::new(CardNumber::BLANK, CardNumber::ACE_CLUBS).is_valid());
310 assert!(!Two::new(CardNumber::ACE_CLUBS, CardNumber::BLANK).is_valid());
311 assert!(Two::new(CardNumber::ACE_SPADES, CardNumber::ACE_CLUBS).is_valid());
312 }
313
314 #[rstest]
315 #[case(20, Two::new(CardNumber::ACE_SPADES, CardNumber::ACE_CLUBS))]
316 #[case(12, Two::new(CardNumber::ACE_SPADES, CardNumber::KING_SPADES))]
317 #[case(10, Two::new(CardNumber::ACE_SPADES, CardNumber::KING_CLUBS))]
318 #[case(16, Two::new(CardNumber::KING_SPADES, CardNumber::KING_CLUBS))]
319 #[case(14, Two::new(CardNumber::QUEEN_SPADES, CardNumber::QUEEN_CLUBS))]
320 #[case(12, Two::new(CardNumber::JACK_SPADES, CardNumber::JACK_CLUBS))]
321 #[case(10, Two::new(CardNumber::TEN_SPADES, CardNumber::TEN_CLUBS))]
322 #[case(9, Two::new(CardNumber::JACK_SPADES, CardNumber::TEN_SPADES))]
323 #[case(5, Two::new(CardNumber::FIVE_SPADES, CardNumber::ACE_CLUBS))]
324 #[case(7, Two::new(CardNumber::FIVE_SPADES, CardNumber::ACE_SPADES))]
325 #[case(6, Two::new(CardNumber::FIVE_SPADES, CardNumber::SIX_SPADES))]
326 #[case(5, Two::new(CardNumber::TREY_SPADES, CardNumber::TREY_CLUBS))]
327 #[case(5, Two::new(CardNumber::DEUCE_SPADES, CardNumber::DEUCE_CLUBS))]
328 #[case(-1, Two::new(CardNumber::DEUCE_SPADES, CardNumber::SEVEN_CLUBS))]
329 fn chen_formula(#[case] chen_number: i8, #[case] hand: Two) {
330 assert_eq!(chen_number, hand.chen_formula());
331 }
332
333 #[test]
334 fn get_gap() {
335 assert_eq!(11, Two::new(CardNumber::DEUCE_CLUBS, CardNumber::ACE_CLUBS).get_gap());
336 assert_eq!(11, Two::new(CardNumber::ACE_SPADES, CardNumber::DEUCE_CLUBS).get_gap());
337 assert_eq!(0, Two::new(CardNumber::ACE_SPADES, CardNumber::ACE_CLUBS).get_gap());
338 }
339
340 #[test]
341 fn high_card() {
342 let hand = Two::new(CardNumber::ACE_CLUBS, CardNumber::KING_SPADES);
343
344 assert_eq!(hand.high_card(), CardNumber::ACE_CLUBS);
345 }
346
347 #[test]
348 fn is_connector() {
349 assert!(Two::new(CardNumber::ACE_CLUBS, CardNumber::KING_SPADES).is_connector());
350 assert!(!Two::new(CardNumber::ACE_CLUBS, CardNumber::DEUCE_CLUBS).is_connector());
351 }
352
353 #[test]
354 fn is_pocket_pair() {
355 assert!(Two::new(CardNumber::ACE_CLUBS, CardNumber::ACE_SPADES).is_pocket_pair());
356 assert!(!Two::new(CardNumber::ACE_CLUBS, CardNumber::KING_SPADES).is_pocket_pair());
357 }
358
359 #[test]
360 fn is_suited() {
361 assert!(Two::new(CardNumber::ACE_CLUBS, CardNumber::KING_CLUBS).is_suited());
362 assert!(!Two::new(CardNumber::ACE_CLUBS, CardNumber::KING_SPADES).is_suited());
363 }
364
365 #[test]
366 fn is_suited_connector() {
367 assert!(Two::new(CardNumber::ACE_CLUBS, CardNumber::KING_CLUBS).is_suited_connector());
368 assert!(Two::new(CardNumber::NINE_CLUBS, CardNumber::EIGHT_CLUBS).is_suited_connector());
369 assert!(!Two::new(CardNumber::NINE_CLUBS, CardNumber::EIGHT_DIAMONDS).is_suited_connector());
370 assert!(!Two::new(CardNumber::NINE_CLUBS, CardNumber::SEVEN_CLUBS).is_suited_connector());
371 assert!(!Two::new(CardNumber::ACE_CLUBS, CardNumber::KING_SPADES).is_suited_connector());
372 }
373
374 #[test]
375 fn shifty__shift_suit() {
376 assert_eq!(
377 Two::try_from("AS AD").unwrap().shift_suit(),
378 Two::try_from("AH AC").unwrap()
379 )
380 }
381
382 #[test]
383 fn try_from__binary_card() {
384 let t = Two::try_from(BinaryCard::ACE_SPADES.fold_in(BinaryCard::ACE_DIAMONDS));
385 assert!(t.is_ok());
386 assert!(!t.is_err());
387 assert_eq!(Two::new(CardNumber::ACE_SPADES, CardNumber::ACE_DIAMONDS), t.unwrap());
388 }
389
390 #[test]
391 fn try_from__binary_card__not_enough() {
392 let t = Two::try_from(BinaryCard::ACE_SPADES);
393 assert!(t.is_err());
394 assert_eq!(t.unwrap_err(), HandError::NotEnoughCards);
395 assert_eq!(Two::try_from(BinaryCard::BLANK).unwrap_err(), HandError::NotEnoughCards);
396 }
397
398 #[test]
399 fn try_from__binary_card__too_many() {
400 let t = Two::try_from(
401 BinaryCard::ACE_SPADES
402 .fold_in(BinaryCard::ACE_DIAMONDS)
403 .fold_in(BinaryCard::ACE_CLUBS),
404 );
405 assert!(t.is_err());
406 assert_eq!(t.unwrap_err(), HandError::TooManyCards);
407 }
408
409 #[test]
410 fn try_from__binary_card__invalid_binary_format() {
411 let bc = 0b1_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000
412 .fold_in(0b10_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000);
413 let t = Two::try_from(bc);
414 assert!(t.is_err());
415 assert_eq!(t.unwrap_err(), HandError::InvalidBinaryFormat);
416 }
417
418 #[test]
419 fn try_from__index() {
420 let two = Two::try_from("J♠ T♠");
421
422 assert!(two.is_ok());
423 let two = two.unwrap();
424 assert_eq!(two.first(), CardNumber::JACK_SPADES);
425 assert_eq!(two.second(), CardNumber::TEN_SPADES);
426 }
427
428 #[test]
429 fn try_from__index__blank() {
430 let two = Two::try_from("A♠ XX");
431
432 assert!(two.is_ok());
433 let two = two.unwrap();
434 assert_eq!(two.first(), CardNumber::ACE_SPADES);
435 assert_eq!(two.second(), CardNumber::BLANK);
436 }
437
438 #[test]
439 fn try_from__index__too_short() {
440 let two = Two::try_from("A♠");
441
442 assert!(two.is_err());
443 }
444}