当前位置: 首页 > news >正文

Rust 练习册 :Poker与扑克牌游戏

扑克牌游戏是世界上最流行的纸牌游戏之一,具有复杂的规则和策略。在 Exercism 的 “poker” 练习中,我们需要实现一个函数来确定给定扑克牌手牌中的获胜者。这不仅能帮助我们掌握复杂规则的实现和比较算法,还能深入学习Rust中的枚举、模式匹配、排序和算法设计。

什么是扑克牌游戏?

扑克牌游戏有许多变种,其中最流行的是德州扑克。在这个练习中,我们需要实现标准扑克牌游戏中各种牌型的比较规则。扑克牌的牌型从低到高依次为:

  1. 高牌(High Card):没有形成任何特定组合的牌
  2. 一对(One Pair):两张相同点数的牌
  3. 两对(Two Pair):两个不同点数的对子
  4. 三条(Three of a Kind):三张相同点数的牌
  5. 顺子(Straight):五张连续点数的牌
  6. 同花(Flush):五张相同花色的牌
  7. 葫芦(Full House):三条加一对
  8. 四条(Four of a Kind):四张相同点数的牌
  9. 同花顺(Straight Flush):五张相同花色的连续牌
  10. 皇家同花顺(Royal Flush):10、J、Q、K、A的同花顺

让我们先看看练习提供的函数签名:

/// 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.
pub fn winning_hands<'a>(hands: &[&'a str]) -> Vec<&'a str> {unimplemented!("Out of {:?}, which hand wins?", hands)
}

我们需要实现 winning_hands 函数,从给定的扑克牌手牌中找出获胜的手牌。

设计分析

1. 核心要求

  1. 牌面解析:正确解析字符串表示的扑克牌
  2. 牌型识别:识别手牌的牌型并进行分类
  3. 牌型比较:比较不同手牌的牌型和点数
  4. 平局处理:正确处理多个获胜者的情况

2. 技术要点

  1. 枚举设计:设计合适的枚举表示牌型和牌面
  2. 模式匹配:使用模式匹配处理复杂的比较逻辑
  3. 排序算法:实现牌面的排序和比较
  4. 引用处理:正确处理字符串引用,返回原始引用

完整实现

1. 基础实现

