rusty_marvin/docs/api.md
Jan-Peter von Hunnius 5c271b279f Added Documentation
Added Test Cases
Added Comments
2025-10-02 18:33:31 +02:00

8.9 KiB

API Documentation

Core Types

LookupHandEvaluator

The main hand evaluation engine that uses precomputed lookup tables for O(1) hand evaluation.

Constructor

pub fn new() -> io::Result<Self>

Creates a new evaluator instance. On first use, generates evaluation tables if they don't exist.

Returns:

  • Ok(LookupHandEvaluator) - Successfully initialized evaluator
  • Err(io::Error) - Failed to load or generate tables

Methods

rank_hand(&self, hand: &Hand) -> u32

Evaluates any poker hand (5, 6, or 7 cards) and returns its rank value.

let rank = evaluator.rank_hand(&hand);

Parameters:

  • hand: &Hand - Poker hand containing 5-7 cards

Returns:

  • u32 - Hand rank (higher values = stronger hands)
rank_hand5(&self, hand: &Hand) -> u32

Optimized evaluation for exactly 5-card hands.

let rank = evaluator.rank_hand5(&hand);

Parameters:

  • hand: &Hand - Poker hand containing exactly 5 cards

Returns:

  • u32 - Hand rank value
rank_hand6(&self, cards: &[u32]) -> u32

Evaluates a 6-card hand by finding the best 5-card combination.

let rank = evaluator.rank_hand6(&card_indices);

Parameters:

  • cards: &[u32] - Array of 6 card indices

Returns:

  • u32 - Best possible 5-card hand rank
rank_hand7(&self, cards: &[u32]) -> u32

Evaluates a 7-card hand by finding the best 5-card combination.

let rank = evaluator.rank_hand7(&card_indices);

Parameters:

  • cards: &[u32] - Array of 7 card indices

Returns:

  • u32 - Best possible 5-card hand rank

HandEval Trait

Standardized interface for poker hand evaluators.

Constants

const HAND_CATEGORY_SHIFT: u32 = 24;
const HAND_CATEGORY_MASK: u32 = 0xF << Self::HAND_CATEGORY_SHIFT;
const VALUE_MASK: u32 = 0x000FFFFF;

Methods

rank_hand(&self, hand: &Hand) -> u32

Evaluates a poker hand of any supported size.

rank_hand7(&self, cards: &[Card; 7]) -> u32

Evaluates exactly 7 cards.

hand_description(&self, hand_value: u32) -> String

Returns human-readable description of a hand rank.

hand_description_hand(&self, hand: &Hand) -> String

Describes a hand by evaluating it first.

hand_description7(&self, cards: &[Card; 7]) -> String

Describes the best 5-card hand from 7 cards.

Supporting Types

Hand

Collection of cards that can be evaluated.

Key Methods

// Construction
let mut hand = Hand::new();
hand.add_card(card).unwrap();

// Information
let size = hand.size();
let is_valid = hand.size() >= 5;

// String representation
println!("{}", hand.to_string()); // "As Ks Qs Js Ts"

Card

Individual playing card representation.

Construction

// From string notation
let card = Card::from_string("As").unwrap();
let card = Card::from_string("Kh").unwrap();

// String representation
assert_eq!(card.to_string(), "As");

Properties

let rank = card.rank();  // Card rank (0-12)
let suit = card.suit();  // Card suit (0-3)
let index = card.index(); // Unique card index (0-51)

Hand Ranking Values

Category Extraction

// Extract hand category (bits 24-27)
let category = (hand_value & HandEval::HAND_CATEGORY_MASK) >> HandEval::HAND_CATEGORY_SHIFT;

// Extract relative value (bits 0-23)
let value = hand_value & HandEval::VALUE_MASK;

Hand Categories

Category Value Description
Straight Flush 9 Five cards in sequence, same suit
Four of a Kind 8 Four cards of same rank
Full House 7 Three of a kind + pair
Flush 6 Five cards, same suit
Straight 5 Five cards in sequence
Three of a Kind 4 Three cards of same rank
Two Pair 3 Two different pairs
One Pair 2 Two cards of same rank
High Card 1 No matching cards

Rank Value Ranges

Hand Type Rank Range Count
Straight Flush 368,793,253 - 371,292,191 40
Four of a Kind 184,396,629 - 368,793,252 624
Full House 92,198,315 - 184,396,628 3,744
Flush 46,099,158 - 92,198,314 5,108
Straight 23,049,579 - 46,099,157 10,200
Three of a Kind 11,524,790 - 23,049,578 54,912
Two Pair 5,762,395 - 11,524,789 123,552
One Pair 2,881,198 - 5,762,394 1,098,240
High Card 1 - 2,881,197 1,302,540

