从一组纸牌中挑选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],
)
学习语言,不变的是设计,差异的是一些数据结构,只要我们能够想象到它应该一种什么样子,然后一步一步寻找好的数据结构,构建代码流程,总能实现目标。好的坏的,差异是精巧带来的优雅!