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