use crate::basic::decks::cards::canasta::{CanastaBasicCard, FLUENT_KEY_BASE_NAME_CANASTA};
use crate::basic::decks::cards::french::FrenchBasicCard;
use crate::basic::decks::french::French;
use crate::basic::types::basic_card::BasicCard;
use crate::basic::types::card::Card;
use crate::basic::types::pile::Pile;
use crate::basic::types::pips::Pip;
use crate::basic::types::traits::{Decked, DeckedBase};
use colored::Color;
use std::collections::HashMap;
/// [Canasta](https://en.wikipedia.org/wiki/Canasta) deck
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Canasta;
#[expect(clippy::module_name_repetitions)]
pub type CanastaDeck = Pile<Canasta>;
#[expect(clippy::module_name_repetitions)]
pub type CanastaCard = Card<Canasta>;
impl Canasta {
pub const DECK_SIZE: usize = 108;
pub const DECK: [BasicCard; Self::DECK_SIZE] = [
CanastaBasicCard::TREY_HEARTS,
CanastaBasicCard::TREY_HEARTS,
CanastaBasicCard::TREY_DIAMONDS,
CanastaBasicCard::TREY_DIAMONDS,
CanastaBasicCard::BIG_JOKER,
CanastaBasicCard::BIG_JOKER,
CanastaBasicCard::LITTLE_JOKER,
CanastaBasicCard::LITTLE_JOKER,
CanastaBasicCard::DEUCE_SPADES,
CanastaBasicCard::DEUCE_SPADES,
CanastaBasicCard::DEUCE_HEARTS,
CanastaBasicCard::DEUCE_HEARTS,
CanastaBasicCard::DEUCE_DIAMONDS,
CanastaBasicCard::DEUCE_DIAMONDS,
CanastaBasicCard::DEUCE_CLUBS,
CanastaBasicCard::DEUCE_CLUBS,
FrenchBasicCard::ACE_SPADES,
FrenchBasicCard::ACE_SPADES,
FrenchBasicCard::KING_SPADES,
FrenchBasicCard::KING_SPADES,
FrenchBasicCard::QUEEN_SPADES,
FrenchBasicCard::QUEEN_SPADES,
FrenchBasicCard::JACK_SPADES,
FrenchBasicCard::JACK_SPADES,
FrenchBasicCard::TEN_SPADES,
FrenchBasicCard::TEN_SPADES,
FrenchBasicCard::NINE_SPADES,
FrenchBasicCard::NINE_SPADES,
FrenchBasicCard::EIGHT_SPADES,
FrenchBasicCard::EIGHT_SPADES,
FrenchBasicCard::SEVEN_SPADES,
FrenchBasicCard::SEVEN_SPADES,
FrenchBasicCard::SIX_SPADES,
FrenchBasicCard::SIX_SPADES,
FrenchBasicCard::FIVE_SPADES,
FrenchBasicCard::FIVE_SPADES,
FrenchBasicCard::FOUR_SPADES,
FrenchBasicCard::FOUR_SPADES,
FrenchBasicCard::TREY_SPADES,
FrenchBasicCard::TREY_SPADES,
FrenchBasicCard::ACE_HEARTS,
FrenchBasicCard::ACE_HEARTS,
FrenchBasicCard::KING_HEARTS,
FrenchBasicCard::KING_HEARTS,
FrenchBasicCard::QUEEN_HEARTS,
FrenchBasicCard::QUEEN_HEARTS,
FrenchBasicCard::JACK_HEARTS,
FrenchBasicCard::JACK_HEARTS,
FrenchBasicCard::TEN_HEARTS,
FrenchBasicCard::TEN_HEARTS,
FrenchBasicCard::NINE_HEARTS,
FrenchBasicCard::NINE_HEARTS,
FrenchBasicCard::EIGHT_HEARTS,
FrenchBasicCard::EIGHT_HEARTS,
FrenchBasicCard::SEVEN_HEARTS,
FrenchBasicCard::SEVEN_HEARTS,
FrenchBasicCard::SIX_HEARTS,
FrenchBasicCard::SIX_HEARTS,
FrenchBasicCard::FIVE_HEARTS,
FrenchBasicCard::FIVE_HEARTS,
FrenchBasicCard::FOUR_HEARTS,
FrenchBasicCard::FOUR_HEARTS,
FrenchBasicCard::ACE_DIAMONDS,
FrenchBasicCard::ACE_DIAMONDS,
FrenchBasicCard::KING_DIAMONDS,
FrenchBasicCard::KING_DIAMONDS,
FrenchBasicCard::QUEEN_DIAMONDS,
FrenchBasicCard::QUEEN_DIAMONDS,
FrenchBasicCard::JACK_DIAMONDS,
FrenchBasicCard::JACK_DIAMONDS,
FrenchBasicCard::TEN_DIAMONDS,
FrenchBasicCard::TEN_DIAMONDS,
FrenchBasicCard::NINE_DIAMONDS,
FrenchBasicCard::NINE_DIAMONDS,
FrenchBasicCard::EIGHT_DIAMONDS,
FrenchBasicCard::EIGHT_DIAMONDS,
FrenchBasicCard::SEVEN_DIAMONDS,
FrenchBasicCard::SEVEN_DIAMONDS,
FrenchBasicCard::SIX_DIAMONDS,
FrenchBasicCard::SIX_DIAMONDS,
FrenchBasicCard::FIVE_DIAMONDS,
FrenchBasicCard::FIVE_DIAMONDS,
FrenchBasicCard::FOUR_DIAMONDS,
FrenchBasicCard::FOUR_DIAMONDS,
FrenchBasicCard::ACE_CLUBS,
FrenchBasicCard::ACE_CLUBS,
FrenchBasicCard::KING_CLUBS,
FrenchBasicCard::KING_CLUBS,
FrenchBasicCard::QUEEN_CLUBS,
FrenchBasicCard::QUEEN_CLUBS,
FrenchBasicCard::JACK_CLUBS,
FrenchBasicCard::JACK_CLUBS,
FrenchBasicCard::TEN_CLUBS,
FrenchBasicCard::TEN_CLUBS,
FrenchBasicCard::NINE_CLUBS,
FrenchBasicCard::NINE_CLUBS,
FrenchBasicCard::EIGHT_CLUBS,
FrenchBasicCard::EIGHT_CLUBS,
FrenchBasicCard::SEVEN_CLUBS,
FrenchBasicCard::SEVEN_CLUBS,
FrenchBasicCard::SIX_CLUBS,
FrenchBasicCard::SIX_CLUBS,
FrenchBasicCard::FIVE_CLUBS,
FrenchBasicCard::FIVE_CLUBS,
FrenchBasicCard::FOUR_CLUBS,
FrenchBasicCard::FOUR_CLUBS,
FrenchBasicCard::TREY_CLUBS,
FrenchBasicCard::TREY_CLUBS,
];
}
impl DeckedBase for Canasta {
fn base_vec() -> Vec<BasicCard> {
Self::DECK.to_vec()
}
fn colors() -> HashMap<Pip, Color> {
French::colors()
}
fn deck_name() -> String {
"Canasta".to_string()
}
fn fluent_deck_key() -> String {
FLUENT_KEY_BASE_NAME_CANASTA.to_string()
}
}
impl Decked<Self> for Canasta {}
#[cfg(test)]
#[allow(non_snake_case, unused_imports)]
mod basic__decks__canasta_tests {
use super::*;
use crate::basic::types::pile::Pile;
use crate::basic::types::traits::{Decked, Ranged};
#[test]
fn decked__deck() {
assert_eq!(
Canasta::deck().to_string(),
"3♥ 3♥ 3♦ 3♦ B🃟 B🃟 L🃟 L🃟 2♠ 2♠ 2♥ 2♥ 2♦ 2♦ 2♣ 2♣ A♠ A♠ K♠ K♠ Q♠ Q♠ J♠ J♠ T♠ T♠ 9♠ 9♠ 8♠ 8♠ 7♠ 7♠ 6♠ 6♠ 5♠ 5♠ 4♠ 4♠ 3♠ 3♠ A♥ A♥ K♥ K♥ Q♥ Q♥ J♥ J♥ T♥ T♥ 9♥ 9♥ 8♥ 8♥ 7♥ 7♥ 6♥ 6♥ 5♥ 5♥ 4♥ 4♥ A♦ A♦ K♦ K♦ Q♦ Q♦ J♦ J♦ T♦ T♦ 9♦ 9♦ 8♦ 8♦ 7♦ 7♦ 6♦ 6♦ 5♦ 5♦ 4♦ 4♦ A♣ A♣ K♣ K♣ Q♣ Q♣ J♣ J♣ T♣ T♣ 9♣ 9♣ 8♣ 8♣ 7♣ 7♣ 6♣ 6♣ 5♣ 5♣ 4♣ 4♣ 3♣ 3♣"
);
}
#[test]
pub fn ranks_index() {
let pile = Canasta::deck();
let expected = "3~B~L~2~A~K~Q~J~T~9~8~7~6~5~4~3";
let ranks_index = pile.ranks_index("~");
assert_eq!(ranks_index, expected);
}
/// `suits_index` deduplicates and sorts by `Pip` weight, so the result is deterministic
/// regardless of card order. Canasta has 11 distinct suit `Pip` objects (7 canasta-specific
/// + 4 French), some sharing an index character (e.g. two different 'H' pips with different
/// weights), which is why the output contains apparent duplicates.
#[test]
pub fn suits_index() {
let pile = Canasta::deck();
let expected = "H~D~J~S~H~D~C~S~H~D~C";
let suits_index = pile.suits_index("~");
assert_eq!(suits_index, expected);
}
#[test]
fn decked__validate() {
assert!(Canasta::validate());
}
#[test]
fn decked__colors() {
assert!(!Canasta::colors().is_empty());
}
#[test]
fn decked__deck_name() {
assert_eq!(Canasta::deck_name(), "Canasta");
}
#[test]
fn decked__fluent_deck_key() {
assert_eq!(
Canasta::fluent_deck_key(),
FLUENT_KEY_BASE_NAME_CANASTA.to_string()
);
}
}