use crate::basic::decks::cards::french::FLUENT_KEY_BASE_NAME_FRENCH;
use crate::basic::types::basic_card::BasicCard;
pub use crate::basic::types::basic_pile::BasicPile;
pub use crate::basic::types::card::Card;
use crate::basic::types::combos::Combos;
pub use crate::basic::types::pile::Pile;
use crate::basic::types::pips::Pip;
use crate::prelude::PipType;
use itertools::Itertools;
use std::cell::Cell;
use std::collections::{HashMap, HashSet};
use std::hash::Hash;
use std::str::FromStr;
pub trait DeckedBase {
#[must_use]
fn basic_pile() -> BasicPile {
BasicPile::from(Self::base_vec())
}
#[must_use]
fn basic_pile_cell() -> Cell<BasicPile> {
Cell::from(BasicPile::from(Self::base_vec()))
}
fn base_vec() -> Vec<BasicCard>;
fn colors() -> HashMap<Pip, colored::Color>;
fn deck_name() -> String;
#[must_use]
fn fluent_name_base() -> String {
FLUENT_KEY_BASE_NAME_FRENCH.to_string()
}
fn fluent_deck_key() -> String;
}
pub trait Decked<DeckType>: DeckedBase
where
DeckType: Copy + Default + Ord + DeckedBase + Hash,
{
#[must_use]
fn deck() -> Pile<DeckType> {
Pile::<DeckType>::from(Self::deckvec())
}
#[must_use]
fn decks(count: usize) -> Pile<DeckType> {
Pile::<DeckType>::from(Self::deckvec().repeat(count))
}
#[must_use]
fn deckvec() -> Vec<Card<DeckType>> {
let v = Card::<DeckType>::base_vec();
v.iter().map(|card| Card::<DeckType>::from(*card)).collect()
}
fn demo(verbose: bool) {
Self::deck().demo_cards(verbose);
}
#[must_use]
fn into_cards(base_cards: &[BasicCard]) -> Vec<Card<DeckType>> {
base_cards
.iter()
.map(|card| Card::<DeckType>::from(*card))
.collect()
}
#[must_use]
fn validate() -> bool {
let deck = Self::deck();
Pile::<DeckType>::from_str(&deck.to_string()).is_ok_and(|deckfromstr| {
deck == deck.clone().shuffled().sorted() && deck == deckfromstr
})
}
}
pub trait CKCRevised {
#[must_use]
fn get_ckc_number(&self) -> usize;
#[must_use]
fn ckc_rank_number(&self) -> usize;
#[must_use]
fn ckc_suit_number(&self) -> usize;
#[must_use]
fn ckc_rank_bits(&self) -> usize;
#[must_use]
fn ckc_get_prime(&self) -> usize;
#[must_use]
fn ckc_rank_shift8(&self) -> usize;
}
pub trait Ranged {
fn my_basic_pile(&self) -> BasicPile;
fn combos(&self, k: usize) -> Combos {
let mut hs: HashSet<BasicPile> = HashSet::new();
for combo in self.my_basic_pile().into_iter().combinations(k) {
let pile = BasicPile::from(combo).sorted_by_rank();
hs.insert(pile);
}
let mut combos = hs.into_iter().collect::<Vec<_>>();
combos.sort();
Combos::from(combos)
}
fn combos_with_dups(&self, k: usize) -> Combos {
let mut combos = Combos::default();
for combo in self.my_basic_pile().into_iter().combinations(k) {
let pile = BasicPile::from(combo).sorted_by_rank();
combos.push(pile);
}
combos.sort();
combos.reverse();
combos
}
fn all_of_rank(&self, rank: Pip) -> bool {
self.my_basic_pile().iter().all(|card| card.rank == rank)
}
fn all_of_same_rank(&self) -> bool {
self.my_basic_pile().v().first().is_none_or(|first_card| {
self.my_basic_pile()
.iter()
.all(|card| card.rank == first_card.rank)
})
}
fn all_of_same_suit(&self) -> bool {
self.my_basic_pile().v().first().is_none_or(|first_card| {
self.my_basic_pile()
.iter()
.all(|card| card.suit == first_card.suit)
})
}
fn is_connector(&self) -> bool {
let mut pile = self.my_basic_pile();
pile.sort_by_rank();
pile.v()
.windows(2)
.all(|w| w[0].rank.weight == w[1].rank.weight + 1)
}
fn of_same_or_greater_rank(&self, rank: Pip) -> bool {
self.my_basic_pile().iter().all(|card| card.rank >= rank)
}
#[must_use]
fn filter_cards<F>(&self, filter: F) -> BasicPile
where
F: Fn(&BasicCard) -> bool,
{
self.my_basic_pile()
.iter()
.filter(|&card| filter(card))
.copied()
.collect()
}
#[must_use]
fn cards_of_rank_pip_type(&self, pip_type: PipType) -> BasicPile {
let rank_types_filter = |basic_card: &BasicCard| basic_card.rank.pip_type == pip_type;
self.filter_cards(rank_types_filter)
}
#[must_use]
fn cards_of_suit_pip_type(&self, pip_type: PipType) -> BasicPile {
let rank_types_filter = |basic_card: &BasicCard| basic_card.suit.pip_type == pip_type;
self.filter_cards(rank_types_filter)
}
#[must_use]
fn cards_with_pip_type(&self, pip_type: PipType) -> BasicPile {
let rank_types_filter = |basic_card: &BasicCard| {
basic_card.rank.pip_type == pip_type || basic_card.suit.pip_type == pip_type
};
self.filter_cards(rank_types_filter)
}
fn extract_pips<F>(&self, f: F) -> Vec<Pip>
where
F: Fn(&BasicCard) -> Pip,
{
let set: HashSet<Pip> = self.my_basic_pile().iter().map(f).collect();
let mut vec: Vec<Pip> = set.into_iter().collect::<Vec<_>>();
vec.sort();
vec.reverse();
vec
}
fn map_by_rank(&self) -> HashMap<Pip, BasicPile> {
let mut mappy: HashMap<Pip, BasicPile> = HashMap::new();
for card in &self.my_basic_pile() {
let rank = card.rank;
if let std::collections::hash_map::Entry::Vacant(e) = mappy.entry(rank) {
let pile = BasicPile::from(vec![*card]);
e.insert(pile);
} else if let Some(pile) = mappy.get_mut(&rank) {
pile.push(*card);
}
}
mappy
}
fn combos_by_rank(&self) -> Combos {
let mappy = self.map_by_rank();
let v: Vec<BasicPile> = mappy.values().map(Clone::clone).collect::<Vec<_>>();
let mut combos = Combos::from(v);
combos.sort_by_length();
combos.reverse();
combos
}
fn pip_index<F>(&self, f: F, joiner: &str) -> String
where
F: Fn(&BasicCard) -> Pip,
{
self.extract_pips(f)
.iter()
.map(|pip| pip.index.to_string())
.collect::<Vec<String>>()
.join(joiner)
}
#[must_use]
fn ranks(&self) -> Vec<Pip> {
self.extract_pips(|card| card.rank)
}
#[must_use]
fn ranks_index(&self, joiner: &str) -> String {
self.pip_index(|card| card.rank, joiner)
}
#[must_use]
fn ranks_by_suit(&self, suit: Pip) -> Option<Vec<Pip>> {
let ranks: Vec<Pip> = self
.my_basic_pile()
.iter()
.filter(|card| card.suit == suit)
.map(|card| card.rank)
.collect();
match ranks.len() {
0 => None,
_ => Some(ranks),
}
}
#[must_use]
fn ranks_index_by_suit(&self, suit: Pip, joiner: &str) -> Option<String> {
self.ranks_by_suit(suit).map(|ranks| {
ranks
.iter()
.map(|pip| pip.index.to_string())
.collect::<Vec<String>>()
.join(joiner)
})
}
#[must_use]
fn suits(&self) -> Vec<Pip> {
self.extract_pips(|card| card.suit)
}
#[must_use]
fn suits_index(&self, joiner: &str) -> String {
self.pip_index(|card| card.suit, joiner)
}
#[must_use]
fn suit_symbol_index(&self, joiner: &str) -> String {
self.suits()
.iter()
.map(|pip| pip.symbol.to_string())
.collect::<Vec<String>>()
.join(joiner)
}
}
#[cfg(test)]
#[allow(non_snake_case, unused_imports)]
mod basic__types__traits_tests {
use super::*;
use crate::basic::decks::standard52::Standard52;
use crate::prelude::{Decked, DeckedBase, FrenchBasicCard};
#[test]
fn basic_pile_cell__not_default() {
let cell = Standard52::basic_pile_cell();
let pile = cell.take();
assert_eq!(pile.len(), Standard52::DECK_SIZE);
assert_ne!(pile.len(), 0);
}
#[test]
fn into_cards__correct_length() {
let base_cards = Standard52::DECK.as_slice();
let cards = Standard52::into_cards(base_cards);
assert_eq!(cards.len(), Standard52::DECK_SIZE);
assert!(!cards.is_empty());
}
#[test]
fn into_cards__not_empty_collection() {
let single = &[FrenchBasicCard::ACE_SPADES];
let cards = Standard52::into_cards(single);
assert_eq!(cards.len(), 1);
assert_ne!(cards[0].base(), FrenchBasicCard::DEUCE_CLUBS);
}
#[test]
fn demo__does_not_panic() {
Standard52::demo(false);
}
#[test]
fn validate__true_for_valid_deck() {
assert!(Standard52::validate());
}
}