cardpack/
lib.rs

1#![warn(
2    clippy::all,
3    // clippy::restriction,
4    clippy::pedantic,
5    clippy::nursery,
6    clippy::cargo,
7    clippy::suspicious,
8)]
9#![allow(
10    clippy::disallowed_script_idents,
11    clippy::missing_const_for_fn,
12    clippy::multiple_crate_versions,
13    clippy::self_named_module_files,
14    clippy::struct_field_names
15)]
16#![allow(dead_code)]
17// clippy::as_conversions
18// clippy::float_arithmetic
19// clippy::integer_arithmetic
20// clippy::unwrap_used
21// clippy::expect_used
22// clippy::panic
23// clippy::print_stdout
24// clippy::print_stderr
25// clippy::dbg_macro
26// clippy::todo
27// clippy::unimplemented
28// clippy::wildcard_enum_match_arm
29// clippy::wildcard_imports
30// clippy::mod_module_files
31// clippy::missing_docs_in_private_items
32// clippy::shadow_unrelated
33// clippy::exhaustive_enums
34// clippy::exhaustive_structs
35// clippy::pub_use
36// clippy::pub_with_shorthand
37// clippy::missing_inline_in_public_items
38
39//! [Cardpack](https://crates.io/crates/cardpack) is a library to represent various decks of playing
40//! cards. The library is designed to support the following features:
41//!
42//! - Custom `Rank` and `Suit` [`Pips`](basic::types::pips::Pip).
43//! - Ability to sort a [`Deck`](basic::types::pile::Pile) of [`Cards`](basic::types::card::Card) in various ways.
44//! - Localization of card names using [fluent-templates](https://github.com/XAMPPRocky/fluent-templates).
45//!
46//! ## Overview
47//!
48//! The structure of the library is the following:
49//!
50//! - [`Pile`](basic::types::pile::Pile) - A generic collection of [`Cards`](basic::types::card::Card) that implement the [`DeckedBase`](basic::types::traits::DeckedBase) trait
51//!   - [`Card`](basic::types::card::Card) - A generic wrapper around [`BasicCard`](basic::types::basic_card::BasicCard) that implements the [`DeckedBase`](basic::types::traits::DeckedBase) trait.
52//!     - [`BasicCard`](basic::types::basic_card::BasicCard) - The basic data of a [`Card`](basic::types::card::Card) without any generic constraints. Made up of a `Rank` and `Suit` [`Pip`](basic::types::pips::Pip).
53//!       - [`Pip`](basic::types::pips::Pip) - The basic data of a `Rank` and `Suit`, used for sorting, evaluating, and displaying [`Cards`](basic::types::card::Card).
54//!
55//! The library supports the following decks:
56//!
57//! ## French Deck
58//!
59//! The [`French`](basic::decks::french::French) deck is the foundation [`Deck`](basic::types::pile::Pile)
60//! of playing cards. It is made up of a collection of 54 `Cards` with 13 ranks in each of the four suits,
61//! and two jokers. Most of the other decks are made up on the [`French BasicCards`](basic::decks::cards::french::FrenchBasicCard).
62//!
63//! ```rust
64//! use cardpack::prelude::*;
65//!
66//! let mut french_deck = Pile::<French>::deck();
67//!
68//! // It's also possible to call the deck method directly on the specific generic implementing type:
69//! let mut french_deck = French::deck();
70//!
71//! assert_eq!(french_deck.len(), 54);
72//! assert_eq!(
73//!     french_deck.to_string(),
74//!     "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♣"
75//! );
76//! assert!(french_deck.contains(&Card::<French>::new(FrenchBasicCard::ACE_SPADES)));
77//!
78//! let shuffled = french_deck.shuffled();
79//!
80//! // Use the `french_cards!` macro to parse the shuffled deck as a string:
81//! let parsed = french_cards!(shuffled.to_string().as_str());
82//!
83//! // Verify that the cards, in any order, are the same:
84//! assert!(french_deck.same(&parsed));
85//!
86//! // When sorted, they should be exactly the same:
87//! assert_eq!(parsed.sorted(), french_deck);
88//!
89//! // For a joker card's index string, `B` stands for the Big or Full-Color Joker and `L` for the
90//! // Little or One-Color Joker, with `🃟` being the symbol character for the joker suit.
91//! let jokers = french_deck.draw(2).unwrap();
92//! assert_eq!(jokers.to_string(), "B🃟 L🃟");
93//!
94//! let royal_flush = french_deck.draw(5).unwrap();
95//! assert_eq!(royal_flush.to_string(), "Aâ™  Kâ™  Qâ™  Jâ™  Tâ™ ");
96//! assert_eq!(royal_flush.index(), "AS KS QS JS TS");
97//!
98//! // The original deck should now have five cards less:
99//! assert_eq!(french_deck.len(), 47);
100//!
101//! // Cards can provide a longer description in English and German:
102//! assert_eq!(Card::<French>::new(FrenchBasicCard::ACE_SPADES).fluent_name_default(), "Ace of Spades");
103//! assert_eq!(Card::<French>::new(FrenchBasicCard::QUEEN_HEARTS).fluent_name(&FluentName::DEUTSCH), "Dame Herzen");
104//! ```
105//!
106//! At some point I would love to add support for more languages.
107//!
108//! ## Standard 52 Card Deck
109//!
110//! A [`Standard52`](basic::decks::standard52::Standard52) deck is a
111//! [`French`](basic::decks::french::French) deck without the two jokers.
112//!
113//! ```rust
114//! use cardpack::prelude::*;
115//!
116//! let mut standard52_deck = Pile::<Standard52>::deck();
117//!
118//! assert_eq!(standard52_deck.len(), 52);
119//! assert_eq!(
120//!     standard52_deck.to_string(),
121//!     "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♣"
122//! );
123//!
124//! // It includes the card! and cards! macros for easy Standard52 card creation:
125//! assert_eq!(card!(AS), Card::<Standard52>::new(FrenchBasicCard::ACE_SPADES));
126//! assert_eq!(cards!("AS KS QS JS TS"), standard52_deck.draw(5).unwrap());
127//! ```
128//!
129//! By default, a [`Deck`](basic::types::pile::Pile) displays the suit symbols when you display the
130//! values. It also has the ability to return the letter values, or what are called "index strings".
131//!
132//! ```rust
133//! use cardpack::prelude::*;
134//!
135//! assert_eq!(
136//!     Pile::<Standard52>::deck().index(),
137//!     "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"
138//! );
139//! ```
140//!
141//! An important thing to remember about the decks is that the cards have their weight inside them
142//! to facilitate sorting. If you wanted a deck for a game of poker where the lowest hand wins, you
143//! would need to create a separate deck file with the card's `Rank` weights inverted. The
144//! [`Razz Deck`](basic::decks::razz::Razz) is an example of this. It is also an example of
145//! how you can create a [`Deck`](basic::types::pile::Pile)  where the
146//! [`BasicCard`](basic::types::basic_card::BasicCard) for the deck are generated programmatically
147//! in YAML instead using the power of [Serde](https://serde.rs/)
148//!
149//! ```
150//! use cardpack::prelude::*;
151//! assert_eq!(Pile::<Razz>::deck().draw(5).unwrap().to_string(), "Aâ™  2â™  3â™  4â™  5â™ ");
152//! assert_eq!(Pile::<Standard52>::deck().draw(5).unwrap().to_string(), "Aâ™  Kâ™  Qâ™  Jâ™  Tâ™ ");
153//! ```
154//!
155//! The raw YAML that was used to create the [`Razz Deck`](basic::decks::razz::Razz) is available
156//! in the source code.
157//!
158//!
159//! Other decks include:
160//!
161//! - [`Canasta`](basic::decks::canasta::Canasta) - 2 Modern decks with the red 3s made jokers.
162//! - [`Euchre24`](basic::decks::euchre24::Euchre24) - A 24 card version of a Euchre deck.
163//! - [`Euchre32`](basic::decks::euchre32::Euchre32) - A 32 card version of a Euchre deck.
164//! - [`ShortDeck`](basic::decks::short::Short) - A 36 card deck with ranks 6 through Ace.
165//! - [`Pinochle`](basic::decks::pinochle::Pinochle) - A 48 card deck with two copies of the 9 through Ace ranks.
166//! - [`Skat`](basic::decks::skat::Skat) - A 32 card German card game with different suits and ranks.
167//! - [`Spades`](basic::decks::spades::Spades) - A Modern deck with the 2 of Clubs and 2 of Diamonds removed.
168//! - [`Tarot`](basic::decks::tarot::Tarot) - A 78 card deck with 22 Major Arcana and 56 Minor Arcana cards.
169//!
170//! In past versions of the library there was a [Hand and Foot](https://gamerules.com/rules/hand-and-foot-card-game/)
171//! deck. This has been removed because it can simply be created using a
172//! [`French`](basic::decks::french::French) and what functionality is available in the Decked trait:
173//!
174//! ```
175//! use cardpack::prelude::*;
176//!
177//! let hand_and_foot_4players = French::decks(4);
178//! assert_eq!(hand_and_foot_4players.len(), 216);
179//!
180//! let hand_and_foot_5players = French::decks(5);
181//! assert_eq!(hand_and_foot_5players.len(), 270);
182//! ```
183//!
184//! ## Custom Deck example:
185//!
186//! Here's a very simple example where we create a tiny deck with only the ace and kink ranks,
187//! and only the spades and hearts suits. Just for fun, we'll include a `tiny!` macro for one `Tiny` card.
188//!
189//! ```rust
190//! use std::collections::HashMap;
191//! use colored::Color;
192//! use cardpack::prelude::*;
193//!
194//! #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
195//! pub struct Tiny {}
196//!
197//! impl Tiny {
198//!    pub const DECK_SIZE: usize = 4;
199//!
200//!     pub const DECK: [BasicCard; Tiny::DECK_SIZE] = [
201//!         FrenchBasicCard::ACE_SPADES,
202//!         FrenchBasicCard::KING_SPADES,
203//!         FrenchBasicCard::ACE_HEARTS,
204//!         FrenchBasicCard::KING_HEARTS,
205//!     ];
206//! }
207//!
208//! impl DeckedBase for Tiny {
209//!     fn base_vec() -> Vec<BasicCard> {
210//!         Tiny::DECK.to_vec()
211//!     }
212//!
213//!     fn colors() -> HashMap<Pip, Color> {
214//!         Standard52::colors()
215//!     }
216//!
217//!     fn deck_name() -> String {
218//!         "Tiny".to_string()
219//!     }
220//!
221//!     fn fluent_deck_key() -> String {
222//!         FLUENT_KEY_BASE_NAME_FRENCH.to_string()
223//!     }
224//! }
225//!
226//! // Let's you call Decked methods directly on the Tiny type:
227//! impl Decked<Tiny> for Tiny {}
228//!
229//! macro_rules! tiny {
230//!     (AS) => {
231//!         Card::<Tiny>::new(FrenchBasicCard::ACE_SPADES)
232//!     };
233//!     (KS) => {
234//!         Card::<Tiny>::new(FrenchBasicCard::KING_SPADES)
235//!     };
236//!     (AH) => {
237//!         Card::<Tiny>::new(FrenchBasicCard::ACE_HEARTS)
238//!     };
239//!     (KH) => {
240//!         Card::<Tiny>::new(FrenchBasicCard::KING_HEARTS)
241//!     };
242//!     (__) => {
243//!         Card::<Tiny>::default()
244//!     };
245//! }
246//!
247//! let mut deck = Tiny::deck();
248//!
249//! assert_eq!(deck.to_string(), "A♠ K♠ A♥ K♥");
250//!
251//! // Every deck comes with the Ranged trait automatically:
252//! assert_eq!(deck.combos(2).to_string(), "A♠ K♠, A♠ A♥, A♠ K♥, K♠ K♥, A♥ K♠, A♥ K♥");
253//!
254//! // Deal from the top of the deck:
255//! assert_eq!(deck.draw_first().unwrap().to_string(), "Aâ™ ");
256//!
257//! // Deal from the bottom of the deck:
258//! assert_eq!(deck.draw_last().unwrap().to_string(), "K♥");
259//!
260//! // Should be two cards remaining:
261//! assert_eq!(deck.len(), 2);
262//! assert_eq!(deck.index(), "KS AH");
263//!
264//! // Draw a remaining card:
265//! assert_eq!(deck.draw_first().unwrap(), tiny!(KS));
266//!
267//! // Draw the last card:
268//! assert_eq!(deck.draw_last().unwrap(), tiny!(AH));
269//!
270//! // And now the deck is empty:
271//! assert!(deck.draw_first().is_none());
272//! assert!(deck.draw_last().is_none());
273//!
274//! // Of all the tests you could use to make sure that your deck is setup correctly, the most
275//! // fundamental is the validate method.
276//! assert!(Tiny::validate());
277//! ```
278
279#![allow(clippy::needless_doctest_main)]
280#![cfg_attr(doc, doc = include_str!("../README.md"))]
281
282pub mod basic;
283pub mod common;
284pub mod localization;
285pub mod prelude;