Created
August 30, 2016 12:31
-
-
Save zlx/1921b80a81b22a898a10b32369ecb936 to your computer and use it in GitHub Desktop.
Poker Hand in Rust
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
mod poker { | |
use std::cmp::Ordering; | |
use std::collections::HashMap; | |
#[derive(Eq, Debug, Clone, Copy, Hash)] | |
enum CardValue { | |
DEFAULT = 0, V2, V3, V4, V5, V6, V7, V8, V9, VT, VJ, VQ, VK, VA, | |
} | |
impl CardValue { | |
fn is_straight(&self, other: &CardValue) -> bool { | |
(*self as i32) + 1 == (*other as i32) || (*self as i32) - 1 == (*other as i32) | |
} | |
} | |
impl Ord for CardValue { | |
fn cmp(&self, other: &CardValue) -> Ordering { | |
(*self as i32).cmp(&(*other as i32)) | |
} | |
} | |
impl PartialOrd for CardValue { | |
fn partial_cmp(&self, other: &CardValue) -> Option<Ordering> { | |
Some(self.cmp(other)) | |
} | |
} | |
impl PartialEq for CardValue { | |
fn eq(&self, other: &CardValue) -> bool { | |
*self as i32 == *other as i32 | |
} | |
} | |
impl CardValue { | |
fn new(value: char) -> CardValue { | |
match value { | |
'2' => CardValue::V2, | |
'3' => CardValue::V3, | |
'4' => CardValue::V4, | |
'5' => CardValue::V5, | |
'6' => CardValue::V6, | |
'7' => CardValue::V7, | |
'8' => CardValue::V8, | |
'9' => CardValue::V9, | |
'T' => CardValue::VT, | |
'J' => CardValue::VJ, | |
'Q' => CardValue::VQ, | |
'K' => CardValue::VK, | |
'A' => CardValue::VA, | |
_ => panic!("Invalid Card Value"), | |
} | |
} | |
} | |
#[derive(Eq, Debug, Clone, Copy, Hash)] | |
enum CardSuit { | |
DEFAULT, C, D, H, S, | |
} | |
impl PartialEq for CardSuit { | |
fn eq(&self, other: &CardSuit) -> bool { | |
*self as i32 == *other as i32 | |
} | |
} | |
impl CardSuit { | |
fn new(suit: char) -> CardSuit { | |
match suit { | |
'C' => CardSuit::C, | |
'D' => CardSuit::D, | |
'H' => CardSuit::H, | |
'S' => CardSuit::S, | |
_ => panic!("Invalid Card Suit"), | |
} | |
} | |
} | |
#[derive(Eq, Debug, Clone, Copy, Hash)] | |
struct Card { | |
value: CardValue, | |
suit: CardSuit, | |
} | |
impl Card { | |
fn new(card: &str) -> Card { | |
let mut chars = card.chars(); | |
Card { | |
value: CardValue::new(chars.next().unwrap()), | |
suit: CardSuit::new(chars.next().unwrap()), | |
} | |
} | |
} | |
impl Ord for Card { | |
fn cmp(&self, other: &Card) -> Ordering { | |
if self.value > other.value { | |
Ordering::Greater | |
} else if self.value < other.value { | |
Ordering::Less | |
} else { | |
Ordering::Equal | |
} | |
} | |
} | |
impl PartialOrd for Card { | |
fn partial_cmp(&self, other: &Card) -> Option<Ordering> { | |
Some(self.cmp(other)) | |
} | |
} | |
impl PartialEq for Card { | |
fn eq(&self, other: &Card) -> bool { | |
self.value == other.value | |
} | |
} | |
#[derive(PartialEq, PartialOrd, Eq, Debug)] | |
enum HandLevel { | |
HighCard, | |
Pair, | |
TwoPair, | |
ThreeKing, | |
Straight, | |
Flush, | |
FullHouse, | |
FourKing, | |
StraightFlush, | |
} | |
#[derive(Debug)] | |
struct Hand { | |
cards: [Card; 5], | |
} | |
impl Hand { | |
fn new(cards: Vec<&str>) -> Hand { | |
Hand { | |
cards: [ | |
Card::new(cards[0]), | |
Card::new(cards[1]), | |
Card::new(cards[2]), | |
Card::new(cards[3]), | |
Card::new(cards[4]) | |
] | |
} | |
} | |
fn sort_cards(&self) -> [Card; 5] { | |
let mut mut_cards: [Card; 5] = [Card { value: CardValue::DEFAULT, suit: CardSuit::DEFAULT }; 5]; | |
mut_cards.clone_from_slice(&(self.cards)); | |
mut_cards.sort_by(|a, b| b.cmp(a)); | |
mut_cards | |
} | |
fn count_map(&self) -> HashMap<CardValue, i32> { | |
self.cards.iter().fold(HashMap::new(), |mut acc, &card| { | |
*acc.entry(card.value).or_insert(0) += 1; | |
acc | |
}) | |
} | |
fn count_for(map: HashMap<CardValue, i32>, value: i32) -> i32 { | |
map.values().fold(0, |acc, &v| if v == value { acc + 1 } else { acc }) | |
} | |
fn values_with_count(map: HashMap<CardValue, i32>, count: i32) -> Vec<CardValue> { | |
let mut card_values: Vec<CardValue> = Vec::new(); | |
for (&value, &c) in map.iter() { | |
if c == count { | |
card_values.push(value); | |
} | |
} | |
return card_values; | |
} | |
fn card_values_cmp(card_values: Vec<CardValue>, other_card_values: Vec<CardValue>) -> Ordering { | |
let mut i = 0; | |
loop { | |
if i > card_values.len() - 1 { | |
return Ordering::Equal | |
} else { | |
if card_values[i] > other_card_values[i] { | |
return Ordering::Greater | |
} else if card_values[i] < other_card_values[i] { | |
return Ordering::Less | |
} else { | |
i += 1; | |
} | |
} | |
} | |
} | |
fn cards_cmp(cards: Vec<Card>, other_cards: Vec<Card>) -> Ordering { | |
Hand::card_values_cmp(cards.iter().map(|card| card.value).collect(), other_cards.iter().map(|card| card.value).collect()) | |
} | |
fn is_four_king(&self) -> bool { | |
Hand::count_for(self.count_map(), 4) == 1 | |
} | |
fn is_full_house(&self) -> bool { | |
Hand::count_for(self.count_map(), 3) == 1 && Hand::count_for(self.count_map(), 2) == 1 | |
} | |
fn is_flush(&self) -> bool { | |
let suit = self.cards[0].suit; | |
suit == self.cards[1].suit && | |
suit == self.cards[2].suit && | |
suit == self.cards[3].suit && | |
suit == self.cards[4].suit | |
} | |
fn is_straight(&self) -> bool { | |
let sorted_cards = self.sort_cards(); | |
sorted_cards[0].value.is_straight(&sorted_cards[1].value) && | |
sorted_cards[1].value.is_straight(&sorted_cards[2].value) && | |
sorted_cards[2].value.is_straight(&sorted_cards[3].value) && | |
sorted_cards[3].value.is_straight(&sorted_cards[4].value) | |
} | |
fn is_three_king(&self) -> bool { | |
!self.is_full_house() && Hand::count_for(self.count_map(), 3) == 1 | |
} | |
fn is_two_pair(&self) -> bool { | |
Hand::count_for(self.count_map(), 2) == 2 | |
} | |
fn is_pair(&self) -> bool { | |
!self.is_full_house() && Hand::count_for(self.count_map(), 2) == 1 | |
} | |
fn is_straight_flush(&self) -> bool { | |
self.is_straight() && self.is_flush() | |
} | |
fn straight_flush_cmp(&self, other: &Hand) -> Ordering { | |
self.sort_cards()[0].cmp(&other.sort_cards()[0]) | |
} | |
fn four_king_cmp(&self, other: &Hand) -> Ordering { | |
Hand::values_with_count(self.count_map(), 4)[0].cmp(&Hand::values_with_count(other.count_map(), 4)[0]) | |
} | |
fn full_house_cmp(&self, other: &Hand) -> Ordering { | |
Hand::values_with_count(self.count_map(), 3)[0].cmp(&Hand::values_with_count(other.count_map(), 3)[0]) | |
} | |
fn high_card_cmp(&self, other: &Hand) -> Ordering { | |
let self_sorted_cards = self.sort_cards().to_vec(); | |
let other_sorted_cards = other.sort_cards().to_vec(); | |
Hand::cards_cmp(self_sorted_cards, other_sorted_cards) | |
} | |
fn flush_cmp(&self, other: &Hand) -> Ordering { | |
self.high_card_cmp(other) | |
} | |
fn three_king_cmp(&self, other: &Hand) -> Ordering { | |
Hand::values_with_count(self.count_map(), 3)[0].cmp(&Hand::values_with_count(other.count_map(), 3)[0]) | |
} | |
fn straight_cmp(&self, other: &Hand) -> Ordering { | |
self.sort_cards()[0].cmp(&(other.sort_cards()[0])) | |
} | |
fn two_pair_cmp(&self, other: &Hand) -> Ordering { | |
let mut self_value: Vec<CardValue> = Hand::values_with_count(self.count_map(), 2); | |
let mut other_value: Vec<CardValue> = Hand::values_with_count(other.count_map(), 2); | |
self_value.sort_by(|a, b| b.cmp(a)); | |
other_value.sort_by(|a, b| b.cmp(a)); | |
self_value.push(Hand::values_with_count(self.count_map(), 1)[0]); | |
other_value.push(Hand::values_with_count(other.count_map(), 1)[0]); | |
Hand::card_values_cmp(self_value, other_value) | |
} | |
fn pair_cmp(&self, other: &Hand) -> Ordering { | |
let mut self_values: Vec<CardValue> = Hand::values_with_count(self.count_map(), 1); | |
let mut other_values: Vec<CardValue> = Hand::values_with_count(other.count_map(), 1); | |
self_values.sort_by(|a, b| b.cmp(a)); | |
other_values.sort_by(|a, b| b.cmp(a)); | |
self_values.insert(0, Hand::values_with_count(self.count_map(), 2)[0]); | |
other_values.insert(0, Hand::values_with_count(other.count_map(), 2)[0]); | |
Hand::card_values_cmp(self_values, other_values) | |
} | |
fn level(&self) -> HandLevel { | |
if self.is_straight_flush() { | |
// println!("HandLevel is StraightFlush"); | |
HandLevel::StraightFlush | |
} else if self.is_four_king() { | |
// println!("HandLevel is FourKing"); | |
HandLevel::FourKing | |
} else if self.is_full_house() { | |
// println!("HandLevel is FullHouse"); | |
HandLevel::FullHouse | |
} else if self.is_flush() { | |
// println!("HandLevel is Flush"); | |
HandLevel::Flush | |
} else if self.is_straight() { | |
// println!("HandLevel is Straight"); | |
HandLevel::Straight | |
} else if self.is_three_king() { | |
// println!("HandLevel is ThreeKing"); | |
HandLevel::ThreeKing | |
} else if self.is_two_pair() { | |
// println!("HandLevel is TwoPair"); | |
HandLevel::TwoPair | |
} else if self.is_pair() { | |
// println!("HandLevel is Pair"); | |
HandLevel::Pair | |
} else { | |
// println!("HandLevel is HighCard"); | |
HandLevel::HighCard | |
} | |
} | |
fn level_cmp(&self, other: &Hand) -> Ordering { | |
if self.is_straight_flush() { | |
self.straight_flush_cmp(other) | |
} else if self.is_four_king() { | |
self.four_king_cmp(other) | |
} else if self.is_full_house() { | |
self.full_house_cmp(other) | |
} else if self.is_flush() { | |
self.flush_cmp(other) | |
} else if self.is_straight() { | |
self.straight_cmp(other) | |
} else if self.is_three_king() { | |
self.three_king_cmp(other) | |
} else if self.is_two_pair() { | |
self.two_pair_cmp(other) | |
} else if self.is_pair() { | |
self.pair_cmp(other) | |
} else { | |
self.high_card_cmp(other) | |
} | |
} | |
fn compare(&self, other: &Hand) -> Ordering { | |
let self_level = self.level(); | |
let other_level = other.level(); | |
if self_level > other_level { | |
Ordering::Greater | |
} else if self_level < other_level { | |
Ordering::Less | |
} else { | |
self.level_cmp(other) | |
} | |
} | |
} | |
pub fn vs(left: &str, right: &str) -> &'static str { | |
let left_hand = Hand::new(left.split(' ').collect()); | |
let right_hand = Hand::new(right.split(' ').collect()); | |
match left_hand.compare(&right_hand) { | |
Ordering::Greater => "left wins", | |
Ordering::Less => "right wins", | |
Ordering::Equal => "Tie", | |
} | |
} | |
} | |
fn assert_poker_compare(left: &str, right: &str, result: &str) { | |
if result == poker::vs(left, right) { | |
println!("\"{}\" vs \"{}\" result: {}", left, right, result); | |
} else { | |
panic!("\"{}\" vs \"{}\" expected: {}, actual: {}", left, right, result, poker::vs(left, right)); | |
} | |
} | |
fn main() { | |
assert_poker_compare("2H 3D 5S 9C KD", "2C 3H 4S 8C AH", "right wins"); | |
assert_poker_compare("2H 4S 4C 2D 4H", "2S 8S AS QS 3S", "left wins"); | |
assert_poker_compare("2H 3D 5S 9C KD", "2C 3H 4S 8C KH", "left wins"); | |
assert_poker_compare("2H 3D 5S 9C KD", "2D 3H 5C 9S KH", "Tie"); | |
// flush | |
assert_poker_compare("7C 9C TC 2C QC", "7C 9C TC 2C JC", "left wins"); | |
// full house | |
assert_poker_compare("7C 7D 7H 2D 7S", "6C 6D 6H 2D 6S", "left wins"); | |
assert_poker_compare("7C 7D 2H 2D 7S", "6C 6D 8H 8D 6S", "left wins"); | |
// pair | |
assert_poker_compare("7C 9D 8H 4D 8S", "7C TD 4H 4D 8S", "left wins"); | |
assert_poker_compare("7C 9D 8H 4D 8S", "7C TD 8H 4D 8S", "right wins"); | |
// flush straight | |
assert_poker_compare("7C 8C 9C TC JC", "7D 8D 9D TD 6D", "left wins"); | |
assert_poker_compare("2C 3C 4C 5C 6C", "AC 2C 3C 4C 5C", "left wins"); | |
assert_poker_compare("TC JC QC KC AC", "AC 2C 3C 4C 5C", "left wins"); | |
// straight | |
assert_poker_compare("7D TH 9H 8C JC", "7D TH 9H 8C 6C", "left wins"); | |
assert_poker_compare("2D 3D 4H 5C 6D", "AD 2D 3H 4C 5D", "left wins"); | |
assert_poker_compare("TD JD QH KC AD", "AD 2D 3H 4C 5D", "left wins"); | |
// tree king | |
assert_poker_compare("7C 7D 2H 4D 7S", "6C 6D 2H 4D 6S", "left wins"); | |
assert_poker_compare("7C 7D 2H 8D 7S", "7C 7D 2H 4D 7S", "Tie"); | |
assert_poker_compare("7C 7D 2H 5D 7S", "7C 7D 2H 4D 7S", "Tie"); | |
// two pair | |
assert_poker_compare("3C 3D 9H 2D 9S", "7C 7D 8H 4D 8S", "left wins"); | |
assert_poker_compare("3C 3D 8H 2D 8S", "2C 2D 8H 4D 8S", "left wins"); | |
assert_poker_compare("3C 3D 8H 5D 8S", "3C 3D 8H 4D 8S", "left wins"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment