Fixed hand evaluator LUT generation

This commit is contained in:
Jan-Peter von Hunnius 2025-11-16 16:02:26 +01:00
parent 7819b1ee27
commit 51e1f58ef9
17 changed files with 2045 additions and 660 deletions

View file

@ -17,7 +17,6 @@ A comprehensive poker bot testing framework with Java-compatible hand evaluation
## Features ## Features
- **Java-Compatible**: Full compatibility with Java Meerkat API for seamless tool integration
- **Bot Testing Framework**: Specifically designed for automated poker bot testing and development - **Bot Testing Framework**: Specifically designed for automated poker bot testing and development
- **Blazing Fast**: O(1) hand evaluation using perfect hash jump tables - **Blazing Fast**: O(1) hand evaluation using perfect hash jump tables
- **Memory Efficient**: ~130MB for complete evaluation tables (79% reduction from 625MB) - **Memory Efficient**: ~130MB for complete evaluation tables (79% reduction from 625MB)

View file

@ -1,27 +1,5 @@
# Poker AI Testbed & Live Server Achievements # Poker AI Testbed & Live Server Achievements
## Major Technical Achievements
### 🚀 Perfect Hash Jump Table Implementation ✅
**Revolutionary Memory Optimization**
- **79% Memory Reduction**: Reduced evaluation table size from 625MB to ~130MB
- **Advanced Algorithm**: Perfect hash jump table with suit canonicalization
- **Bottom-up Construction**: Cache-optimized trie construction for optimal performance
- **Zero Breaking Changes**: Full API compatibility maintained for existing users
**Technical Innovation Details**
- **Suit Canonicalization**: Lexicographically smallest suit permutation algorithm
- **Three-Level Trie Structure**: Level 5 (terminal), Level 6 (intermediate), Level 7 (root)
- **Memory Layout Optimization**: Sequential access patterns for CPU cache efficiency
- **Streaming Construction**: Handles large datasets without excessive memory allocation
**Performance Improvements**
- **Faster 6-Card Evaluation**: Improved evaluation speed for 6-card hands
- **Enhanced 7-Card Performance**: Better performance for 7-card hand evaluation
- **Cache-Friendly Access**: Optimized memory access patterns for modern CPUs
- **Reduced Memory Pressure**: Significantly lower memory footprint for large-scale simulations
## Completed Phases and Items ## Completed Phases and Items
### Phase 1: Foundation ✅ ### Phase 1: Foundation ✅
@ -29,38 +7,12 @@
**Basic poker engine** **Basic poker engine**
- Card/Hand/Board representations ✅ - Card/Hand/Board representations ✅
- Fast LUT hand evaluator (5, 6, and 7 cards) ✅ - Fast LUT hand evaluator (5, 6, and 7 cards) ✅
- Game state management ✅
- Game Info and Player Info traits ✅
- Event reporting and action scheme ✅
- Basic game rules (NL/FL/PL) ✅
**Configuration system**
- TOML configuration structures ✅
- Bot parameter configuration ✅
- Game type configurations (Cash/SNG/MTT, buyins, blinds, hands per level, etc.) ✅
- Simulation parameters (hands/tournaments count, buyin thresholds for bots, etc.) ✅
## Success Metrics ## Success Metrics
- **Bot Performance:** Profitability across different game types - **Bot Performance:** Profitability across different game types
- **Simulation Speed:** Ability to run millions of hands efficiently - **Simulation Speed:** Ability to run millions of hands efficiently
- **Memory Efficiency:** 79% reduction in evaluation table memory usage
- **Live Reliability:** 99.9% uptime with correct action execution - **Live Reliability:** 99.9% uptime with correct action execution
- **Flexibility:** Easy configuration for new sites and game types - **Flexibility:** Easy configuration for new sites and game types
- **Technical Innovation:** State-of-the-art perfect hash jump table implementation
- **Evolution:** Continuous improvement through simulation results - **Evolution:** Continuous improvement through simulation results
- **Monitoring:** Meaningful debug and performance logging and visualization - **Monitoring:** Meaningful debug and performance logging and visualization
## Technical Specifications
### Memory Achievement Breakdown
- **Original Implementation**: 625MB for complete 7-card evaluation tables
- **New Implementation**: ~130MB for equivalent functionality (79% reduction)
- **Target Size**: Exactly 32-35 million u32 entries for optimal memory usage
- **Construction Time**: 2-3 minutes for complete table generation
### Performance Characteristics
- **5-Card Evaluation**: ~50-100 nanoseconds (O(1) lookup)
- **6-Card Evaluation**: ~200-300 nanoseconds (best 5-card selection)
- **7-Card Evaluation**: ~400-600 nanoseconds (best 5-card selection)
- **Hash Calculation**: ~20-30 nanoseconds (card canonicalization)

