use crate::types::playing_card::PlayingCard;
use crate::types::U32Card;
use bitvec::field::BitField;
use bitvec::prelude::{BitArray, BitSlice, BitVec, Msb0};
use cardpack::Card;
use ckc_rs::{HandError, PokerCard};
use std::fmt;
use std::fmt::{Display, Formatter};
use wyz::FmtForward;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct BitCard(BitArray<Msb0, [u8; 4]>);
impl BitCard {
#[must_use]
pub fn new(b: BitArray<Msb0, [u8; 4]>) -> BitCard {
BitCard(b)
}
#[must_use]
pub fn display(&self, split: bool) -> String {
let mut word_string = String::with_capacity(35);
let start_bit: usize = 0;
let bits = start_bit..start_bit + 32;
for (bit, idx) in self.0.as_bitslice().iter().by_val().zip(bits) {
word_string.push_str(if bit { "1" } else { "0" });
if split && idx % 8 == 7 && idx % 32 != 31 {
word_string.push(' ');
}
}
word_string
}
#[must_use]
pub fn as_bitarray(&self) -> BitArray<Msb0, [u8; 4]> {
self.0
}
#[must_use]
pub fn as_bitslice(&self) -> &BitSlice<Msb0, u8> {
self.0.as_bitslice()
}
#[must_use]
pub fn get_rank(&self) -> cardpack::Rank {
match self.get_rank_bitslice().trailing_zeros() {
12 => cardpack::Rank::new(cardpack::ACE),
11 => cardpack::Rank::new(cardpack::KING),
10 => cardpack::Rank::new(cardpack::QUEEN),
9 => cardpack::Rank::new(cardpack::JACK),
8 => cardpack::Rank::new(cardpack::TEN),
7 => cardpack::Rank::new(cardpack::NINE),
6 => cardpack::Rank::new(cardpack::EIGHT),
5 => cardpack::Rank::new(cardpack::SEVEN),
4 => cardpack::Rank::new(cardpack::SIX),
3 => cardpack::Rank::new(cardpack::FIVE),
2 => cardpack::Rank::new(cardpack::FOUR),
1 => cardpack::Rank::new(cardpack::THREE),
0 => cardpack::Rank::new(cardpack::TWO),
_ => cardpack::Rank::default(),
}
}
#[must_use]
pub fn get_rank_bitslice(&self) -> &BitSlice<Msb0, u8> {
&self.0[..16]
}
#[must_use]
pub fn get_suit(&self) -> cardpack::Suit {
match self.get_suit_bitslice().load_le::<u8>() {
8 => cardpack::Suit::new(cardpack::SPADES),
4 => cardpack::Suit::new(cardpack::HEARTS),
2 => cardpack::Suit::new(cardpack::DIAMONDS),
1 => cardpack::Suit::new(cardpack::CLUBS),
_ => cardpack::Suit::default(),
}
}
#[must_use]
pub fn get_suit_bitslice(&self) -> &BitSlice<Msb0, u8> {
&self.0[16..20]
}
#[must_use]
pub fn get_suit_binary_signature(&self) -> u32 {
let s = self.get_suit_bitslice().load_be::<u32>();
s << 12
}
#[must_use]
pub fn is_blank(&self) -> bool {
self.0.count_zeros() == 32
}
#[must_use]
pub fn and(&self, bc: &BitSlice<Msb0, u8>) -> BitVec<Msb0, u8> {
self.as_bitslice().to_bitvec() | bc.to_bitvec()
}
#[must_use]
pub fn or(&self, bc: &BitSlice<Msb0, u8>) -> BitVec<Msb0, u8> {
self.as_bitslice().to_bitvec() | bc.to_bitvec()
}
#[must_use]
pub fn or_rank_bitslice(&self, bc: &BitSlice<Msb0, u8>) -> BitVec<Msb0, u8> {
self.get_rank_bitslice().to_bitvec() | bc.to_bitvec()
}
#[must_use]
pub fn and_suit_bitslice(&self, bc: &BitSlice<Msb0, u8>) -> BitVec<Msb0, u8> {
self.get_suit_bitslice().to_bitvec() & bc.to_bitvec()
}
#[must_use]
pub fn or_suit_bitslice(&self, bc: &BitSlice<Msb0, u8>) -> BitVec<Msb0, u8> {
self.get_suit_bitslice().to_bitvec() | bc.to_bitvec()
}
#[must_use]
pub fn to_bitvec(&self) -> BitVec<Msb0, u8> {
self.0.to_bitvec()
}
#[must_use]
pub fn to_card(&self) -> cardpack::Card {
if self.is_blank() {
return cardpack::Card::default();
}
cardpack::Card::new(self.get_rank(), self.get_suit())
}
#[must_use]
pub fn to_poker_card(&self) -> U32Card {
self.as_bitslice().load_be::<u32>()
}
fn set_rank(&mut self, card: &cardpack::Card) {
self.0[20..24].store_be(card.rank.weight);
}
fn set_rank_flag(&mut self, card: &cardpack::Card) {
match card.rank.weight {
12 => self.0.set(3, true), 11 => self.0.set(4, true), 10 => self.0.set(5, true), 9 => self.0.set(6, true), 8 => self.0.set(7, true), 7 => self.0.set(8, true), 6 => self.0.set(9, true), 5 => self.0.set(10, true), 4 => self.0.set(11, true), 3 => self.0.set(12, true), 2 => self.0.set(13, true), 1 => self.0.set(14, true), 0 => self.0.set(15, true), _ => (),
}
}
fn set_rank_prime(&mut self, card: &cardpack::Card) {
self.0[26..32].store_be(card.rank.prime);
}
fn set_suit(&mut self, card: &cardpack::Card) {
match card.suit.weight {
4 => self.0.set(16, true), 3 => self.0.set(17, true), 2 => self.0.set(18, true), 1 => self.0.set(19, true), _ => (),
}
}
}
impl Default for BitCard {
fn default() -> BitCard {
BitCard::new(BitArray::zeroed())
}
}
impl Display for BitCard {
fn fmt(&self, fmt: &mut Formatter<'_>) -> std::fmt::Result {
write!(fmt, "{}", self.display(true))
}
}
impl fmt::Binary for BitCard {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Binary::fmt(&self.to_poker_card(), f)
}
}
impl From<&cardpack::Card> for BitCard {
fn from(value: &Card) -> Self {
let mut bit_card: BitCard = BitCard::default();
bit_card.set_rank(value);
bit_card.set_rank_flag(value);
bit_card.set_rank_prime(value);
bit_card.set_suit(value);
bit_card
}
}
impl From<PlayingCard> for BitCard {
fn from(playing_card: PlayingCard) -> Self {
BitCard::from(playing_card.as_u32())
}
}
impl From<U32Card> for BitCard {
fn from(number: U32Card) -> Self {
let mut bc: BitCard = BitCard::default();
if number == 0_u32 {
return bc;
}
bc.0[..32].store_be(number);
bc
}
}
impl TryFrom<&'static str> for BitCard {
type Error = HandError;
fn try_from(index: &'static str) -> Result<Self, Self::Error> {
let c = cardpack::Standard52::card_from_index(index);
if c.is_valid() {
Ok(BitCard::from(&c))
} else {
Err(HandError::InvalidCard)
}
}
}
#[allow(clippy::module_name_repetitions)]
pub struct AnnotatedBitCard(BitCard);
impl AnnotatedBitCard {
#[must_use]
pub fn new(bit_card: BitCard) -> AnnotatedBitCard {
AnnotatedBitCard(bit_card)
}
}
impl Display for AnnotatedBitCard {
fn fmt(&self, fmt: &mut Formatter<'_>) -> std::fmt::Result {
let mut out = fmt.debug_list();
let mut mark_string = String::with_capacity(35);
mark_string.push_str("xxxAKQJT 98765432 SHDCrrrr xxpppppp");
out.entry(&(self.0.display(true)).fmt_display());
out.entry(&(&mark_string).fmt_display());
out.finish()
}
}
#[cfg(test)]
#[allow(non_snake_case)]
mod bit_card_tests {
use super::*;
use crate::types::playing_card::PlayingCard;
use rstest::rstest;
#[test]
fn len() {
assert_eq!(BitCard::default().0.len(), 32);
}
#[test]
fn from__card() {
let card = cardpack::Standard52::card_from_index("K♦");
let cactusKevCard: BitCard = BitCard::from(&card);
assert_eq!(
"00001000 00000000 00101011 00100101",
cactusKevCard.display(true)
);
}
#[test]
fn from__card__complete() {
let standard52 = cardpack::Standard52::default();
for card in standard52.deck {
let cactusKevCard: BitCard = BitCard::from(&card);
let s = format!("{:032b}", cactusKevCard).to_string();
assert_eq!(s, cactusKevCard.display(false));
}
}
#[test]
fn from_index() {
let card = cardpack::Standard52::card_from_index("KS");
let expected = BitCard::from(&card);
let actual = BitCard::try_from("KS").unwrap();
assert_eq!(expected, actual);
}
#[test]
fn from_index__invalid() {
assert!(BitCard::try_from("xx").is_err());
}
#[test]
fn from_poker_card() {
let ace_spades: u32 = 268_471_337;
let s = "00010000 00000000 10001100 00101001".to_string();
let actual = BitCard::from(ace_spades);
assert_eq!(actual.display(true), s);
assert_eq!(actual, BitCard::try_from("A♤").unwrap());
}
#[rstest]
#[case("A♠", 268_471_337)]
#[case("K♠", 134_253_349)]
#[case("Q♠", 67_144_223)]
#[case("J♠", 33_589_533)]
#[case("T♠", 16_812_055)]
#[case("9♠", 8_423_187)]
#[case("8♠", 4_228_625)]
#[case("7♠", 2_131_213)]
#[case("6♠", 1_082_379)]
#[case("5♠", 557_831)]
#[case("4♠", 295_429)]
#[case("3♠", 164_099)]
#[case("2♠", 98_306)]
#[case("A♥", 268_454_953)]
#[case("K♥", 134_236_965)]
#[case("Q♥", 67_127_839)]
#[case("J♥", 33_573_149)]
#[case("T♥", 16_795_671)]
#[case("9♥", 8_406_803)]
#[case("8♥", 4_212_241)]
#[case("7♥", 2_114_829)]
#[case("6♥", 1_065_995)]
#[case("5♥", 541_447)]
#[case("4♥", 279_045)]
#[case("3♥", 147_715)]
#[case("2♥", 81_922)]
#[case("A♦", 268_446_761)]
#[case("K♦", 134_228_773)]
#[case("Q♦", 67_119_647)]
#[case("J♦", 33_564_957)]
#[case("T♦", 16_787_479)]
#[case("9♦", 8_398_611)]
#[case("8♦", 4_204_049)]
#[case("7♦", 2_106_637)]
#[case("6♦", 1_057_803)]
#[case("5♦", 533_255)]
#[case("4♦", 270_853)]
#[case("3♦", 139_523)]
#[case("2♦", 73_730)]
#[case("A♣", 268_442_665)]
#[case("K♣", 134_224_677)]
#[case("Q♣", 67_115_551)]
#[case("J♣", 33_560_861)]
#[case("T♣", 16_783_383)]
#[case("9♣", 8_394_515)]
#[case("8♣", 4_199_953)]
#[case("7♣", 2_102_541)]
#[case("6♣", 1_053_707)]
#[case("5♣", 529_159)]
#[case("4♣", 266_757)]
#[case("3♣", 135_427)]
#[case("2♣", 69_634)]
fn from_poker_card__comprehensive(#[case] expected: &'static str, #[case] input: u32) {
let actual = BitCard::from(input);
assert_eq!(actual, BitCard::try_from(expected).unwrap());
}
#[test]
fn from_u64__comprehensive_too() {
let standard52 = cardpack::Standard52::default();
for card in standard52.deck {
let actual = BitCard::from(PlayingCard::from(&card).as_u32());
assert_eq!(actual.to_card(), card);
}
}
#[test]
fn to_poker_card() {
let standard52 = cardpack::Standard52::new_shuffled();
for card in standard52.deck {
let bit_card = BitCard::from(&card);
assert_eq!(bit_card.to_poker_card(), PlayingCard::from(&card).as_u32());
}
}
#[test]
fn to_card() {
let standard52 = cardpack::Standard52::default();
for card in standard52.deck {
let bit_card = BitCard::from(&card);
assert_eq!(bit_card.to_card(), card);
let bit_card = BitCard::from(&card);
assert_eq!(bit_card.to_card(), card);
let leaked: &'static str = Box::leak(card.clone().index.into_boxed_str());
let bit_card = BitCard::try_from(leaked).unwrap();
assert_eq!(bit_card.to_card(), card);
}
}
#[test]
fn get_rank() {
assert_eq!(
BitCard::try_from("AS").unwrap().get_rank(),
cardpack::Rank::new(cardpack::ACE)
);
assert_eq!(
BitCard::try_from("KS").unwrap().get_rank(),
cardpack::Rank::new(cardpack::KING)
);
assert_eq!(
BitCard::try_from("QS").unwrap().get_rank(),
cardpack::Rank::new(cardpack::QUEEN)
);
assert_eq!(
BitCard::try_from("JS").unwrap().get_rank(),
cardpack::Rank::new(cardpack::JACK)
);
assert_eq!(
BitCard::try_from("TS").unwrap().get_rank(),
cardpack::Rank::new(cardpack::TEN)
);
assert_eq!(
BitCard::try_from("9S").unwrap().get_rank(),
cardpack::Rank::new(cardpack::NINE)
);
assert_eq!(
BitCard::try_from("8S").unwrap().get_rank(),
cardpack::Rank::new(cardpack::EIGHT)
);
assert_eq!(
BitCard::try_from("7S").unwrap().get_rank(),
cardpack::Rank::new(cardpack::SEVEN)
);
assert_eq!(
BitCard::try_from("6S").unwrap().get_rank(),
cardpack::Rank::new(cardpack::SIX)
);
assert_eq!(
BitCard::try_from("5S").unwrap().get_rank(),
cardpack::Rank::new(cardpack::FIVE)
);
assert_eq!(
BitCard::try_from("4S").unwrap().get_rank(),
cardpack::Rank::new(cardpack::FOUR)
);
assert_eq!(
BitCard::try_from("3S").unwrap().get_rank(),
cardpack::Rank::new(cardpack::THREE)
);
assert_eq!(
BitCard::try_from("2S").unwrap().get_rank(),
cardpack::Rank::new(cardpack::TWO)
);
}
#[test]
fn get_rank_bitslice() {
let card: BitCard = BitCard::try_from("KS").unwrap();
assert_eq!(
"[00001000, 00000000]",
format!("{:b}", card.get_rank_bitslice())
);
}
#[test]
fn get_suit() {
assert_eq!(
BitCard::try_from("AS").unwrap().get_suit(),
cardpack::Suit::new(cardpack::SPADES)
);
assert_eq!(
BitCard::try_from("2H").unwrap().get_suit(),
cardpack::Suit::new(cardpack::HEARTS)
);
assert_eq!(
BitCard::try_from("3♦").unwrap().get_suit(),
cardpack::Suit::new(cardpack::DIAMONDS)
);
assert_eq!(
BitCard::try_from("TC").unwrap().get_suit(),
cardpack::Suit::new(cardpack::CLUBS)
)
}
#[test]
fn get_suit_bitslice() {
let card: BitCard = BitCard::try_from("KS").unwrap();
assert_eq!("[1000]", format!("{:04b}", card.get_suit_bitslice()));
let card: BitCard = BitCard::try_from("KH").unwrap();
assert_eq!("[0100]", format!("{:04b}", card.get_suit_bitslice()));
let card: BitCard = BitCard::try_from("K♦").unwrap();
assert_eq!("[0010]", format!("{:04b}", card.get_suit_bitslice()));
let card: BitCard = BitCard::try_from("KC").unwrap();
assert_eq!("[0001]", format!("{:04b}", card.get_suit_bitslice()));
}
#[rstest]
#[case("2C", 4096)]
#[case("2D", 8192)]
#[case("2H", 16384)]
#[case("2♠", 32768)]
fn get_suit_binary_signature(#[case] index: &'static str, #[case] expected: u32) {
let bit_card: BitCard = BitCard::try_from(index).unwrap();
assert_eq!(bit_card.get_suit_binary_signature(), expected);
}
#[test]
fn is_blank() {
assert!(BitCard::default().is_blank());
}
#[test]
fn is_blank__false() {
assert!(!BitCard::try_from("KS").unwrap().is_blank());
}
#[test]
fn or_rank_bitslice() {
let ace_spades = BitCard::try_from("AS").unwrap();
let king_spades = BitCard::try_from("KS").unwrap();
let result = ace_spades.or_rank_bitslice(&king_spades.get_rank_bitslice());
assert_eq!(format!("{}", result), "[00011000, 00000000]");
}
#[test]
fn and_suit_bitslice() {
let king_spades: BitCard = BitCard::try_from("KS").unwrap();
let queen_spades: BitCard = BitCard::try_from("QS").unwrap();
let actual = king_spades.or_suit_bitslice(&queen_spades.get_suit_bitslice());
assert_eq!("[1000]", format!("{:04b}", actual));
}
#[test]
fn or_suit_bitslice() {
let king_spades: BitCard = BitCard::try_from("KS").unwrap();
let king_hearts: BitCard = BitCard::try_from("KH").unwrap();
let king_diamonds: BitCard = BitCard::try_from("KD").unwrap();
let king_clubs: BitCard = BitCard::try_from("KC").unwrap();
let actual = king_spades.or_suit_bitslice(&king_hearts.get_suit_bitslice());
assert_eq!("[1100]", format!("{:04b}", actual));
let actual = king_diamonds.or_suit_bitslice(&actual);
assert_eq!("[1110]", format!("{:04b}", actual));
let actual = king_clubs.or_suit_bitslice(&actual);
assert_eq!("[1111]", format!("{:04b}", actual));
}
#[test]
fn set_rank() {
let mut bit_card: BitCard = BitCard::default();
let card = cardpack::Standard52::card_from_index("K♦");
bit_card.set_rank(&card);
assert_eq!(
"00000000 00000000 00001011 00000000",
bit_card.display(true)
);
}
#[test]
fn set_rank_flag() {
let mut bit_card: BitCard = BitCard::default();
let card = cardpack::Standard52::card_from_index("K♦");
bit_card.set_rank_flag(&card);
assert_eq!(
"00001000 00000000 00000000 00000000",
bit_card.display(true)
);
}
#[test]
fn set_rank_prime() {
let mut bit_card: BitCard = BitCard::default();
let card = cardpack::Standard52::card_from_index("K♦");
bit_card.set_rank_prime(&card);
assert_eq!(
"00000000 00000000 00000000 00100101",
bit_card.display(true)
);
}
#[test]
fn set_suit() {
let mut bit_card: BitCard = BitCard::default();
let card = cardpack::Standard52::card_from_index("KS");
bit_card.set_suit(&card);
assert_eq!(
"00000000 00000000 10000000 00000000",
bit_card.display(true)
);
let card = cardpack::Standard52::card_from_index("KH");
let mut bit_card: BitCard = BitCard::default();
bit_card.set_suit(&card);
assert_eq!(
"00000000 00000000 01000000 00000000",
bit_card.display(true)
);
let card = cardpack::Standard52::card_from_index("K♦");
let mut bit_card: BitCard = BitCard::default();
bit_card.set_suit(&card);
assert_eq!(
"00000000 00000000 00100000 00000000",
bit_card.display(true)
);
let card = cardpack::Standard52::card_from_index("KC");
let mut bit_card: BitCard = BitCard::default();
bit_card.set_suit(&card);
assert_eq!(
"00000000 00000000 00010000 00000000",
bit_card.display(true)
);
}
}