简体   繁体   中英

Checking for consecutiveness of values in Rust

I am currently implementing a poker game and I have encountered an issue that sounds really simple; it may be so, but I can't think of an idiomatic solution: how do I check if a hand is a straight?

A straight is when all the ranks of the cards in a hand (ie 5 cards) are consecutive, eg Two, Three, Four, Five, Six. The suit can be disregarded (otherwise this would be a check for a straight flush).

Consider the following code:

#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
enum Rank {
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Ten,
    Jack,
    Queen,
    King,
    Ace
}

#[derive(Clone, Copy, PartialEq)]
enum Suit {
    Spades,
    Clubs,
    Diamonds,
    Hearts
}

#[derive(PartialEq)]
struct Card {
    rank: Rank,
    suit: Suit
}

struct Hand(Vec<Card>);

impl Hand {
    fn is_straight(&self) -> bool {
        // this should be easy
    }
}

The point is not iterating over an enum - I know this currently can't be done in Rust. I am not sure how to implement this if Rank s were an array or a Vec<Rank> (can be already sorted). The issue is checking if the Hand 's Rank s are consecutive (like succ or pred in Haskell).

Any ideas how to do it idiomatically in vanilla Rust? The best I have come up with is checking if a sorted array of all Rank s contains the sorted array of Hand 's Ranks .

As Chris Emerson already said, you can assign integer values to enum variants and then cast it via the as operator. So we have:

#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
enum Rank {
    Two = 2,
    Three,
    Four,
    // ...
}

The method is_straight() can be implemented using iterator magic:

fn is_straight(&self) -> bool {
    self.0
        .iter()
        .map(|c| c.rank as i8 - self.0[0].rank as i8)
        .eq(0..5)
}

The unchecked indexing self.0[0] is safe, because the closure in map() is only called when at least one element is within the vector. And it seems to work (I removed the Suit to reduce noise).

You can give enum variants values (C-like) and easily compare them that way:

#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
enum Rank {
    Two=2,
    Three,
    Four,
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Ten,
    Jack,
    Queen,
    King,
    Ace
}

impl Rank {
    // Passing by value as Rank: Copy
    pub fn next_is(self, other: Rank) -> bool {
        ((self as i32) + 1) == (other as i32)
    }
}

Then is_straight is simple to implement using the next_is method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM