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.
Setup
Build and run common tasks with GNU make:
make
Run make help to see all available targets.
Usage
use *;
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, German, French, Latin, and Klingon.
Cargo features
cardpack is pure by default: a bare dependency is an alloc-only,
no_std, no-I/O domain kernel. Every dependency-bearing or I/O-bearing
capability is gated behind a Cargo feature, so consumers opt in to exactly
what they need:
| Feature | Default | Pulls in | What it turns on |
|---|---|---|---|
full |
no | everything below | Umbrella turning on std + i18n + colored-display + yaml + serde |
std |
no | libstd | std-only APIs (thread-RNG shuffle, draw_random, etc.) |
i18n |
no | fluent-templates |
FluentName, Named, Card::fluent_name*, localization |
colored-display |
no | colored |
Color, Colorize, Card::color*, Pile::to_color_* |
yaml |
no | serde_norway |
BasicCard::cards_from_yaml_str (pure, in-memory), the Razz deck |
serde |
no | serde |
Serialize/Deserialize derives on Pip/Card/Pile etc. |
std-io |
no | — | BasicCard::cards_from_yaml_file — reads decks from YAML files (std::fs). The crate's one filesystem seam; not in full |
funky |
no | std |
The Balatro-style engine — see Funky below |
To get the previous "batteries-included" behavior, opt into full:
# Full convenience stack (i18n, colored display, YAML, serde):
= { = "0.8", = ["full"] }
# Or trim to just what you need — e.g. the pure kernel plus serde:
= { = "0.8", = ["serde"] }
# Or the pure, no_std, alloc-only kernel with no extra deps at all:
= "0.8"
yaml implies serde (it deserializes into the serde-derived structs).
std-io implies yaml and adds the filesystem reader on top of it; it is the
only feature that lets the crate touch std::fs, and it is intentionally left
out of full so the pure kernel and the convenience stack both stay I/O-free.
Funky — Balatro-style cards
The funky feature is a result of having my mind blown🤯 playing the
amazing solitare game Balatro. It honestly
changed the way I look at playing cards. Suddenly, suits and ranks are
just two of an infinite possible number of pips that can be attached to a
"playing card". I started realizing that there is little difference between
a French Deck of cards and creating heros in the
Evercraft Kata.
The goal of the feature is to see how hard I need to push the architecture of
this library to support decks such as those in Balatro. I guess the big idea
was the MPip, a sort of functional version of a pip on a card.
TBH, this experiment demonstrates the rational behind designing games in flexible languages such as Lua, over tyrannical ones such as my beloved Rust.
There are a couple of use cases that are in the back of my mind for something like this. One is a Balatro score solver, as a way to teach the math mechanics behind the game. The other is a library that would be able to create modded Balatro decks from simple yaml configuration files, similar to what the library already supports in simpler decks.
It is still very much a work in progress, which is documented here:
docs/EPIC-01_Funky.md.
There are two examples to see it in action:
# The four-phase scoring pipeline, phase by phase:
cargo run --example buffoon --features funky
# A seeded four-act tour — round loop, editions, shop & vouchers, spectrals:
cargo run --example funky_tour --features funky
WebAssembly
cardpack compiles cleanly to wasm32-unknown-unknown (browser WASM)
with every feature combination. See docs/wasm.md for
the consumer-side getrandom backend setup, recommended feature
combos, and runtime gotchas. A working example lives at
examples/wasm.rs.
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
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/demo --french -v`
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♠
English | German | French | Latin | Klingon
------------------------ | ------------------------ | ------------------------ | ------------------------ | ------------------------
Joker Full-Color | Joker Großer | Joker Grand | Joker Magnus | Joker qoH'a'
Joker One-Color | Joker Kleiner | Joker Petit | Joker Parvus | Joker qoHHom
Ace of Spades | Ass Spaten | As de Pique | As Spathae | wa'DIch yan
King of Spades | König Spaten | Roi de Pique | Rex Spathae | ta' yan
Queen of Spades | Dame Spaten | Dame de Pique | Regina Spathae | ta'be' yan
Jack of Spades | Bube Spaten | Valet de Pique | Famulus Spathae | toy'wI' yan
Ten of Spades | Zhen Spaten | Dix de Pique | Decem Spathae | wa'maH yan
...
Display a hand of Bridge:
❯ cargo run --example bridge
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.33s
Running `target/debug/examples/bridge`
First, let's deal out a random bridge hand.
Here it is in Portable Bridge Notation:
W:KJT.JT63.K8.QJT9 A75.KQ9874.65.AK Q6432.5.AJ74.853 98.A2.QT932.7642
How does it look as a traditional compass?
NORTH
♠ A 7 5
♥ K Q 9 8 7 4
♦ 6 5
♣ A K
WEST EAST
♠ K J T ♠ Q 6 4 3 2
♥ J T 6 3 ♥ 5
♦ K 8 ♦ A J 7 4
♣ Q J T 9 ♣ 8 5 3
SOUTH
♠ 9 8
♥ A 2
♦ Q T 9 3 2
♣ 7 6 4 2
Now, let's take a PBN Deal String and convert it into a bridge hand.
Here's the original' Portable Bridge Notation:
S:Q42.Q52.AQT943.Q 97.AT93.652.T743 AJT85.J76.KJ.A65 K63.K84.87.KJ982
As a bridge compass:
NORTH
♠ A J T 8 5
♥ J 7 6
♦ K J
♣ A 6 5
WEST EAST
♠ 9 7 ♠ K 6 3
♥ A T 9 3 ♥ K 8 4
♦ 6 5 2 ♦ 8 7
♣ T 7 4 3 ♣ K J 9 8 2
SOUTH
♠ Q 4 2
♥ Q 5 2
♦ A Q T 9 4 3
♣ Q
Other decks in the demo program are canasta, euchre, short, pinochle, skat, spades,
standard, and tarot.
Other examples are:
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.cargo run --example buffoon --features funky- The Balatro four-phase scoring pipeline, phase by phase (see Funky).cargo run --example funky_tour --features funky- A seeded tour of the funky engine: round loop, editions, shop & vouchers, spectral cards.
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