Crate cardpack

Crate cardpack 

Source
Expand description

Cardpack is a library to represent various decks of playing cards. The library is designed to support the following features:

§Overview

The structure of the library is the following:

  • Pile - A generic collection of Cards that implement the DeckedBase trait
    • Card - A generic wrapper around BasicCard that implements the DeckedBase trait.
      • BasicCard - The basic data of a Card without any generic constraints. Made up of a Rank and Suit Pip.
        • Pip - The basic data of a Rank and Suit, used for sorting, evaluating, and displaying Cards.

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

Build and Test Crates.io Version Rustdocs

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:

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

§Dependencies

§Dev Dependencies

§TODO

Modules§

basic
common
localization
prelude

Macros§

basic
basic_cell
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.