crusty_cards

Crates.iocrusty_cards
lib.rscrusty_cards
version0.0.3
created_at2025-12-22 21:15:55.051161+00
updated_at2026-01-15 04:54:22.197118+00
descriptionPlaying Card and Deck Library
homepage
repositoryhttps://github.com/ernman37/crusty_cards
max_upload_size
id2000269
size175,539
Ernest Duckworth IV (ernman37)

documentation

README

crusty_cards

Crates.io Documentation License Downloads

CI codecov Dependency Status Rust

SBOM OpenSSF Scorecard PRs Welcome Contributors Maintenance GitHub code size

A Rust library for working with playing cards and decks. Provides flexible primitives for building card games.

Installation

Add to your Cargo.toml:

[dependencies]
crusty_cards = "x.x.x"

Quick Start

use crusty_cards::{Deck, Standard52, Card, Suit, Rank};

// Create a standard 52-card deck
let mut deck = Deck::from_factory(Standard52);

// Shuffle the deck
deck.shuffle();

// Deal cards
let card = deck.deal().unwrap();
println!("{}", card);  // e.g., "A♠"

// Create specific cards
let ace_of_spades = Card::new(Suit::Spades, Rank::Ace);

Features

Cards

use crusty_cards::{Card, Suit, Rank};

let card = Card::new(Suit::Hearts, Rank::Queen);

// Access properties
card.suit();   // Suit::Hearts
card.rank();   // Rank::Queen
card.color();  // Color::Red

// Parse from string (supports Unicode)
let card: Card = "K♠".parse().unwrap();
let card: Card = "King♠".parse().unwrap();
let card: Card = "KingS".parse().unwrap();
let card: Card = "Kspades".parse().unwrap();
let card: Card = "Kingspades".parse().unwrap();
let card: Card = "KiNgsPaDeS".parse().unwrap();

Decks

use crusty_cards::{Deck, Standard52, Standard54};

// Create decks
let deck = Deck::from_factory(Standard52);      // 52 cards
let deck = Deck::from_factory(Standard54);      // 54 cards (with jokers)
let deck: Deck = "A♠ K♠ Q♠".parse().unwrap();   // From string

// Shuffling
deck.shuffle();              // Random shuffle
deck.riffle_shuffle();       // Riffle shuffle
deck.overhand_shuffle();     // Overhand shuffle
deck.cut(26);                // Cut at position

// Dealing
let card = deck.deal();              // From top
let card = deck.deal_bottom();       // From bottom
let cards = deck.deal_n(5);          // Multiple cards from top
let cards = deck.deal_n_bottom(5);   // Multiple cards from bottom

// Adding cards
deck.add_card(card);         // To top
deck.add_cards(cards);       // To top
deck.add_card_bottom(card);  // To bottom
deck.add_cards_bottom(card); // To bottom

// Inspection
deck.len();
deck.is_empty();
deck.peek();
deck.contains(&card);
deck.find(&card);

Custom Sorting

use crusty_cards::{Deck, Standard52, StandardComparator, AceLowComparator};

let mut deck = Deck::from_factory(Standard52);

// Sort with Ace high (default)
deck.sort_by_comparator(&StandardComparator);

// Sort with Ace low
deck.sort_by_comparator(&AceLowComparator);

// Custom sort
deck.sort_by(|a, b| b.rank().value().cmp(&a.rank().value()));

Serialization

use crusty_cards::{Deck, Standard52};

let deck = Deck::from_factory(Standard52);

// JSON
let json = deck.to_json().unwrap();
let deck = Deck::from_json(&json).unwrap();

// YAML
let yaml = deck.to_yaml().unwrap();
let deck = Deck::from_yaml(&yaml).unwrap();

// CSV
let csv = deck.as_csv();
let deck = Deck::from_csv(&csv).unwrap();

// Custom String
let custom = deck.as_str_delimiter('\n')
let deck = Deck::from_str_delimiter('\n')

Operator Overloads

use crusty_cards::{Deck, Card, Suit, Rank};

let mut deck = Deck::default();
let card = Card::new(Suit::Spades, Rank::Ace);

// Add/remove cards
deck += card;           // Add card
deck -= card;           // Remove card

// Combine decks
let combined = deck1 + deck2;

// Duplicate deck
let double_deck = deck * 2;

// Index access
let card = deck[0];
deck[0] = another_card;

Custom Deck Factories

use crusty_cards::{Card, Deck, DeckFactory, Suit, Rank};
use std::collections::VecDeque;

struct PinochleDeck;

impl DeckFactory for PinochleDeck {
    fn generate(&self) -> VecDeque<Card> {
        let ranks = [Rank::Nine, Rank::Ten, Rank::Jack, Rank::Queen, Rank::King, Rank::Ace];
        let mut cards = VecDeque::new();

        // Two of each card
        for _ in 0..2 {
            for suit in Suit::ALL {
                for rank in ranks {
                    cards.push_back(Card::new(suit, rank));
                }
            }
        }
        cards
    }
}

let deck = Deck::from_factory(PinochleDeck);

Custom Card Comparators

use crusty_cards::{CardComparator, Card, Rank, Suit};
use std::cmp::Ordering;

struct EuchreComparator {
    trump: Suit,
}

impl CardComparator for EuchreComparator {
    fn rank_value(&self, rank: Rank) -> i32 {
        match rank {
            Rank::Nine => 0,
            Rank::Ten => 1,
            Rank::Jack => 2,  // Adjusted by suit in compare()
            Rank::Queen => 3,
            Rank::King => 4,
            Rank::Ace => 5,
            _ => -1,
        }
    }
}

Thread Safety

Card and Deck are both Send and Sync. For concurrent mutable access, wrap in Arc<Mutex<Deck>> or Arc<RwLock<Deck>>.

License

BEERWARE - If we meet one day and you think this stuff was worth it, you can buy me a beer in return.

Commit count: 35

cargo fmt