I'm new at Rust and I'm trying to implement a cards' deck on Rust, I'm trying to use most idiomatic code I can, but since I'm new at this language, I'd like to hear advice from more experienced programmers. There's my code. (P.D: Thanks in advance! :>)
use std::fmt;
use rand::seq::SliceRandom;
use rand::thread_rng;
#[derive(PartialEq)]
pub struct Card<'a> {
name: &'a str,
value: i8,
suit: char,
}
impl<'a> fmt::Debug for Card<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} of {}", self.name, self.suit)
}
}
pub struct Deck<'a> {
pub cards: Vec<Card<'a>>,
}
impl<'a> Deck<'a> {
const SUITS: [char; 4] = ['♥', '♦', '♣', '♠'];
const CARDS: [(&'a str, i8); 13] = [
("ACE", 11),
("TWO", 2),
("THREE", 3),
("FOUR", 4),
("FIVE", 5),
("SIX", 6),
("SEVEN", 7),
("EIGHT", 8),
("NINE", 9),
("TEN", 10),
("JACK", 10),
("QUEEN", 10),
("KING", 10),
];
pub fn new() -> Deck<'a> {
let mut deck: Vec<Card> = Vec::new();
for suit in Deck::SUITS.iter() {
for (card_name, card_value) in Deck::CARDS.iter() {
deck.push(Card {
name: card_name,
value: *card_value,
suit: *suit,
});
}
}
let mut rng = thread_rng();
deck.shuffle(&mut rng);
Deck { cards: deck }
}
pub fn deal_card(&mut self) -> Card<'a> {
if self.cards.is_empty() {
self.initialize_deck()
}
self.cards.pop().unwrap()
}
pub fn get_initial_cards(&mut self) -> Vec<Card> {
vec![self.deal_card(), self.deal_card()]
}
/// When the cards vector is empty, shuffle a new deck
fn initialize_deck(&mut self) {
*self = Deck::new();
}
}
EDIT: This module is meaned to be used with a BlackJack game, that is the reason because the cards have those particular values
-
\$\begingroup\$ If you're only going to use a constant once, why not just inline it? That makes it clearer that it's only for a single purpose. Declaring it separately implies you're going to reuse it. And rather than storing the name on the card, it would be more memory efficient to implement a function to look up the name based on the value. \$\endgroup\$Turksarama– Turksarama2019年08月21日 23:20:45 +00:00Commented Aug 21, 2019 at 23:20
-
\$\begingroup\$ The constants are there because I want the user to be able to see the cards and suits that I'm using on the deck. I want them to be what in other languages you'd call static variables. About storing both name and value, you're totally right, but I think that for readibility, I'm going to delete the value, and create the function that gives the value according to the name. Thx a lot! \$\endgroup\$Davichete– Davichete2019年08月22日 07:44:22 +00:00Commented Aug 22, 2019 at 7:44
1 Answer 1
You’ve made a fairly classic mistake when it comes to modeling cards in software. Cards have different values depending on the game. In some games, an Ace == 1, others 11, sometimes it could be either in the same game (Blackjack for example). Non-face cards aren’t always their value either. In some games, they’re all "5 points", regardless of the number on the card.
In short, the value of a particular card depends on the game, not the card. How might you redesign to account for that?
-
\$\begingroup\$ Another example: in the "Belote" (a French game), both the strength and the points of a card depend on if the suit is trump or not. Before the players chose the trumps suit, the cards do not have those characteristics fixed. \$\endgroup\$Boiethios– Boiethios2019年08月22日 07:09:40 +00:00Commented Aug 22, 2019 at 7:09
-
\$\begingroup\$ I forgot to mention that I'm going to use this module with a BlackJack game I'm developing, that is the reason because the cards have those particular values \$\endgroup\$Davichete– Davichete2019年08月22日 07:40:09 +00:00Commented Aug 22, 2019 at 7:40