View file

@ -2,27 +2,19 @@
## Step-by-Step Implementation Plan ## Step-by-Step Implementation Plan
### Phase 1: Foundation ✅ ### Phase 1: Foundation
**Basic poker engine** **Basic poker engine**
- Fast LUT hand evaluator (5, 6, and 7 cards) ✅ - Game state management
- Game state management ✅ - Game Info and Player Info traits
- Game Info and Player Info traits ✅ - Event reporting and action scheme
- Event reporting and action scheme ✅ - Basic game rules (NL/FL/PL)
- Basic game rules (NL/FL/PL) ✅
**Configuration system** **Configuration system**
- TOML configuration structures ✅ - TOML configuration structures
- Bot parameter configuration ✅ - Bot parameter configuration
- Game type configurations (Cash/SNG/MTT, buyins, blinds, hands per level, etc.) ✅ - Game type configurations (Cash/SNG/MTT, buyins, blinds, hands per level, etc.)
- Simulation parameters (hands/tournaments count, buyin thresholds for bots, etc.) ✅ - Simulation parameters (hands/tournaments count, buyin thresholds for bots, etc.)
**🚀 Memory Optimization Achievement ✅**
- **Perfect Hash Jump Table**: Revolutionary 79% memory reduction (625MB → ~130MB)
- **Suit Canonicalization**: Advanced isomorphic hand reduction algorithm
- **Bottom-up Trie Construction**: Cache-optimized memory layout
- **Zero Breaking Changes**: Full API compatibility maintained
- **Performance Enhancement**: Faster evaluation for 6 and 7-card hands
### Phase 2: Simulation Testbed ### Phase 2: Simulation Testbed
**Goal:** Mass simulation capability **Goal:** Mass simulation capability

View file

@ -6,19 +6,11 @@ Create a **comprehensive poker AI ecosystem** with two main components:
- **Massive Simulation Testbed** – For developing and testing bot strategies across all game types (NL/FL/PL, Cash/SNG/MTT) with configurable parameters - **Massive Simulation Testbed** – For developing and testing bot strategies across all game types (NL/FL/PL, Cash/SNG/MTT) with configurable parameters
- **Live Action Server** – For deploying bots in real poker environments with automatic table selection - **Live Action Server** – For deploying bots in real poker environments with automatic table selection
--- ---
## Core Architecture Components ## Core Architecture Components
### 1. Advanced Evaluation Engine ### 1. Simulation Testbed
- **Perfect Hash Jump Tables**: Revolutionary memory-efficient evaluation (~130MB vs 625MB)
- **Suit Canonicalization**: Advanced isomorphic hand reduction for optimal performance
- **Bottom-up Trie Construction**: Cache-optimized memory layout for CPU efficiency
- **Zero Breaking Changes**: Full backward compatibility with existing evaluation APIs
### 2. Simulation Testbed
- **Configurable bots** (Rule-based, Nash EQ, Statistical, Hand-reading, Reinforcement Learning) - **Configurable bots** (Rule-based, Nash EQ, Statistical, Hand-reading, Reinforcement Learning)
- **Mass parallel simulations** with GPU acceleration (e.g., for turn and river rollouts) - **Mass parallel simulations** with GPU acceleration (e.g., for turn and river rollouts)

View file

