Best way to implement a collection of items to copy?

I'm writing my first program in Rust that takes a list of cards and tries to find the best deck of those cards. I want to have a catalog of cards that can be copied to decks. I am trying to find an idiomatic way to do this. My first thought was a vector or array containing one of each card and a function to return a copy of that card to the deck.

Here is my code:

pub trait Card {
    fn get_name(&self) -> &String;
    fn get_card_type(&self) -> &CardType;
    fn get_cost(&self) -> Option<&i32>;

    fn play(&self){
        println!("Played {} for {} mana.", self.get_name(), self.get_cost().unwrap());
    }
}

pub enum CardType {
    Creature,
    Spell,
    Land,
}

pub struct Creature {
    pub name: String,
    pub card_type: CardType,
    pub cost: i32,
    pub attack: i32,
    pub defense: i32,
    pub tapped: bool,
}

impl Card for Creature{
    fn get_name(&self) -> &String {
        &self.name
    }

    fn get_card_type(&self) -> &CardType {
        &self.card_type
    }

    fn get_cost(&self) -> Option<&i32> {
        Some(&self.cost)
    }
}

pub struct Spell {
    pub name: String,
    pub card_type: CardType,
    pub cost: i32,
    pub damage: i32,
}

impl Card for Spell{
    fn get_name(&self) -> &String {
        &self.name
    }

    fn get_card_type(&self) -> &CardType {
        &self.card_type
    }

    fn get_cost(&self) -> Option<&i32> {
        Some(&self.cost)
    }
}

pub struct Land {
    pub name: String,
    pub card_type: CardType,
    pub tapped: bool,
}

impl Card for Land{
    fn play(&self) {
        println!("Played {}.", self.get_name());
    }

    fn get_name(&self) -> &String {
        &self.name
    }

    fn get_card_type(&self) -> &CardType {
        &self.card_type
    }

    fn get_cost(&self) -> Option<&i32> {
        None
    }
}

pub fn get_random_card() -> Box<Card> {
    Box::new( Creature{
        name: "My Card".to_string(), 
        card_type: CardType::Creature, 
        cost: 1, 
        attack: 2, 
        defense: 2,
        tapped: false,
    })
}

      

Get_random_card () function contains an example card. So essentially I just need a static array or vector of cards and a function to copy them into the deck, but I haven't been able to implement it. Any suggestions? Feel free to point out anything else that I am doing wrong.

Edit: some clarifications -

The code works here, but I need a variable containing a list of available maps. for example

// some pseudocode, in file cards.rs
let cards = [
    Creature {
        name = "Creature 1"
        //...
    },
    Land {
        name = "Land 1"
        //...
    },
    Spell {
        name = "Spell 1"
        //...
    },
];

fn get_card(name) -> mut Card {
    // return a mutable copy/clone of a card, not a reference
}

      

And I would prefer it to be declared outside of the main function in a separate file. I've tried several different things trying to make the compiler happy, but I'm sure I'm missing something obvious. Memory is not a big concern at the moment, there won't be many cards in var "maps". But the decks will be generated dynamically, so I need to get the cards in the deck somewhere.

Thank.

+3


source to share


1 answer


If you're not worried about allocating too much memory, you have everything you need right now:

fn main() {
    let hand: Vec<_> = (0..5).map(|_| get_random_card()).collect();
    for card in &hand {
        println!("{}", card.get_name());
    }
}

      

We just take 5 cards and store them in Vec

. We can then iterate over the vector and print the map names.

If you're worried about memory and you have a bunch of cards that you want to "reuse", you can do as above and then grab the links to them:

fn main() {
    let deck: Vec<_> = (0..52).map(|_| get_random_card()).collect();

    let hand1 = &deck[0..5];
    let hand2 = &deck[5..10];
    let hand3 = &deck[10..15];
    let hand4 = &deck[15..20];

    for card in hand1 {
        println!("{}", card.get_name());
    }
}

      

This is where the compiler won't let you use the card after the deck is out of bounds. If you need more flexibility, you can use Rc

in addition to Box

:



use std::rc::Rc;

pub fn get_random_card() -> Rc<Box<Card>> {
    Rc::new(Box::new(Creature {
        name: "My Card".to_string(), 
        card_type: CardType::Creature, 
        cost: 1, 
        attack: 2, 
        defense: 2,
        tapped: false,
    }))
}

fn main() {
    let deck: Vec<_> = (0..52).map(|_| get_random_card()).collect();

    let hand1 = deck[0..5].to_owned();
    let hand2 = deck[5..10].to_owned();
    let hand3 = deck[10..15].to_owned();
    let hand4 = deck[15..20].to_owned();

    for card in &hand1 {
        println!("{}", card.get_name());
    }
}

      

This allows each map to manage the reference count of active links. When the links go to 0, the card is released.

Note During the night hours of Rust you can only use Rc<T>

instead Rc<Box<T>>

.

Feel free to point out anything else that I am doing wrong.

Three things seemed to me:

  • You should probably use #[derive(Debug)]

    for every structure. Other things that can be obtained are Copy

    and / or Clone

    , PartialEq

    and Hash

    . Of course, you can wait until you need one of these before adding it, but Debug

    very useful right away.
  • Return &str

    instead &String

    . 99.9% of the time, you want to use it &str

    instead &String

    - it's more flexible.
  • There is no reason to return small type references like CardType

    or i32

    - just return them directly.
+4


source







All Articles