Skip to content

Instantly share code, notes, and snippets.

@zlx
Created August 30, 2016 12:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zlx/1921b80a81b22a898a10b32369ecb936 to your computer and use it in GitHub Desktop.
Save zlx/1921b80a81b22a898a10b32369ecb936 to your computer and use it in GitHub Desktop.
Poker Hand in Rust
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