@ -23,9 +23,11 @@ sha2 = "0.10"
tempfile = "3.23.0" tempfile = "3.23.0"
once_cell = "1.19" once_cell = "1.19"
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
indicatif = "0.17"
[dev-dependencies] [dev-dependencies]
proptest = "1.0" proptest = "1.0"
indicatif = "0.17"
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,7 @@
use super::errors::EvaluatorError; use super::errors::EvaluatorError;
use super::evaluator::{HandRank, HandValue}; use super::evaluator::{HandRank, HandValue};
use super::integration::{benchmark_evaluation, utils, EvaluatorComparison, MathEvaluator}; use super::integration::{EvaluatorComparison, MathEvaluator};
use super::tables::{CanonicalMapping, JumpTable}; use super::tables::{CanonicalMapping, JumpTable};
use crate::card::PackedCard; use crate::card::PackedCard;
use crate::{Card, Hand}; use crate::{Card, Hand};
@ -250,7 +250,7 @@ pub fn canonicalization_example() -> Result<(), EvaluatorError> {
mapping mapping
.canonical_cards .canonical_cards
.iter() .iter()
.map(|&c| (c & 0x03)) .map(|&c| c & 0x03)
.collect::<Vec<_>>() .collect::<Vec<_>>()
); );
println!(" Suit mapping: {:?}", mapping.suit_map); println!(" Suit mapping: {:?}", mapping.suit_map);
@ -444,14 +444,14 @@ fn extract_suits(cards: &[PackedCard]) -> Vec<u8> {
} }
/// Helper function to extract ranks from cards /// Helper function to extract ranks from cards
fn extract_ranks(cards: &[PackedCard]) -> Vec<u8> {
cards.iter().map(|card| card.rank()).collect()
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
fn extract_ranks(cards: &[PackedCard]) -> Vec<u8> {
cards.iter().map(|card| card.rank()).collect()
}
#[test] #[test]
fn test_basic_usage_example() { fn test_basic_usage_example() {
assert!(basic_usage_example().is_ok()); assert!(basic_usage_example().is_ok());

View file

@ -64,7 +64,7 @@
use super::errors::EvaluatorError; use super::errors::EvaluatorError;
use super::evaluator::{HandRank, HandValue}; use super::evaluator::{HandRank, HandValue};
use super::tables::{CanonicalMapping, JumpTable, JumpTableEntry}; use super::tables::JumpTable;
use crate::card::PackedCard; use crate::card::PackedCard;
use crate::{Card, Hand}; use crate::{Card, Hand};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -215,7 +215,7 @@ impl MathEvaluator {
pub fn convert_cards(cards: &[Card]) -> Vec<PackedCard> { pub fn convert_cards(cards: &[Card]) -> Vec<PackedCard> {
cards cards
.iter() .iter()
.map(|card| PackedCard::from_card(card)) .map(|card| PackedCard::new(card.rank(), card.suit()).unwrap())
.collect() .collect()
} }
@ -223,7 +223,7 @@ pub fn convert_cards(cards: &[Card]) -> Vec<PackedCard> {
pub fn convert_cards_fixed<const N: usize>(cards: &[Card; N]) -> [PackedCard; N] { pub fn convert_cards_fixed<const N: usize>(cards: &[Card; N]) -> [PackedCard; N] {
let mut result = [PackedCard::new(0, 0).unwrap(); N]; let mut result = [PackedCard::new(0, 0).unwrap(); N];
for (i, card) in cards.iter().enumerate() { for (i, card) in cards.iter().enumerate() {
result[i] = PackedCard::from_card(card); result[i] = PackedCard::new(card.rank(), card.suit()).unwrap();
} }
result result
} }
@ -392,13 +392,13 @@ impl EvaluatorComparison {
let mut core_times = Vec::new(); let mut core_times = Vec::new();
// Benchmark math evaluator // Benchmark math evaluator
for cards in &test_cases { for _cards in &test_cases {
let math_time = benchmark_evaluation(|c| self.math_evaluator.evaluate_7_card(c)); let math_time = benchmark_evaluation(|c| self.math_evaluator.evaluate_7_card(c));
math_times.push(math_time); math_times.push(math_time);
} }
// Benchmark core evaluator // Benchmark core evaluator
for cards in &test_cases { for _cards in &test_cases {
let core_time = benchmark_evaluation(|c| self.core_evaluator.evaluate_7_card(c)); let core_time = benchmark_evaluation(|c| self.core_evaluator.evaluate_7_card(c));
core_times.push(core_time); core_times.push(core_time);
} }
@ -473,8 +473,6 @@ pub mod utils {
/// Validate that math evaluator produces same results as holdem_core /// Validate that math evaluator produces same results as holdem_core
pub fn validate_evaluator_compatibility() -> Result<(), EvaluatorError> { pub fn validate_evaluator_compatibility() -> Result<(), EvaluatorError> {
use std::str::FromStr;
let comparison = EvaluatorComparison::new()?; let comparison = EvaluatorComparison::new()?;
// Test a few representative hands // Test a few representative hands
@ -647,19 +645,7 @@ mod tests {
#[test] #[test]
fn test_benchmark_function() { fn test_benchmark_function() {
use std::str::FromStr; let elapsed = benchmark_evaluation(|_cards| {
let test_cards = [
Card::from_str("As").unwrap(),
Card::from_str("Ks").unwrap(),
Card::from_str("Qs").unwrap(),
Card::from_str("Js").unwrap(),
Card::from_str("Ts").unwrap(),
Card::from_str("7h").unwrap(),
Card::from_str("6d").unwrap(),
];
let elapsed = benchmark_evaluation(|cards| {
// Simple evaluation for testing // Simple evaluation for testing
HandValue::new(HandRank::HighCard, 0) HandValue::new(HandRank::HighCard, 0)
}); });
@ -672,7 +658,7 @@ mod tests {
let comparison = EvaluatorComparison::new(); let comparison = EvaluatorComparison::new();
assert!(comparison.is_ok()); assert!(comparison.is_ok());
let mut comparison = EvaluatorComparison::new().unwrap(); let comparison = EvaluatorComparison::new().unwrap();
let results = comparison.compare_evaluations(&[]); let results = comparison.compare_evaluations(&[]);
// Should handle empty hand list gracefully // Should handle empty hand list gracefully

View file

@ -13,16 +13,25 @@
//! - **`property_tests`**: Property-based testing for evaluation correctness //! - **`property_tests`**: Property-based testing for evaluation correctness
//! - **`examples`**: Usage examples and performance benchmarks //! - **`examples`**: Usage examples and performance benchmarks
pub mod algorithmic_evaluator;
pub mod benchmark_utils;
pub mod errors; pub mod errors;
pub mod evaluator; pub mod evaluator;
pub mod examples; pub mod examples;
pub mod file_io; pub mod file_io;
pub mod integration; pub mod integration;
pub mod integration_utils;
pub mod property_tests; pub mod property_tests;
pub mod singleton; pub mod singleton;
pub mod tables; pub mod tables;
pub mod tables_new;
pub mod test_utils;
#[cfg(test)]
mod algorithmic_evaluator_test;
// Re-export commonly used types from local modules // Re-export commonly used types from local modules
pub use algorithmic_evaluator::{AlgorithmicEvaluator, EvaluationStats};
pub use errors::EvaluatorError; pub use errors::EvaluatorError;
pub use evaluator::{Evaluator, HandRank, HandValue}; pub use evaluator::{Evaluator, HandRank, HandValue};

View file

@ -22,11 +22,11 @@
//! 4. **Coverage**: Ensure comprehensive testing of all code paths //! 4. **Coverage**: Ensure comprehensive testing of all code paths
use super::errors::EvaluatorError; use super::errors::EvaluatorError;
use super::evaluator::{HandRank, HandValue}; use super::evaluator::HandRank;
use super::integration::{benchmark_evaluation, utils, EvaluatorComparison, MathEvaluator}; use super::integration::{benchmark_evaluation, EvaluatorComparison, MathEvaluator};
use super::tables::{CanonicalMapping, JumpTable}; use super::tables::{CanonicalMapping, JumpTable};
use crate::card::PackedCard; use crate::card::PackedCard;
use crate::{Card, Hand}; use crate::Card;
use std::str::FromStr; use std::str::FromStr;
/// Comprehensive test suite for the math evaluator system /// Comprehensive test suite for the math evaluator system
@ -103,6 +103,19 @@ impl EvaluatorTestSuite {
let mut passed = 0; let mut passed = 0;
let mut failed = 0; let mut failed = 0;
// Create progress bar for correctness tests
let total_tests = test_hands.len() * 3; // 3 tests per hand (5, 6, 7 card)
let pb = indicatif::ProgressBar::new(total_tests as u64);
pb.set_style(
indicatif::ProgressStyle::default_bar()
.template(
"{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})",
)
.unwrap()
.progress_chars("#>-"),
);
pb.set_message("Testing correctness");
for (i, cards) in test_hands.iter().enumerate() { for (i, cards) in test_hands.iter().enumerate() {
// Test 5-card evaluation // Test 5-card evaluation
let math_result_5 = self let math_result_5 = self
@ -112,6 +125,7 @@ impl EvaluatorTestSuite {
.core_evaluator .core_evaluator
.evaluate_5_card(&cards[..5].try_into().unwrap()); .evaluate_5_card(&cards[..5].try_into().unwrap());
pb.inc(1);
if math_result_5 == core_result_5 { if math_result_5 == core_result_5 {
passed += 1; passed += 1;
} else { } else {
@ -131,6 +145,7 @@ impl EvaluatorTestSuite {
.core_evaluator .core_evaluator
.evaluate_6_card(&cards[..6].try_into().unwrap()); .evaluate_6_card(&cards[..6].try_into().unwrap());
pb.inc(1);
if math_result_6 == core_result_6 { if math_result_6 == core_result_6 {
passed += 1; passed += 1;
} else { } else {
@ -147,6 +162,7 @@ impl EvaluatorTestSuite {
let math_result_7 = self.math_evaluator.evaluate_7_card(cards); let math_result_7 = self.math_evaluator.evaluate_7_card(cards);
let core_result_7 = self.core_evaluator.evaluate_7_card(cards); let core_result_7 = self.core_evaluator.evaluate_7_card(cards);
pb.inc(1);
if math_result_7 == core_result_7 { if math_result_7 == core_result_7 {
passed += 1; passed += 1;
} else { } else {
@ -161,6 +177,8 @@ impl EvaluatorTestSuite {
self.stats.total_tests += 3; self.stats.total_tests += 3;
} }
pb.finish_with_message("Correctness tests completed");
self.stats.passed_tests += passed; self.stats.passed_tests += passed;
self.stats.failed_tests += failed; self.stats.failed_tests += failed;
@ -172,16 +190,6 @@ impl EvaluatorTestSuite {
fn run_performance_tests(&mut self) -> Result<(), EvaluatorError> { fn run_performance_tests(&mut self) -> Result<(), EvaluatorError> {
println!("Running performance tests..."); println!("Running performance tests...");
let test_cards = [
Card::from_str("As").unwrap(),
Card::from_str("Ks").unwrap(),
Card::from_str("Qs").unwrap(),
Card::from_str("Js").unwrap(),
Card::from_str("Ts").unwrap(),
Card::from_str("7h").unwrap(),
Card::from_str("6d").unwrap(),
];
// Test 5-card performance // Test 5-card performance
let time_5 = benchmark_evaluation(|cards| { let time_5 = benchmark_evaluation(|cards| {
self.math_evaluator self.math_evaluator
@ -259,7 +267,6 @@ impl EvaluatorTestSuite {
// Test evaluator comparison utility // Test evaluator comparison utility
let comparison = EvaluatorComparison::new()?; let comparison = EvaluatorComparison::new()?;
let test_hands = utils::generate_test_hands();
let results = comparison.compare_evaluations(&[]); let results = comparison.compare_evaluations(&[]);
// Should handle empty input gracefully // Should handle empty input gracefully
@ -301,21 +308,21 @@ impl EvaluatorTestSuite {
let result_5 = self let result_5 = self
.math_evaluator .math_evaluator
.evaluate_5_card(&cards[..5].try_into().unwrap()); .evaluate_5_card(&cards[..5].try_into().unwrap());
assert!(result_5.rank as u8 <= HandRank::HighCard as u8); assert!(result_5.rank as u8 <= HandRank::RoyalFlush as u8);
} }
if cards.len() >= 6 { if cards.len() >= 6 {
let result_6 = self let result_6 = self
.math_evaluator .math_evaluator
.evaluate_6_card(&cards[..6].try_into().unwrap()); .evaluate_6_card(&cards[..6].try_into().unwrap());
assert!(result_6.rank as u8 <= HandRank::HighCard as u8); assert!(result_6.rank as u8 <= HandRank::RoyalFlush as u8);
} }
if cards.len() >= 7 { if cards.len() >= 7 {
let result_7 = self let result_7 = self
.math_evaluator .math_evaluator
.evaluate_7_card(&cards[..7].try_into().unwrap()); .evaluate_7_card(&cards[..7].try_into().unwrap());
assert!(result_7.rank as u8 <= HandRank::HighCard as u8); assert!(result_7.rank as u8 <= HandRank::RoyalFlush as u8);
} }
println!("Regression test {} passed", i); println!("Regression test {} passed", i);
@ -480,6 +487,7 @@ pub fn test_jump_table_properties() -> Result<(), EvaluatorError> {
// Test memory usage is within bounds // Test memory usage is within bounds
let memory_usage = table.memory_usage(); let memory_usage = table.memory_usage();
let max_memory = 200_000_000; // 200MB absolute max let max_memory = 200_000_000; // 200MB absolute max
let target_memory = 130_000_000; // ~130MB target for Cactus Kev's algorithm
assert!( assert!(
memory_usage <= max_memory, memory_usage <= max_memory,
@ -487,6 +495,14 @@ pub fn test_jump_table_properties() -> Result<(), EvaluatorError> {
memory_usage memory_usage
); );
// Verify memory usage is close to target (~130MB)
assert!(
memory_usage < target_memory * 2, // Allow some tolerance but should be much less than 2x target
"Memory usage far exceeds target: {} (target ~{}MB)",
memory_usage,
target_memory / 1024 / 1024
);
// Test that all entries are properly initialized // Test that all entries are properly initialized
for i in 0..100 { for i in 0..100 {
let entry = table.get(i); let entry = table.get(i);
@ -502,10 +518,23 @@ pub fn run_stress_tests() -> Result<(), EvaluatorError> {
println!("Running stress tests..."); println!("Running stress tests...");
let mut evaluator = MathEvaluator::new()?; let mut evaluator = MathEvaluator::new()?;
let num_hands = 1000;
// Create progress bar for stress test setup
let setup_pb = indicatif::ProgressBar::new(num_hands);
setup_pb.set_style(
indicatif::ProgressStyle::default_bar()
.template(
"{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})",
)
.unwrap()
.progress_chars("#>-"),
);
setup_pb.set_message("Generating test hands");
// Generate many test hands // Generate many test hands
let mut test_hands = Vec::new(); let mut test_hands = Vec::new();
for i in 0..1000 { for i in 0..num_hands {
let rank1 = (i * 7) % 13; let rank1 = (i * 7) % 13;
let suit1 = (i * 7) % 4; let suit1 = (i * 7) % 4;
let rank2 = (i * 11) % 13; let rank2 = (i * 11) % 13;
@ -526,18 +555,37 @@ pub fn run_stress_tests() -> Result<(), EvaluatorError> {
]; ];
test_hands.push(cards); test_hands.push(cards);
} }
setup_pb.inc(1);
} }
setup_pb.finish_with_message("Test hands generated");
// Create progress bar for evaluation
let eval_pb = indicatif::ProgressBar::new(test_hands.len() as u64);
eval_pb.set_style(
indicatif::ProgressStyle::default_bar()
.template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta}) - {msg}")
.unwrap()
.progress_chars("#>-")
);
eval_pb.set_message("Evaluating hands");
// Evaluate all hands and measure performance // Evaluate all hands and measure performance
let start_time = std::time::Instant::now(); let start_time = std::time::Instant::now();
for cards in &test_hands { for (i, cards) in test_hands.iter().enumerate() {
let _result = evaluator.evaluate_7_card(cards); let _result = evaluator.evaluate_7_card(cards);
// Update progress every 100 hands to minimize overhead
if i % 100 == 0 || i == test_hands.len() - 1 {
eval_pb.set_position((i + 1) as u64);
}
} }
let elapsed = start_time.elapsed(); let elapsed = start_time.elapsed();
let avg_time = elapsed / test_hands.len() as u32; let avg_time = elapsed / test_hands.len() as u32;
eval_pb.finish_with_message("Stress test completed");
println!( println!(
"Stress test: {} hands in {:?}, average: {:?}", "Stress test: {} hands in {:?}, average: {:?}",
test_hands.len(), test_hands.len(),

File diff suppressed because it is too large Load diff

View file

@ -355,7 +355,7 @@ impl Hand {
/// println!("Card: {}", card); /// println!("Card: {}", card);
/// } /// }
/// ``` /// ```
pub fn iter(&self) -> std::slice::Iter<Card> { pub fn iter(&self) -> std::slice::Iter<'_, Card> {
self.cards[0..self.len].iter() self.cards[0..self.len].iter()
} }

View file

@ -76,6 +76,8 @@ pub use board::Street;
/// Re-export error types for convenience /// Re-export error types for convenience
pub use errors::PokerError; pub use errors::PokerError;
/// Re-export algorithmic evaluator for direct evaluation
pub use evaluator::algorithmic_evaluator::{AlgorithmicEvaluator, EvaluationStats};
/// Re-export evaluator types for convenience /// Re-export evaluator types for convenience
pub use evaluator::evaluator::{Evaluator, HandRank, HandValue}; pub use evaluator::evaluator::{Evaluator, HandRank, HandValue};

View file

@ -403,11 +403,8 @@ fn test_integration_data_consistency() {
// This module provides integration tests for the advanced evaluator system, // This module provides integration tests for the advanced evaluator system,
// ensuring compatibility and correct interaction with the holdem_core evaluator. // ensuring compatibility and correct interaction with the holdem_core evaluator.
use holdem_core::card::PackedCard; use holdem_core::evaluator::integration::MathEvaluator;
use holdem_core::evaluator::integration::{EvaluatorComparison, MathEvaluator}; use holdem_core::Card;
use holdem_core::evaluator::tables::JumpTable;
use holdem_core::evaluator::{HandRank, HandValue};
use holdem_core::{Card, Hand};
use std::str::FromStr; use std::str::FromStr;
/// Integration test suite for the math evaluator /// Integration test suite for the math evaluator

View file

@ -53,9 +53,9 @@ mod card_properties {
((12, 3), (12, 0)), // Ace spades > Ace hearts (Spades=4 > Hearts=3) ((12, 3), (12, 0)), // Ace spades > Ace hearts (Spades=4 > Hearts=3)
((12, 0), (12, 1)), // Ace hearts > Ace diamonds (Hearts=3 > Diamonds=2) ((12, 0), (12, 1)), // Ace hearts > Ace diamonds (Hearts=3 > Diamonds=2)
((12, 1), (12, 2)), // Ace diamonds > Ace clubs (Diamonds=2 > Clubs=1) ((12, 1), (12, 2)), // Ace diamonds > Ace clubs (Diamonds=2 > Clubs=1)
((5, 3), (5, 0)), // Five spades > Five hearts ((3, 3), (3, 0)), // Five spades > Five hearts
((5, 0), (5, 1)), // Five hearts > Five diamonds ((3, 0), (3, 1)), // Five hearts > Five diamonds
((5, 1), (5, 2)), // Five diamonds > Five clubs ((3, 1), (3, 2)), // Five diamonds > Five clubs
]; ];
for ((rank1, suit1), (rank2, suit2)) in test_cases { for ((rank1, suit1), (rank2, suit2)) in test_cases {

View file

@ -1,13 +0,0 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc d0090c2206be394c0d408b3e3007d10014ef89cc0b289519b1a830b2c3626662 # shrinks to cards = [Card { rank: 4, suit: 0 }, Card { rank: 5, suit: 0 }, Card { rank: 2, suit: 1 }, Card { rank: 8, suit: 2 }, Card { rank: 1, suit: 3 }, Card { rank: 9, suit: 3 }]
cc ca376e60fdef26e0b7b9a3d96000de681d13a84e917ab55f95cbf536b720412c # shrinks to cards = [Card { rank: 5, suit: 0 }, Card { rank: 8, suit: 0 }, Card { rank: 12, suit: 1 }, Card { rank: 0, suit: 2 }, Card { rank: 7, suit: 2 }, Card { rank: 12, suit: 2 }, Card { rank: 7, suit: 3 }]
cc e8cbfce99bb2beeefb23c4cc317e22fb04bae3f8acd7a7edc81f7457fdb6e1fb # shrinks to hand = [Card { rank: 12, suit: 3 }, Card { rank: 11, suit: 3 }, Card { rank: 10, suit: 3 }, Card { rank: 9, suit: 3 }, Card { rank: 8, suit: 3 }]
cc d2513c8954856423773cdcdfd3a336a17f92a6984c9a4ef803b74d960678ec7f # shrinks to cards = [Card { rank: 8, suit: 0 }, Card { rank: 11, suit: 1 }, Card { rank: 2, suit: 2 }, Card { rank: 11, suit: 3 }, Card { rank: 12, suit: 3 }]
cc 3cb176da85ed776101a34339707b68f3bafa03ebd9f7fb495c36590e6a64c3f5 # shrinks to cards = [Card { rank: 7, suit: 1 }, Card { rank: 6, suit: 2 }, Card { rank: 11, suit: 2 }, Card { rank: 12, suit: 2 }, Card { rank: 11, suit: 3 }]
cc 487fd65fa6cee58dfe754c01cdff15913fea01e4b90b6c0bfc2d45823a587f90 # shrinks to hand = [Card { rank: 12, suit: 3 }, Card { rank: 11, suit: 3 }, Card { rank: 10, suit: 3 }, Card { rank: 9, suit: 3 }, Card { rank: 8, suit: 3 }]
cc 7e46fa2f7f3ecb235753efedbca0d196d025147d51fc16f863e965af0da32654 # shrinks to hand1 = [Card { rank: 12, suit: 3 }, Card { rank: 11, suit: 3 }, Card { rank: 10, suit: 3 }, Card { rank: 9, suit: 3 }, Card { rank: 8, suit: 3 }], hand2 = [Card { rank: 12, suit: 3 }, Card { rank: 11, suit: 3 }, Card { rank: 10, suit: 3 }, Card { rank: 9, suit: 3 }, Card { rank: 8, suit: 3 }]

View file

@ -1 +1 @@
{"rustc_fingerprint":9168241167641241758,"outputs":{"16495917692426387086":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\n","stderr":""},"15729799797837862367":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\jan\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\npacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"cmpxchg16b\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"pc\"\nwindows\n","stderr":""},"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.81.0 (eeb90cda1 2024-09-04)\nbinary: rustc\ncommit-hash: eeb90cda1969383f56a2637cbd3037bdf598841c\ncommit-date: 2024-09-04\nhost: x86_64-pc-windows-msvc\nrelease: 1.81.0\nLLVM version: 18.1.7\n","stderr":""}},"successes":{}} {"rustc_fingerprint":12042185895431567242,"outputs":{"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.90.0 (1159e78c4 2025-09-14)\nbinary: rustc\ncommit-hash: 1159e78c4747b02ef996e55082b704c09b970588\ncommit-date: 2025-09-14\nhost: x86_64-pc-windows-msvc\nrelease: 1.90.0\nLLVM version: 20.1.8\n","stderr":""},"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\jan\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\npacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"cmpxchg16b\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"pc\"\nwindows\n","stderr":""}},"successes":{}}