Skip to content

Commit

Permalink
Implemented Macros
Browse files Browse the repository at this point in the history
  • Loading branch information
davassi committed Sep 16, 2023
1 parent 254e92e commit b1be043
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 24 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
POKER FACE - ♥️♦️♣️♠️ 🦀
POKER FACE - ♥️♦️♣️♠️
===========================

[<img alt="github" src="https://img.shields.io/badge/github-davassi/davassi?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/poker-face/yarer)
Expand Down
2 changes: 1 addition & 1 deletion src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fn main() {
println!("\n3. Let's evaluate the hands...");
let score1: Rank = MatchHandEvaluator::slow_eval(&mut hand_p1);
let score2: Rank = MatchHandEvaluator::slow_eval(&mut hand_p2);

println!("Player 1 has a {}", score1);
match score1 {
Rank::HighCard(c) => println!("With highcard of value {c}"),
Expand Down
137 changes: 120 additions & 17 deletions src/card.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use strum_macros::EnumIter;

use crate::hash_tables::CARDS;

use thiserror::Error;

/// The [`Suit`] enum. It represents the categories into which the cards of a deck are divided: [`Suit::Hearts`], [`Suit::Diamonds`], [`Suit::Spades`], [`Suit::Clubs`]
///
#[derive(Debug, PartialEq, Clone, Copy, EnumIter, Eq, Hash)]
Expand All @@ -31,45 +33,93 @@ pub enum Suit {
///
#[derive(Debug, PartialEq, Clone, Copy, Eq, Hash)]
pub struct Card {
pub val: u8,
pub suit: Suit,
pub num: u8,
}

impl Card {
pub fn new(suit: Suit, num: u8) -> Self {
Card { suit, num }
pub fn new(val: u8, suit: Suit) -> Self {
Card { suit, val }
}
}

impl PartialOrd for Card {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.num.partial_cmp(&other.num)
self.val.partial_cmp(&other.val)
}
}

impl Ord for Card {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.num.cmp(&other.num)
self.val.cmp(&other.val)
}
}

#[derive(Error, Debug)]
pub enum CardError {
#[error("the card string is not the correct length")]
InvalidLength,

#[error("the card value is invalid")]
InvalidValue,

#[error("the card suit is invalid")]
InvalidSuit,
}

impl TryFrom<&str> for Card {
type Error = CardError;

fn try_from(card: &str) -> Result<Self, Self::Error> {

if card.len() != 2 && card.len() != 3 {
return Err(CardError::InvalidLength);
}

let chars: Vec<char> = card.chars().collect();
let val = chars[0];

let val = match val {
'A' => 14,
'K' => 13,
'Q' => 12,
'J' => 11,
'1' => 10,
'2'..='9' => val.to_digit(10).unwrap() as u8,
_ => return Err(CardError::InvalidValue),
};

let suit = chars[if val == 10 {2} else {1}];

let suit = match suit.to_ascii_lowercase() {
'h' => Suit::Hearts,
's' => Suit::Spades,
'd' => Suit::Diamonds,
'c' => Suit::Clubs,
_ => return Err(CardError::InvalidSuit),
};

Ok(Card::new(val, suit))
}
}

impl Display for Card {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, " ")?;
match self.num {
match self.val {
n @ 2..=10 => write!(f, "{n}"),
14 => write!(f, "A"),
11 => write!(f, "J"),
12 => write!(f, "Q"),
13 => write!(f, "K"),
12 => write!(f, "Q"),
11 => write!(f, "J"),
_ => panic!("Indeed, that's a good reason to panic."),
}?;
match self.suit {
//
Suit::Hearts => write!(f, "♥️"),
Suit::Spades => write!(f, "♠️"),
Suit::Diamonds => write!(f, "♦️"),
Suit::Clubs => write!(f, "♣️"),
Suit::Hearts => write!(f, "♥️"), //H
Suit::Spades => write!(f, "♠️"), //S
Suit::Diamonds => write!(f, "♦️"), //D
Suit::Clubs => write!(f, "♣️"), //C
}?;
write!(f, " ")
}
Expand Down Expand Up @@ -107,14 +157,24 @@ pub struct Hand {
}

impl Hand {
/// sorting the hand in a descenting order
///
pub fn sort(&mut self) -> () {
self.hand.sort();
self.hand.reverse();
}

/// Returns a reference to the Card slice of this [`Hand`].
/// Borrowing the cards of this [`Hand`].
///
pub const fn get_hand_slice(&self) -> &[Card; 5] {
&self.hand
}

/// An handy constructor for tests
///
pub fn new(hand: [Card; 5]) -> Hand {
Hand { hand }
}
}

#[derive(Debug, PartialEq, Clone)]
Expand All @@ -132,8 +192,8 @@ impl Deck {
let mut deck: Vec<Card> = Vec::new();

for suit in Suit::iter() {
for num in 2..=14 {
let card = Card::new(suit, num);
for val in 2..=14 {
let card = Card::new(val, suit);
deck.push(card);
card_hash.insert(card, CARDS[counter]);
counter += 1;
Expand Down Expand Up @@ -175,7 +235,6 @@ impl Display for Deck {
}
}


/*
2. Straight Flush
Expand All @@ -199,4 +258,48 @@ Two different pairs of cards and one unrelated card. For example, a hand with tw
9. One Pair
Two cards of the same rank and three unrelated cards. An example would be two 7s and three unrelated cards.
*/
*/

#[macro_export]
macro_rules! newcard {
($val:expr, $suit:tt) => {
Card::new($val, $suit)
};
($val:expr) => {
Card::try_from($val).unwrap()
};
}

macro_rules! newhand {

($c:expr,$c1:expr,$c2:expr,$c3:expr,$c4:expr) => {
[newcard![$c],newcard![$c1],newcard![$c2],newcard![$c3],newcard![$c4]]
};
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_try_from_valid_card() {

let card = newcard!["Ad"];
assert_eq!(card.val, 14);
assert_eq!(card.suit, Suit::Diamonds);

assert_eq!(newcard!["Ah"], Card::new(14, Suit::Hearts));
assert_eq!(newcard!["Kh"], Card::new(13, Suit::Hearts));
assert_eq!(newcard!["Qh"], Card::new(12, Suit::Hearts));
assert_eq!(newcard!["Jh"], Card::new(11, Suit::Hearts));
assert_eq!(newcard!["10h"], Card::new(10, Suit::Hearts));
assert_eq!(newcard!["9h"], Card::new(9, Suit::Hearts));
}

#[test]
fn test_try_from_valid_hand() {

let hand = newhand!["Ad","Kd","Qd","Jd","10d"];
assert_eq!(hand[0], Card::new(14, Suit::Diamonds));
}
}
7 changes: 5 additions & 2 deletions src/fast_evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ pub struct FastEvaluator;
impl FastEvaluator {
pub fn rank_name(rank: u64) -> Rank {
match rank {
0..=1276 => Rank::HighCard(Card { suit: Suit::Hearts, num: 13 }), // placecholder todo!()
0..=1276 => Rank::HighCard(Card {
suit: Suit::Hearts,
val: 13,
}), // placecholder todo!()
1277..=4136 => Rank::OnePair,
4137..=4994 => Rank::TwoPair,
4995..=5852 => Rank::ThreeOfAKind,
Expand Down Expand Up @@ -58,7 +61,7 @@ impl FastEvaluator {

for suit in Suit::iter() {
for num in 1..=13 {
deck.push(Card::new(suit, num));
deck.push(Card::new(num, suit));
}
}
// Generate all combinations
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ pub mod hash_tables;

pub mod fast_evaluator;
pub mod match_evaluator;

33 changes: 30 additions & 3 deletions src/match_evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ use crate::{
pub struct MatchHandEvaluator;

impl MatchHandEvaluator {
/// It evaluates the [Rank] of a [Hand]
///
pub fn slow_eval(hand: &mut Hand) -> Rank {
// first let's sort the hand, that's the reason we need a mutable reference
hand.sort();

println!("Sorted: {}", hand);

// now we can safely shadow the mutable reference behind an immutable one
let hand = hand.get_hand_slice();

match hand {
[Card { suit: s1, num: 13 }, Card { suit: s2, num: 12 }, Card { suit: s3, num: 11 }, Card { suit: s4, num: 10 }, Card { suit: s5, num: 9 }]
[Card { suit: s1, val: 14 }, Card { suit: s2, val: 13 }, Card { suit: s3, val: 12 }, Card { suit: s4, val: 11 }, Card { suit: s5, val: 10 }]
if Self::suits(s1, s2, s3, s4, s5) =>
{
Rank::RoyalFlush
Expand All @@ -28,9 +32,32 @@ impl MatchHandEvaluator {
hand => Rank::HighCard(hand[0]),
}
}

/// checks if all suits are all the same (i.e. all [Suit::Hearts])

/// function that checks if 5 given suits are all the same (i.e. all [Suit::Hearts])
///
fn suits(c0: &Suit, c1: &Suit, c2: &Suit, c3: &Suit, c4: &Suit) -> bool {
c0 == c1 && c1 == c2 && c2 == c3 && c3 == c4
}
}



#[cfg(test)]
mod test {
use super::MatchHandEvaluator;
use crate::card::{Card, Hand, Rank, Suit, self};
use crate::newcard;

#[test]
fn rank_royal_flush() {
let cards = [
newcard!["Ah"],
newcard!["Kh"],
newcard!["Jh"],
newcard!["Qh"],
newcard!["10h"],
];
let mut hand = Hand::new(cards);
assert_eq!(MatchHandEvaluator::slow_eval(&mut hand), Rank::RoyalFlush);
}
}

0 comments on commit b1be043

Please sign in to comment.