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.