Rust实现:从一组纸牌中挑选Winner纸牌

从一组纸牌中挑选Winner纸牌

在纸牌游戏中,一手包含五张牌并且每一手都有自己的排序,从低到高排序:

此处省略描述,请参考以下链接

[Python版本-纸牌游戏]](https://www.cnblogs.com/metaquant/p/11846933.html)

参考上述文章中的Python实现,用Rust实现此版本。
代码运行

use std::{collections::HashMap, collections::HashSet};
use core::hash::Hash;
use std::cmp::Ordering;
use std::cmp::Ordering::{Less, Greater, Equal};

//牌面字母映射为数字,便于比较
const ARRAY_CHAR_CARD_NUM: [(&str, u8);5]  = [
    ("T", 10),
    ("J", 11),
    ("Q", 12),
    ("K", 13),
    ("A", 14),
];

//将card数字转为原字符
fn transer_card_num_char(num: u8) -> String {
    match ARRAY_CHAR_CARD_NUM.iter().find(|item| item.1 == num) {
        Some(str) => str.0.to_string(),
        None => num.to_string(),
    }
}
//将card字符(T J A...)转为数字,便于比较
fn tranfer_card_num(card_value: &str) -> u8 {
    let card_char_num_map = HashMap::from(ARRAY_CHAR_CARD_NUM);
    if card_char_num_map.contains_key(card_value) {
        card_char_num_map.get(card_value).unwrap().to_owned() as u8
    }else{
        card_value.parse().unwrap()
    }
}

#[derive(Debug)]
struct Card {
    value: u8,
    color: char,
    
}
#[derive(Debug)]
struct Hand {
    values: Vec<u8>,
    raw_values: Vec<u8>,
    colors: Vec<char>,
    value_counter: Vec<(u8,u8)>,
    color_kind: usize,
    first_count: u8,
    second_count: u8,
    diff: HashSet<u8>
}

impl Card {
    fn new(value: u8, color: char) -> Self {
        Self { value: value, color: color }
    }
}

impl From<&str> for Card {
    fn from(str: &str) -> Self {
        let value = tranfer_card_num(str.get(..(str.len() - 1)).unwrap());
        let color = str.get((str.len() - 1)..).unwrap();
        Card::new(value, color.chars().next().unwrap())
    }
}

impl From<&str> for Hand {
    fn from(item: &str) -> Self {
        let hand_vec = item.split(' ').collect::<Vec<_>>();
        let card_vec = hand_vec.iter()
                                        .map(|&item| item.into())
                                        .collect::<Vec<Card>>();
        let values = card_vec.iter().map(|item| item.value).collect::<Vec<u8>>();
        let colors = card_vec.iter().map(|item| item.color).collect::<Vec<char>>();
        let hand = Hand::new(values, colors);
        hand
    }
}
impl PartialEq for Hand{
    fn eq(&self, other: &Self) -> bool {
        self.categories() == other.categories() && self.values == other.values
    }
    
}
impl PartialOrd for Hand{
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        if self.eq(&other) {
            Some(Equal)
        }else if &self.categories().1 > &other.categories().1 {
            Some(Greater)
        }else if &self.categories().1 < &other.categories().1  {
            Some(Less)
        }else if [8,7,4,3,2].contains(&self.categories().1){
            if &self.value_counter > &other.value_counter {
                Some(Greater)
            }else {
                Some(Less)
            }
        }else {
            if &self.values > &other.values {
                Some(Greater)
            }else{
                Some(Less)
            }
        }
    }
}
impl From<&Hand> for String {
    fn from(_hand: &Hand) -> Self {
        let mut vec = Vec::new();
        for i in (0.._hand.colors.len())  {
            let color1 = _hand.colors[i].to_string();
            let num1 = transer_card_num_char(_hand.raw_values[i]);
            let c = num1 + color1.as_ref();
            vec.push(c);
        }
        let hand_string = vec.join(" ");
        hand_string
    }
}
impl Hand {
    fn new(values: Vec<u8>, colors: Vec<char>) -> Self {
        let set = colors.iter().map(|item| *item).collect::<HashSet<char>>();
        let counter = Counter::new(&values);
        let most = counter.most_common();
        let first_count = most.get(0).unwrap().1;
        let second_count = most.get(1).unwrap().1;
        let mut values_sort = values.clone();
        values_sort.sort_by(|a, b| b.cmp(a));
        let diff = values_sort.windows(2).map(|item| item[0] - item[1]).collect::<HashSet<u8>>();
        Self { raw_values:values, values: values_sort, colors: colors, value_counter: most[..].to_vec(), color_kind: set.len(), first_count, second_count, diff}
    }
    fn categories(&self) -> (&str, u8) {
        if self.color_kind.eq(&1) && (self.diff.len() == 1 && self.diff.contains(&1)){
            ("Straight Flush",9)
        }
        else if self.color_kind == 1{
            ("Flush",6)
        }
        else if (self.diff.len() == 1 && self.diff.contains(&1)){
            ("Straight",5)
        }
        else if self.first_count == 4{
            ("Four of a Kind",8)
        }
        else if self.first_count == 3 && self.second_count == 2 {
            ("Full House",7)
        }
        else if self.first_count == 3 && self.second_count == 1 {
            ("Three of a Kind",4)
        }
        else if self.first_count == 2 && self.second_count == 2 {
            ("Two Pairs",3)
        }
        else if self.value_counter.len() == 4 && self.first_count == 2 {
            ("One Pair",2)
        }
        else {
            ("High Card",1)
        }
    }
}

