Poker
This is one of more hardcore exercise, and I don’t think my solution is good. My original plan was to represent score with a large number, however that became over large so I used a vector to represent scores instead.
/// Given a list of poker hands, return a list of those hands which win. /// /// Note the type signature: this function should return _the same_ reference to /// the winning hand(s) as were passed in, not reconstructed strings which happen to be equal. use std::cmp::Ordering; #[derive(Debug, Eq)] struct Hand { spade: Vec<usize>, heart: Vec<usize>, club: Vec<usize>, diamond: Vec<usize>, } impl Hand { fn new(hand: &str) -> Hand { hand.split(" ").fold( Hand { spade: vec![0; 14], heart: vec![0; 14], club: vec![0; 14], diamond: vec![0; 14], }, |current, incoming| { let index = Self::get_index( &incoming .chars() .take(incoming.len() - 1) .collect::<String>(), ); match incoming.chars().last().unwrap() { 'S' => Hand { spade: Self::suit_increment(¤t.spade, index), heart: current.heart, club: current.club, diamond: current.diamond, }, 'H' => Hand { heart: Self::suit_increment(¤t.heart, index), spade: current.spade, club: current.club, diamond: current.diamond, }, 'C' => Hand { club: Self::suit_increment(¤t.club, index), spade: current.spade, heart: current.heart, diamond: current.diamond, }, _ => Hand { diamond: Self::suit_increment(¤t.diamond, index), spade: current.spade, heart: current.heart, club: current.club, }, } }, ) } fn suit_increment(original: &Vec<usize>, index: usize) -> Vec<usize> { original .clone() .iter() .enumerate() .map(|(idx, &x)| { if (index == 0 && idx == 13) || index == idx { x + 1 } else { x } }) .collect::<Vec<usize>>() } fn flatten(&self) -> Vec<usize> { vec![0; 14] .iter() .enumerate() .map(|(idx, _)| { &self.spade[idx] + &self.heart[idx] + &self.club[idx] + &self.diamond[idx] }) .collect() } // // Score vector // [type] [# of threes/fours] [# of pair/largest] [#High A] [#B] .. [#2] [#Low A] // only exception is straight flush A1234, then set [#High A] to 0 // fn score(&self) -> Vec<usize> { let flattened = self.flatten(); (0..17) .into_iter() .map(|x| { if x == 0 { self.type_bit() } else if x == 1 { if self.has_threes() && self.has_pairs(1) { flattened.iter().rposition(|&y| y == 3).unwrap() } else if self.is_four_of_a_kind() { flattened.iter().rposition(|&y| y == 4).unwrap() } else { 0 } } else if x == 2 { if self.has_threes() && self.has_pairs(1) { flattened.iter().rposition(|&y| y == 2).unwrap() } else if self.is_four_of_a_kind() { flattened.iter().rposition(|&y| y == 1).unwrap() } else { 0 } } else if x == 3 && flattened.iter().take(5).all(|&x| x > 0) { 0 } else { flattened[flattened.len() + 2 - x] } }) .collect() } fn type_bit(&self) -> usize { if self.is_flush() && self.is_straight() { 8 } else if self.is_four_of_a_kind() { 7 } else if self.has_threes() && self.has_pairs(1) { 6 } else if self.is_flush() { 5 } else if self.is_straight() { 4 } else if self.has_threes() { 3 } else if self.has_pairs(2) { 2 } else if self.has_pairs(1) { 1 } else { 0 } } fn has_threes(&self) -> bool { self.has_repeat(3) > 0 } fn has_pairs(&self, count: usize) -> bool { self.has_repeat(2) == count } fn has_repeat(&self, repeat: usize) -> usize { self.flatten() .iter() .take(13) .filter(|&x| *x == repeat) .count() } fn is_straight(&self) -> bool { let flattened = self.flatten(); (0..flattened.len() - 4).any(|x| flattened.iter().skip(x).take(5).all(|&x| x > 0)) } fn is_flush(&self) -> bool { [&self.spade, &self.heart, &self.club, &self.diamond] .iter() .any(|&x| x.into_iter().sum::<usize>() == 5) } fn is_four_of_a_kind(&self) -> bool { self.has_repeat(4) > 0 } fn get_index(value: &str) -> usize { let cards = [ "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", ]; cards.iter().position(|&x| x == value).unwrap() } } impl PartialEq for Hand { fn eq(&self, other: &Hand) -> bool { self.score() == other.score() } } impl PartialOrd for Hand { fn partial_cmp(&self, other: &Hand) -> Option<Ordering> { self.score().partial_cmp(&other.score()) } } impl Ord for Hand { fn cmp(&self, other: &Hand) -> Ordering { self.score().as_slice().cmp(other.score().as_slice()) } } pub fn winning_hands<'a>(hands: &[&'a str]) -> Option<Vec<&'a str>> { let shands = hands.iter().map(|x| Hand::new(x)).collect::<Vec<Hand>>(); if hands.len() == 1 { Some(hands.to_vec()) } else { let scores = shands .iter() .map(|x| x.score()) .collect::<Vec<Vec<usize>>>(); let max = &scores.iter().max().unwrap(); Some( scores .iter() .enumerate() .filter(|(_, x)| x == max) .map(|(idx, _)| hands[idx]) .collect::<Vec<&str>>(), ) } }
As shown in the score method, I am using a vector with 17 dimensions to represent the score of a given hand. In a way it is something like a number, just a not so efficient version when it comes to comparing. I don’t quite like the complexity of the puzzle (too complex for the purpose of learning a language) so I didn’t spent too much time polishing the solution.