use std::cmp::Ordering;
use std::collections::HashMap;/// 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.
pub fn winning_hands<'a>(hands: &[&'a str]) -> Vec<&'a str> {if hands.is_empty() {return vec![];}// 将字符串手牌转换为Hand结构体let mut parsed_hands: Vec<(Hand, &str)> = hands.iter().map(|&hand_str| (Hand::from_str(hand_str), hand_str)).collect();// 按照牌力排序(降序)parsed_hands.sort_by(|a, b| b.0.cmp(&a.0));// 找到所有获胜的手牌(与最高牌力相同的手牌)let winning_hand = &parsed_hands[0].0;parsed_hands.into_iter().take_while(|(hand, _)| hand == winning_hand).map(|(_, hand_str)| hand_str).collect()
}#[derive(Debug, Clone, PartialEq, Eq)]
struct Card {rank: Rank,suit: Suit,
}#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum Rank {Two = 2,Three = 3,Four = 4,Five = 5,Six = 6,Seven = 7,Eight = 8,Nine = 9,Ten = 10,Jack = 11,Queen = 12,King = 13,Ace = 14,
}#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum Suit {Clubs,Diamonds,Hearts,Spades,
}impl Card {fn from_str(s: &str) -> Card {let chars: Vec<char> = s.chars().collect();if chars.len() < 2 {panic!("Invalid card string: {}", s);}let rank = match chars[0] {'2' => Rank::Two,'3' => Rank::Three,'4' => Rank::Four,'5' => Rank::Five,'6' => Rank::Six,'7' => Rank::Seven,'8' => Rank::Eight,'9' => Rank::Nine,'1' if chars.len() >= 2 && chars[1] == '0' => Rank::Ten,'T' | '0' => Rank::Ten,'J' => Rank::Jack,'Q' => Rank::Queen,'K' => Rank::King,'A' => Rank::Ace,_ => panic!("Invalid rank in card string: {}", s),};let suit_char = if chars[0] == '1' && chars.len() >= 2 && chars[1] == '0' {// 处理"10"的情况if chars.len() >= 3 {chars[2]} else {panic!("Invalid card string: {}", s);}} else {chars[chars.len() - 1]};let suit = match suit_char {'C' => Suit::Clubs,'D' => Suit::Diamonds,'H' => Suit::Hearts,'S' => Suit::Spades,_ => panic!("Invalid suit in card string: {}", s),};Card { rank, suit }}
}#[derive(Debug, Clone)]
struct Hand {cards: Vec<Card>,hand_type: HandType,rank_counts: HashMap<Rank, usize>,sorted_ranks: Vec<Rank>,
}#[derive(Debug, Clone, PartialEq, Eq)]
enum HandType {HighCard,OnePair,TwoPair,ThreeOfAKind,Straight,Flush,FullHouse,FourOfAKind,StraightFlush,
}impl Hand {fn from_str(hand_str: &str) -> Hand {let card_strs: Vec<&str> = hand_str.split_whitespace().collect();let cards: Vec<Card> = card_strs.iter().map(|&s| Card::from_str(s)).collect();if cards.len() != 5 {panic!("Invalid hand: must contain exactly 5 cards");}let mut rank_counts = HashMap::new();for card in &cards {*rank_counts.entry(card.rank.clone()).or_insert(0) += 1;}let hand_type = Self::determine_hand_type(&cards, &rank_counts);let mut sorted_ranks: Vec<Rank> = cards.iter().map(|c| c.rank.clone()).collect();sorted_ranks.sort_by(|a, b| b.cmp(a)); // 降序排列Hand {cards,hand_type,rank_counts,sorted_ranks,}}fn determine_hand_type(cards: &[Card], rank_counts: &HashMap<Rank, usize>) -> HandType {let is_flush = cards.iter().all(|card| card.suit == cards[0].suit);let mut ranks: Vec<Rank> = cards.iter().map(|c| c.rank.clone()).collect();ranks.sort();let is_straight = Self::is_straight(&ranks);// 特殊情况:A,2,3,4,5 是顺子let is_low_straight = ranks == vec![Rank::Two, Rank::Three, Rank::Four, Rank::Five, Rank::Ace];if is_flush && (is_straight || is_low_straight) {return HandType::StraightFlush;}if is_flush {return HandType::Flush;}if is_straight || is_low_straight {return HandType::Straight;}let counts: Vec<usize> = rank_counts.values().cloned().collect();let mut count_counts = HashMap::new();for &count in &counts {*count_counts.entry(count).or_insert(0) += 1;}if count_counts.get(&4).unwrap_or(&0) == &1 {HandType::FourOfAKind} else if count_counts.get(&3).unwrap_or(&0) == &1 && count_counts.get(&2).unwrap_or(&0) == &1 {HandType::FullHouse} else if count_counts.get(&3).unwrap_or(&0) == &1 {HandType::ThreeOfAKind} else if count_counts.get(&2).unwrap_or(&0) == &2 {HandType::TwoPair} else if count_counts.get(&2).unwrap_or(&0) == &1 {HandType::OnePair} else {HandType::HighCard}}fn is_straight(ranks: &[Rank]) -> bool {if ranks.len() != 5 {return false;}for i in 0..4 {if (ranks[i + 1] as u8) != (ranks[i] as u8) + 1 {return false;}}true}
}impl PartialEq for Hand {fn eq(&self, other: &Self) -> bool {self.hand_type == other.hand_type && self.sorted_ranks == other.sorted_ranks}
}impl Eq for Hand {}impl PartialOrd for Hand {fn partial_cmp(&self, other: &Self) -> Option<Ordering> {Some(self.cmp(other))}
}impl Ord for Hand {fn cmp(&self, other: &Self) -> Ordering {// 首先比较牌型let hand_type_order = |hand_type: &HandType| -> u8 {match hand_type {HandType::HighCard => 0,HandType::OnePair => 1,HandType::TwoPair => 2,HandType::ThreeOfAKind => 3,HandType::Straight => 4,HandType::Flush => 5,HandType::FullHouse => 6,HandType::FourOfAKind => 7,HandType::StraightFlush => 8,}};let self_type_order = hand_type_order(&self.hand_type);let other_type_order = hand_type_order(&other.hand_type);match self_type_order.cmp(&other_type_order) {Ordering::Equal => {// 牌型相同时,比较具体的牌力self.compare_same_type(other)}other => other,}}
}impl Hand {fn compare_same_type(&self, other: &Self) -> Ordering {match self.hand_type {HandType::HighCard | HandType::Flush | HandType::Straight | HandType::StraightFlush => {// 比较最高牌,然后依次比较for (self_rank, other_rank) in self.sorted_ranks.iter().zip(other.sorted_ranks.iter()) {match self_rank.cmp(other_rank) {Ordering::Equal => continue,other => return other,}}Ordering::Equal}HandType::OnePair => {// 比较对子的点数let self_pair_rank = self.get_rank_by_count(2);let other_pair_rank = other.get_rank_by_count(2);match self_pair_rank.cmp(&other_pair_rank) {Ordering::Equal => {// 对子相同,比较剩余牌self.compare_kickers(&[self_pair_rank], &other, &[other_pair_rank])}other => other,}}HandType::TwoPair => {// 比较两对的点数(从高到低)let mut self_pairs: Vec<Rank> = self.get_ranks_by_count(2);let mut other_pairs: Vec<Rank> = other.get_ranks_by_count(2);self_pairs.sort_by(|a, b| b.cmp(a));other_pairs.sort_by(|a, b| b.cmp(a));for (self_rank, other_rank) in self_pairs.iter().zip(other_pairs.iter()) {match self_rank.cmp(other_rank) {Ordering::Equal => continue,other => return other,}}// 两对都相同,比较单牌self.compare_kickers(&self_pairs, &other, &other_pairs)}HandType::ThreeOfAKind => {// 比较三条的点数let self_three_rank = self.get_rank_by_count(3);let other_three_rank = other.get_rank_by_count(3);match self_three_rank.cmp(&other_three_rank) {Ordering::Equal => {// 三条相同,比较剩余牌self.compare_kickers(&[self_three_rank], &other, &[other_three_rank])}other => other,}}HandType::FullHouse => {// 比较三条的点数let self_three_rank = self.get_rank_by_count(3);let other_three_rank = other.get_rank_by_count(3);match self_three_rank.cmp(&other_three_rank) {Ordering::Equal => {// 三条相同,比较对子let self_pair_rank = self.get_rank_by_count(2);let other_pair_rank = other.get_rank_by_count(2);self_pair_rank.cmp(&other_pair_rank)}other => other,}}HandType::FourOfAKind => {// 比较四条的点数let self_four_rank = self.get_rank_by_count(4);let other_four_rank = other.get_rank_by_count(4);match self_four_rank.cmp(&other_four_rank) {Ordering::Equal => {// 四条相同,比较剩余牌self.compare_kickers(&[self_four_rank], &other, &[other_four_rank])}other => other,}}}}fn get_rank_by_count(&self, count: usize) -> Rank {self.rank_counts.iter().find(|(_, &c)| c == count).map(|(rank, _)| rank.clone()).unwrap()}fn get_ranks_by_count(&self, count: usize) -> Vec<Rank> {self.rank_counts.iter().filter(|(_, &c)| c == count).map(|(rank, _)| rank.clone()).collect()}fn compare_kickers(&self, exclude_ranks: &[Rank], other: &Self, other_exclude_ranks: &[Rank]) -> Ordering {let mut self_ranks: Vec<Rank> = self.sorted_ranks.clone();let mut other_ranks: Vec<Rank> = other.sorted_ranks.clone();// 移除需要排除的点数self_ranks.retain(|r| !exclude_ranks.contains(r));other_ranks.retain(|r| !other_exclude_ranks.contains(r));// 比较剩余牌for (self_rank, other_rank) in self_ranks.iter().zip(other_ranks.iter()) {match self_rank.cmp(other_rank) {Ordering::Equal => continue,other => return other,}}Ordering::Equal}
}

2. 优化实现

use std::cmp::Ordering;
use std::collections::HashMap;/// 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.
pub fn winning_hands<'a>(hands: &[&'a str]) -> Vec<&'a str> {if hands.is_empty() {return vec![];}// 将字符串手牌转换为Hand结构体let mut parsed_hands: Vec<(Hand, &str)> = hands.iter().map(|&hand_str| (Hand::from_str(hand_str), hand_str)).collect();// 按照牌力排序(降序)parsed_hands.sort_unstable_by(|a, b| b.0.cmp(&a.0));// 找到所有获胜的手牌(与最高牌力相同的手牌)let winning_hand = &parsed_hands[0].0;parsed_hands.into_iter().take_while(|(hand, _)| hand.cmp(winning_hand) == Ordering::Equal).map(|(_, hand_str)| hand_str).collect()
}#[derive(Debug, Clone, PartialEq, Eq)]
struct Card {rank: Rank,suit: Suit,
}#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum Rank {Two = 2,Three = 3,Four = 4,Five = 5,Six = 6,Seven = 7,Eight = 8,Nine = 9,Ten = 10,Jack = 11,Queen = 12,King = 13,Ace = 14,
}impl Rank {fn from_char(c: char) -> Option<Rank> {match c {'2' => Some(Rank::Two),'3' => Some(Rank::Three),'4' => Some(Rank::Four),'5' => Some(Rank::Five),'6' => Some(Rank::Six),'7' => Some(Rank::Seven),'8' => Some(Rank::Eight),'9' => Some(Rank::Nine),'T' | '0' => Some(Rank::Ten),'J' => Some(Rank::Jack),'Q' => Some(Rank::Queen),'K' => Some(Rank::King),'A' => Some(Rank::Ace),_ => None,}}
}#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum Suit {Clubs,Diamonds,Hearts,Spades,
}impl Suit {fn from_char(c: char) -> Option<Suit> {match c {'C' => Some(Suit::Clubs),'D' => Some(Suit::Diamonds),'H' => Some(Suit::Hearts),'S' => Some(Suit::Spades),_ => None,}}
}impl Card {fn from_str(s: &str) -> Card {let chars: Vec<char> = s.chars().collect();if chars.is_empty() {panic!("Invalid card string: {}", s);}let (rank_char, suit_char) = if chars.len() >= 3 && chars[0] == '1' && chars[1] == '0' {// 处理"10"的情况if chars.len() >= 3 {('0', chars[2])} else {panic!("Invalid card string: {}", s);}} else if chars.len() >= 2 {(chars[0], chars[chars.len() - 1])} else {panic!("Invalid card string: {}", s);};let rank = Rank::from_char(rank_char).unwrap_or_else(|| panic!("Invalid rank in card string: {}", s));let suit = Suit::from_char(suit_char).unwrap_or_else(|| panic!("Invalid suit in card string: {}", s));Card { rank, suit }}
}#[derive(Debug, Clone)]
struct Hand {cards: Vec<Card>,hand_type: HandType,rank_counts: HashMap<Rank, usize>,sorted_ranks: Vec<Rank>,
}#[derive(Debug, Clone, PartialEq, Eq)]
enum HandType {HighCard,OnePair,TwoPair,ThreeOfAKind,Straight,Flush,FullHouse,FourOfAKind,StraightFlush,
}impl HandType {fn rank(&self) -> u8 {match self {HandType::HighCard => 0,HandType::OnePair => 1,HandType::TwoPair => 2,HandType::ThreeOfAKind => 3,HandType::Straight => 4,HandType::Flush => 5,HandType::FullHouse => 6,HandType::FourOfAKind => 7,HandType::StraightFlush => 8,}}
}impl Hand {fn from_str(hand_str: &str) -> Hand {let card_strs: Vec<&str> = hand_str.split_whitespace().collect();if card_strs.len() != 5 {panic!("Invalid hand: must contain exactly 5 cards");}let cards: Vec<Card> = card_strs.iter().map(|&s| Card::from_str(s)).collect();let mut rank_counts = HashMap::new();for card in &cards {*rank_counts.entry(card.rank.clone()).or_insert(0) += 1;}let hand_type = Self::determine_hand_type(&cards, &rank_counts);let mut sorted_ranks: Vec<Rank> = cards.iter().map(|c| c.rank.clone()).collect();sorted_ranks.sort_by(|a, b| b.cmp(a)); // 降序排列Hand {cards,hand_type,rank_counts,sorted_ranks,}}fn determine_hand_type(cards: &[Card], rank_counts: &HashMap<Rank, usize>) -> HandType {let is_flush = cards.iter().all(|card| card.suit == cards[0].suit);let mut ranks: Vec<Rank> = cards.iter().map(|c| c.rank.clone()).collect();ranks.sort();let is_straight = Self::is_straight(&ranks);// 特殊情况:A,2,3,4,5 是顺子let is_low_straight = ranks == vec![Rank::Two, Rank::Three, Rank::Four, Rank::Five, Rank::Ace];if is_flush && (is_straight || is_low_straight) {return HandType::StraightFlush;}let counts: Vec<usize> = rank_counts.values().cloned().collect();let mut count_counts = HashMap::new();for &count in &counts {*count_counts.entry(count).or_insert(0) += 1;}if count_counts.get(&4).unwrap_or(&0) == &1 {HandType::FourOfAKind} else if count_counts.get(&3).unwrap_or(&0) == &1 && count_counts.get(&2).unwrap_or(&0) == &1 {HandType::FullHouse} else if is_flush {HandType::Flush} else if is_straight || is_low_straight {HandType::Straight} else if count_counts.get(&3).unwrap_or(&0) == &1 {HandType::ThreeOfAKind} else if count_counts.get(&2).unwrap_or(&0) == &2 {HandType::TwoPair} else if count_counts.get(&2).unwrap_or(&0) == &1 {HandType::OnePair} else {HandType::HighCard}}fn is_straight(ranks: &[Rank]) -> bool {if ranks.len() != 5 {return false;}for i in 0..4 {if (ranks[i + 1] as u8) != (ranks[i] as u8) + 1 {return false;}}true}
}impl PartialEq for Hand {fn eq(&self, other: &Self) -> bool {self.cmp(other) == Ordering::Equal}
}impl Eq for Hand {}impl PartialOrd for Hand {fn partial_cmp(&self, other: &Self) -> Option<Ordering> {Some(self.cmp(other))}
}impl Ord for Hand {fn cmp(&self, other: &Self) -> Ordering {// 首先比较牌型match self.hand_type.rank().cmp(&other.hand_type.rank()) {Ordering::Equal => {// 牌型相同时,比较具体的牌力self.compare_same_type(other)}other => other,}}
}impl Hand {fn compare_same_type(&self, other: &Self) -> Ordering {match self.hand_type {HandType::HighCard | HandType::Flush | HandType::Straight | HandType::StraightFlush => {// 比较最高牌,然后依次比较for (self_rank, other_rank) in self.sorted_ranks.iter().zip(other.sorted_ranks.iter()) {match self_rank.cmp(other_rank) {Ordering::Equal => continue,other => return other,}}Ordering::Equal}HandType::OnePair => {// 比较对子的点数let self_pair_rank = self.get_rank_by_count(2);let other_pair_rank = other.get_rank_by_count(2);match self_pair_rank.cmp(&other_pair_rank) {Ordering::Equal => {// 对子相同,比较剩余牌self.compare_kickers(&[self_pair_rank], other, &[other_pair_rank])}other => other,}}HandType::TwoPair => {// 比较两对的点数(从高到低)let mut self_pairs: Vec<Rank> = self.get_ranks_by_count(2);let mut other_pairs: Vec<Rank> = other.get_ranks_by_count(2);self_pairs.sort_by(|a, b| b.cmp(a));other_pairs.sort_by(|a, b| b.cmp(a));for (self_rank, other_rank) in self_pairs.iter().zip(other_pairs.iter()) {match self_rank.cmp(other_rank) {Ordering::Equal => continue,other => return other,}}// 两对都相同,比较单牌self.compare_kickers(&self_pairs, other, &other_pairs)}HandType::ThreeOfAKind => {// 比较三条的点数let self_three_rank = self.get_rank_by_count(3);let other_three_rank = other.get_rank_by_count(3);match self_three_rank.cmp(&other_three_rank) {Ordering::Equal => {// 三条相同,比较剩余牌self.compare_kickers(&[self_three_rank], other, &[other_three_rank])}other => other,}}HandType::FullHouse => {// 比较三条的点数let self_three_rank = self.get_rank_by_count(3);let other_three_rank = other.get_rank_by_count(3);match self_three_rank.cmp(&other_three_rank) {Ordering::Equal => {// 三条相同,比较对子let self_pair_rank = self.get_rank_by_count(2);let other_pair_rank = other.get_rank_by_count(2);self_pair_rank.cmp(&other_pair_rank)}other => other,}}HandType::FourOfAKind => {// 比较四条的点数let self_four_rank = self.get_rank_by_count(4);let other_four_rank = other.get_rank_by_count(4);match self_four_rank.cmp(&other_four_rank) {Ordering::Equal => {// 四条相同,比较剩余牌self.compare_kickers(&[self_four_rank], other, &[other_four_rank])}other => other,}}}}fn get_rank_by_count(&self, count: usize) -> Rank {self.rank_counts.iter().find(|(_, &c)| c == count).map(|(rank, _)| rank.clone()).unwrap()}fn get_ranks_by_count(&self, count: usize) -> Vec<Rank> {self.rank_counts.iter().filter(|(_, &c)| c == count).map(|(rank, _)| rank.clone()).collect()}fn compare_kickers(&self, exclude_ranks: &[Rank], other: &Self, other_exclude_ranks: &[Rank]) -> Ordering {let mut self_ranks: Vec<Rank> = self.sorted_ranks.clone();let mut other_ranks: Vec<Rank> = other.sorted_ranks.clone();// 移除需要排除的点数self_ranks.retain(|r| !exclude_ranks.contains(r));other_ranks.retain(|r| !other_exclude_ranks.contains(r));// 比较剩余牌for (self_rank, other_rank) in self_ranks.iter().zip(other_ranks.iter()) {match self_rank.cmp(other_rank) {Ordering::Equal => continue,other => return other,}}Ordering::Equal}
}

测试用例分析

通过查看测试用例,我们可以更好地理解需求:

#[test]
fn test_single_hand_always_wins() {test(&["4S 5S 7H 8D JC"], &["4S 5S 7H 8D JC"])
}

单个手牌总是获胜。

#[test]
fn test_duplicate_hands_always_tie() {let input = &["3S 4S 5D 6H JH", "3S 4S 5D 6H JH", "3S 4S 5D 6H JH"];assert_eq!(&winning_hands(input), input)
}

重复的手牌总是平局。

#[test]
fn test_highest_card_of_all_hands_wins() {test(&["4D 5S 6S 8D 3C", "2S 4C 7S 9H 10H", "3S 4S 5D 6H JH"],&["3S 4S 5D 6H JH"],)
}

所有手牌中的最高牌获胜(在这种情况下是J)。

#[test]
fn test_a_tie_has_multiple_winners() {test(&["4D 5S 6S 8D 3C","2S 4C 7S 9H 10H","3S 4S 5D 6H JH","3H 4H 5C 6C JD",],&["3S 4S 5D 6H JH", "3H 4H 5C 6C JD"],)
}

平局时有多个获胜者。

#[test]
fn test_high_card_can_be_low_card_in_an_otherwise_tie() {// multiple hands with the same high cards, tie compares next highest ranked,// down to last cardtest(&["3S 5H 6S 8D 7H", "2S 5D 6D 8C 7S"], &["3S 5H 6S 8D 7H"])
}

当最高牌相同时,比较次高牌,以此类推。

#[test]
fn test_one_pair_beats_high_card() {test(&["4S 5H 6C 8D KH", "2S 4H 6S 4D JH"], &["2S 4H 6S 4D JH"])
}

一对牌胜过高牌。

#[test]
fn test_highest_pair_wins() {test(&["4S 2H 6S 2D JH", "2S 4H 6C 4D JD"], &["2S 4H 6C 4D JD"])
}

比较对子的点数,高对子获胜。

#[test]
fn test_two_pairs_beats_one_pair() {test(&["2S 8H 6S 8D JH", "4S 5H 4C 8C 5C"], &["4S 5H 4C 8C 5C"])
}

两对胜过一对。

#[test]
fn test_two_pair_ranks() {// both hands have two pairs, highest ranked pair winstest(&["2S 8H 2D 8D 3H", "4S 5H 4C 8S 5D"], &["2S 8H 2D 8D 3H"])
}

两对牌比较时,首先比较最高对子。

#[test]
fn test_two_pairs_second_pair_cascade() {// both hands have two pairs, with the same highest ranked pair,// tie goes to low pairtest(&["2S QS 2C QD JH", "JD QH JS 8D QC"], &["JD QH JS 8D QC"])
}

最高对子相同时,比较次高对子。

#[test]
fn test_two_pairs_last_card_cascade() {// both hands have two identically ranked pairs,// tie goes to remaining card (kicker)test(&["JD QH JS 8D QC", "JS QS JC 2D QD"], &["JD QH JS 8D QC"])
}

两对完全相同时,比较剩余的牌(kicker)。

#[test]
fn test_three_of_a_kind_beats_two_pair() {test(&["2S 8H 2H 8D JH", "4S 5H 4C 8S 4H"], &["4S 5H 4C 8S 4H"])
}

三条胜过两对。

#[test]
fn test_three_of_a_kind_ranks() {//both hands have three of a kind, tie goes to highest ranked triplettest(&["2S 2H 2C 8D JH", "4S AH AS 8C AD"], &["4S AH AS 8C AD"])
}

三条比较时,点数高的三条获胜。

#[test]
fn test_three_of_a_kind_cascade_ranks() {// with multiple decks, two players can have same three of a kind,// ties go to highest remaining cardstest(&["4S AH AS 7C AD", "4S AH AS 8C AD"], &["4S AH AS 8C AD"])
}

三条相同时,比较剩余牌的点数。

#[test]
fn test_straight_beats_three_of_a_kind() {test(&["4S 5H 4C 8D 4H", "3S 4D 2S 6D 5C"], &["3S 4D 2S 6D 5C"])
}

顺子胜过三条。

#[test]
fn test_aces_can_end_a_straight_high() {// aces can end a straight (10 J Q K A)test(&["4S 5H 4C 8D 4H", "10D JH QS KD AC"], &["10D JH QS KD AC"])
}

A可以作为顺子的最高牌(10 J Q K A)。

#[test]
fn test_aces_can_end_a_straight_low() {// aces can start a straight (A 2 3 4 5)test(&["4S 5H 4C 8D 4H", "4D AH 3S 2D 5C"], &["4D AH 3S 2D 5C"])
}

A可以作为顺子的最低牌(A 2 3 4 5)。

#[test]
fn test_straight_cascade() {// both hands with a straight, tie goes to highest ranked cardtest(&["4S 6C 7S 8D 5H", "5S 7H 8S 9D 6H"], &["5S 7H 8S 9D 6H"])
}

顺子比较时,点数高的顺子获胜。

#[test]
fn test_straight_scoring() {// even though an ace is usually high, a 5-high straight is the lowest-scoring straighttest(&["2H 3C 4D 5D 6H", "4S AH 3S 2D 5H"], &["2H 3C 4D 5D 6H"])
}

尽管A通常作为高牌,但5-high顺子(A 2 3 4 5)是最低的顺子。

#[test]
fn test_flush_beats_a_straight() {test(&["4C 6H 7D 8D 5H", "2S 4S 5S 6S 7S"], &["2S 4S 5S 6S 7S"])
}

同花胜过顺子。

#[test]
fn test_flush_cascade() {// both hands have a flush, tie goes to high card, down to the last one if necessarytest(&["4H 7H 8H 9H 6H", "2S 4S 5S 6S 7S"], &["4H 7H 8H 9H 6H"])
}

同花比较时,比较最高牌,然后依次比较。

#[test]
fn test_full_house_beats_a_flush() {test(&["3H 6H 7H 8H 5H", "4S 5C 4C 5D 4H"], &["4S 5C 4C 5D 4H"])
}

葫芦胜过同花。

#[test]
fn test_full_house_ranks() {// both hands have a full house, tie goes to highest-ranked triplettest(&["4H 4S 4D 9S 9D", "5H 5S 5D 8S 8D"], &["5H 5S 5D 8S 8D"])
}

葫芦比较时,三条点数高的获胜。

#[test]
fn test_full_house_cascade() {// with multiple decks, both hands have a full house with the same triplet, tie goes to the pairtest(&["5H 5S 5D 9S 9D", "5H 5S 5D 8S 8D"], &["5H 5S 5D 9S 9D"])
}

三条相同时,比较对子点数。

#[test]
fn test_four_of_a_kind_beats_full_house() {test(&["4S 5H 4D 5D 4H", "3S 3H 2S 3D 3C"], &["3S 3H 2S 3D 3C"])
}

四条胜过葫芦。

#[test]
fn test_four_of_a_kind_ranks() {// both hands have four of a kind, tie goes to high quadtest(&["2S 2H 2C 8D 2D", "4S 5H 5S 5D 5C"], &["4S 5H 5S 5D 5C"])
}

四条比较时,点数高的四条获胜。

#[test]
fn test_four_of_a_kind_cascade() {// with multiple decks, both hands with identical four of a kind, tie determined by kickertest(&["3S 3H 2S 3D 3C", "3S 3H 4S 3D 3C"], &["3S 3H 4S 3D 3C"])
}

四条相同时,比较剩余牌(kicker)。

#[test]
fn test_straight_flush_beats_four_of_a_kind() {test(&["4S 5H 5S 5D 5C", "7S 8S 9S 6S 10S"], &["7S 8S 9S 6S 10S"])
}

同花顺胜过四条。

#[test]
fn test_straight_flush_ranks() {// both hands have straight flush, tie goes to highest-ranked cardtest(&["4H 6H 7H 8H 5H", "5S 7S 8S 9S 6S"], &["5S 7S 8S 9S 6S"])
}

同花顺比较时,点数高的获胜。

性能优化版本

考虑性能的优化实现:

use std::cmp::Ordering;
use std::collections::HashMap;/// 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.
pub fn winning_hands<'a>(hands: &[&'a str]) -> Vec<&'a str> {if hands.is_empty() {return vec![];}// 预分配结果向量以避免多次重新分配let mut parsed_hands: Vec<(Hand, &str)> = Vec::with_capacity(hands.len());// 将字符串手牌转换为Hand结构体for &hand_str in hands {parsed_hands.push((Hand::from_str(hand_str), hand_str));}// 按照牌力排序(降序)parsed_hands.sort_unstable_by(|a, b| b.0.cmp(&a.0));// 找到所有获胜的手牌(与最高牌力相同的手牌)let winning_hand = &parsed_hands[0].0;let mut winners = Vec::with_capacity(parsed_hands.len());for (hand, hand_str) in parsed_hands {if hand.cmp(winning_hand) == Ordering::Equal {winners.push(hand_str);} else {break; // 由于已排序,后续手牌都不可能是获胜者}}winners
}// 使用数组代替HashMap以提高性能
#[derive(Debug, Clone, PartialEq, Eq)]
struct Card {rank: Rank,suit: Suit,
}#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
enum Rank {Two = 2,Three = 3,Four = 4,Five = 5,Six = 6,Seven = 7,Eight = 8,Nine = 9,Ten = 10,Jack = 11,Queen = 12,King = 13,Ace = 14,
}impl Rank {fn from_char(c: char) -> Option<Rank> {match c {'2' => Some(Rank::Two),'3' => Some(Rank::Three),'4' => Some(Rank::Four),'5' => Some(Rank::Five),'6' => Some(Rank::Six),'7' => Some(Rank::Seven),'8' => Some(Rank::Eight),'9' => Some(Rank::Nine),'T' | '0' => Some(Rank::Ten),'J' => Some(Rank::Jack),'Q' => Some(Rank::Queen),'K' => Some(Rank::King),'A' => Some(Rank::Ace),_ => None,}}
}#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
enum Suit {Clubs = 0,Diamonds = 1,Hearts = 2,Spades = 3,
}impl Suit {fn from_char(c: char) -> Option<Suit> {match c {'C' => Some(Suit::Clubs),'D' => Some(Suit::Diamonds),'H' => Some(Suit::Hearts),'S' => Some(Suit::Spades),_ => None,}}
}impl Card {fn from_str(s: &str) -> Card {let chars: Vec<char> = s.chars().collect();if chars.is_empty() {panic!("Invalid card string: {}", s);}let (rank_char, suit_char) = if chars.len() >= 3 && chars[0] == '1' && chars[1] == '0' {// 处理"10"的情况if chars.len() >= 3 {('0', chars[2])} else {panic!("Invalid card string: {}", s);}} else if chars.len() >= 2 {(chars[0], chars[chars.len() - 1])} else {panic!("Invalid card string: {}", s);};let rank = Rank::from_char(rank_char).unwrap_or_else(|| panic!("Invalid rank in card string: {}", s));let suit = Suit::from_char(suit_char).unwrap_or_else(|| panic!("Invalid suit in card string: {}", s));Card { rank, suit }}
}#[derive(Debug, Clone)]
struct Hand {cards: [Card; 5],hand_type: HandType,rank_counts: [u8; 15], // 索引0未使用,索引1未使用,索引2-14对应Rank::Two-Rank::Acesorted_ranks: [Rank; 5],
}#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
enum HandType {HighCard = 0,OnePair = 1,TwoPair = 2,ThreeOfAKind = 3,Straight = 4,Flush = 5,FullHouse = 6,FourOfAKind = 7,StraightFlush = 8,
}impl HandType {fn rank(&self) -> u8 {*self as u8}
}impl Hand {fn from_str(hand_str: &str) -> Hand {let card_strs: Vec<&str> = hand_str.split_whitespace().collect();if card_strs.len() != 5 {panic!("Invalid hand: must contain exactly 5 cards");}let cards: [Card; 5] = [Card::from_str(card_strs[0]),Card::from_str(card_strs[1]),Card::from_str(card_strs[2]),Card::from_str(card_strs[3]),Card::from_str(card_strs[4]),];let mut rank_counts = [0u8; 15];for card in &cards {rank_counts[card.rank as usize] += 1;}let hand_type = Self::determine_hand_type(&cards, &rank_counts);let mut sorted_ranks: [Rank; 5] = [cards[0].rank,cards[1].rank,cards[2].rank,cards[3].rank,cards[4].rank,];sorted_ranks.sort_by(|a, b| b.cmp(a)); // 降序排列Hand {cards,hand_type,rank_counts,sorted_ranks,}}fn determine_hand_type(cards: &[Card; 5], rank_counts: &[u8; 15]) -> HandType {let is_flush = cards.iter().all(|card| card.suit == cards[0].suit);let mut ranks: [Rank; 5] = [cards[0].rank,cards[1].rank,cards[2].rank,cards[3].rank,cards[4].rank,];ranks.sort();let is_straight = Self::is_straight(&ranks);// 特殊情况:A,2,3,4,5 是顺子let is_low_straight = ranks == [Rank::Two, Rank::Three, Rank::Four, Rank::Five, Rank::Ace];if is_flush && (is_straight || is_low_straight) {return HandType::StraightFlush;}// 统计不同点数的牌的数量let mut count_counts = [0u8; 5]; // 索引表示相同点数的牌的数量,值表示有几种这样的牌for &count in rank_counts.iter() {if count > 0 {count_counts[count as usize] += 1;}}if count_counts[4] == 1 {HandType::FourOfAKind} else if count_counts[3] == 1 && count_counts[2] == 1 {HandType::FullHouse} else if is_flush {HandType::Flush} else if is_straight || is_low_straight {HandType::Straight} else if count_counts[3] == 1 {HandType::ThreeOfAKind} else if count_counts[2] == 2 {HandType::TwoPair} else if count_counts[2] == 1 {HandType::OnePair} else {HandType::HighCard}}fn is_straight(ranks: &[Rank; 5]) -> bool {for i in 0..4 {if (ranks[i + 1] as u8) != (ranks[i] as u8) + 1 {return false;}}true}
}impl PartialEq for Hand {fn eq(&self, other: &Self) -> bool {self.cmp(other) == Ordering::Equal}
}impl Eq for Hand {}impl PartialOrd for Hand {fn partial_cmp(&self, other: &Self) -> Option<Ordering> {Some(self.cmp(other))}
}impl Ord for Hand {fn cmp(&self, other: &Self) -> Ordering {// 首先比较牌型match self.hand_type.rank().cmp(&other.hand_type.rank()) {Ordering::Equal => {// 牌型相同时,比较具体的牌力self.compare_same_type(other)}other => other,}}
}impl Hand {fn compare_same_type(&self, other: &Self) -> Ordering {match self.hand_type {HandType::HighCard | HandType::Flush | HandType::Straight | HandType::StraightFlush => {// 比较最高牌,然后依次比较for i in 0..5 {match self.sorted_ranks[i].cmp(&other.sorted_ranks[i]) {Ordering::Equal => continue,other => return other,}}Ordering::Equal}HandType::OnePair => {// 比较对子的点数let self_pair_rank = self.get_rank_by_count(2);let other_pair_rank = other.get_rank_by_count(2);match self_pair_rank.cmp(&other_pair_rank) {Ordering::Equal => {// 对子相同,比较剩余牌self.compare_kickers(&[self_pair_rank], other, &[other_pair_rank])}other => other,}}HandType::TwoPair => {// 比较两对的点数(从高到低)let mut self_pairs: Vec<Rank> = self.get_ranks_by_count(2);let mut other_pairs: Vec<Rank> = other.get_ranks_by_count(2);self_pairs.sort_by(|a, b| b.cmp(a));other_pairs.sort_by(|a, b| b.cmp(a));for i in 0..2 {match self_pairs[i].cmp(&other_pairs[i]) {Ordering::Equal => continue,other => return other,}}// 两对都相同,比较单牌self.compare_kickers(&self_pairs, other, &other_pairs)}HandType::ThreeOfAKind => {// 比较三条的点数let self_three_rank = self.get_rank_by_count(3);let other_three_rank = other.get_rank_by_count(3);match self_three_rank.cmp(&other_three_rank) {Ordering::Equal => {// 三条相同,比较剩余牌self.compare_kickers(&[self_three_rank], other, &[other_three_rank])}other => other,}}HandType::FullHouse => {// 比较三条的点数let self_three_rank = self.get_rank_by_count(3);let other_three_rank = other.get_rank_by_count(3);match self_three_rank.cmp(&other_three_rank) {Ordering::Equal => {// 三条相同,比较对子let self_pair_rank = self.get_rank_by_count(2);let other_pair_rank = other.get_rank_by_count(2);self_pair_rank.cmp(&other_pair_rank)}other => other,}}HandType::FourOfAKind => {// 比较四条的点数let self_four_rank = self.get_rank_by_count(4);let other_four_rank = other.get_rank_by_count(4);match self_four_rank.cmp(&other_four_rank) {Ordering::Equal => {// 四条相同,比较剩余牌self.compare_kickers(&[self_four_rank], other, &[other_four_rank])}other => other,}}}}fn get_rank_by_count(&self, count: u8) -> Rank {for (rank_index, &rank_count) in self.rank_counts.iter().enumerate() {if rank_count == count {// 安全地将索引转换为Rankreturn unsafe { std::mem::transmute(rank_index as u8) };}}panic!("No rank with count {}", count);}fn get_ranks_by_count(&self, count: u8) -> Vec<Rank> {let mut ranks = Vec::new();for (rank_index, &rank_count) in self.rank_counts.iter().enumerate() {if rank_count == count {// 安全地将索引转换为Rankranks.push(unsafe { std::mem::transmute(rank_index as u8) });}}ranks}fn compare_kickers(&self, exclude_ranks: &[Rank], other: &Self, other_exclude_ranks: &[Rank]) -> Ordering {let mut self_ranks = self.sorted_ranks;let mut other_ranks = other.sorted_ranks;// 移除需要排除的点数self_ranks.retain(|r| !exclude_ranks.contains(r));other_ranks.retain(|r| !other_exclude_ranks.contains(r));// 比较剩余牌for i in 0..self_ranks.len() {match self_ranks[i].cmp(&other_ranks[i]) {Ordering::Equal => continue,other => return other,}}Ordering::Equal}
}

错误处理和边界情况

考虑更多边界情况的实现:

use std::cmp::Ordering;
use std::collections::HashMap;/// 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.
pub fn winning_hands<'a>(hands: &[&'a str]) -> Vec<&'a str> {// 处理空输入if hands.is_empty() {return vec![];}// 将字符串手牌转换为Hand结构体let mut parsed_hands: Vec<Result<(Hand, &str), String>> = hands.iter().map(|&hand_str| {Hand::from_str(hand_str).map(|hand| (hand, hand_str)).map_err(|e| format!("Invalid hand '{}': {}", hand_str, e))}).collect();// 检查是否有无效手牌if let Some(Err(error)) = parsed_hands.iter().find(|result| result.is_err()) {panic!("{}", error);}// 解包有效手牌let mut valid_hands: Vec<(Hand, &str)> = parsed_hands.into_iter().map(|result| result.unwrap()).collect();// 按照牌力排序(降序)valid_hands.sort_unstable_by(|a, b| b.0.cmp(&a.0));// 找到所有获胜的手牌(与最高牌力相同的手牌)let winning_hand = &valid_hands[0].0;valid_hands.into_iter().take_while(|(hand, _)| hand.cmp(winning_hand) == Ordering::Equal).map(|(_, hand_str)| hand_str).collect()
}#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Card {rank: Rank,suit: Suit,
}#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Rank {Two = 2,Three = 3,Four = 4,Five = 5,Six = 6,Seven = 7,Eight = 8,Nine = 9,Ten = 10,Jack = 11,Queen = 12,King = 13,Ace = 14,
}impl Rank {fn from_char(c: char) -> Result<Rank, String> {match c {'2' => Ok(Rank::Two),'3' => Ok(Rank::Three),'4' => Ok(Rank::Four),'5' => Ok(Rank::Five),'6' => Ok(Rank::Six),'7' => Ok(Rank::Seven),'8' => Ok(Rank::Eight),'9' => Ok(Rank::Nine),'T' | '0' => Ok(Rank::Ten),'J' => Ok(Rank::Jack),'Q' => Ok(Rank::Queen),'K' => Ok(Rank::King),'A' => Ok(Rank::Ace),_ => Err(format!("Invalid rank character: {}", c)),}}
}#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Suit {Clubs,Diamonds,Hearts,Spades,
}impl Suit {fn from_char(c: char) -> Result<Suit, String> {match c {'C' => Ok(Suit::Clubs),'D' => Ok(Suit::Diamonds),'H' => Ok(Suit::Hearts),'S' => Ok(Suit::Spades),_ => Err(format!("Invalid suit character: {}", c)),}}
}impl Card {fn from_str(s: &str) -> Result<Card, String> {let chars: Vec<char> = s.chars().collect();if chars.is_empty() {return Err("Empty card string".to_string());}let (rank_char, suit_char) = if chars.len() >= 3 && chars[0] == '1' && chars[1] == '0' {// 处理"10"的情况if chars.len() >= 3 {('0', chars[2])} else {return Err("Invalid card string for 10".to_string());}} else if chars.len() >= 2 {(chars[0], chars[chars.len() - 1])} else {return Err("Card string too short".to_string());};let rank = Rank::from_char(rank_char)?;let suit = Suit::from_char(suit_char)?;Ok(Card { rank, suit })}
}#[derive(Debug, Clone)]
pub struct Hand {cards: Vec<Card>,hand_type: HandType,rank_counts: HashMap<Rank, usize>,sorted_ranks: Vec<Rank>,
}#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HandType {HighCard,OnePair,TwoPair,ThreeOfAKind,Straight,Flush,FullHouse,FourOfAKind,StraightFlush,
}impl HandType {fn rank(&self) -> u8 {match self {HandType::HighCard => 0,HandType::OnePair => 1,HandType::TwoPair => 2,HandType::ThreeOfAKind => 3,HandType::Straight => 4,HandType::Flush => 5,HandType::FullHouse => 6,HandType::FourOfAKind => 7,HandType::StraightFlush => 8,}}
}#[derive(Debug)]
pub enum PokerError {InvalidHand(String),InvalidCard(String),
}impl std::fmt::Display for PokerError {fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {match self {PokerError::InvalidHand(msg) => write!(f, "Invalid hand: {}", msg),PokerError::InvalidCard(msg) => write!(f, "Invalid card: {}", msg),}}
}impl std::error::Error for PokerError {}impl Hand {fn from_str(hand_str: &str) -> Result<Hand, PokerError> {let card_strs: Vec<&str> = hand_str.split_whitespace().collect();if card_strs.len() != 5 {return Err(PokerError::InvalidHand("must contain exactly 5 cards".to_string(),));}let mut cards = Vec::with_capacity(5);for card_str in card_strs {cards.push(Card::from_str(card_str).map_err(PokerError::InvalidCard)?);}let mut rank_counts = HashMap::new();for card in &cards {*rank_counts.entry(card.rank.clone()).or_insert(0) += 1;}let hand_type = Self::determine_hand_type(&cards, &rank_counts);let mut sorted_ranks: Vec<Rank> = cards.iter().map(|c| c.rank.clone()).collect();sorted_ranks.sort_by(|a, b| b.cmp(a)); // 降序排列Ok(Hand {cards,hand_type,rank_counts,sorted_ranks,})}fn determine_hand_type(cards: &[Card], rank_counts: &HashMap<Rank, usize>) -> HandType {let is_flush = cards.iter().all(|card| card.suit == cards[0].suit);let mut ranks: Vec<Rank> = cards.iter().map(|c| c.rank.clone()).collect();ranks.sort();let is_straight = Self::is_straight(&ranks);// 特殊情况:A,2,3,4,5 是顺子let is_low_straight = ranks == vec![Rank::Two, Rank::Three, Rank::Four, Rank::Five, Rank::Ace];if is_flush && (is_straight || is_low_straight) {return HandType::StraightFlush;}let counts: Vec<usize> = rank_counts.values().cloned().collect();let mut count_counts = HashMap::new();for &count in &counts {*count_counts.entry(count).or_insert(0) += 1;}if count_counts.get(&4).unwrap_or(&0) == &1 {HandType::FourOfAKind} else if count_counts.get(&3).unwrap_or(&0) == &1 && count_counts.get(&2).unwrap_or(&0) == &1 {HandType::FullHouse} else if is_flush {HandType::Flush} else if is_straight || is_low_straight {HandType::Straight} else if count_counts.get(&3).unwrap_or(&0) == &1 {HandType::ThreeOfAKind} else if count_counts.get(&2).unwrap_or(&0) == &2 {HandType::TwoPair} else if count_counts.get(&2).unwrap_or(&0) == &1 {HandType::OnePair} else {HandType::HighCard}}fn is_straight(ranks: &[Rank]) -> bool {if ranks.len() != 5 {return false;}for i in 0..4 {if (ranks[i + 1] as u8) != (ranks[i] as u8) + 1 {return false;}}true}
}impl PartialEq for Hand {fn eq(&self, other: &Self) -> bool {self.cmp(other) == Ordering::Equal}
}impl Eq for Hand {}impl PartialOrd for Hand {fn partial_cmp(&self, other: &Self) -> Option<Ordering> {Some(self.cmp(other))}
}impl Ord for Hand {fn cmp(&self, other: &Self) -> Ordering {// 首先比较牌型match self.hand_type.rank().cmp(&other.hand_type.rank()) {Ordering::Equal => {// 牌型相同时,比较具体的牌力self.compare_same_type(other)}other => other,}}
}impl Hand {fn compare_same_type(&self, other: &Self) -> Ordering {match self.hand_type {HandType::HighCard | HandType::Flush | HandType::Straight | HandType::StraightFlush => {// 比较最高牌,然后依次比较for (self_rank, other_rank) in self.sorted_ranks.iter().zip(other.sorted_ranks.iter()) {match self_rank.cmp(other_rank) {Ordering::Equal => continue,other => return other,}}Ordering::Equal}HandType::OnePair => {// 比较对子的点数let self_pair_rank = self.get_rank_by_count(2);let other_pair_rank = other.get_rank_by_count(2);match self_pair_rank.cmp(&other_pair_rank) {Ordering::Equal => {// 对子相同,比较剩余牌self.compare_kickers(&[self_pair_rank], other, &[other_pair_rank])}other => other,}}HandType::TwoPair => {// 比较两对的点数(从高到低)let mut self_pairs: Vec<Rank> = self.get_ranks_by_count(2);let mut other_pairs: Vec<Rank> = other.get_ranks_by_count(2);self_pairs.sort_by(|a, b| b.cmp(a));other_pairs.sort_by(|a, b| b.cmp(a));for (self_rank, other_rank) in self_pairs.iter().zip(other_pairs.iter()) {match self_rank.cmp(other_rank) {Ordering::Equal => continue,other => return other,}}// 两对都相同,比较单牌self.compare_kickers(&self_pairs, other, &other_pairs)}HandType::ThreeOfAKind => {// 比较三条的点数let self_three_rank = self.get_rank_by_count(3);let other_three_rank = other.get_rank_by_count(3);match self_three_rank.cmp(&other_three_rank) {Ordering::Equal => {// 三条相同,比较剩余牌self.compare_kickers(&[self_three_rank], other, &[other_three_rank])}other => other,}}HandType::FullHouse => {// 比较三条的点数let self_three_rank = self.get_rank_by_count(3);let other_three_rank = other.get_rank_by_count(3);match self_three_rank.cmp(&other_three_rank) {Ordering::Equal => {// 三条相同,比较对子let self_pair_rank = self.get_rank_by_count(2);let other_pair_rank = other.get_rank_by_count(2);self_pair_rank.cmp(&other_pair_rank)}other => other,}}HandType::FourOfAKind => {// 比较四条的点数let self_four_rank = self.get_rank_by_count(4);let other_four_rank = other.get_rank_by_count(4);match self_four_rank.cmp(&other_four_rank) {Ordering::Equal => {// 四条相同,比较剩余牌self.compare_kickers(&[self_four_rank], other, &[other_four_rank])}other => other,}}}}fn get_rank_by_count(&self, count: usize) -> Rank {self.rank_counts.iter().find(|(_, &c)| c == count).map(|(rank, _)| rank.clone()).unwrap()}fn get_ranks_by_count(&self, count: usize) -> Vec<Rank> {self.rank_counts.iter().filter(|(_, &c)| c == count).map(|(rank, _)| rank.clone()).collect()}fn compare_kickers(&self, exclude_ranks: &[Rank], other: &Self, other_exclude_ranks: &[Rank]) -> Ordering {let mut self_ranks: Vec<Rank> = self.sorted_ranks.clone();let mut other_ranks: Vec<Rank> = other.sorted_ranks.clone();// 移除需要排除的点数self_ranks.retain(|r| !exclude_ranks.contains(r));other_ranks.retain(|r| !other_exclude_ranks.contains(r));// 比较剩余牌for (self_rank, other_rank) in self_ranks.iter().zip(other_ranks.iter()) {match self_rank.cmp(other_rank) {Ordering::Equal => continue,other => return other,}}Ordering::Equal}
}

扩展功能

基于基础实现,我们可以添加更多功能:

use std::cmp::Ordering;
use std::collections::HashMap;/// 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.
pub fn winning_hands<'a>(hands: &[&'a str]) -> Vec<&'a str> {if hands.is_empty() {return vec![];}let mut parsed_hands: Vec<(Hand, &str)> = hands.iter().map(|&hand_str| (Hand::from_str(hand_str), hand_str)).collect();parsed_hands.sort_unstable_by(|a, b| b.0.cmp(&a.0));let winning_hand = &parsed_hands[0].0;parsed_hands.into_iter().take_while(|(hand, _)| hand.cmp(winning_hand) == Ordering::Equal).map(|(_, hand_str)| hand_str).collect()
}pub struct PokerGame {hands: Vec<Hand>,original_strings: Vec<String>,
}impl PokerGame {pub fn new() -> Self {PokerGame {hands: Vec::new(),original_strings: Vec::new(),}}pub fn add_hand(&mut self, hand_str: &str) -> Result<(), String> {let hand = Hand::from_str(hand_str)?;self.hands.push(hand);self.original_strings.push(hand_str.to_string());Ok(())}pub fn get_winning_hands(&self) -> Vec<&str> {if self.hands.is_empty() {return vec![];}let mut indices: Vec<usize> = (0..self.hands.len()).collect();indices.sort_by(|&a, &b| self.hands[b].cmp(&self.hands[a]));let winning_hand = &self.hands[indices[0]];indices.into_iter().take_while(|&i| self.hands[i].cmp(winning_hand) == Ordering::Equal).map(|i| self.original_strings[i].as_str()).collect()}pub fn get_hand_type(&self, index: usize) -> Option<&HandType> {self.hands.get(index).map(|hand| &hand.hand_type)}pub fn get_hand_ranking(&self, hand_str: &str) -> Option<usize> {let mut indices: Vec<usize> = (0..self.hands.len()).collect();indices.sort_by(|&a, &b| self.hands[b].cmp(&self.hands[a]));indices.iter().position(|&i| self.original_strings[i] == hand_str)}pub fn compare_hands(&self, index1: usize, index2: usize) -> Option<Ordering> {match (self.hands.get(index1), self.hands.get(index2)) {(Some(hand1), Some(hand2)) => Some(hand1.cmp(hand2)),_ => None,}}
}#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Card {rank: Rank,suit: Suit,
}#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Rank {Two = 2,Three = 3,Four = 4,Five = 5,Six = 6,Seven = 7,Eight = 8,Nine = 9,Ten = 10,Jack = 11,Queen = 12,King = 13,Ace = 14,
}impl Rank {fn from_char(c: char) -> Result<Rank, String> {match c {'2' => Ok(Rank::Two),'3' => Ok(Rank::Three),'4' => Ok(Rank::Four),'5' => Ok(Rank::Five),'6' => Ok(Rank::Six),'7' => Ok(Rank::Seven),'8' => Ok(Rank::Eight),'9' => Ok(Rank::Nine),'T' | '0' => Ok(Rank::Ten),'J' => Ok(Rank::Jack),'Q' => Ok(Rank::Queen),'K' => Ok(Rank::King),'A' => Ok(Rank::Ace),_ => Err(format!("Invalid rank character: {}", c)),}}
}#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Suit {Clubs,Diamonds,Hearts,Spades,
}impl Suit {fn from_char(c: char) -> Result<Suit, String> {match c {'C' => Ok(Suit::Clubs),'D' => Ok(Suit::Diamonds),'H' => Ok(Suit::Hearts),'S' => Ok(Suit::Spades),_ => Err(format!("Invalid suit character: {}", c)),}}
}impl Card {fn from_str(s: &str) -> Result<Card, String> {let chars: Vec<char> = s.chars().collect();if chars.is_empty() {return Err("Empty card string".to_string());}let (rank_char, suit_char) = if chars.len() >= 3 && chars[0] == '1' && chars[1] == '0' {// 处理"10"的情况if chars.len() >= 3 {('0', chars[2])} else {return Err("Invalid card string for 10".to_string());}} else if chars.len() >= 2 {(chars[0], chars[chars.len() - 1])} else {return Err("Card string too short".to_string());};let rank = Rank::from_char(rank_char)?;let suit = Suit::from_char(suit_char)?;Ok(Card { rank, suit })}
}#[derive(Debug, Clone)]
pub struct Hand {cards: Vec<Card>,pub hand_type: HandType,rank_counts: HashMap<Rank, usize>,sorted_ranks: Vec<Rank>,
}#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HandType {HighCard,OnePair,TwoPair,ThreeOfAKind,Straight,Flush,FullHouse,FourOfAKind,StraightFlush,
}impl HandType {pub fn name(&self) -> &'static str {match self {HandType::HighCard => "High Card",HandType::OnePair => "One Pair",HandType::TwoPair => "Two Pair",HandType::ThreeOfAKind => "Three of a Kind",HandType::Straight => "Straight",HandType::Flush => "Flush",HandType::FullHouse => "Full House",HandType::FourOfAKind => "Four of a Kind",HandType::StraightFlush => "Straight Flush",}}fn rank(&self) -> u8 {match self {HandType::HighCard => 0,HandType::OnePair => 1,HandType::TwoPair => 2,HandType::ThreeOfAKind => 3,HandType::Straight => 4,HandType::Flush => 5,HandType::FullHouse => 6,HandType::FourOfAKind => 7,HandType::StraightFlush => 8,}}
}impl Hand {fn from_str(hand_str: &str) -> Result<Hand, String> {let card_strs: Vec<&str> = hand_str.split_whitespace().collect();if card_strs.len() != 5 {return Err("must contain exactly 5 cards".to_string());}let mut cards = Vec::with_capacity(5);for card_str in card_strs {cards.push(Card::from_str(card_str)?);}let mut rank_counts = HashMap::new();for card in &cards {*rank_counts.entry(card.rank.clone()).or_insert(0) += 1;}let hand_type = Self::determine_hand_type(&cards, &rank_counts);let mut sorted_ranks: Vec<Rank> = cards.iter().map(|c| c.rank.clone()).collect();sorted_ranks.sort_by(|a, b| b.cmp(a)); // 降序排列Ok(Hand {cards,hand_type,rank_counts,sorted_ranks,})}fn determine_hand_type(cards: &[Card], rank_counts: &HashMap<Rank, usize>) -> HandType {let is_flush = cards.iter().all(|card| card.suit == cards[0].suit);let mut ranks: Vec<Rank> = cards.iter().map(|c| c.rank.clone()).collect();ranks.sort();let is_straight = Self::is_straight(&ranks);// 特殊情况:A,2,3,4,5 是顺子let is_low_straight = ranks == vec![Rank::Two, Rank::Three, Rank::Four, Rank::Five, Rank::Ace];if is_flush && (is_straight || is_low_straight) {return HandType::StraightFlush;}let counts: Vec<usize> = rank_counts.values().cloned().collect();let mut count_counts = HashMap::new();for &count in &counts {*count_counts.entry(count).or_insert(0) += 1;}if count_counts.get(&4).unwrap_or(&0) == &1 {HandType::FourOfAKind} else if count_counts.get(&3).unwrap_or(&0) == &1 && count_counts.get(&2).unwrap_or(&0) == &1 {HandType::FullHouse} else if is_flush {HandType::Flush} else if is_straight || is_low_straight {HandType::Straight} else if count_counts.get(&3).unwrap_or(&0) == &1 {HandType::ThreeOfAKind} else if count_counts.get(&2).unwrap_or(&0) == &2 {HandType::TwoPair} else if count_counts.get(&2).unwrap_or(&0) == &1 {HandType::OnePair} else {HandType::HighCard}}fn is_straight(ranks: &[Rank]) -> bool {if ranks.len() != 5 {return false;}for i in 0..4 {if (ranks[i + 1] as u8) != (ranks[i] as u8) + 1 {return false;}}true}
}impl PartialEq for Hand {fn eq(&self, other: &Self) -> bool {self.cmp(other) == Ordering::Equal}
}impl Eq for Hand {}impl PartialOrd for Hand {fn partial_cmp(&self, other: &Self) -> Option<Ordering> {Some(self.cmp(other))}
}impl Ord for Hand {fn cmp(&self, other: &Self) -> Ordering {match self.hand_type.rank().cmp(&other.hand_type.rank()) {Ordering::Equal => {self.compare_same_type(other)}other => other,}}
}impl Hand {fn compare_same_type(&self, other: &Self) -> Ordering {match self.hand_type {HandType::HighCard | HandType::Flush | HandType::Straight | HandType::StraightFlush => {for (self_rank, other_rank) in self.sorted_ranks.iter().zip(other.sorted_ranks.iter()) {match self_rank.cmp(other_rank) {Ordering::Equal => continue,other => return other,}}Ordering::Equal}HandType::OnePair => {let self_pair_rank = self.get_rank_by_count(2);let other_pair_rank = other.get_rank_by_count(2);match self_pair_rank.cmp(&other_pair_rank) {Ordering::Equal => {self.compare_kickers(&[self_pair_rank], other, &[other_pair_rank])}other => other,}}HandType::TwoPair => {let mut self_pairs: Vec<Rank> = self.get_ranks_by_count(2);let mut other_pairs: Vec<Rank> = other.get_ranks_by_count(2);self_pairs.sort_by(|a, b| b.cmp(a));other_pairs.sort_by(|a, b| b.cmp(a));for (self_rank, other_rank) in self_pairs.iter().zip(other_pairs.iter()) {match self_rank.cmp(other_rank) {Ordering::Equal => continue,other => return other,}}self.compare_kickers(&self_pairs, other, &other_pairs)}HandType::ThreeOfAKind => {let self_three_rank = self.get_rank_by_count(3);let other_three_rank = other.get_rank_by_count(3);match self_three_rank.cmp(&other_three_rank) {Ordering::Equal => {self.compare_kickers(&[self_three_rank], other, &[other_three_rank])}other => other,}}HandType::FullHouse => {let self_three_rank = self.get_rank_by_count(3);let other_three_rank = other.get_rank_by_count(3);match self_three_rank.cmp(&other_three_rank) {Ordering::Equal => {let self_pair_rank = self.get_rank_by_count(2);let other_pair_rank = other.get_rank_by_count(2);self_pair_rank.cmp(&other_pair_rank)}other => other,}}HandType::FourOfAKind => {let self_four_rank = self.get_rank_by_count(4);let other_four_rank = other.get_rank_by_count(4);match self_four_rank.cmp(&other_four_rank) {Ordering::Equal => {self.compare_kickers(&[self_four_rank], other, &[other_four_rank])}other => other,}}}}fn get_rank_by_count(&self, count: usize) -> Rank {self.rank_counts.iter().find(|(_, &c)| c == count).map(|(rank, _)| rank.clone()).unwrap()}fn get_ranks_by_count(&self, count: usize) -> Vec<Rank> {self.rank_counts.iter().filter(|(_, &c)| c == count).map(|(rank, _)| rank.clone()).collect()}fn compare_kickers(&self, exclude_ranks: &[Rank], other: &Self, other_exclude_ranks: &[Rank]) -> Ordering {let mut self_ranks: Vec<Rank> = self.sorted_ranks.clone();let mut other_ranks: Vec<Rank> = other.sorted_ranks.clone();self_ranks.retain(|r| !exclude_ranks.contains(r));other_ranks.retain(|r| !other_exclude_ranks.contains(r));for (self_rank, other_rank) in self_ranks.iter().zip(other_ranks.iter()) {match self_rank.cmp(other_rank) {Ordering::Equal => continue,other => return other,}}Ordering::Equal}
}// 便利函数
pub fn analyze_hand(hand_str: &str) -> Result<String, String> {let hand = Hand::from_str(hand_str)?;Ok(format!("Hand: {} - Type: {}", hand_str, hand.hand_type.name()))
}pub fn compare_two_hands(hand1_str: &str, hand2_str: &str) -> Result<String, String> {let hand1 = Hand::from_str(hand1_str)?;let hand2 = Hand::from_str(hand2_str)?;match hand1.cmp(&hand2) {Ordering::Greater => Ok(format!("{} wins over {}", hand1_str, hand2_str)),Ordering::Less => Ok(format!("{} wins over {}", hand2_str, hand1_str)),Ordering::Equal => Ok(format!("{} ties with {}", hand1_str, hand2_str)),}
}

实际应用场景

扑克牌游戏在实际开发中有以下应用:

  1. 在线扑克平台:在线扑克游戏和锦标赛平台
  2. 手机游戏:移动端扑克游戏应用
  3. 社交应用:社交平台中的扑克游戏功能
  4. 教育软件:扑克规则教学和练习工具
  5. AI研究:扑克AI和游戏理论研究
  6. 赌场系统:数字赌场和博彩系统
  7. 数据分析:扑克策略分析和统计工具
  8. 娱乐应用:休闲娱乐类扑克应用

算法复杂度分析

  1. 时间复杂度

    • 单手牌分析:O(1)(固定5张牌)
    • 多手牌比较:O(n log n),其中n是手牌数量(主要是排序开销)
  2. 空间复杂度:O(n)

    • 其中n是手牌数量,需要存储所有手牌的解析结果

与其他实现方式的比较

// 使用第三方库的实现
// [dependencies]
// cardgame = "0.1"pub fn winning_hands_with_library<'a>(hands: &[&'a str]) -> Vec<&'a str> {// 使用第三方扑克牌库实现// 这里只是一个示例,实际实现会更复杂unimplemented!()
}// 使用宏的实现
macro_rules! poker_hand {($($card:tt),*) => {// 宏实现的扑克手牌};
}// 使用trait的实现
pub trait PokerHand {fn hand_type(&self) -> HandType;fn compare(&self, other: &Self) -> Ordering;
}// 函数式实现
pub fn winning_hands_functional<'a>(hands: &[&'a str]) -> Vec<&'a str> {hands.iter().map(|&hand_str| (Hand::from_str(hand_str), hand_str)).collect::<Vec<_>>().as_mut_slice().sort_by(|a, b| b.0.cmp(&a.0));let winning_hand = &parsed_hands[0].0;hands.iter().filter(|&&hand_str| Hand::from_str(hand_str).cmp(winning_hand) == Ordering::Equal).copied().collect()
}

总结

通过 poker 练习,我们学到了:

  1. 复杂规则实现:掌握了如何实现复杂的扑克牌规则和比较逻辑
  2. 枚举设计:学会了使用枚举表示复杂的分类和状态
  3. 模式匹配:深入理解了Rust中模式匹配的强大功能
  4. 排序算法:理解了自定义排序规则的实现方法
  5. 错误处理:学会了处理各种输入错误和边界情况
  6. 性能优化:了解了如何优化数据结构和算法以提高性能

这些技能在实际开发中非常有用,特别是在游戏开发、规则引擎、复杂业务逻辑处理等场景中。扑克牌游戏虽然是一个具体的应用问题,但它涉及到了枚举设计、模式匹配、排序算法、错误处理、性能优化等许多核心概念,是学习Rust高级编程的良好起点。

通过这个练习,我们也看到了Rust在实现复杂业务规则和处理复杂数据结构方面的强大能力,以及如何用安全且高效的方式实现复杂的比较逻辑。这种结合了安全性和性能的语言特性正是Rust的魅力所在。

http://www.dtcms.com/a/585151.html

相关文章:

  • 【python】基础案例分析
  • LeetCode(python)——15.三数之和
  • Java基础——集合进阶用到的数据结构知识点1
  • 无线交换机(AC)核心技术详解:构建集中式Wi-Fi网络的基石
  • DNS的正向、反向解析的服务配置知识点及实验
  • 庖丁解牛:深度剖析 Ascend C 算子开发流程与核心概念
  • 《Learn Python Programming(4th)》读后感
  • 网站开发毕业生报告网页设计与制作项目教程陈义文
  • SSM基于JAVA的物流管理系统ztwfg(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 如何在 Ubuntu 上安装 PostgreSQL
  • openssl-1_0_0-1.0.2p-3.49.1.x86_64.rpm 怎么安装?CentOS/RHEL 手动安装RPM包详细步骤
  • C++ 面试高频题 链表 模拟 力扣 143. 重排链表 题解 每日一题
  • 快速定位bug,编写测试用例
  • 力扣第 474 场周赛
  • Node与Npm国内最新镜像配置(淘宝镜像/清华大学镜像)
  • 超越时空网上书城网站建设方案网站人员队伍建设落后
  • 海外云手机是指什么
  • react native 手搓数字键盘
  • 算法复杂度解析:时间与空间的衡量
  • 开源鸿蒙SIG-Qt技术沙龙成都站成功举办,产品方案展示
  • 2025年渗透测试面试题总结-235(题目+回答)
  • C语言进阶:深入理解函数
  • 计算机图形学·11 变换(Transformations)
  • Rust编程学习 - 如何利用代数类型系统做错误处理的另外一大好处是可组合性(composability)
  • LocalAI:一个免费开源的AI替代方案,让创意更自由!
  • 深入理解Ext2:Linux文件系统的基石与它的设计哲学
  • 泉州网站的建设html网页制作我的家乡
  • PHP 魔术常量
  • 【iOS】音频与视频播放
  • php通过身份证号码计算年龄