// 类比Python Counter数据结构
struct Counter<T>{
    map: HashMap<T,u8>
}
impl<T> Counter<T>  where T:Hash + Eq + Copy {
    fn new(vec: &[T]) -> Self{
        let mut map = HashMap::new();
        for one in vec  {
            if let Some(x) = map.get_mut(one){
                *x = *x + 1;
            }else{
                map.insert(*one, 1 as u8);
            }
        }
        Self { map: map }
    }
    fn most_common(self) -> Vec<(T,u8)>{
        let map = self.map;
        let mut result:Vec<(T,u8)> = Vec::new();
        let mut value_vec:Vec<u8> = map.iter()
            .map(|(key, val)| *val)
            .collect::<HashSet<u8>>()
            .iter()
            .map(|val| *val)
            .collect();
        value_vec.sort_by(|a, b| b.cmp(a));
        // let mut i = 0 ;
        for item in value_vec  {
            let mut f = map.iter()
                .map(|(key,value)| (key.to_owned(), *value))
                .filter(|(key, value)| (value).eq(&item))
                .collect::<Vec<(T,u8)>>();
            result.append(&mut f);
        }
        result
        
    }
   
}

//两手牌比较
fn tow_hand_cmp(){
    let _hand1: Hand = "5D 5S 5S 8D 8C".into();
    let _hand2: Hand = "2S 3C 4S 5H 6H".into();
    let c = _hand1.partial_cmp(&_hand2);
    println!("{:?}", c);
}
//挑选出打牌,可能是多个
fn pickup_winner<'a>(hands: &[&'a str]) -> Vec<String> {
    let mut hand_vec = hands.iter().map(|item| item.to_owned().into()).collect::<Vec<Hand>>();
    hand_vec.sort_by(|a,b| b.partial_cmp(a).unwrap());
    let winners: Vec<&Hand> = hand_vec.iter()
                            .filter(|item| item.to_owned().partial_cmp(&(hand_vec[0]))
                            .eq(&Some(Equal))).collect();
    let vec_str = winners.iter()
        .map(|&item| item.into())
        .collect::<Vec<String>>();
    println!("{:?}", vec_str);
    vec_str
}
fn main() {
    let card_str_vec = [ "3H 4H 5D 6H JH", "4D 5S 5S 8D 3C", "4D 5S 8D 3C 5S","2S 4C JS JH 10H"];
    pickup_winner(&card_str_vec);
    //tow_hand_cmp()
}

刚开始写Rust,对于一些方法的作用不太熟悉,导致没有实现的代码比较麻烦,没有实现方法的最优组合。
请参考下一个实现。

use std::cmp::Reverse;
use std::collections::{BinaryHeap, HashMap};

