1mod crib_cards;
2mod crib_part;
3mod dealt_cards;
4mod kept_cards;
5
6use crate::card::{Card, Rank};
7use crate::deck::Deck;
8use crate::CribbageCoreError;
9
10pub use self::crib_cards::CribCards;
11pub use self::crib_part::FourPlayerCribPart;
12pub use self::crib_part::ThreePlayerCribPart;
13pub use self::crib_part::TwoPlayerCribPart;
14pub use self::dealt_cards::FourPlayerDeal;
15pub use self::dealt_cards::ThreePlayerDeal;
16pub use self::dealt_cards::TwoPlayerDeal;
17pub use self::kept_cards::KeptCards;
18
19pub fn deal_four_player_hand(deck: &mut Deck) -> Result<FourPlayerDeal, CribbageCoreError> {
20 let cards = deck.draw_n(5)?;
21 Ok(FourPlayerDeal::new([
22 cards[0], cards[1], cards[2], cards[3], cards[4],
23 ]))
24}
25
26pub fn deal_three_player_hand(deck: &mut Deck) -> Result<ThreePlayerDeal, CribbageCoreError> {
27 let cards = deck.draw_n(5)?;
28 Ok(ThreePlayerDeal::new([
29 cards[0], cards[1], cards[2], cards[3], cards[4],
30 ]))
31}
32
33pub fn deal_two_player_hand(deck: &mut Deck) -> Result<TwoPlayerDeal, CribbageCoreError> {
34 let cards = deck.draw_n(6)?;
35 Ok(TwoPlayerDeal::new([
36 cards[0], cards[1], cards[2], cards[3], cards[4], cards[5],
37 ]))
38}
39
40#[derive(Debug)]
41pub struct Hand {
42 cards: [Card; 4],
43 cut: Card,
44 is_crib: bool,
45 score: Option<u8>,
46}
47
48impl Hand {
49 pub fn new(cards: [Card; 4], cut: Card, is_crib: bool) -> Hand {
50 Hand {
51 cards,
52 cut,
53 is_crib,
54 score: None,
55 }
56 }
57
58 pub fn cards(&self) -> &[Card] {
59 &self.cards
60 }
61
62 pub fn score(&mut self) -> u8 {
63 if let Some(score) = self.score {
64 return score;
65 }
66
67 let mut score = 0u8;
68 score += self.score_fifteens();
69 score += self.score_pairs();
70 score += self.score_runs();
71 score += self.score_flush();
72 score += self.score_nobs();
73 self.score = Some(score);
74 score
75 }
76
77 fn score_fifteens(&self) -> u8 {
78 let mut points = 0u8;
79 let subsets = vec![
80 self.sets_of_two(),
81 self.sets_of_three(),
82 self.sets_of_four(),
83 self.sets_of_five(),
84 ];
85
86 for subset in subsets {
87 for set in subset {
88 let mut sum = 0u8;
89 for card in &set {
90 sum += card.rank().value();
91 }
92
93 if sum == 15 {
94 points += 2;
95 }
96 }
97 }
98
99 points
100 }
101
102 fn score_flush(&self) -> u8 {
103 let mut points = 0;
104 let suit = self.cards[0].suit();
105 if self.cards[1..4].iter().all(|c| c.suit() == suit) {
106 points += 4;
107
108 if self.cut.suit() == suit {
109 points += 1;
110 } else if self.is_crib {
111 points = 0;
112 }
113 }
114
115 points
116 }
117
118 fn score_nobs(&self) -> u8 {
119 for card in &self.cards {
120 if card.rank() == Rank::Jack && card.suit() == self.cut.suit() {
121 return 1;
122 }
123 }
124
125 0
126 }
127
128 fn score_pairs(&self) -> u8 {
129 let mut points = 0u8;
130 let sets_of_two = self.sets_of_two();
131 for set in sets_of_two {
132 if set[0].rank() == set[1].rank() {
133 points += 2u8;
134 }
135 }
136
137 points
138 }
139
140 fn score_runs(&self) -> u8 {
141 let mut points = 0u8;
142 let mut temp = 0u8;
143
144 for mut set in self.sets_of_five() {
145 set.sort();
146 temp += Hand::score_run(&set);
147 }
148
149 points += temp;
150 if points != 0u8 {
151 return points;
152 }
153
154 for mut set in self.sets_of_four() {
155 set.sort();
156 temp += Hand::score_run(&set);
157 }
158
159 points += temp;
160 if points != 0u8 {
161 return points;
162 }
163
164 for mut set in self.sets_of_three() {
165 set.sort();
166 temp += Hand::score_run(&set);
167 }
168
169 points += temp;
170 if points != 0u8 {
171 return points;
172 }
173
174 points
175 }
176
177 fn score_run(cards: &[Card]) -> u8 {
178 if cards.len() < 3usize {
179 return 0u8;
180 }
181
182 let mut points = 1u8;
183 for i in 1..cards.len() {
184 if cards[i].rank().ordinal() == (cards[i - 1].rank().ordinal() + 1) {
185 points += 1u8;
186 } else {
187 points = 0u8;
188 break;
189 }
190 }
191
192 points
193 }
194
195 fn sets_of_two(&self) -> Vec<Vec<Card>> {
196 vec![
197 vec![self.cards[0], self.cards[1]],
198 vec![self.cards[0], self.cards[2]],
199 vec![self.cards[0], self.cards[3]],
200 vec![self.cards[0], self.cut],
201 vec![self.cards[1], self.cards[2]],
202 vec![self.cards[1], self.cards[3]],
203 vec![self.cards[1], self.cut],
204 vec![self.cards[2], self.cards[3]],
205 vec![self.cards[2], self.cut],
206 vec![self.cards[3], self.cut],
207 ]
208 }
209
210 fn sets_of_three(&self) -> Vec<Vec<Card>> {
211 vec![
212 vec![self.cards[0], self.cards[1], self.cards[2]],
213 vec![self.cards[0], self.cards[1], self.cards[3]],
214 vec![self.cards[0], self.cards[2], self.cards[3]],
215 vec![self.cards[1], self.cards[2], self.cards[3]],
216 vec![self.cards[0], self.cards[1], self.cut],
217 vec![self.cards[0], self.cards[2], self.cut],
218 vec![self.cards[1], self.cards[2], self.cut],
219 vec![self.cards[0], self.cards[3], self.cut],
220 vec![self.cards[1], self.cards[3], self.cut],
221 vec![self.cards[2], self.cards[3], self.cut],
222 ]
223 }
224
225 fn sets_of_four(&self) -> Vec<Vec<Card>> {
226 vec![
227 vec![self.cards[0], self.cards[1], self.cards[2], self.cards[3]],
228 vec![self.cards[0], self.cards[1], self.cards[2], self.cut],
229 vec![self.cards[0], self.cards[1], self.cards[3], self.cut],
230 vec![self.cards[0], self.cards[2], self.cards[3], self.cut],
231 vec![self.cards[1], self.cards[2], self.cards[3], self.cut],
232 ]
233 }
234
235 fn sets_of_five(&self) -> Vec<Vec<Card>> {
236 vec![vec![
237 self.cards[0],
238 self.cards[1],
239 self.cards[2],
240 self.cards[3],
241 self.cut,
242 ]]
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use crate::card::Card;
249 use crate::hand::Hand;
250 use std::str::FromStr;
251
252 #[allow(dead_code)]
253 #[cfg_attr(feature = "extensive-tests", test)]
254 fn test_hands() {
255 use std::env;
256 use std::fs::File;
257 use std::io::{BufRead, BufReader};
258 use std::path::PathBuf;
259
260 let key = "CRIBBAGE_CORE_RESOURCES_DIRECTORY";
261 let resources = match env::var(key) {
262 Ok(val) => val,
263 Err(_) => panic!("Environment variable {} not set", key),
264 };
265
266 let mut path = PathBuf::new();
267 path.push(resources);
268 path.push("cribbage_hand_scores");
269
270 let filenames = [
271 "A.txt", "2.txt", "3.txt", "4.txt", "5.txt", "6.txt", "7.txt", "8.txt", "9.txt",
272 "T.txt", "J.txt", "Q.txt", "K.txt",
273 ];
274
275 for filename in &filenames {
276 path.push(filename);
277 println!("Processing: {:?}", path);
278 let reader = BufReader::new(File::open(&path).unwrap());
279 for (line_number, line) in reader.lines().enumerate() {
280 let line = line.unwrap().to_string();
281 let tokens: Vec<&str> = line.split(' ').collect();
282 if tokens.len() != 7 {
283 panic!("Line {} in {:?} is malformed", line_number + 1, path);
284 }
285
286 let cut = Card::from_str(tokens[0]).unwrap();
287 let card1 = Card::from_str(tokens[1]).unwrap();
288 let card2 = Card::from_str(tokens[2]).unwrap();
289 let card3 = Card::from_str(tokens[3]).unwrap();
290 let card4 = Card::from_str(tokens[4]).unwrap();
291 let expected_non_crib_hand_score: u8 = tokens[5].parse().unwrap();
292 let expected_crib_hand_score: u8 = tokens[6].parse().unwrap();
293
294 let mut hand = Hand::new([card1, card2, card3, card4], cut, false);
295 let non_crib_hand_score = hand.score();
296 if non_crib_hand_score != expected_non_crib_hand_score {
297 panic!(
298 "Line {} in {:?}: {:?} scored {} points as a non-crib hand,\
299 but should have scored {} points",
300 line_number + 1,
301 path,
302 hand,
303 non_crib_hand_score,
304 expected_non_crib_hand_score
305 );
306 }
307
308 let mut crib_hand = Hand::new([card1, card2, card3, card4], cut, true);
309 let crib_hand_score = crib_hand.score();
310 if crib_hand_score != expected_crib_hand_score {
311 panic!(
312 "Line {} in {:?}: {:?} scored {} points as a crib hand,\
313 but should have scored {} points",
314 line_number + 1,
315 path,
316 hand,
317 non_crib_hand_score,
318 expected_non_crib_hand_score
319 );
320 }
321 }
322
323 path.pop();
324 }
325 }
326}