[Beginner] - Struct Logic for Card Game

Hello,

I am fairly new to programming but have found Rust to be a great teacher of programming concepts. as a way of gaining familiarity with Rust I am programming the simple logic behind a deck of cards. Before even moving to the methods that will be programmed and the logic to the game, I would like to start by structuring the data.

So far I have the concept of a card. It will have a number to represent value, a suit to represent the type of card, and a boolean value. To be honest i'm not sure exactly what data type to assign to 'Number' or the 'Suit.'

struct Card {
Number: () , // Will be values [2,3,4,5,6,7,8,9,10,J,Q,K,A]
Suit: () , // Will be values [Hearts, Diamonds, Clubs, Spades]
InDeck: bool, // Used to indicate if the card has already been drawn from the deck.
}

Now my questions are:

  1. What datatype is best fitting for the 'Number' and 'Suit' for this struct?

  2. In Rust how would I store all 52 cards in a 'Deck' so to speak?

I would choose String for the number and an enum consists of four variants, which are the four suits, for suit.

Something like this:

enum Suit {
    Heart,
    Diamond,
    Club,
    Spade,
}

And for the 'deck', I may choose a vector, which can store a bunch of items of the same type

Why not an enum for the number? You could also derive Ord and get the ordering of the numbers for free:

#[derive(PartialEq,Eq,PartialOrd,Order)]
enum Number{
  Value(u8),
  J,QK,A,
}

(You could also replace Value(u8) with the concrete possibilities: Two,Three,Four,...).

I would not have a field InDeck as part of the card, but rather a struct Deck that contains a Vec<Card> field (or if each card can only exist once in the deck maybe a HashMap).

3 Likes

Okay this makes some sense. Now I have to restructure my Struct. So how would I associate my Struct fields with an enum?

Thanks for pointing out that, your answer is much better.

(I have not finished the book, so I can just solve that with just String. Thank you again.

something like this:

Struct Card {
    number: Number,
    suit: Suit,
}

the Number type will be the enum that @raidwas wrote above.

Then when you construct the Card, you can do something like this:

let card1 = Card { Number::Value(1), Suit::Heard };  //  card that has "number 1" of "suit heart"
let card2 = Card { Number::J, Suit::Club };  // card that has "number J" of "suit club"

So correct me if i'm wrong, but what this code is saying is:

Number can be either (U8) or These (J,Q,K,A). And this '#[derive(PartialEq,Eq,PartialOrd,Order)]' is doing what exactly?

You are right for the first part, but for the "derive" part, I think it just make the Number type comparable.

Okay awesome. Im still a bit confused about these ' #[derive(PartialEq,Eq,PartialOrd,Order)] '

Does this addition add extra properties to the block of code?

In my opinion, the #derive[...] is something like "auto-implement" something. and the stuffs in the parenthesis are the traits (or something else?) that need to be implemented. You may get more information here

1 Like

Okay got ya, so i'm associating traits to this field. So in this case it's just saying ' Right off the back order what is inside this struct?'

The part #[derive(...)] calls something called derive macros.
Derive macros are special macros (basically just other code that runs on your code and produces code as output) that can be used to implement common traits.

In this case I used the derive macros to automatically implement the traits PartialOrd, PartialEq, Ord, and Eq for the type Number. The implication of the automatic implementation is that they provide a "basic" implementation, if you need any custom functionality while, for example, comparing two values you would have to implement the traits yourself. For the differences between the Partial and non Partial versions I would recommend reading through the docs pangz mentioned above. If I remember correctly the corresponding trait pages also state how the derive macros implement the traits (for example while deriving PartialOrd the order of the fields in your struct/enum does matter).

There also exist other derive macros and they can also be provided by the user (you or other libraries).

2 Likes

That means you can do this (playground):

fn main() {
    let num1 = Number::Value(2);
    let num2 = Number::Value(10);
    let num3 = Number::J;
    let num4 = Number::K;
    
    assert_eq!(num1<num2, true);  // checks that 'num1<num2' is true, or it panic.
    assert_eq!(num2<num3, true);
    assert_eq!(num3>num4, false);
}
1 Like

Okay so i'm a bit confused as to why this won't compile. I thought after 1.30 there was no need to declare this as it was a built in Macro?

**error** **: cannot find derive macro Order` in this scope**

It is actually Ord.

( and you can experiment and share your code here

1 Like

Im a bit stuck on this approach you recommended. I've been trying to construct a 'Deck' struct with a field of a hash map built out of the original 'Card' struct. However It does not work with the following error:

Deck: HashMap,

| ^^^^^^^^^^^^^ expected at least 2 type arguments

I thought that this struct 'Card' would have the 2 arguments required for a hashmap?

If your card is used as both key and value, you need a HashSet, instead.

The HashMap is a data structure that maps elements from some type A to some type B. If you only want to store elements of a single type a HashSet is one of the possible choices.
However, for a beginner, I would recommend using a Vec<Card> as it is conceptually simpler and don't worry about performance (some might consider HashSets simpler, this is just my personal opinion).

It’s also important to note that HashMap and HashSet are unordered collections: If you want to support operations like shuffling or cutting the deck, you’ll need something like a Vec to store the order.

3 Likes

Could someone by any chance show me an example of how I would construct this Vec. I seem to just not get the logic of how this will be constructed. This is one of the attempts I made. I think Im getting this concept of constructing unique data types with structs, but being able to construct something that stores multiples of this new type is a bit blurry to me atm.

> struct Deck {
>     cards: Vec<Card>,
> }
> 
> fn main() {   
> 
>     let deck = vec![Deck{cards:2, Heart}];
> }