Skip to content

Commit

Permalink
Better Readme, implemented cases for StraightFlush rank
Browse files Browse the repository at this point in the history
  • Loading branch information
davassi committed Sep 16, 2023
1 parent a84ee64 commit bc6645c
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 30 deletions.
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[package]
name = "pokerface"
name = "poker-face"
version = "0.1.0"
edition = "2021"
description = "A blazing fast library for the evaluation of Poker hands with lookup tables and perfect hashing"
repository = "https://github.com/davassi/pokerface"
homepage = "https://github.com/davassi/pokerface"
repository = "https://github.com/davassi/poker-face"
homepage = "https://github.com/davassi/poker-face"
license = "MIT OR Apache-2.0"
authors = ["Davassi <[email protected]>"]
categories = ["command-line-interface","games","game development"]
Expand Down Expand Up @@ -36,8 +36,8 @@ opt-level = 3
debug = true

[[bin]]
name = "pokerust"
name = "pokerface"
path = "src/bin/main.rs"

[lib]
name = "pokerust"
name = "pokerface"
47 changes: 37 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
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)
[<img alt="build status" src="https://github.com/davassi/yarer/actions/workflows/rust.yml/badge.svg" height="20">](https://github.com/davassi/poker-face/actions?query=branch%3Amaster)
[<img alt="crates.io" src="https://img.shields.io/crates/v/yarer.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/poker-face)
[<img alt="docs.rs" src="https://img.shields.io/docsrs/yarer?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/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/davassi/poker-face)
[<img alt="build status" src="https://github.com/davassi/poker-face/actions/workflows/rust.yml/badge.svg" height="20">](https://github.com/davassi/poker-face/actions?query=branch%3Amaster)
[<img alt="crates.io" src="https://img.shields.io/crates/v/poker-face.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/poker-face)
[<img alt="docs.rs" src="https://img.shields.io/docsrs/poker-face?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/poker-face)
[![Downloads](https://img.shields.io/crates/d/poker-face.svg)](https://crates.io/crates/poker-face)
[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)

Poker-Face is a rust virtusism exercise implementation of a Texas Hold'em poker engine. It evaluates the rank of a 5-card hand using the typical Rust match control flow construct.
It determines the Rank of a 5 card hand examining the properties using Array Matching and Struct Matching [MatchHandEvaluator].

Poker-Face is a texas hold'em poker engine implementation written in Rust. It evaluates the rank of a 5-card hand using Cactus Kev's Poker Hand Evaluator (fast, but relies heavily on hash tables) or a match control flow construct (slow, but readable).

Example of usage of the library:
Example of usage of the library, as a binary:

```rust
Poker Face 0.1.0 - 🦀 for ♠️ ♣️ ♥️ ♦️
Expand All @@ -22,7 +23,7 @@ Prehashing cards...
7♦️ 7♣️ 5♠️ 10♣️ J♣️ 9♠️ 6♥️ 9♦️ 6♦️ A♣️ 9♣️ Q♥️ 3♦️
4♠️ 6♣️ 10♥️ 2♦️ 5♣️ J♥️ 4♣️ Q♣️ 2♥️ J♠️ Q♦️ 7♠️ A♦️

2. Let's take (borrow) 2 hands of 5 cards each from the deck
1. Let's take (borrow) 2 hands of 5 cards each from the deck
Player 1: 9♥️ 7♥️ K♦️ 4♦️ 2♣️

Player 2: 10♠️ 8♥️ 4♥️ 3♥️ 2♠️
Expand All @@ -35,14 +36,40 @@ The winner is Player 1

```

At programmatic level, there are some handy macros (newcard!, hand!, assert_rank!) implemented to deal with cards, hands and ranks. Example:
As a library, there are some handy macros (newcard!, hand!, assert_rank!) implemented to deal with cards, hands and ranks. Example:

```rust
assert_rank!(hand!["Ad","Kd","Qd","Jd","10d"], Rank::RoyalFlush);

assert_rank!(hand!["Kd", "Kh", "Kc", "Ks", "Qd"], Rank::FourOfAKind);

assert_rank!(hand!["2d", "2h", "Qc", "Qs", "Qd"], Rank::FullHouse);
```

or

```rust
assert_eq!(newcard!["Ah"], Card::new(14, Suit::Hearts));
```
```

## Execute

To run it from cargo, just type:

```console
cargo run -q --
```

or to build and install a release from the code:

```console
cargo build --release
cargo install --path .
./target/release/poker-face
```

## Contribution

If you have any suggestions for features (i.e. more functionality to implement), or if you find any problems in the code, design, interface, etc., please feel free to share them on our [GitHub](https://github.com/davassi/poker-face/issues).

I really appreciate your feedback!
2 changes: 1 addition & 1 deletion src/bin/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use pokerust::{card::*, match_evaluator::{MatchHandEvaluator, Rank}};
use pokerface::{card::*, match_evaluator::{MatchHandEvaluator, Rank}};

static VERSION: &str = env!("CARGO_PKG_VERSION");

Expand Down
23 changes: 9 additions & 14 deletions src/card.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ impl Ord for Card {

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

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

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

Expand All @@ -83,6 +83,10 @@ impl TryFrom<&str> for Card {
'2'..='9' => val.to_digit(10).unwrap() as u8,
_ => return Err(CardError::InvalidValue),
};

if val == 1 && card.len() ==2 {
return Err(CardError::InvalidValue);
}

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

Expand All @@ -99,6 +103,7 @@ impl TryFrom<&str> for Card {
}

impl Display for Card {

fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, " ")?;
match self.val {
Expand Down Expand Up @@ -208,23 +213,13 @@ impl Display for Deck {
}
}

/*
2. Straight Flush
Any sequence of five consecutive cards all of the same suit. For instance, a hand with the cards 5, 6, 7, 8, and 9 of diamonds is a straight flush.
6. Straight
Five cards in a sequence, but not all of the same suit. An example would be a hand containing a 2, 3, 4, 5, and 6, of various suits.
*/

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

Expand Down
35 changes: 35 additions & 0 deletions src/match_evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ pub enum Rank {
///
/// The highest rank possible, consisting of the Ace, King, Queen, Jack, and Ten all of the same suit.
RoyalFlush,
/// 2. Straight Flush
///
/// Any sequence of five consecutive cards all of the same suit. For instance, a hand with the cards 5, 6, 7, 8, and 9 of diamonds is a straight flush.
StraightFlush,
/// 3. Four of a Kind (Poker)
///
Expand All @@ -20,6 +23,9 @@ pub enum Rank {
///
/// Any five cards of the same suit, but not in sequence. For instance, if a player has five heart cards, they have a flush.
Flush,
/// 6. Straight
///
/// Five cards in a sequence, but not all of the same suit. An example would be a hand containing a 2, 3, 4, 5, and 6, of various suits.
Straight,
/// 7. Three of a Kind (Trips or Set)
///
Expand Down Expand Up @@ -65,6 +71,16 @@ impl MatchHandEvaluator {
{
Rank::RoyalFlush
}
[Card { suit: s1, val: v1 }, Card { suit: s2, val: v2 }, Card { suit: s3, val: v3 }, Card { suit: s4, val: v4 }, Card { suit: s5, val: v5 }]
if Self::seq(*v1,*v2,*v3,*v4,*v5) && Self::suits(s1, s2, s3, s4, s5) =>
{
Rank::StraightFlush
}
[Card { suit: s1, val: 14 }, Card { suit: s2, val: 5 }, Card { suit: s3, val: 4 }, Card { suit: s4, val: 3 }, Card { suit: s5, val: 2 }]
if Self::suits(s1, s2, s3, s4, s5) =>
{
Rank::StraightFlush // special case with Ace as 1
}
[Card { val : v1, .. }, Card { val: v2, .. }, Card { val: v3, .. }, Card { val: v4, .. }, Card { val: v5, .. }]
if (v1 == v2 && v2 == v3 && v3 == v4 || v2 == v3 && v3 == v4 && v4 == v5) =>
{
Expand Down Expand Up @@ -105,6 +121,12 @@ impl MatchHandEvaluator {
fn suits(c0: &Suit, c1: &Suit, c2: &Suit, c3: &Suit, c4: &Suit) -> bool {
c0 == c1 && c1 == c2 && c2 == c3 && c3 == c4
}

/// function that checks if 5 given card ranks are a numeric sequence
///
fn seq(v0 : u8, v1 : u8, v2 : u8, v3 : u8, v4 : u8) -> bool {
v0 == (v1+1) && v1 == (v2+1) && v2 == (v3+1) && v3 == (v4+1)
}
}

#[macro_export]
Expand All @@ -130,6 +152,13 @@ mod test {
assert_rank!(hand!["As", "Ks", "Qs", "Js", "10s"], Rank::RoyalFlush);
}

#[test]
fn rank_straight_flush() {
assert_rank!(hand!["5d", "4d", "3d", "2d", "Ad"], Rank::StraightFlush);
assert_rank!(hand!["10h", "9h", "8h", "7h", "6h"], Rank::StraightFlush);
assert_rank!(hand!["Ks", "Qs", "Js", "10s", "9s"], Rank::StraightFlush);
}

#[test]
fn rank_four_of_a_kind() {
assert_rank!(hand!["Kd", "Kh", "Kc", "Ks", "Qd"], Rank::FourOfAKind);
Expand Down Expand Up @@ -159,4 +188,10 @@ mod test {
assert_rank!(hand!["Kd", "Kh", "2c", "Js", "10d"], Rank::OnePair);
assert_rank!(hand!["9d", "5h", "5c", "3s", "6d"], Rank::OnePair);
}

#[test]
fn rank_seq() {
assert_eq!(MatchHandEvaluator::seq(14, 13, 12, 11, 10),true);
assert_eq!(MatchHandEvaluator::seq(13, 14, 12, 11, 5),false);
}
}

0 comments on commit bc6645c

Please sign in to comment.