10 KiB
User Guide
Introduction
This guide explains how to use the Poker Hand Evaluator library for evaluating poker hands in your applications. Whether you're building a poker game, analysis tool, or AI bot, this library provides the fast and accurate hand evaluation you need.
Quick Start
Basic Setup
- Add dependency to your
Cargo.toml:
[dependencies]
poker-api = "0.1.0"
- Import and use in your code:
use poker_api::hand_evaluator::LookupHandEvaluator;
use poker_api::api::hand::Hand;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create the evaluator (generates tables on first run)
let evaluator = LookupHandEvaluator::new()?;
// Create a poker hand
let mut hand = Hand::new();
// ... add cards
// Evaluate the hand
let rank = evaluator.rank_hand(&hand);
println!("Hand strength: {}", rank);
Ok(())
}
First Run Experience
On the first run, you'll see:
Evaluation tables do not exist, generating them...
Generating state table...
Hand evaluation tables saved to math/HandRanks.dat
Evaluation tables loaded.
This generates the lookup tables (~1-2 seconds) and saves them for future use.
Working with Cards
Card Creation
Cards can be created from string notation:
use poker_api::api::card::Card;
// Face cards
let ace_spades = Card::from_string("As")?;
let king_hearts = Card::from_string("Kh")?;
// Number cards
let five_clubs = Card::from_string("5c")?;
let ten_diamonds = Card::from_string("Td")?;
// All suits: s (spades), h (hearts), d (diamonds), c (clubs)
// All ranks: A, K, Q, J, T, 9, 8, 7, 6, 5, 4, 3, 2
Card Properties
let card = Card::from_string("As")?;
println!("Rank: {}", card.rank()); // 12 (Ace)
println!("Suit: {}", card.suit()); // 0 (Spades)
println!("Index: {}", card.index()); // 51 (unique card ID)
println!("String: {}", card.to_string()); // "As"
Working with Hands
Creating Hands
use poker_api::api::hand::Hand;
use poker_api::api::card::Card;
// Create empty hand
let mut hand = Hand::new();
// Add cards one by one
hand.add_card(Card::from_string("As")?).unwrap();
hand.add_card(Card::from_string("Ks")?).unwrap();
hand.add_card(Card::from_string("Qs")?).unwrap();
hand.add_card(Card::from_string("Js")?).unwrap();
hand.add_card(Card::from_string("Ts")?).unwrap();
println!("Hand size: {}", hand.size()); // 5
println!("Hand: {}", hand.to_string()); // "As Ks Qs Js Ts"
Hand Evaluation
// 5-card hand evaluation
let rank = evaluator.rank_hand(&hand);
println!("Royal flush rank: {}", rank);
// 6-card hand (finds best 5-card combination)
let mut six_card_hand = Hand::new();
six_card_hand.add_card(Card::from_string("As")?).unwrap();
six_card_hand.add_card(Card::from_string("Ks")?).unwrap();
// ... add 4 more cards
let rank = evaluator.rank_hand(&six_card_hand);
// 7-card hand (Texas Hold'em)
let mut seven_card_hand = Hand::new();
// ... add 7 cards
let rank = evaluator.rank_hand(&seven_card_hand);
Understanding Hand Ranks
Rank Values
Higher rank values indicate stronger hands:
// Compare hand strengths
let hand1_rank = evaluator.rank_hand(&hand1);
let hand2_rank = evaluator.rank_hand(&hand2);
if hand1_rank > hand2_rank {
println!("Hand 1 is stronger");
} else if hand1_rank < hand2_rank {
println!("Hand 2 is stronger");
} else {
println!("Hands are equal");
}
Hand Categories
Extract hand category from rank:
use poker_api::api::hand_eval::HandEval;
fn get_hand_category(rank: u32) -> u32 {
(rank & HandEval::HAND_CATEGORY_MASK) >> HandEval::HAND_CATEGORY_SHIFT
}
let category = get_hand_category(rank);
match category {
9 => println!("Straight Flush"),
8 => println!("Four of a Kind"),
7 => println!("Full House"),
6 => println!("Flush"),
5 => println!("Straight"),
4 => println!("Three of a Kind"),
3 => println!("Two Pair"),
2 => println!("One Pair"),
1 => println!("High Card"),
_ => println!("Unknown"),
}
Texas Hold'em Example
Here's a complete example of evaluating Texas Hold'em hands:
use poker_api::hand_evaluator::LookupHandEvaluator;
use poker_api::api::hand::Hand;
use poker_api::api::card::Card;
struct PokerGame {
evaluator: LookupHandEvaluator,
}
impl PokerGame {
fn new() -> Result<Self, Box<dyn std::error::Error>> {
Ok(Self {
evaluator: LookupHandEvaluator::new()?,
})
}
fn evaluate_texas_holdem(
&self,
hole_cards: &[Card; 2],
board: &[Card; 5]
) -> u32 {
let mut hand = Hand::new();
// Add hole cards
hand.add_card(hole_cards[0]).unwrap();
hand.add_card(hole_cards[1]).unwrap();
// Add community cards
for &card in board {
hand.add_card(card).unwrap();
}
self.evaluator.rank_hand(&hand)
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let game = PokerGame::new()?;
// Example: Royal Flush
let hole_cards = [
Card::from_string("As").unwrap(),
Card::from_string("Ks").unwrap(),
];
let board = [
Card::from_string("Qs").unwrap(),
Card::from_string("Js").unwrap(),
Card::from_string("Ts").unwrap(),
Card::from_string("2d").unwrap(),
Card::from_string("3c").unwrap(),
];
let rank = game.evaluate_texas_holdem(&hole_cards, &board);
println!("Royal flush rank: {}", rank);
Ok(())
}
Performance Optimization
Table Preloading
For applications that need immediate response:
// Warm up the evaluator before use
fn preload_evaluator() -> LookupHandEvaluator {
println!("Loading poker evaluator...");
let start = std::time::Instant::now();
let evaluator = LookupHandEvaluator::new().unwrap();
let duration = start.elapsed();
println!("Evaluator ready in {:?}", duration);
evaluator
}
Batch Evaluation
For evaluating many hands:
fn evaluate_multiple_hands(evaluator: &LookupHandEvaluator, hands: &[Hand]) -> Vec<u32> {
hands.iter()
.map(|hand| evaluator.rank_hand(hand))
.collect()
}
Error Handling
Common Errors
use poker_api::api::card::Card;
// Invalid card strings
let invalid = Card::from_string("XX"); // Err
let invalid = Card::from_string("53"); // Err
// Hand capacity exceeded
let mut hand = Hand::new();
// Adding more than 7 cards will fail
// Table loading failures
let evaluator = LookupHandEvaluator::new();
// May fail if disk is full or permissions are insufficient
Proper Error Handling
use std::io;
fn create_evaluator() -> Result<LookupHandEvaluator, io::Error> {
match LookupHandEvaluator::new() {
Ok(evaluator) => {
println!("Evaluator created successfully");
Ok(evaluator)
}
Err(e) => {
eprintln!("Failed to create evaluator: {}", e);
Err(e)
}
}
}
Best Practices
1. Reuse Evaluators
// Good: Reuse the same evaluator
struct GameEngine {
evaluator: LookupHandEvaluator,
}
impl GameEngine {
fn new() -> Result<Self, io::Error> {
Ok(Self {
evaluator: LookupHandEvaluator::new()?,
})
}
fn evaluate_hand(&self, hand: &Hand) -> u32 {
self.evaluator.rank_hand(hand)
}
}
2. Validate Input Data
fn safe_evaluate_hand(evaluator: &LookupHandEvaluator, hand: &Hand) -> Option<u32> {
if hand.size() >= 5 && hand.size() <= 7 {
Some(evaluator.rank_hand(hand))
} else {
None
}
}
3. Handle Edge Cases
// Check for invalid hands
let rank = evaluator.rank_hand(&hand);
if rank == 0 {
println!("Invalid hand: must have 5-7 cards");
}
Integration Examples
Poker Bot
use poker_api::hand_evaluator::LookupHandEvaluator;
struct PokerBot {
evaluator: LookupHandEvaluator,
}
impl PokerBot {
fn should_raise(&self, my_hand: &Hand, pot_odds: f32) -> bool {
let hand_strength = self.evaluator.rank_hand(my_hand);
// Simple strategy: raise with strong hands
match hand_strength {
rank if rank > 300_000_000 => true, // Very strong
rank if rank > 100_000_000 => pot_odds > 0.1, // Medium strength
_ => false, // Weak hands
}
}
}
Hand Range Analysis
use poker_api::hand_evaluator::LookupHandEvaluator;
use poker_api::api::hand::Hand;
struct RangeAnalyzer {
evaluator: LookupHandEvaluator,
}
impl RangeAnalyzer {
fn analyze_range(&self, hands: &[Hand]) -> HandStrengthStats {
let ranks: Vec<u32> = hands.iter()
.map(|hand| self.evaluator.rank_hand(hand))
.collect();
HandStrengthStats {
average_rank: ranks.iter().sum::<u32>() / ranks.len() as u32,
median_rank: self.calculate_median(&ranks),
strongest_hand: ranks.iter().max().copied().unwrap_or(0),
weakest_hand: ranks.iter().min().copied().unwrap_or(0),
}
}
}
Troubleshooting
Common Issues
Problem: "Evaluation tables do not exist, generating them..." every time
Solution: Ensure write permissions in the current directory, or specify a custom path
Problem: Slow first startup
Solution: Expected behavior - table generation takes 1-2 seconds
Problem: High memory usage
Solution: The 128MB lookup table is required for performance. Consider if this fits your deployment constraints
Problem: Card parsing fails
Solution: Verify card strings use correct format: rank + suit (e.g., "As", "Kh", "Td")
Performance Issues
If evaluation seems slow:
- Check build mode: Use
cargo build --release - Verify table loading: Ensure tables are generated and cached
- Monitor memory: Ensure sufficient RAM for table storage
- Consider batching: Evaluate multiple hands together when possible
Support
For additional help:
- Check the API Documentation for detailed reference
- Review test files for usage examples
- Open issues on GitHub for bugs or feature requests
- Study the source code comments for implementation details
This user guide covers the most common use cases. For advanced usage or customization, see the developer guide and source code documentation.