Performance Characteristics

Time Complexity

Operation Time Complexity Notes
5-Card evaluation O(1) Direct table lookup
6-Card evaluation O(1) 6 table lookups
7-Card evaluation O(1) 21 table lookups
Table generation O(N) N = 32M combinations

Memory Usage

Component Size Description
Hand ranks table ~128 MB Complete evaluation table
Working tables ~2.4 MB Generation workspace
Code size ~50 KB Compiled binary

Cache Performance

The lookup table design provides excellent cache performance:

  • Sequential Access: Table generation processes data sequentially
  • Compact Encoding: Cards encoded in 8 bits each
  • Prefetch Friendly: Linear table layout enables hardware prefetching
  • Low Branch Misprediction: Perfect hash eliminates conditional logic

Error Handling

Common Error Cases

// Invalid hand size
let rank = evaluator.rank_hand(&hand); // Returns 0 for < 5 cards

// Card parsing errors
let card = Card::from_string("XX"); // Returns Err

// Table loading errors
let evaluator = LookupHandEvaluator::new(); // May fail if disk full

Error Types

  • io::Error - File system errors during table loading/generation
  • CardParseError - Invalid card string format
  • HandFullError - Attempting to add cards to full hand

Thread Safety

The LookupHandEvaluator is designed for read-only usage after construction:

// Safe: Read-only evaluator
let evaluator = LookupHandEvaluator::new().unwrap();

// Multiple threads can use the same evaluator
let rank1 = evaluator.rank_hand(&hand1);
let rank2 = evaluator.rank_hand(&hand2);

// NOT safe: Modifying evaluator during use
// (evaluator is not Sync + Send for mutation)

Integration Examples

Texas Hold'em Engine

use poker_api::hand_evaluator::LookupHandEvaluator;
use poker_api::api::{hand::Hand, card::Card};

struct HoldemEngine {
    evaluator: LookupHandEvaluator,
}

impl HoldemEngine {
    fn evaluate_board(&self, hole_cards: &[Card; 2], board: &[Card; 5]) -> u32 {
        let mut all_cards = Vec::new();
        all_cards.extend_from_slice(hole_cards);
        all_cards.extend_from_slice(board);

        let mut hand = Hand::new();
        for card in all_cards {
            hand.add_card(card).unwrap();
        }

        self.evaluator.rank_hand(&hand)
    }
}

Hand Range Analysis

use poker_api::hand_evaluator::LookupHandEvaluator;

struct RangeAnalyzer {
    evaluator: LookupHandEvaluator,
}

impl RangeAnalyzer {
    fn compare_hands(&self, hand1: &Hand, hand2: &Hand) -> std::cmp::Ordering {
        let rank1 = self.evaluator.rank_hand(hand1);
        let rank2 = self.evaluator.rank_hand(hand2);
        rank1.cmp(&rank2)
    }
}

Testing Integration

Unit Tests

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

    #[test]
    fn test_royal_flush() {
        let evaluator = LookupHandEvaluator::new().unwrap();
        let mut hand = Hand::new();
        // Add A,K,Q,J,10 of same suit
        let rank = evaluator.rank_hand(&hand);
        assert!(rank > 368_793_253); // Royal flush range
    }
}

Benchmark Tests

#[cfg(test)]
mod benchmarks {
    use super::*;
    use std::time::Instant;

    #[test]
    fn benchmark_evaluation() {
        let evaluator = LookupHandEvaluator::new().unwrap();
        let hand = create_test_hand();

        let start = Instant::now();
        for _ in 0..1_000_000 {
            let _rank = evaluator.rank_hand(&hand);
        }
        let duration = start.elapsed();

        println!("1M evaluations in {:?}", duration);
    }
}

Advanced Configuration

Custom Table Paths

The evaluator uses default paths, but this can be customized by modifying the source:

// In hand_evaluator.rs
const FILE_NAME: &'static str = "custom/path/HandRanks.dat";

Memory Management

For systems with limited memory, consider:

// Use Box<[u32]> for heap allocation control
let hand_ranks: Box<[u32]> = vec![0; SIZE].into_boxed_slice();

// Monitor memory usage
println!("Table memory usage: {} MB",
         std::mem::size_of_val(&*hand_ranks) / 1_048_576);

Performance Tuning

For maximum performance:

// Use release mode
cargo build --release

// Consider SIMD optimizations for batch evaluation
// (requires custom implementation)

This API documentation covers the complete public interface for poker hand evaluation. For implementation details, see the source code comments and the developer guide.