#![cfg_attr(not(test), no_std)]
#![warn(clippy::pedantic)]
#![allow(clippy::unreadable_literal)]
extern crate alloc;
use crate::cards::binary_card::{BinaryCard, BC64};
use crate::parse::get_rank_and_suit;
use strum::EnumIter;
pub mod cards;
pub mod deck;
pub mod hand_rank;
mod lookups;
pub mod parse;
pub type CKCNumber = u32;
pub struct CardNumber;
impl CardNumber {
pub const RANK_FLAG_FILTER: u32 = 0x1FFF0000; pub const RANK_FLAG_SHIFT: u32 = 16;
pub const RANK_PRIME_FILTER: u32 = 0b00111111;
pub const SUIT_FILTER: u32 = 0xF000; pub const SUIT_SHORT_MASK: u32 = 0b1111;
pub const SUIT_SHIFT: u32 = 12;
pub const PAIR: u32 = 536_870_912;
pub const TRIPS: u32 = 1_073_741_824;
pub const QUADS: u32 = 2_147_483_648;
pub const MULTIPLES_FILTER: u32 = 536_870_911;
pub const ACE_SPADES: CKCNumber = 268_471_337;
pub const KING_SPADES: CKCNumber = 134_253_349;
pub const QUEEN_SPADES: CKCNumber = 67_144_223;
pub const JACK_SPADES: CKCNumber = 33_589_533;
pub const TEN_SPADES: CKCNumber = 16_812_055;
pub const NINE_SPADES: CKCNumber = 8_423_187;
pub const EIGHT_SPADES: CKCNumber = 4_228_625;
pub const SEVEN_SPADES: CKCNumber = 2_131_213;
pub const SIX_SPADES: CKCNumber = 1_082_379;
pub const FIVE_SPADES: CKCNumber = 557_831;
pub const FOUR_SPADES: CKCNumber = 295_429;
pub const TREY_SPADES: CKCNumber = 164_099;
pub const DEUCE_SPADES: CKCNumber = 98_306;
pub const ACE_HEARTS: CKCNumber = 268_454_953;
pub const KING_HEARTS: CKCNumber = 134_236_965;
pub const QUEEN_HEARTS: CKCNumber = 67_127_839;
pub const JACK_HEARTS: CKCNumber = 33_573_149;
pub const TEN_HEARTS: CKCNumber = 16_795_671;
pub const NINE_HEARTS: CKCNumber = 8_406_803;
pub const EIGHT_HEARTS: CKCNumber = 4_212_241;
pub const SEVEN_HEARTS: CKCNumber = 2_114_829;
pub const SIX_HEARTS: CKCNumber = 1_065_995;
pub const FIVE_HEARTS: CKCNumber = 541_447;
pub const FOUR_HEARTS: CKCNumber = 279_045;
pub const TREY_HEARTS: CKCNumber = 147_715;
pub const DEUCE_HEARTS: CKCNumber = 81_922;
pub const ACE_DIAMONDS: CKCNumber = 268_446_761;
pub const KING_DIAMONDS: CKCNumber = 134_228_773;
pub const QUEEN_DIAMONDS: CKCNumber = 67_119_647;
pub const JACK_DIAMONDS: CKCNumber = 33_564_957;
pub const TEN_DIAMONDS: CKCNumber = 16_787_479;
pub const NINE_DIAMONDS: CKCNumber = 8_398_611;
pub const EIGHT_DIAMONDS: CKCNumber = 4_204_049;
pub const SEVEN_DIAMONDS: CKCNumber = 2_106_637;
pub const SIX_DIAMONDS: CKCNumber = 1_057_803;
pub const FIVE_DIAMONDS: CKCNumber = 533_255;
pub const FOUR_DIAMONDS: CKCNumber = 270_853;
pub const TREY_DIAMONDS: CKCNumber = 139_523;
pub const DEUCE_DIAMONDS: CKCNumber = 73_730;
pub const ACE_CLUBS: CKCNumber = 268_442_665;
pub const KING_CLUBS: CKCNumber = 134_224_677;
pub const QUEEN_CLUBS: CKCNumber = 67_115_551;
pub const JACK_CLUBS: CKCNumber = 33_560_861;
pub const TEN_CLUBS: CKCNumber = 16_783_383;
pub const NINE_CLUBS: CKCNumber = 8_394_515;
pub const EIGHT_CLUBS: CKCNumber = 4_199_953;
pub const SEVEN_CLUBS: CKCNumber = 2_102_541;
pub const SIX_CLUBS: CKCNumber = 1_053_707;
pub const FIVE_CLUBS: CKCNumber = 529_159;
pub const FOUR_CLUBS: CKCNumber = 266_757;
pub const TREY_CLUBS: CKCNumber = 135_427;
pub const DEUCE_CLUBS: CKCNumber = 69_634;
pub const BLANK: CKCNumber = 0;
#[must_use]
pub fn filter(number: CKCNumber) -> CKCNumber {
<CKCNumber as PokerCard>::filter(number)
}
}
#[cfg(test)]
mod card_number_tests {
use super::*;
#[test]
fn filter() {
assert_eq!(CardNumber::filter(2), CardNumber::BLANK);
assert_eq!(CardNumber::filter(CardNumber::NINE_CLUBS), CardNumber::NINE_CLUBS);
}
}
#[derive(Clone, Copy, Debug, EnumIter, Eq, Hash, PartialEq)]
pub enum CardRank {
ACE = 14,
KING = 13,
QUEEN = 12,
JACK = 11,
TEN = 10,
NINE = 9,
EIGHT = 8,
SEVEN = 7,
SIX = 6,
FIVE = 5,
FOUR = 4,
THREE = 3,
TWO = 2,
BLANK = 0,
}
impl CardRank {
#[must_use]
pub fn from_char(index: char) -> CardRank {
match index {
'A' | 'a' => CardRank::ACE,
'K' | 'k' => CardRank::KING,
'Q' | 'q' => CardRank::QUEEN,
'J' | 'j' => CardRank::JACK,
'T' | 't' | '0' => CardRank::TEN,
'9' => CardRank::NINE,
'8' => CardRank::EIGHT,
'7' => CardRank::SEVEN,
'6' => CardRank::SIX,
'5' => CardRank::FIVE,
'4' => CardRank::FOUR,
'3' => CardRank::THREE,
'2' => CardRank::TWO,
_ => CardRank::BLANK,
}
}
fn bits(self) -> u32 {
1 << (16 + self.number())
}
fn number(self) -> u32 {
match self {
CardRank::ACE => 12,
CardRank::KING => 11,
CardRank::QUEEN => 10,
CardRank::JACK => 9,
CardRank::TEN => 8,
CardRank::NINE => 7,
CardRank::EIGHT => 6,
CardRank::SEVEN => 5,
CardRank::SIX => 4,
CardRank::FIVE => 3,
CardRank::FOUR => 2,
CardRank::THREE => 1,
_ => 0,
}
}
fn prime(self) -> u32 {
match self {
CardRank::ACE => 41,
CardRank::KING => 37,
CardRank::QUEEN => 31,
CardRank::JACK => 29,
CardRank::TEN => 23,
CardRank::NINE => 19,
CardRank::EIGHT => 17,
CardRank::SEVEN => 13,
CardRank::SIX => 11,
CardRank::FIVE => 7,
CardRank::FOUR => 5,
CardRank::THREE => 3,
CardRank::TWO => 2,
CardRank::BLANK => 0,
}
}
fn shift8(self) -> u32 {
self.number() << 8
}
}
#[cfg(test)]
mod card_rank_tests {
use super::*;
use rstest::rstest;
#[rstest]
#[case('A', CardRank::ACE)]
#[case('a', CardRank::ACE)]
#[case('K', CardRank::KING)]
#[case('k', CardRank::KING)]
#[case('Q', CardRank::QUEEN)]
#[case('q', CardRank::QUEEN)]
#[case('J', CardRank::JACK)]
#[case('j', CardRank::JACK)]
#[case('T', CardRank::TEN)]
#[case('t', CardRank::TEN)]
#[case('0', CardRank::TEN)]
#[case('9', CardRank::NINE)]
#[case('8', CardRank::EIGHT)]
#[case('7', CardRank::SEVEN)]
#[case('6', CardRank::SIX)]
#[case('5', CardRank::FIVE)]
#[case('4', CardRank::FOUR)]
#[case('3', CardRank::THREE)]
#[case('2', CardRank::TWO)]
#[case('_', CardRank::BLANK)]
#[case(' ', CardRank::BLANK)]
fn from_char(#[case] input: char, #[case] expected: CardRank) {
assert_eq!(expected, CardRank::from_char(input));
}
}
#[derive(Clone, Copy, Debug, EnumIter, Eq, Hash, PartialEq)]
pub enum CardSuit {
SPADES = 4,
HEARTS = 3,
DIAMONDS = 2,
CLUBS = 1,
BLANK = 0,
}
impl CardSuit {
#[must_use]
pub fn binary_signature(&self) -> u32 {
match self {
CardSuit::SPADES => 0x8000,
CardSuit::HEARTS => 0x4000,
CardSuit::DIAMONDS => 0x2000,
CardSuit::CLUBS => 0x1000,
CardSuit::BLANK => 0,
}
}
#[must_use]
pub fn from_char(symbol: char) -> CardSuit {
match symbol {
'♤' | '♠' | 'S' | 's' => CardSuit::SPADES,
'♡' | '♥' | 'H' | 'h' => CardSuit::HEARTS,
'♢' | '♦' | 'D' | 'd' => CardSuit::DIAMONDS,
'♧' | '♣' | 'C' | 'c' => CardSuit::CLUBS,
_ => CardSuit::BLANK,
}
}
}
#[cfg(test)]
mod card_suit_tests {
use super::*;
use rstest::rstest;
#[test]
fn binary_signature() {
assert_eq!(32768, CardSuit::SPADES.binary_signature());
assert_eq!(16384, CardSuit::HEARTS.binary_signature());
assert_eq!(8192, CardSuit::DIAMONDS.binary_signature());
assert_eq!(4096, CardSuit::CLUBS.binary_signature());
assert_eq!(0, CardSuit::BLANK.binary_signature());
}
#[rstest]
#[case('â™ ', CardSuit::SPADES)]
#[case('♤', CardSuit::SPADES)]
#[case('S', CardSuit::SPADES)]
#[case('s', CardSuit::SPADES)]
#[case('♥', CardSuit::HEARTS)]
#[case('♡', CardSuit::HEARTS)]
#[case('H', CardSuit::HEARTS)]
#[case('h', CardSuit::HEARTS)]
#[case('♦', CardSuit::DIAMONDS)]
#[case('♢', CardSuit::DIAMONDS)]
#[case('D', CardSuit::DIAMONDS)]
#[case('d', CardSuit::DIAMONDS)]
#[case('♣', CardSuit::CLUBS)]
#[case('♧', CardSuit::CLUBS)]
#[case('C', CardSuit::CLUBS)]
#[case('c', CardSuit::CLUBS)]
#[case(' ', CardSuit::BLANK)]
#[case('F', CardSuit::BLANK)]
fn from_char(#[case] input: char, #[case] expected: CardSuit) {
assert_eq!(expected, CardSuit::from_char(input));
}
}
pub mod evaluate {
use crate::cards::five::Five;
use crate::cards::HandRanker;
use crate::hand_rank::HandRankValue;
use crate::{CKCNumber, CardNumber};
pub const POSSIBLE_COMBINATIONS: usize = 7937;
#[must_use]
#[allow(clippy::cast_possible_truncation)]
pub fn five_cards(five_cards: [CKCNumber; 5]) -> HandRankValue {
Five::from(five_cards).hand_rank_value_validated()
}
#[must_use]
#[deprecated(since = "0.1.9", note = "use Five.is_flush()")]
pub fn is_flush(five_cards: [CKCNumber; 5]) -> bool {
(five_cards[0] & five_cards[1] & five_cards[2] & five_cards[3] & five_cards[4] & CardNumber::SUIT_FILTER) != 0
}
#[must_use]
#[deprecated(since = "0.1.9", note = "use Five.or_rank_bits()")]
pub fn or_rank_bits(five_cards: [CKCNumber; 5]) -> usize {
Five::from(five_cards).or_rank_bits() as usize
}
}
#[cfg(test)]
mod evaluate_tests {
use super::*;
#[test]
fn five_cards_royal_flush() {
let cards = [
CardNumber::ACE_SPADES,
CardNumber::KING_SPADES,
CardNumber::QUEEN_SPADES,
CardNumber::JACK_SPADES,
CardNumber::TEN_SPADES,
];
assert_eq!(evaluate::five_cards(cards), 1);
}
#[test]
fn five_cards_straight() {
let first = [
CardNumber::NINE_CLUBS,
CardNumber::KING_SPADES,
CardNumber::QUEEN_SPADES,
CardNumber::JACK_SPADES,
CardNumber::TEN_SPADES,
];
let second = [
CardNumber::NINE_CLUBS,
CardNumber::QUEEN_SPADES,
CardNumber::JACK_SPADES,
CardNumber::TEN_SPADES,
CardNumber::EIGHT_CLUBS,
];
assert_eq!(evaluate::five_cards(first), 1601);
assert_eq!(evaluate::five_cards(second), 1602);
}
#[test]
fn five_cards_two_pair() {
let cards = [
CardNumber::JACK_CLUBS,
CardNumber::DEUCE_CLUBS,
CardNumber::DEUCE_DIAMONDS,
CardNumber::JACK_SPADES,
CardNumber::TEN_SPADES,
];
assert_eq!(evaluate::five_cards(cards), 2922);
}
#[test]
fn five_cards_king_high() {
let first = [
CardNumber::JACK_CLUBS,
CardNumber::DEUCE_CLUBS,
CardNumber::TREY_CLUBS,
CardNumber::KING_SPADES,
CardNumber::TEN_SPADES,
];
let second = [
CardNumber::JACK_CLUBS,
CardNumber::QUEEN_DIAMONDS,
CardNumber::TREY_CLUBS,
CardNumber::KING_SPADES,
CardNumber::TEN_SPADES,
];
assert_eq!(evaluate::five_cards(first), 6825);
assert_eq!(evaluate::five_cards(second), 6684);
}
#[test]
fn check_dupes() {
let hand = [
CardNumber::JACK_CLUBS,
CardNumber::DEUCE_CLUBS,
CardNumber::TREY_CLUBS,
CardNumber::KING_SPADES,
CardNumber::JACK_CLUBS,
];
assert_eq!(evaluate::five_cards(hand), 0);
}
#[test]
fn check_corrupt() {
let first = [
CardNumber::JACK_CLUBS,
CardNumber::DEUCE_CLUBS,
23,
CardNumber::KING_SPADES,
CardNumber::TEN_SPADES,
];
let second = [
CardNumber::JACK_CLUBS,
CardNumber::QUEEN_DIAMONDS,
CardNumber::TREY_CLUBS,
CardNumber::KING_SPADES,
CardNumber::BLANK,
];
assert_eq!(evaluate::five_cards(first), 0);
assert_eq!(evaluate::five_cards(second), 0);
}
}
#[derive(Debug, PartialEq)]
pub enum HandError {
BlankCard,
DuplicateCard,
Incomplete,
InvalidBinaryFormat,
InvalidCard,
InvalidCardCount,
InvalidIndex,
NotEnoughCards,
TooManyCards,
}
pub trait PokerCard {
#[must_use]
fn create(rank: CardRank, suit: CardSuit) -> CKCNumber {
CKCNumber::filter(rank.bits() | rank.prime() | rank.shift8() | suit.binary_signature())
}
#[must_use]
fn filter(number: CKCNumber) -> CKCNumber {
match number {
CardNumber::ACE_SPADES
| CardNumber::KING_SPADES
| CardNumber::QUEEN_SPADES
| CardNumber::JACK_SPADES
| CardNumber::TEN_SPADES
| CardNumber::NINE_SPADES
| CardNumber::EIGHT_SPADES
| CardNumber::SEVEN_SPADES
| CardNumber::SIX_SPADES
| CardNumber::FIVE_SPADES
| CardNumber::FOUR_SPADES
| CardNumber::TREY_SPADES
| CardNumber::DEUCE_SPADES
| CardNumber::ACE_HEARTS
| CardNumber::KING_HEARTS
| CardNumber::QUEEN_HEARTS
| CardNumber::JACK_HEARTS
| CardNumber::TEN_HEARTS
| CardNumber::NINE_HEARTS
| CardNumber::EIGHT_HEARTS
| CardNumber::SEVEN_HEARTS
| CardNumber::SIX_HEARTS
| CardNumber::FIVE_HEARTS
| CardNumber::FOUR_HEARTS
| CardNumber::TREY_HEARTS
| CardNumber::DEUCE_HEARTS
| CardNumber::ACE_DIAMONDS
| CardNumber::KING_DIAMONDS
| CardNumber::QUEEN_DIAMONDS
| CardNumber::JACK_DIAMONDS
| CardNumber::TEN_DIAMONDS
| CardNumber::NINE_DIAMONDS
| CardNumber::EIGHT_DIAMONDS
| CardNumber::SEVEN_DIAMONDS
| CardNumber::SIX_DIAMONDS
| CardNumber::FIVE_DIAMONDS
| CardNumber::FOUR_DIAMONDS
| CardNumber::TREY_DIAMONDS
| CardNumber::DEUCE_DIAMONDS
| CardNumber::ACE_CLUBS
| CardNumber::KING_CLUBS
| CardNumber::QUEEN_CLUBS
| CardNumber::JACK_CLUBS
| CardNumber::TEN_CLUBS
| CardNumber::NINE_CLUBS
| CardNumber::EIGHT_CLUBS
| CardNumber::SEVEN_CLUBS
| CardNumber::SIX_CLUBS
| CardNumber::FIVE_CLUBS
| CardNumber::FOUR_CLUBS
| CardNumber::TREY_CLUBS
| CardNumber::DEUCE_CLUBS => number,
_ => CardNumber::BLANK,
}
}
#[must_use]
fn from_binary_card(bc: BinaryCard) -> CKCNumber {
match bc {
BinaryCard::ACE_SPADES => CardNumber::ACE_SPADES,
BinaryCard::KING_SPADES => CardNumber::KING_SPADES,
BinaryCard::QUEEN_SPADES => CardNumber::QUEEN_SPADES,
BinaryCard::JACK_SPADES => CardNumber::JACK_SPADES,
BinaryCard::TEN_SPADES => CardNumber::TEN_SPADES,
BinaryCard::NINE_SPADES => CardNumber::NINE_SPADES,
BinaryCard::EIGHT_SPADES => CardNumber::EIGHT_SPADES,
BinaryCard::SEVEN_SPADES => CardNumber::SEVEN_SPADES,
BinaryCard::SIX_SPADES => CardNumber::SIX_SPADES,
BinaryCard::FIVE_SPADES => CardNumber::FIVE_SPADES,
BinaryCard::FOUR_SPADES => CardNumber::FOUR_SPADES,
BinaryCard::TREY_SPADES => CardNumber::TREY_SPADES,
BinaryCard::DEUCE_SPADES => CardNumber::DEUCE_SPADES,
BinaryCard::ACE_HEARTS => CardNumber::ACE_HEARTS,
BinaryCard::KING_HEARTS => CardNumber::KING_HEARTS,
BinaryCard::QUEEN_HEARTS => CardNumber::QUEEN_HEARTS,
BinaryCard::JACK_HEARTS => CardNumber::JACK_HEARTS,
BinaryCard::TEN_HEARTS => CardNumber::TEN_HEARTS,
BinaryCard::NINE_HEARTS => CardNumber::NINE_HEARTS,
BinaryCard::EIGHT_HEARTS => CardNumber::EIGHT_HEARTS,
BinaryCard::SEVEN_HEARTS => CardNumber::SEVEN_HEARTS,
BinaryCard::SIX_HEARTS => CardNumber::SIX_HEARTS,
BinaryCard::FIVE_HEARTS => CardNumber::FIVE_HEARTS,
BinaryCard::FOUR_HEARTS => CardNumber::FOUR_HEARTS,
BinaryCard::TREY_HEARTS => CardNumber::TREY_HEARTS,
BinaryCard::DEUCE_HEARTS => CardNumber::DEUCE_HEARTS,
BinaryCard::ACE_DIAMONDS => CardNumber::ACE_DIAMONDS,
BinaryCard::KING_DIAMONDS => CardNumber::KING_DIAMONDS,
BinaryCard::QUEEN_DIAMONDS => CardNumber::QUEEN_DIAMONDS,
BinaryCard::JACK_DIAMONDS => CardNumber::JACK_DIAMONDS,
BinaryCard::TEN_DIAMONDS => CardNumber::TEN_DIAMONDS,
BinaryCard::NINE_DIAMONDS => CardNumber::NINE_DIAMONDS,
BinaryCard::EIGHT_DIAMONDS => CardNumber::EIGHT_DIAMONDS,
BinaryCard::SEVEN_DIAMONDS => CardNumber::SEVEN_DIAMONDS,
BinaryCard::SIX_DIAMONDS => CardNumber::SIX_DIAMONDS,
BinaryCard::FIVE_DIAMONDS => CardNumber::FIVE_DIAMONDS,
BinaryCard::FOUR_DIAMONDS => CardNumber::FOUR_DIAMONDS,
BinaryCard::TREY_DIAMONDS => CardNumber::TREY_DIAMONDS,
BinaryCard::DEUCE_DIAMONDS => CardNumber::DEUCE_DIAMONDS,
BinaryCard::ACE_CLUBS => CardNumber::ACE_CLUBS,
BinaryCard::KING_CLUBS => CardNumber::KING_CLUBS,
BinaryCard::QUEEN_CLUBS => CardNumber::QUEEN_CLUBS,
BinaryCard::JACK_CLUBS => CardNumber::JACK_CLUBS,
BinaryCard::TEN_CLUBS => CardNumber::TEN_CLUBS,
BinaryCard::NINE_CLUBS => CardNumber::NINE_CLUBS,
BinaryCard::EIGHT_CLUBS => CardNumber::EIGHT_CLUBS,
BinaryCard::SEVEN_CLUBS => CardNumber::SEVEN_CLUBS,
BinaryCard::SIX_CLUBS => CardNumber::SIX_CLUBS,
BinaryCard::FIVE_CLUBS => CardNumber::FIVE_CLUBS,
BinaryCard::FOUR_CLUBS => CardNumber::FOUR_CLUBS,
BinaryCard::TREY_CLUBS => CardNumber::TREY_CLUBS,
BinaryCard::DEUCE_CLUBS => CardNumber::DEUCE_CLUBS,
_ => CardNumber::BLANK,
}
}
#[must_use]
fn from_index(index: &str) -> CKCNumber {
let (rank, suit) = get_rank_and_suit(index);
CKCNumber::create(rank, suit)
}
fn as_u32(&self) -> u32;
fn get_card_rank(&self) -> CardRank {
match self.get_rank_bit() {
4096 => CardRank::ACE,
2048 => CardRank::KING,
1024 => CardRank::QUEEN,
512 => CardRank::JACK,
256 => CardRank::TEN,
128 => CardRank::NINE,
64 => CardRank::EIGHT,
32 => CardRank::SEVEN,
16 => CardRank::SIX,
8 => CardRank::FIVE,
4 => CardRank::FOUR,
2 => CardRank::THREE,
1 => CardRank::TWO,
_ => CardRank::BLANK,
}
}
fn get_card_suit(&self) -> CardSuit {
match self.get_suit_bit() {
8 => CardSuit::SPADES,
4 => CardSuit::HEARTS,
2 => CardSuit::DIAMONDS,
1 => CardSuit::CLUBS,
_ => CardSuit::BLANK,
}
}
fn get_chen_points(&self) -> f32 {
match self.get_card_rank() {
CardRank::ACE => 10.0,
CardRank::KING => 8.0,
CardRank::QUEEN => 7.0,
CardRank::JACK => 6.0,
CardRank::BLANK => 0.0,
_ => f32::from(self.get_card_rank() as u8) / 2.0,
}
}
fn get_rank_bit(&self) -> u32 {
self.get_rank_flag() >> CardNumber::RANK_FLAG_SHIFT
}
fn get_rank_char(&self) -> char {
match self.get_rank_bit() {
4096 => 'A',
2048 => 'K',
1024 => 'Q',
512 => 'J',
256 => 'T',
128 => '9',
64 => '8',
32 => '7',
16 => '6',
8 => '5',
4 => '4',
2 => '3',
1 => '2',
_ => '_',
}
}
fn get_rank_flag(&self) -> u32 {
self.as_u32() & CardNumber::RANK_FLAG_FILTER
}
fn get_rank_prime(&self) -> u32 {
self.as_u32() & CardNumber::RANK_PRIME_FILTER
}
fn get_suit_bit(&self) -> u32 {
self.get_suit_flag() >> CardNumber::SUIT_SHIFT
}
fn get_suit_char(&self) -> char {
match self.get_suit_bit() {
8 => 'â™ ',
4 => '♥',
2 => '♦',
1 => '♣',
_ => '_',
}
}
fn get_suit_letter(&self) -> char {
match self.get_suit_bit() {
8 => 'S',
4 => 'H',
2 => 'D',
1 => 'C',
_ => '_',
}
}
fn get_suit_flag(&self) -> u32 {
self.as_u32() & CardNumber::SUIT_FILTER
}
fn is_blank(&self) -> bool;
fn flag_as_pair(&self) -> CKCNumber {
self.as_u32() | CardNumber::PAIR
}
fn flag_as_trips(&self) -> CKCNumber {
self.as_u32() | CardNumber::TRIPS
}
fn flag_as_quads(&self) -> CKCNumber {
self.as_u32() | CardNumber::QUADS
}
fn next_suit(&self) -> CardSuit {
match self.get_card_suit() {
CardSuit::SPADES => CardSuit::HEARTS,
CardSuit::HEARTS => CardSuit::DIAMONDS,
CardSuit::DIAMONDS => CardSuit::CLUBS,
CardSuit::CLUBS => CardSuit::SPADES,
CardSuit::BLANK => CardSuit::BLANK,
}
}
fn strip_multiples_flags(&self) -> CKCNumber {
CardNumber::MULTIPLES_FILTER & self.as_u32()
}
}
impl PokerCard for CKCNumber {
fn as_u32(&self) -> u32 {
*self
}
fn is_blank(&self) -> bool {
*self == CardNumber::BLANK
}
}
pub trait Shifty {
#[must_use]
fn shift_suit(&self) -> Self;
}
impl Shifty for CKCNumber {
fn shift_suit(&self) -> Self {
CKCNumber::create(self.get_card_rank(), self.next_suit())
}
}
#[cfg(test)]
mod poker_card_tests {
use super::*;
use rstest::rstest;
#[test]
fn filter() {
assert_eq!(
<CKCNumber as PokerCard>::filter(CardNumber::ACE_SPADES),
CardNumber::ACE_SPADES
);
assert_eq!(
<CKCNumber as PokerCard>::filter(CardNumber::KING_CLUBS),
CardNumber::filter(CardNumber::KING_CLUBS)
);
assert_eq!(<CKCNumber as PokerCard>::filter(2), CardNumber::BLANK);
}
#[rstest]
#[case("Aâ™ ", CardNumber::ACE_SPADES)]
#[case("ks", CardNumber::KING_SPADES)]
#[case("QS", CardNumber::QUEEN_SPADES)]
#[case("Jâ™ ", CardNumber::JACK_SPADES)]
#[case("TS", CardNumber::TEN_SPADES)]
#[case("9s", CardNumber::NINE_SPADES)]
#[case("8â™ ", CardNumber::EIGHT_SPADES)]
#[case("7S", CardNumber::SEVEN_SPADES)]
#[case("6â™ ", CardNumber::SIX_SPADES)]
#[case("5S", CardNumber::FIVE_SPADES)]
#[case("4â™ ", CardNumber::FOUR_SPADES)]
#[case("3s", CardNumber::TREY_SPADES)]
#[case("2S", CardNumber::DEUCE_SPADES)]
#[case("A♥", CardNumber::ACE_HEARTS)]
#[case("k♥", CardNumber::KING_HEARTS)]
#[case("QH", CardNumber::QUEEN_HEARTS)]
#[case("jh", CardNumber::JACK_HEARTS)]
#[case("T♥", CardNumber::TEN_HEARTS)]
#[case("9♥", CardNumber::NINE_HEARTS)]
#[case("8h", CardNumber::EIGHT_HEARTS)]
#[case("7H", CardNumber::SEVEN_HEARTS)]
#[case("6h", CardNumber::SIX_HEARTS)]
#[case("5H", CardNumber::FIVE_HEARTS)]
#[case("4♥", CardNumber::FOUR_HEARTS)]
#[case("3♥", CardNumber::TREY_HEARTS)]
#[case("2h", CardNumber::DEUCE_HEARTS)]
#[case("A♦", CardNumber::ACE_DIAMONDS)]
#[case("k♦", CardNumber::KING_DIAMONDS)]
#[case("Q♦", CardNumber::QUEEN_DIAMONDS)]
#[case("Jd", CardNumber::JACK_DIAMONDS)]
#[case("tD", CardNumber::TEN_DIAMONDS)]
#[case("9♦", CardNumber::NINE_DIAMONDS)]
#[case("8D", CardNumber::EIGHT_DIAMONDS)]
#[case("7♦", CardNumber::SEVEN_DIAMONDS)]
#[case("6D", CardNumber::SIX_DIAMONDS)]
#[case("5D", CardNumber::FIVE_DIAMONDS)]
#[case("4♦", CardNumber::FOUR_DIAMONDS)]
#[case("3♦", CardNumber::TREY_DIAMONDS)]
#[case("2d", CardNumber::DEUCE_DIAMONDS)]
#[case("a♣", CardNumber::ACE_CLUBS)]
#[case("k♣", CardNumber::KING_CLUBS)]
#[case("QC", CardNumber::QUEEN_CLUBS)]
#[case("jc", CardNumber::JACK_CLUBS)]
#[case("tC", CardNumber::TEN_CLUBS)]
#[case("9♣", CardNumber::NINE_CLUBS)]
#[case("8♣", CardNumber::EIGHT_CLUBS)]
#[case("7c", CardNumber::SEVEN_CLUBS)]
#[case("6♣", CardNumber::SIX_CLUBS)]
#[case("5C", CardNumber::FIVE_CLUBS)]
#[case("4c", CardNumber::FOUR_CLUBS)]
#[case("3C", CardNumber::TREY_CLUBS)]
#[case("2C", CardNumber::DEUCE_CLUBS)]
fn from_index(#[case] index: &str, #[case] expected: CKCNumber) {
assert_eq!(CKCNumber::from_index(index), expected);
}
#[rstest]
#[case("Aâ™ ", 10.0)]
#[case("ks", 8.0)]
#[case("QS", 7.0)]
#[case("Jâ™ ", 6.0)]
#[case("TS", 5.0)]
#[case("9s", 4.5)]
#[case("8â™ ", 4.0)]
#[case("7S", 3.5)]
#[case("6â™ ", 3.0)]
#[case("5S", 2.5)]
#[case("4â™ ", 2.0)]
#[case("3s", 1.5)]
#[case("2S", 1.0)]
fn get_chen_points(#[case] index: &str, #[case] expected: f32) {
assert_eq!(CKCNumber::from_index(index).get_chen_points(), expected);
}
#[test]
fn get_rank() {
let card = CardNumber::ACE_CLUBS as CKCNumber;
assert_eq!(0b00010000_00000000, card.get_rank_bit());
assert_eq!(0b00101001, card.get_rank_prime());
assert_eq!(CardRank::ACE, card.get_card_rank());
let card = CardNumber::KING_DIAMONDS as CKCNumber;
assert_eq!(0b00001000_00000000, card.get_rank_bit());
assert_eq!(0b00100101, card.get_rank_prime());
assert_eq!(CardRank::KING, card.get_card_rank());
let card = CardNumber::QUEEN_SPADES as CKCNumber;
assert_eq!(0b00000100_00000000, card.get_rank_bit());
assert_eq!(0b00011111, card.get_rank_prime());
assert_eq!(CardRank::QUEEN, card.get_card_rank());
let card = CardNumber::JACK_HEARTS as CKCNumber;
assert_eq!(0b00000010_00000000, card.get_rank_bit());
assert_eq!(0b00011101, card.get_rank_prime());
assert_eq!(CardRank::JACK, card.get_card_rank());
let card = CardNumber::TEN_SPADES as CKCNumber;
assert_eq!(0b00000001_00000000, card.get_rank_bit());
assert_eq!(0b00010111, card.get_rank_prime());
assert_eq!(CardRank::TEN, card.get_card_rank());
let card = CardNumber::NINE_HEARTS as CKCNumber;
assert_eq!(0b00000000_10000000, card.get_rank_bit());
assert_eq!(0b00010011, card.get_rank_prime());
assert_eq!(CardRank::NINE, card.get_card_rank());
let card = CardNumber::EIGHT_DIAMONDS as CKCNumber;
assert_eq!(0b00000000_01000000, card.get_rank_bit());
assert_eq!(0b00010001, card.get_rank_prime());
assert_eq!(CardRank::EIGHT, card.get_card_rank());
let card = CardNumber::SEVEN_CLUBS as CKCNumber;
assert_eq!(0b00000000_00100000, card.get_rank_bit());
assert_eq!(0b00001101, card.get_rank_prime());
assert_eq!(CardRank::SEVEN, card.get_card_rank());
let card = CardNumber::SIX_SPADES as CKCNumber;
assert_eq!(0b00000000_00010000, card.get_rank_bit());
assert_eq!(0b00001011, card.get_rank_prime());
assert_eq!(CardRank::SIX, card.get_card_rank());
let card = CardNumber::FIVE_HEARTS as CKCNumber;
assert_eq!(0b00000000_00001000, card.get_rank_bit());
assert_eq!(0b00000111, card.get_rank_prime());
assert_eq!(CardRank::FIVE, card.get_card_rank());
let card = CardNumber::FOUR_DIAMONDS as CKCNumber;
assert_eq!(0b00000000_00000100, card.get_rank_bit());
assert_eq!(0b00000101, card.get_rank_prime());
assert_eq!(CardRank::FOUR, card.get_card_rank());
let card = CardNumber::TREY_CLUBS as CKCNumber;
assert_eq!(0b00000000_00000010, card.get_rank_bit());
assert_eq!(0b00000011, card.get_rank_prime());
assert_eq!(CardRank::THREE, card.get_card_rank());
let card = CardNumber::DEUCE_SPADES as CKCNumber;
assert_eq!(0b00000000_00000001, card.get_rank_bit());
assert_eq!(0b00000010, card.get_rank_prime());
assert_eq!(CardRank::TWO, card.get_card_rank());
let card = CardNumber::BLANK as CKCNumber;
assert_eq!(0b00000000_00000000, card.get_rank_bit());
assert_eq!(0b00000000, card.get_rank_prime());
assert_eq!(CardRank::BLANK, card.get_card_rank());
assert_eq!(CardSuit::BLANK, card.get_card_suit());
}
#[test]
fn get_rank_flag() {
let card = CardNumber::ACE_CLUBS as CKCNumber;
assert_eq!(0b00010000_00000000_00000000_00000000, card.get_rank_flag());
let card = CardNumber::KING_DIAMONDS as CKCNumber;
assert_eq!(0b00001000_00000000_00000000_00000000, card.get_rank_flag());
let card = CardNumber::QUEEN_SPADES as CKCNumber;
assert_eq!(0b00000100_00000000_00000000_00000000, card.get_rank_flag());
let card = CardNumber::JACK_HEARTS as CKCNumber;
assert_eq!(0b00000010_00000000_00000000_00000000, card.get_rank_flag());
let card = CardNumber::TEN_SPADES as CKCNumber;
assert_eq!(0b00000001_00000000_00000000_00000000, card.get_rank_flag());
let card = CardNumber::NINE_HEARTS as CKCNumber;
assert_eq!(0b00000000_10000000_00000000_00000000, card.get_rank_flag());
let card = CardNumber::EIGHT_DIAMONDS as CKCNumber;
assert_eq!(0b00000000_01000000_00000000_00000000, card.get_rank_flag());
let card = CardNumber::SEVEN_CLUBS as CKCNumber;
assert_eq!(0b00000000_00100000_00000000_00000000, card.get_rank_flag());
let card = CardNumber::SIX_SPADES as CKCNumber;
assert_eq!(0b00000000_00010000_00000000_00000000, card.get_rank_flag());
let card = CardNumber::FIVE_HEARTS as CKCNumber;
assert_eq!(0b00000000_00001000_00000000_00000000, card.get_rank_flag());
let card = CardNumber::FOUR_DIAMONDS as CKCNumber;
assert_eq!(0b00000000_00000100_00000000_00000000, card.get_rank_flag());
let card = CardNumber::TREY_CLUBS as CKCNumber;
assert_eq!(0b00000000_00000010_00000000_00000000, card.get_rank_flag());
let card = CardNumber::DEUCE_SPADES as CKCNumber;
assert_eq!(0b00000000_00000001_00000000_00000000, card.get_rank_flag());
}
#[test]
fn get_suit_bit() {
let card = CardNumber::SEVEN_SPADES as CKCNumber;
assert_eq!(0b1000, card.get_suit_bit());
assert_eq!(CardSuit::SPADES, card.get_card_suit());
let card = CardNumber::SEVEN_HEARTS as CKCNumber;
assert_eq!(0b0100, card.get_suit_bit());
assert_eq!(CardSuit::HEARTS, card.get_card_suit());
let card = CardNumber::SEVEN_DIAMONDS as CKCNumber;
assert_eq!(0b0010, card.get_suit_bit());
assert_eq!(CardSuit::DIAMONDS, card.get_card_suit());
let card = CardNumber::SEVEN_CLUBS as CKCNumber;
assert_eq!(0b0001, card.get_suit_bit());
assert_eq!(CardSuit::CLUBS, card.get_card_suit());
}
#[test]
fn get_suit_flag() {
let card = CardNumber::SEVEN_SPADES as CKCNumber;
assert_eq!(0b10000000_00000000, card.get_suit_flag());
let card = CardNumber::SEVEN_HEARTS as CKCNumber;
assert_eq!(0b01000000_00000000, card.get_suit_flag());
let card = CardNumber::SEVEN_DIAMONDS as CKCNumber;
assert_eq!(0b00100000_00000000, card.get_suit_flag());
let card = CardNumber::SEVEN_CLUBS as CKCNumber;
assert_eq!(0b00010000_00000000, card.get_suit_flag());
}
#[test]
fn is_blank() {
let card = CardNumber::BLANK;
assert!(card.is_blank());
assert!(0_u32.is_blank());
assert!(0.is_blank());
}
#[test]
fn flag_as_pair() {
assert_eq!(805_342_249, CardNumber::ACE_SPADES.flag_as_pair());
}
#[test]
fn flag_as_trips() {
assert_eq!(1_342_213_161, CardNumber::ACE_SPADES.flag_as_trips());
}
#[test]
fn flag_as_quads() {
assert_eq!(2_415_954_985, CardNumber::ACE_SPADES.flag_as_quads());
}
#[test]
fn next_suit() {
assert_eq!(CardNumber::TEN_SPADES.next_suit(), CardSuit::HEARTS);
assert_eq!(CardNumber::TEN_HEARTS.next_suit(), CardSuit::DIAMONDS);
assert_eq!(CardNumber::TEN_DIAMONDS.next_suit(), CardSuit::CLUBS);
assert_eq!(CardNumber::TEN_CLUBS.next_suit(), CardSuit::SPADES);
assert_eq!(CardNumber::BLANK.next_suit(), CardSuit::BLANK);
}
#[test]
fn shift_suit() {
assert_eq!(CardNumber::ACE_SPADES.shift_suit(), CardNumber::ACE_HEARTS);
assert_eq!(CardNumber::ACE_HEARTS.shift_suit(), CardNumber::ACE_DIAMONDS);
assert_eq!(CardNumber::ACE_DIAMONDS.shift_suit(), CardNumber::ACE_CLUBS);
assert_eq!(CardNumber::ACE_CLUBS.shift_suit(), CardNumber::ACE_SPADES);
assert_eq!(CardNumber::BLANK.shift_suit(), CardNumber::BLANK);
}
#[test]
fn strip_multiples_flags() {
assert_eq!(
CardNumber::ACE_SPADES,
CardNumber::ACE_SPADES.flag_as_pair().strip_multiples_flags()
);
assert_eq!(
CardNumber::ACE_SPADES,
CardNumber::ACE_SPADES.flag_as_trips().strip_multiples_flags()
);
assert_eq!(
CardNumber::ACE_SPADES,
CardNumber::ACE_SPADES.flag_as_quads().strip_multiples_flags()
);
assert_eq!(
CardNumber::ACE_SPADES,
CardNumber::ACE_SPADES
.flag_as_pair()
.flag_as_trips()
.flag_as_quads()
.strip_multiples_flags()
);
}
#[test]
fn scratch() {
}
}