Expand description
Cardpack is a library to represent various decks of playing cards. The library is designed to support the following features:
- Custom
Rank
andSuit
Pips
. - Ability to sort a
Deck
ofCards
in various ways. - Localization of card names using fluent-templates.
§Overview
The structure of the library is the following:
Pile
- A generic collection ofCards
that implement theDeckedBase
trait
The library supports the following decks:
§French Deck
The French
deck is the foundation Deck
of playing cards. It is made up of a collection of 54 Cards
with 13 ranks in each of the four suits,
and two jokers. Most of the other decks are made up on the French BasicCards
.
use cardpack::prelude::*;
let mut french_deck = Pile::<French>::deck();
// It's also possible to call the deck method directly on the specific generic implementing type:
let mut french_deck = French::deck();
assert_eq!(french_deck.len(), 54);
assert_eq!(
french_deck.to_string(),
"B🃟 L🃟 A♠ K♠ Q♠ J♠ T♠ 9♠ 8♠ 7♠ 6♠ 5♠ 4♠ 3♠ 2♠ A♥ K♥ Q♥ J♥ T♥ 9♥ 8♥ 7♥ 6♥ 5♥ 4♥ 3♥ 2♥ A♦ K♦ Q♦ J♦ T♦ 9♦ 8♦ 7♦ 6♦ 5♦ 4♦ 3♦ 2♦ A♣ K♣ Q♣ J♣ T♣ 9♣ 8♣ 7♣ 6♣ 5♣ 4♣ 3♣ 2♣"
);
assert!(french_deck.contains(&Card::<French>::new(FrenchBasicCard::ACE_SPADES)));
let shuffled = french_deck.shuffled();
// Use the `french_cards!` macro to parse the shuffled deck as a string:
let parsed = french_cards!(shuffled.to_string().as_str());
// Verify that the cards, in any order, are the same:
assert!(french_deck.same(&parsed));
// When sorted, they should be exactly the same:
assert_eq!(parsed.sorted(), french_deck);
// For a joker card's index string, `B` stands for the Big or Full-Color Joker and `L` for the
// Little or One-Color Joker, with `🃟` being the symbol character for the joker suit.
let jokers = french_deck.draw(2).unwrap();
assert_eq!(jokers.to_string(), "B🃟 L🃟");
let royal_flush = french_deck.draw(5).unwrap();
assert_eq!(royal_flush.to_string(), "A♠ K♠ Q♠ J♠ T♠");
assert_eq!(royal_flush.index(), "AS KS QS JS TS");
// The original deck should now have five cards less:
assert_eq!(french_deck.len(), 47);
// Cards can provide a longer description in English and German:
assert_eq!(Card::<French>::new(FrenchBasicCard::ACE_SPADES).fluent_name_default(), "Ace of Spades");
assert_eq!(Card::<French>::new(FrenchBasicCard::QUEEN_HEARTS).fluent_name(&FluentName::DEUTSCH), "Dame Herzen");
At some point I would love to add support for more languages.
§Standard 52 Card Deck
A Standard52
deck is a
French
deck without the two jokers.
use cardpack::prelude::*;
let mut standard52_deck = Pile::<Standard52>::deck();
assert_eq!(standard52_deck.len(), 52);
assert_eq!(
standard52_deck.to_string(),
"A♠ K♠ Q♠ J♠ T♠ 9♠ 8♠ 7♠ 6♠ 5♠ 4♠ 3♠ 2♠ A♥ K♥ Q♥ J♥ T♥ 9♥ 8♥ 7♥ 6♥ 5♥ 4♥ 3♥ 2♥ A♦ K♦ Q♦ J♦ T♦ 9♦ 8♦ 7♦ 6♦ 5♦ 4♦ 3♦ 2♦ A♣ K♣ Q♣ J♣ T♣ 9♣ 8♣ 7♣ 6♣ 5♣ 4♣ 3♣ 2♣"
);
// It includes the card! and cards! macros for easy Standard52 card creation:
assert_eq!(card!(AS), Card::<Standard52>::new(FrenchBasicCard::ACE_SPADES));
assert_eq!(cards!("AS KS QS JS TS"), standard52_deck.draw(5).unwrap());
By default, a Deck
displays the suit symbols when you display the
values. It also has the ability to return the letter values, or what are called “index strings”.
use cardpack::prelude::*;
assert_eq!(
Pile::<Standard52>::deck().index(),
"AS KS QS JS TS 9S 8S 7S 6S 5S 4S 3S 2S AH KH QH JH TH 9H 8H 7H 6H 5H 4H 3H 2H AD KD QD JD TD 9D 8D 7D 6D 5D 4D 3D 2D AC KC QC JC TC 9C 8C 7C 6C 5C 4C 3C 2C"
);
An important thing to remember about the decks is that the cards have their weight inside them
to facilitate sorting. If you wanted a deck for a game of poker where the lowest hand wins, you
would need to create a separate deck file with the card’s Rank
weights inverted. The
Razz Deck
is an example of this. It is also an example of
how you can create a Deck
where the
BasicCard
for the deck are generated programmatically
in YAML instead using the power of Serde
use cardpack::prelude::*;
assert_eq!(Pile::<Razz>::deck().draw(5).unwrap().to_string(), "A♠ 2♠ 3♠ 4♠ 5♠");
assert_eq!(Pile::<Standard52>::deck().draw(5).unwrap().to_string(), "A♠ K♠ Q♠ J♠ T♠");
The raw YAML that was used to create the Razz Deck
is available
in the source code.
Other decks include:
Canasta
- 2 Modern decks with the red 3s made jokers.Euchre24
- A 24 card version of a Euchre deck.Euchre32
- A 32 card version of a Euchre deck.ShortDeck
- A 36 card deck with ranks 6 through Ace.Pinochle
- A 48 card deck with two copies of the 9 through Ace ranks.Skat
- A 32 card German card game with different suits and ranks.Spades
- A Modern deck with the 2 of Clubs and 2 of Diamonds removed.Tarot
- A 78 card deck with 22 Major Arcana and 56 Minor Arcana cards.
In past versions of the library there was a Hand and Foot
deck. This has been removed because it can simply be created using a
French
and what functionality is available in the Decked trait:
use cardpack::prelude::*;
let hand_and_foot_4players = French::decks(4);
assert_eq!(hand_and_foot_4players.len(), 216);
let hand_and_foot_5players = French::decks(5);
assert_eq!(hand_and_foot_5players.len(), 270);
§Custom Deck example:
Here’s a very simple example where we create a tiny deck with only the ace and kink ranks,
and only the spades and hearts suits. Just for fun, we’ll include a tiny!
macro for one Tiny
card.
use std::collections::HashMap;
use colored::Color;
use cardpack::prelude::*;
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Tiny {}
impl Tiny {
pub const DECK_SIZE: usize = 4;
pub const DECK: [BasicCard; Tiny::DECK_SIZE] = [
FrenchBasicCard::ACE_SPADES,
FrenchBasicCard::KING_SPADES,
FrenchBasicCard::ACE_HEARTS,
FrenchBasicCard::KING_HEARTS,
];
}
impl DeckedBase for Tiny {
fn base_vec() -> Vec<BasicCard> {
Tiny::DECK.to_vec()
}
fn colors() -> HashMap<Pip, Color> {
Standard52::colors()
}
fn deck_name() -> String {
"Tiny".to_string()
}
fn fluent_deck_key() -> String {
FLUENT_KEY_BASE_NAME_FRENCH.to_string()
}
}
// Let's you call Decked methods directly on the Tiny type:
impl Decked<Tiny> for Tiny {}
macro_rules! tiny {
(AS) => {
Card::<Tiny>::new(FrenchBasicCard::ACE_SPADES)
};
(KS) => {
Card::<Tiny>::new(FrenchBasicCard::KING_SPADES)
};
(AH) => {
Card::<Tiny>::new(FrenchBasicCard::ACE_HEARTS)
};
(KH) => {
Card::<Tiny>::new(FrenchBasicCard::KING_HEARTS)
};
(__) => {
Card::<Tiny>::default()
};
}
let mut deck = Tiny::deck();
assert_eq!(deck.to_string(), "A♠ K♠ A♥ K♥");
// Every deck comes with the Ranged trait automatically:
assert_eq!(deck.combos(2).to_string(), "A♠ K♠, A♠ A♥, A♠ K♥, K♠ K♥, A♥ K♠, A♥ K♥");
// Deal from the top of the deck:
assert_eq!(deck.draw_first().unwrap().to_string(), "A♠");
// Deal from the bottom of the deck:
assert_eq!(deck.draw_last().unwrap().to_string(), "K♥");
// Should be two cards remaining:
assert_eq!(deck.len(), 2);
assert_eq!(deck.index(), "KS AH");
// Draw a remaining card:
assert_eq!(deck.draw_first().unwrap(), tiny!(KS));
// Draw the last card:
assert_eq!(deck.draw_last().unwrap(), tiny!(AH));
// And now the deck is empty:
assert!(deck.draw_first().is_none());
assert!(deck.draw_last().is_none());
// Of all the tests you could use to make sure that your deck is setup correctly, the most
// fundamental is the validate method.
assert!(Tiny::validate());
§cardpack.rs
Generic pack of cards library written in Rust. The goals of the library include:
- Various types of decks of cards.
- Internationalization support.
- Ability to create custom sorts for a specific pack of cards.
UPDATE: This is a complete rewrite of the library taking advantage of generics in order to make the code cleaner, and easier to extend.
§Usage
use cardpack::prelude::*;
fn main() {
let mut pack = Standard52::deck();
pack.shuffle();
// Deal no-limit hold'em hands for two players:
let small_blind = pack.draw(2).unwrap().sorted_by_rank();
let big_blind = pack.draw(2).unwrap().sorted_by_rank();
println!("small blind: {}", small_blind.to_string());
println!("big blind: {}", big_blind.to_string());
let flop = pack.draw(3).unwrap();
let turn = pack.draw(1).unwrap();
let river = pack.draw(1).unwrap();
println!();
println!("flop : {}", flop.to_string());
println!("turn : {}", turn.to_string());
println!("river: {}", river.to_string());
// Now, let's validate that the cards when collected back together are a valid Standard52
// deck of cards.
let reconstituted_pile =
Pile::<Standard52>::pile_on(&*vec![pack, small_blind, big_blind, flop, turn, river]);
assert!(Standard52::deck().same(&reconstituted_pile));
}
§Details
The goal of this library is to be able to support the creation of card decks of various sizes and suits. Out of the box, the library supports:
- French Deck
- Short Deck
- Skat
- Tarot with Major and Minor Arcana
The project takes advantage of Project Fluent’s Rust support to offer internationalization. Current languages supported are English and German.
§Responsibilities
- Represent a specific type of card deck.
- Validate that a collection of cards is valid for that type of deck.
- Create a textual representation of a deck that can be serialized and deserialized.
- Shuffle a deck
- Verify that a specific card is playable given a set of discards.
§Examples
The library has several examples programs, including demo
which shows you the different decks
available.
For the traditional 54 card French Deck with Jokers:
❯ cargo run --example demo -- --french -v
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.06s
Running `target/debug/examples/cli --french --verbose`
French Deck: B🃟 L🃟 A♠ K♠ Q♠ J♠ T♠ 9♠ 8♠ 7♠ 6♠ 5♠ 4♠ 3♠ 2♠ A♥ K♥ Q♥ J♥ T♥ 9♥ 8♥ 7♥ 6♥ 5♥ 4♥ 3♥ 2♥ A♦ K♦ Q♦ J♦ T♦ 9♦ 8♦ 7♦ 6♦ 5♦ 4♦ 3♦ 2♦ A♣ K♣ Q♣ J♣ T♣ 9♣ 8♣ 7♣ 6♣ 5♣ 4♣ 3♣ 2♣
French Deck Index: BJ LJ AS KS QS JS TS 9S 8S 7S 6S 5S 4S 3S 2S AH KH QH JH TH 9H 8H 7H 6H 5H 4H 3H 2H AD KD QD JD TD 9D 8D 7D 6D 5D 4D 3D 2D AC KC QC JC TC 9C 8C 7C 6C 5C 4C 3C 2C
French Deck Shuffled: K♣ 7♦ 8♣ Q♥ 6♠ J♦ 4♦ J♥ K♠ 9♥ 6♥ T♥ 2♦ 3♦ 3♣ J♣ 3♥ Q♣ 5♥ Q♦ 3♠ T♣ 7♥ 4♥ K♦ 5♦ 2♠ 6♦ T♠ 8♥ T♦ 7♠ 8♠ 2♣ Q♠ 7♣ A♣ 5♠ A♥ 9♣ 2♥ 9♦ 9♠ 4♠ K♥ 8♦ 5♣ A♦ L🃟 B🃟 A♠ 6♣ 4♣ J♠
Long in English and German:
Joker Full-Color
Joker One-Color
Ace of Spades
King of Spades
Queen of Spades
...
Other decks in the demo program are canasta
, euchre
, short
, pinochle
, skat
, spades
,
standard
, and tarot
.
Other examples are:
cargo run --example bridge
- Prints out a Bridge deal.cargo run --example handandfoot
- Shows how to support more than one decks like in the game Hand and Foot.cargo run --example poker
- A random heads up no-limit Poker deal.
§References
§Other Deck of Cards Libraries
- ascclemens/cards
- locka99/deckofcards-rs
- vsupalov/cards-rs
- droundy/bridge-cards
- Tarot Libraries
§Dependencies
§Dev Dependencies
- term-table
- rstest - Fixture-based test framework for Rust
§TODO
Modules§
Macros§
- card
- This macro is to allow for quick and easy generation of individual cards from the most common
Standard52
deck. - cards
- A macro to create a Pile of Standard52 cards from a string.
- french_
cards - A macro to create a Pile of
French Deck
cards from a string. If the passed in string isn’t valid, it will simply return an empty pile.