pub fn winning_hands<'a>(hands: &[&'a str]) -> Vec<&'a str> {
    let mut hands: BinaryHeap<_> = hands.iter().map(|&s| (PokerHand::parse(s), s)).collect();
    let (winning, s) = hands.pop().unwrap();
    let mut result = vec![s];
    while let Some((value, s)) = hands.pop() {
        if value < winning {
            break;
        }
        result.push(s);
    }
    result
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
struct PokerHand {
    counts: Vec<usize>,
    values: Vec<u8>,
}
fn parse_card(s: &str) -> (u8, u8) {
    let (value, suit) = s.split_at(s.len() - 1);
    (
        match value.parse::<u8>() {
            Ok(v) => v,
            Err(_) => "JQKA".find(value).unwrap() as u8 + 11,
        },
        suit.as_bytes()[0],
    )
}
impl PokerHand {
    fn parse(s: &str) -> Self {
        let (values, suits): (Vec<u8>, Vec<u8>) = s.split_whitespace().map(parse_card).unzip();
        let mut groups = HashMap::<u8, usize>::new();
        for &v in values.iter() {
            *groups.entry(v).or_default() += 1;
        }
        let mut groups: Vec<_> = groups.into_iter().map(|(v, c)| (c, v)).collect();
        groups.sort_unstable_by_key(|&x| Reverse(x));
        let (mut counts, mut values): (Vec<_>, Vec<_>) = groups.iter().copied().unzip();
        if counts.len() == 5 {
            if values == [14, 5, 4, 3, 2] {
                values = vec![5, 4, 3, 2, 1];
            }
            let is_straight = values[0] - values[4] == 4;
            let is_flush = suits[1..].iter().all(|&x| x == suits[0]);
            match (is_straight, is_flush) {
                (true, true) => counts = vec![5],
                (true, false) => counts = vec![3, 1, 2],
                (false, true) => counts = vec![3, 1, 3],
                _ => {}
            }
        }
        Self { counts, values }
    }
}

花了一上午时间,对比了两种实现,越发感觉第一种很“原始”。

首先两种实现思路不同,第二种设想较为简单,直接构造通过排序可以解决问题的PokerHand,

第一种将规则中各类情况考虑较为清楚,分类比较。

将b方案的结构(对比a方案)列举如下:

9 => [5], 8 => [4,1] , 7 => [3,2] ,6 => [3,1,3] , 5 => [3,1,2], 4 => [3,1,1], 3 => [2,2,1], 2 => [2,1,1,1]

可见b方案在设计上就比较简洁。

具体差别:

  • 排序相关trait实现使用派生宏生成

  • Counter结构的实现麻烦

    fn new(vec: &[T]) -> Self{
            let mut map = HashMap::new();
            for one in vec  {
                if let Some(x) = map.get_mut(one){
                    *x = *x + 1;
                }else{
                    map.insert(*one, 1 as u8);
                }
            }
            Self { map: map }
        }
    //简单的如下
    fn new(vec: &[T]) -> Self{
            let mut map = HashMap::new();
            for one in vec  {
                *map.entry(*one).or_default() += 1;
            }
            Self { map: map }
        }
    
  • Counter结构,most_common的实现麻烦

fn most_common(self) -> Vec<(T,u8)>{
        let map = self.map;
        let mut result:Vec<(T,u8)> = Vec::new();
        let mut value_vec:Vec<u8> = map.iter()
            .map(|(key, val)| *val)
            .collect::<HashSet<u8>>()
            .iter()
            .map(|val| *val)
            .collect();
        value_vec.sort_by(|a, b| b.cmp(a));
        // let mut i = 0 ;
        for item in value_vec  {
            let mut f = map.iter()
                .map(|(key,value)| (key.to_owned(), *value))
                .filter(|(key, value)| (value).eq(&item))
                .collect::<Vec<(T,u8)>>();
            f.sort_by(|a, b| (b.0).partial_cmp(&a.0).unwrap());
            result.append(&mut f);
        }
        result
        
    }
//简单如下
    fn most_common(self) -> Vec<(T,u8)>{
        let map = self.map;
        let mut groups: Vec<_> = map.into_iter().map(|(v, c)| (c, v)).collect();
        groups.sort_unstable_by_key(|&x| Reverse(x));
        groups.into_iter().map(|(v, c)| (c, v)).collect()
    }
  • &str转为Card
let (value, suit) = s.split_at(s.len() - 1);
    (
        match value.parse::<u8>() {
            Ok(v) => v,
            Err(_) => "JQKA".find(value).unwrap() as u8 + 11,
        },
        suit.as_bytes()[0],
    )

学习语言,不变的是设计,差异的是一些数据结构,只要我们能够想象到它应该一种什么样子,然后一步一步寻找好的数据结构,构建代码流程,总能实现目标。好的坏的,差异是精巧带来的优雅!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值