From 23ed35da23b28c268a74b6eb9a7d07e4ce448eca Mon Sep 17 00:00:00 2001 From: Gerald Date: Mon, 22 Jun 2020 08:40:12 +0100 Subject: [PATCH 1/2] feat(rust): v2 --- rust/src/lib.rs | 310 +++++++++--------------------------------------- 1 file changed, 58 insertions(+), 252 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 58c75e4..7fa7b90 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,269 +1,75 @@ use std::cmp::Ordering; -use self::PokerHand::*; -use self::Suit::*; use std::collections::BTreeMap; +use std::collections::BTreeSet; +use std::iter::FromIterator; -pub type Cards = Vec<(usize, Suit)>; - -#[derive(Debug)] -pub enum Error { - RankNotFound, - SuitNotFound, -} - -#[derive(Debug, Eq, PartialEq)] -pub enum PokerHand { - RoyalFlush, - StraightFlush, - FourOfAKind, - FullHouse, - Flush, - Straight, - ThreeOfAKind, - TwoPair, - OnePair, - HighCard, -} - -impl PartialOrd for PokerHand { - fn partial_cmp(&self, other: &PokerHand) -> Option { - Some(self.cmp(other)) - } +pub fn pad_vec(vec: Vec) -> Vec { + Vec::from(&[vec, vec![1; 5]].concat()[0..5]) } -impl Ord for PokerHand { - fn cmp(&self, other: &PokerHand) -> Ordering { - let resp = [self, other].iter().map(|x| { - match x { - RoyalFlush => 9, - StraightFlush => 8, - FourOfAKind => 7, - FullHouse => 6, - Flush => 5, - Straight => 4, - ThreeOfAKind => 3, - TwoPair => 2, - OnePair => 1, - HighCard => 0, - } - }).collect::>(); - resp[0].cmp(&resp[1]) +pub fn consecutive(ranks: Vec) -> bool { + if ranks.len() != 5 { + return false; } + let lo = ranks.first().unwrap(); + let hi = ranks.last().unwrap(); + let sum: usize = ranks.iter().sum(); + 2.5 * (lo + hi) as f32 == sum as f32 } -#[derive(Debug, PartialEq)] -pub enum Suit { - Spade, - Heart, - Diamond, - Club, +pub fn parse<'a>(hand: &'a str) -> (BTreeSet, Vec, Vec) { + let mut suits: BTreeSet = BTreeSet::new(); + let mut ranks: BTreeMap = BTreeMap::new(); + for card in hand.split(' ') { + let chars: Vec = format!("{:0>3}", card).chars().collect(); + let rank = match String::from_iter([chars[0], chars[1]].iter()).as_ref() { + "0J" => 11, + "0Q" => 12, + "0K" => 13, + "0A" => 14, + c => c.parse::().unwrap(), + }; + suits.insert(chars[2].to_string()); + *ranks.entry(rank).or_insert(0) += 1; + } + let (ranks, mut freq): (Vec, Vec) = ranks.iter().unzip(); + freq.sort(); + freq.reverse(); + let freq = pad_vec(freq) + .iter() + .map(|x| x.clone()) + .collect::>(); + (suits, ranks, freq) } -#[derive(Debug)] -pub struct Hand<'a> { - cards: Cards, - consecutive: bool, - same_suits: bool, - rank_map: Vec<(usize, usize)>, - original_hand: &'a str -} - -impl<'a> Hand<'a> { - pub fn new(cards: Cards, original_hand: &'a str) -> Hand { - Self { - original_hand, - consecutive: Hand::consecutive(&cards), - same_suits: Hand::same_suits(&cards), - rank_map: Hand::count_same_rank(&cards), - cards, - } +pub fn hand_score<'a>(hand: &'a str, index: usize) -> (Vec, usize) { + let (suits, ranks, mut freq) = parse(hand); + let high_card = *ranks.last().unwrap(); + if suits.len() == 1 { + freq[0] = freq[0] + 2; + freq[1] = freq[1] + 2; } - - pub fn consecutive(cards: &Cards) -> bool { - let cards_length = cards.len(); - let (min_rank, _) = cards[0]; - let (max_rank, _) = cards[cards_length - 1]; - // only check if the range is correct - if max_rank - min_rank + 1 == cards_length { - return (0..(cards_length - 1)).fold(true, |boolean, x| { - let (a, _) = cards[x]; - let (b, _) = cards[x + 1]; - a + 1 == b && boolean - }); - } - false - } - - pub fn same_suits(cards: &Cards) -> bool { - let cards_length = cards.len(); - (0..(cards_length - 1)).fold(true, |boolean, x| { - let (_, a) = &cards[x]; - let (_, b) = &cards[x + 1]; - a == b && boolean - }) - } - - pub fn count_same_rank(cards: &Cards) -> Vec<(usize, usize)> { - cards - .iter() - .fold(BTreeMap::new(), |mut map, (rank, suit)| { - map.entry(*rank).or_insert(0); - if let Some(value) = map.get_mut(rank) { - *value += 1; - } - map - }) - .iter() - .map(|(a, b)| (*a, *b)) - .collect::>() - } - - pub fn royal_flush(&self) -> Option { - let (rank, suit) = &self.cards[0]; - match self.consecutive && self.same_suits && *rank == 10 as usize { - true => Some(RoyalFlush), - false => None, - } - } - - pub fn straight_flush(&self) -> Option { - match self.consecutive && self.same_suits { - true => Some(StraightFlush), - false => None, - } - } - - pub fn full_house(&self) -> Option { - let (_, count) = self.rank_map[0]; - match self.rank_map.len() == 2 && count == 3 as usize { - true => Some(FullHouse), - false => None, - } - } - - pub fn four_of_a_kind(&self) -> Option { - let (_, count) = self.rank_map[0]; - match self.rank_map.len() == 2 && count == 4 as usize { - true => Some(FourOfAKind), - false => None, - } + if consecutive(ranks) { + freq[0] = freq[0] + 2; + freq[1] = freq[1] + 1; } - - pub fn flush(&self) -> Option { - match self.same_suits { - true => Some(Flush), - false => None, - } - } - - pub fn straight(&self) -> Option { - match self.consecutive { - true => Some(Straight), - false => None, - } - } - - pub fn three_of_a_kind(&self) -> Option { - let (_, count) = self.rank_map[0]; - match count == 3 as usize { - true => Some(ThreeOfAKind), - false => None, - } - } - - pub fn two_pair(&self) -> Option { - let (_, count) = self.rank_map[0]; - match self.rank_map.len() == 3 && count == 2 { - true => Some(TwoPair), - false => None, - } - } - - pub fn one_pair(&self) -> Option { - let (_, count) = self.rank_map[0]; - match self.rank_map.len() == 4 && count == 2 { - true => Some(OnePair), - false => None, - } - } - - pub fn evaluate(&self) -> PokerHand { - let funcs: Vec Option>> = vec![ - Box::new(|| self.royal_flush()), - Box::new(|| self.straight_flush()), - Box::new(|| self.four_of_a_kind()), - Box::new(|| self.full_house()), - Box::new(|| self.flush()), - Box::new(|| self.straight()), - Box::new(|| self.three_of_a_kind()), - Box::new(|| self.two_pair()), - Box::new(|| self.one_pair()), - ]; - match funcs.iter().filter_map(|func| func()).next() { - Some(poker_hand) => poker_hand, - None => HighCard, - } - } -} - -pub fn parse_hand<'a>(hand: Vec) -> Cards { - hand.iter() - .map(|card| { - let formatted_card = format!("{:0>3}", card); - let chars: Vec = formatted_card.chars().collect(); - let suit = match chars.last() { - Some('S') => Ok(Spade), - Some('H') => Ok(Heart), - Some('D') => Ok(Diamond), - Some('C') => Ok(Club), - _ => Err(Error::SuitNotFound), - }; - let rank = match [chars[0], chars[1]] { - ['0', '1'] => Ok(1), - ['0', '2'] => Ok(2), - ['0', '3'] => Ok(3), - ['0', '4'] => Ok(4), - ['0', '5'] => Ok(5), - ['0', '6'] => Ok(6), - ['0', '7'] => Ok(7), - ['0', '8'] => Ok(8), - ['0', '9'] => Ok(9), - ['1', '0'] => Ok(10), - ['0', 'J'] => Ok(11), - ['0', 'Q'] => Ok(12), - ['0', 'K'] => Ok(13), - ['0', 'A'] => Ok(14), - _ => Err(Error::RankNotFound), - }; - (rank.unwrap(), suit.unwrap()) - }) - .collect::() -} - -pub fn sort_hand(mut hand: Cards) -> Cards { - hand.sort_by(|(a, _), (b, _)| a.cmp(b)); - hand + freq.push(high_card); + (freq, index) } pub fn winning_hands<'a>(hands: &[&'a str]) -> Option> { - let mut parsed_hands = hands.iter().map(|hand| { - let p = hand - .split(' ') - .map(|b| b.to_string()) - .collect::>(); - - let resp = sort_hand(parse_hand(p)); - let hand = Hand::new(resp, hand); - hand - }).collect::>(); - - parsed_hands.sort_by(|a, b| { - a.evaluate().cmp(&b.evaluate()) - }); - - match parsed_hands.first() { - Some(hand) => Some(vec![hand.original_hand]), - None => None - } + let mut ev_hands = hands + .iter() + .enumerate() + .map(|(i, hand)| hand_score(hand, i)) + .collect::, usize)>>(); + ev_hands.sort_by(|a, b| b.0.cmp(&a.0)); + let (_, i) = ev_hands[0]; + Some(vec![hands[i]]) } +pub fn main() { + // TODO: handle multiple winners + let resp = winning_hands(&["10D JD KD QD AD", "2S 4C 4S 4H 4D", "3S 4S 5D 6H JH"]); + assert_eq!(resp, &["10D JD KD QD AD"]); +} From a0e79baf0095497eda25f0cf633a7506325c8dd9 Mon Sep 17 00:00:00 2001 From: Gerald Date: Mon, 22 Jun 2020 08:49:16 +0100 Subject: [PATCH 2/2] feat(rust/v2): fix assignment --- rust/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 7fa7b90..1426ecc 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,4 +1,3 @@ -use std::cmp::Ordering; use std::collections::BTreeMap; use std::collections::BTreeSet; use std::iter::FromIterator; @@ -46,12 +45,12 @@ pub fn hand_score<'a>(hand: &'a str, index: usize) -> (Vec, usize) { let (suits, ranks, mut freq) = parse(hand); let high_card = *ranks.last().unwrap(); if suits.len() == 1 { - freq[0] = freq[0] + 2; - freq[1] = freq[1] + 2; + freq[0] += 2; + freq[1] += 2; } if consecutive(ranks) { - freq[0] = freq[0] + 2; - freq[1] = freq[1] + 1; + freq[0] += 2; + freq[1] += 1; } freq.push(high_card); (freq, index) @@ -70,6 +69,7 @@ pub fn winning_hands<'a>(hands: &[&'a str]) -> Option> { pub fn main() { // TODO: handle multiple winners - let resp = winning_hands(&["10D JD KD QD AD", "2S 4C 4S 4H 4D", "3S 4S 5D 6H JH"]); - assert_eq!(resp, &["10D JD KD QD AD"]); + let resp = winning_hands(&["10D JD KD QD AD", "2S 4C 4S 4H 4D", "3S 4S 5D 6H JH"]).unwrap(); + assert_eq!(resp, vec!["10D JD KD QD AD"]); } +