feat: add AlgorithmicEvaluator, migrate to log crate, fix flush value calculation

- Add AlgorithmicEvaluator wrapper for direct hand evaluation without
  lookup tables, with proper Result-based error handling
- Replace all println! and #[cfg(debug_assertions)] debug output with
  log crate macros (log::info!, log::debug!, log::warn!)
- Fix calculate_flush_value to use base-13 encoding for unique flush
  hand values instead of simple linear sum
- Fix is_straight to use cleaner control flow for regular and wheel
  straight detection
- Remove jump table HighCard/value=0 guard in evaluate_5_card to
  simplify evaluation path
- Add evaluate_n_card_hand_public as pub(crate) for AlgorithmicEvaluator
- Add .gitignore to exclude target/ and .idea/ from version control
- Add comprehensive tests for AlgorithmicEvaluator (14 test cases)
- Update README with new API usage examples
- Add log dependency to Cargo.toml
- Add placeholder modules for future implementations (tables_new,
  benchmark_utils, integration_utils, test_utils)
This commit is contained in:
Jan-Peter von Hunnius 2026-05-11 20:22:32 +02:00
parent 51e1f58ef9
commit dd605e8a66
16 changed files with 1666 additions and 228 deletions

10
.gitignore vendored Normal file
View file

@ -0,0 +1,10 @@
# Build artifacts
/target
# IDE
.idea/
*.iml
# OS
.DS_Store
Thumbs.db

1145
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
# Poker Testbed - Java-Compatible Hand Evaluator for Bot Testing
[![Crates.io](https://img.shields.io/crates/v/poker-api.svg)](https://crates.io/crates/poker-api)
[![Documentation](https://docs.rs/poker-api/badge.svg)](https://docs.rs/poker-api)
[![Crates.io](https://img.shields.io/crates/v/holdem_core.svg)](https://crates.io/crates/holdem_core)
[![Documentation](https://docs.rs/holdem_core/badge.svg)](https://docs.rs/holdem_core)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
A comprehensive poker bot testing framework with Java-compatible hand evaluation. This library implements the Meerkat API algorithm with full Java compatibility, specifically designed for automated poker bot testing, development, and integration with existing poker tools.
@ -50,31 +50,39 @@ Add this to your `Cargo.toml`:
```toml
[dependencies]
poker-api = "0.1.0"
holdem_core = "0.1.0"
```
## Quick Start - Bot Testing
```rust
use holdem_core::hand_evaluator::LookupHandEvaluator;
use holdem_core::api::{hand::Hand, card::Card};
use holdem_core::{Card, Hand, HoleCards, Board, Evaluator};
use std::str::FromStr;
// Initialize Java-compatible evaluator for bot testing
let evaluator = LookupHandEvaluator::new().unwrap();
// Create cards from string notation
let ace_spades = Card::from_str("As").unwrap();
let king_spades = Card::from_str("Ks").unwrap();
// Create hole cards for bot analysis
let mut hole_cards = Hand::new();
hole_cards.add_card(Card::from_string("As").unwrap()).unwrap();
hole_cards.add_card(Card::from_string("Ks").unwrap()).unwrap();
// Create hole cards from notation
let hole_cards = HoleCards::from_notation("AKs").unwrap();
// Evaluate hand strength for bot decision making
let rank = evaluator.rank_hand(&hole_cards);
println!("Hole card rank: {}", rank); // Lower = stronger hand
// Create a board with community cards
let board = Board::new()
.with_flop([
Card::from_str("Qh").unwrap(),
Card::from_str("Jh").unwrap(),
Card::from_str("Th").unwrap(),
]).unwrap()
.with_turn(Card::from_str("2c").unwrap()).unwrap()
.with_river(Card::from_str("3d").unwrap()).unwrap();
// Use rank for bot strategy decisions
if rank < 1000 { // Strong hand threshold
println!("Bot: This is a premium starting hand!");
}
// Create a hand from hole cards and board
let hand = Hand::from_hole_cards_and_board(&hole_cards, &board).unwrap();
// Evaluate the hand
let evaluator = Evaluator::new().unwrap();
let hand_value = evaluator.evaluate_hand(&hand);
println!("Hand rank: {:?}", hand_value.rank);
```
## Bot Testing Capabilities

View file

@ -8,10 +8,10 @@ readme = "README.md"
license = "MIT"
repository = "https://github.com/jhunnius/rusty_marvin"
documentation = "https://docs.rs/holdem_core"
homepage = "https://github.com/your-org/holdem_core"
homepage = "https://github.com/jhunnius/rusty_marvin"
keywords = ["poker", "bot-testing", "texas-holdem", "testbed"]
categories = ["games", "api-bindings", "mathematics"]
exclude = ["docs/*", "target/*", "*.md"]
exclude = ["docs/*", "target/*"]
[dependencies]
serde = { version = "1.0", features = ["derive"] }
@ -24,6 +24,7 @@ tempfile = "3.23.0"
once_cell = "1.19"
chrono = { version = "0.4", features = ["serde"] }
indicatif = "0.17"
log = "0.4"
[dev-dependencies]
proptest = "1.0"

View file

@ -0,0 +1,67 @@
//! Algorithmic hand evaluator for direct poker hand evaluation.
//!
//! This module provides a standalone evaluator that uses pure algorithmic
//! methods (no lookup tables) for evaluating poker hands. It wraps the
//! direct evaluation logic from [`Evaluator::evaluate_5_card_direct`].
use crate::Card;
use super::errors::EvaluatorError;
use super::evaluator::{Evaluator, HandValue};
use std::sync::Arc;
/// Statistics tracking for evaluation operations.
#[derive(Debug, Clone, Default)]
pub struct EvaluationStats {
/// Total number of evaluations performed.
pub total_evaluations: u64,
/// Average time per evaluation in nanoseconds.
pub average_time_ns: u64,
/// Total time spent evaluating in nanoseconds.
pub total_time_ns: u64,
}
/// Algorithmic poker hand evaluator.
///
/// Performs direct hand evaluation without relying on lookup tables.
/// Delegates to the same core algorithm used by [`Evaluator::evaluate_5_card_direct`].
#[derive(Debug, Clone)]
pub struct AlgorithmicEvaluator {
inner: Arc<Evaluator>,
stats: EvaluationStats,
}
impl AlgorithmicEvaluator {
/// Create a new algorithmic evaluator instance.
pub fn new() -> Result<Self, EvaluatorError> {
let inner = Evaluator::new()?;
Ok(Self {
inner: Arc::new(inner),
stats: EvaluationStats::default(),
})
}
/// Evaluate a 5-card hand directly using the algorithmic approach.
pub fn evaluate_5_card_direct(&self, cards: &[Card; 5]) -> HandValue {
self.inner.evaluate_5_card_direct(cards)
}
/// Evaluate a 6-card hand, finding the best 5-card combination.
pub fn evaluate_6_card(&self, cards: &[Card; 6]) -> HandValue {
self.inner.evaluate_n_card_hand_public(cards)
}
/// Evaluate a 7-card hand, finding the best 5-card combination.
pub fn evaluate_7_card(&self, cards: &[Card; 7]) -> HandValue {
self.inner.evaluate_n_card_hand_public(cards)
}
/// Get a reference to the current evaluation statistics.
pub fn stats(&self) -> &EvaluationStats {
&self.stats
}
/// Reset the evaluation statistics.
pub fn reset_stats(&mut self) {
self.stats = EvaluationStats::default();
}
}

View file

@ -0,0 +1,200 @@
//! Tests for the algorithmic evaluator.
use super::algorithmic_evaluator::AlgorithmicEvaluator;
use super::evaluator::HandRank;
use crate::Card;
use std::str::FromStr;
#[test]
fn test_new_evaluator() {
let evaluator = AlgorithmicEvaluator::new();
assert!(evaluator.is_ok());
}
#[test]
fn test_royal_flush() {
let evaluator = AlgorithmicEvaluator::new().unwrap();
let 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(),
];
let result = evaluator.evaluate_5_card_direct(&cards);
assert_eq!(result.rank, HandRank::RoyalFlush);
}
#[test]
fn test_straight_flush() {
let evaluator = AlgorithmicEvaluator::new().unwrap();
let cards = [
Card::from_str("9s").unwrap(),
Card::from_str("Ks").unwrap(),
Card::from_str("Qs").unwrap(),
Card::from_str("Js").unwrap(),
Card::from_str("Ts").unwrap(),
];
let result = evaluator.evaluate_5_card_direct(&cards);
assert_eq!(result.rank, HandRank::StraightFlush);
}
#[test]
fn test_four_of_a_kind() {
let evaluator = AlgorithmicEvaluator::new().unwrap();
let cards = [
Card::from_str("As").unwrap(),
Card::from_str("Ah").unwrap(),
Card::from_str("Ad").unwrap(),
Card::from_str("Ac").unwrap(),
Card::from_str("Ks").unwrap(),
];
let result = evaluator.evaluate_5_card_direct(&cards);
assert_eq!(result.rank, HandRank::FourOfAKind);
}
#[test]
fn test_full_house() {
let evaluator = AlgorithmicEvaluator::new().unwrap();
let cards = [
Card::from_str("As").unwrap(),
Card::from_str("Ah").unwrap(),
Card::from_str("Ad").unwrap(),
Card::from_str("Ks").unwrap(),
Card::from_str("Kh").unwrap(),
];
let result = evaluator.evaluate_5_card_direct(&cards);
assert_eq!(result.rank, HandRank::FullHouse);
}
#[test]
fn test_flush() {
let evaluator = AlgorithmicEvaluator::new().unwrap();
let cards = [
Card::from_str("As").unwrap(),
Card::from_str("Ks").unwrap(),
Card::from_str("Qs").unwrap(),
Card::from_str("Js").unwrap(),
Card::from_str("9s").unwrap(),
];
let result = evaluator.evaluate_5_card_direct(&cards);
assert_eq!(result.rank, HandRank::Flush);
}
#[test]
fn test_straight() {
let evaluator = AlgorithmicEvaluator::new().unwrap();
let cards = [
Card::from_str("As").unwrap(),
Card::from_str("Kh").unwrap(),
Card::from_str("Qd").unwrap(),
Card::from_str("Jc").unwrap(),
Card::from_str("Ts").unwrap(),
];
let result = evaluator.evaluate_5_card_direct(&cards);
assert_eq!(result.rank, HandRank::Straight);
}
#[test]
fn test_wheel_straight() {
let evaluator = AlgorithmicEvaluator::new().unwrap();
let cards = [
Card::from_str("As").unwrap(),
Card::from_str("2h").unwrap(),
Card::from_str("3d").unwrap(),
Card::from_str("4c").unwrap(),
Card::from_str("5s").unwrap(),
];
let result = evaluator.evaluate_5_card_direct(&cards);
assert_eq!(result.rank, HandRank::Straight);
}
#[test]
fn test_three_of_a_kind() {
let evaluator = AlgorithmicEvaluator::new().unwrap();
let cards = [
Card::from_str("As").unwrap(),
Card::from_str("Ah").unwrap(),
Card::from_str("Ad").unwrap(),
Card::from_str("Ks").unwrap(),
Card::from_str("Qh").unwrap(),
];
let result = evaluator.evaluate_5_card_direct(&cards);
assert_eq!(result.rank, HandRank::ThreeOfAKind);
}
#[test]
fn test_two_pair() {
let evaluator = AlgorithmicEvaluator::new().unwrap();
let cards = [
Card::from_str("As").unwrap(),
Card::from_str("Ah").unwrap(),
Card::from_str("Ks").unwrap(),
Card::from_str("Kh").unwrap(),
Card::from_str("Qd").unwrap(),
];
let result = evaluator.evaluate_5_card_direct(&cards);
assert_eq!(result.rank, HandRank::TwoPair);
}
#[test]
fn test_pair() {
let evaluator = AlgorithmicEvaluator::new().unwrap();
let cards = [
Card::from_str("As").unwrap(),
Card::from_str("Ah").unwrap(),
Card::from_str("Ks").unwrap(),
Card::from_str("Qh").unwrap(),
Card::from_str("Jd").unwrap(),
];
let result = evaluator.evaluate_5_card_direct(&cards);
assert_eq!(result.rank, HandRank::Pair);
}
#[test]
fn test_high_card() {
let evaluator = AlgorithmicEvaluator::new().unwrap();
let cards = [
Card::from_str("As").unwrap(),
Card::from_str("Kh").unwrap(),
Card::from_str("Qd").unwrap(),
Card::from_str("Jc").unwrap(),
Card::from_str("9s").unwrap(),
];
let result = evaluator.evaluate_5_card_direct(&cards);
assert_eq!(result.rank, HandRank::HighCard);
}
#[test]
fn test_hand_rank_ordering() {
let evaluator = AlgorithmicEvaluator::new().unwrap();
let royal_flush = [
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(),
];
let four_of_a_kind = [
Card::from_str("As").unwrap(),
Card::from_str("Ah").unwrap(),
Card::from_str("Ad").unwrap(),
Card::from_str("Ac").unwrap(),
Card::from_str("Ks").unwrap(),
];
let rf = evaluator.evaluate_5_card_direct(&royal_flush);
let foak = evaluator.evaluate_5_card_direct(&four_of_a_kind);
assert!(rf.rank as u8 > foak.rank as u8);
}
#[test]
fn test_stats_initially_default() {
let evaluator = AlgorithmicEvaluator::new().unwrap();
let stats = evaluator.stats();
assert_eq!(stats.total_evaluations, 0);
assert_eq!(stats.average_time_ns, 0);
assert_eq!(stats.total_time_ns, 0);
}

View file

@ -0,0 +1,4 @@
//! Benchmark utilities for the hand evaluator.
//!
//! This module provides helper functions and data structures for benchmarking
//! hand evaluation performance.

View file

@ -321,12 +321,12 @@ impl JumpTableBenchmark {
/// Print benchmark results in a formatted table
pub fn print_results(&self, results: &[BenchmarkResult]) {
println!("\n=== Jump Table Performance Benchmarks ===");
println!("{:<20} {:<15} {:<15} {:<15} {}",
log::info!("\n=== Jump Table Performance Benchmarks ===");
log::info!("{:<20} {:<15} {:<15} {:<15} {}",
"Test", "Total Time", "Avg Time/Op", "Ops/Second", "Memory");
for result in results {
println!(
log::info!(
"{:<20} {:<15} {:<15} {:<15.0} {}",
result.name,
format!("{:.2?}", result.total_time),
@ -339,7 +339,7 @@ impl JumpTableBenchmark {
);
}
println!("\n=== Performance Requirements Check ===");
log::info!("\n=== Performance Requirements Check ===");
self.check_performance_requirements(results);
}
@ -350,7 +350,7 @@ impl JumpTableBenchmark {
"5-Card Evaluation" => {
let target_ns = 100; // Target: 100 nanoseconds
let actual_ns = result.avg_time_per_op.as_nanos() as u64;
println!(
log::info!(
"5-Card Evaluation: {} ns (target: {} ns) - {}",
actual_ns,
target_ns,
@ -360,7 +360,7 @@ impl JumpTableBenchmark {
"6-Card Evaluation" => {
let target_ns = 500; // Target: 500 nanoseconds
let actual_ns = result.avg_time_per_op.as_nanos() as u64;
println!(
log::info!(
"6-Card Evaluation: {} ns (target: {} ns) - {}",
actual_ns,
target_ns,
@ -370,7 +370,7 @@ impl JumpTableBenchmark {
"7-Card Evaluation" => {
let target_us = 2; // Target: 2 microseconds
let actual_us = result.avg_time_per_op.as_micros() as u64;
println!(
log::info!(
"7-Card Evaluation: {} μs (target: {} μs) - {}",
actual_us,
target_us,
@ -381,7 +381,7 @@ impl JumpTableBenchmark {
let target_mb = 130; // Target: 130 MB
if let Some(bytes) = result.memory_usage {
let actual_mb = bytes / 1_000_000;
println!(
log::info!(
"Memory Usage: {} MB (target: {} MB) - {}",
actual_mb,
target_mb,

View file

@ -145,101 +145,88 @@ impl HandValue {
}
// DEBUG: Log the flat rank being processed
#[cfg(debug_assertions)]
println!("DEBUG: Processing flat_rank: {}", flat_rank);
log::debug!("Processing flat_rank: {}", flat_rank);
// Find which rank this flat_rank belongs to using proper poker hand distribution
let result = match flat_rank {
1..=1_302_539 => {
#[cfg(debug_assertions)]
println!("DEBUG: Mapping to HighCard with value {}", flat_rank);
log::debug!("Mapping to HighCard with value {}", flat_rank);
Self::new(HandRank::HighCard, flat_rank)
}
1_302_540..=2_400_779 => {
#[cfg(debug_assertions)]
println!(
"DEBUG: Mapping to Pair with value {}",
log::debug!(
"Mapping to Pair with value {}",
flat_rank - 1_302_540
);
Self::new(HandRank::Pair, flat_rank - 1_302_540)
}
2_400_780..=2_524_331 => {
#[cfg(debug_assertions)]
println!(
"DEBUG: Mapping to TwoPair with value {}",
log::debug!(
"Mapping to TwoPair with value {}",
flat_rank - 2_400_780
);
Self::new(HandRank::TwoPair, flat_rank - 2_400_780)
}
2_524_332..=2_579_243 => {
#[cfg(debug_assertions)]
println!(
"DEBUG: Mapping to ThreeOfAKind with value {}",
log::debug!(
"Mapping to ThreeOfAKind with value {}",
flat_rank - 2_524_332
);
Self::new(HandRank::ThreeOfAKind, flat_rank - 2_524_332)
}
2_579_244..=2_589_443 => {
#[cfg(debug_assertions)]
println!(
"DEBUG: Mapping to Straight with value {}",
log::debug!(
"Mapping to Straight with value {}",
flat_rank - 2_579_244
);
Self::new(HandRank::Straight, flat_rank - 2_579_244)
}
2_589_444..=2_594_551 => {
#[cfg(debug_assertions)]
println!(
"DEBUG: Mapping to Flush with value {}",
log::debug!(
"Mapping to Flush with value {}",
flat_rank - 2_589_444
);
Self::new(HandRank::Flush, flat_rank - 2_589_444)
}
2_594_552..=2_598_295 => {
#[cfg(debug_assertions)]
println!(
"DEBUG: Mapping to FullHouse with value {}",
log::debug!(
"Mapping to FullHouse with value {}",
flat_rank - 2_594_552
);
Self::new(HandRank::FullHouse, flat_rank - 2_594_552)
}
2_598_296..=2_598_919 => {
#[cfg(debug_assertions)]
println!(
"DEBUG: Mapping to FourOfAKind with value {}",
log::debug!(
"Mapping to FourOfAKind with value {}",
flat_rank - 2_598_296
);
Self::new(HandRank::FourOfAKind, flat_rank - 2_598_296)
}
2_598_920..=2_598_955 => {
#[cfg(debug_assertions)]
println!(
"DEBUG: Mapping to StraightFlush with value {}",
log::debug!(
"Mapping to StraightFlush with value {}",
flat_rank - 2_598_920
);
Self::new(HandRank::StraightFlush, flat_rank - 2_598_920)
}
2_598_956..=2_598_959 => {
#[cfg(debug_assertions)]
println!(
"DEBUG: Mapping to RoyalFlush with value {}",
log::debug!(
"Mapping to RoyalFlush with value {}",
flat_rank - 2_598_956
);
Self::new(HandRank::RoyalFlush, flat_rank - 2_598_956)
}
_ => {
// Out of range, fallback to high card
#[cfg(debug_assertions)]
println!(
"DEBUG: Out of range, falling back to HighCard with value {}",
log::debug!(
"Out of range, falling back to HighCard with value {}",
flat_rank
);
Self::new(HandRank::HighCard, flat_rank)
}
};
#[cfg(debug_assertions)]
println!("DEBUG: Result: {:?}", result);
log::debug!("Result: {:?}", result);
result
}
@ -634,11 +621,11 @@ impl Evaluator {
if JumpTable::exists(&path) {
match JumpTable::load_from_file(&path) {
Ok(table) => {
println!("Loaded existing jump table from: {}", path.display());
log::info!("Loaded existing jump table from: {}", path.display());
return Ok(table);
}
Err(e) => {
println!(
log::warn!(
"Failed to load existing jump table ({}), creating new one...",
e
);
@ -652,7 +639,7 @@ impl Evaluator {
/// Create a new jump table and save it to file
fn create_and_save_table(path: &Path) -> Result<JumpTable, EvaluatorError> {
println!("Creating new jump table...");
log::info!("Creating new jump table...");
let mut table = JumpTable::with_target_memory();
// Build the jump table with proper evaluation data
@ -668,7 +655,7 @@ impl Evaluator {
EvaluatorError::table_init_failed(&format!("Failed to save jump table: {}", e))
})?;
println!(
log::info!(
"Evaluator created successfully with {} table entries and saved to: {}",
table.size,
path.display()
@ -696,11 +683,7 @@ impl Evaluator {
let packed_array: [PackedCard; 5] = packed_cards.try_into().unwrap();
if let Ok(result) = self.tables.evaluate_5_card(&packed_array) {
// Only use jump table result if it's not the default HighCard with value 0
// This indicates the jump table lookup succeeded
if result.rank != HandRank::HighCard || result.value != 0 {
return result;
}
return result;
}
}
@ -771,6 +754,12 @@ impl Evaluator {
best_hand_value
}
/// Crate-internal wrapper for evaluating an N-card hand by finding the best 5-card combination.
/// Used by [`AlgorithmicEvaluator`](super::algorithmic_evaluator::AlgorithmicEvaluator).
pub(crate) fn evaluate_n_card_hand_public<T: AsRef<[Card]>>(&self, cards: T) -> HandValue {
self.evaluate_n_card_hand(cards)
}
/// Generate all combinations of K elements from a set of N elements
fn generate_combinations(&self, set: &[usize], k: usize) -> Vec<Vec<usize>> {
let mut result = Vec::new();
@ -909,17 +898,19 @@ impl Evaluator {
/// Check if ranks form a straight
fn is_straight(&self, ranks: &[u8; 5]) -> bool {
// Check for regular straight (consecutive ranks)
// Check for regular straight (consecutive ranks descending)
let mut is_regular = true;
for i in 0..4 {
if ranks[i] != ranks[i + 1] + 1 {
// Check for wheel straight (A,2,3,4,5)
if *ranks == [12, 3, 2, 1, 0] {
return true;
}
return false;
is_regular = false;
break;
}
}
true
if is_regular {
return true;
}
// Check for wheel straight: A,2,3,4,5 → sorted desc: [12,3,2,1,0]
*ranks == [12, 3, 2, 1, 0]
}
/// Check if hand has N of a kind
@ -983,9 +974,9 @@ impl Evaluator {
/// Calculate value for flush
fn calculate_flush_value(&self, ranks: &[u8; 5]) -> u32 {
// For flush, value is based on highest card, then next highest, etc.
// Use minimal range to ensure flush values stay within expected bounds
// Simple linear combination to ensure values stay in range
ranks[0] as u32 * 1 + ranks[1] as u32 + ranks[2] as u32 + ranks[3] as u32 + ranks[4] as u32
// Use base-13 encoding to ensure unique values for different flush hands
ranks[0] as u32 * 28561 + ranks[1] as u32 * 2197
+ ranks[2] as u32 * 169 + ranks[3] as u32 * 13 + ranks[4] as u32
}
/// Calculate value for three of a kind
@ -1094,7 +1085,7 @@ impl Evaluator {
// Validate that the table was built correctly
table.validate()?;
println!(
log::info!(
"Evaluator created successfully with {} table entries (no persistence)",
table.size
);
@ -1108,7 +1099,7 @@ impl Evaluator {
pub fn regenerate_table(&mut self) -> Result<(), EvaluatorError> {
let path = JumpTable::default_file_path();
println!("Regenerating jump table...");
log::info!("Regenerating jump table...");
let mut table = JumpTable::with_target_memory();
// Build the jump table with proper evaluation data
@ -1127,7 +1118,7 @@ impl Evaluator {
// Update the evaluator's table
self.tables = Arc::new(table);
println!("Jump table regenerated and saved successfully");
log::info!("Jump table regenerated and saved successfully");
Ok(())
}
@ -1139,7 +1130,7 @@ impl Evaluator {
/// Validate the compressed ranking system
/// Ensures all 7,462 distinct hands are properly represented and comparison semantics are preserved
pub fn validate_compressed_ranking(&self) -> Result<(), EvaluatorError> {
println!("Validating compressed ranking system...");
log::info!("Validating compressed ranking system...");
// Test 1: Ensure all compressed ranks are within 0-7461 range
let mut compressed_ranks = std::collections::HashSet::new();
@ -1366,9 +1357,9 @@ impl Evaluator {
}
}
println!("Compressed ranking validation completed successfully!");
println!("Tested {} unique compressed ranks", compressed_ranks.len());
println!("Range: {} - {}", min_rank, max_rank);
log::info!("Compressed ranking validation completed successfully!");
log::info!("Tested {} unique compressed ranks", compressed_ranks.len());
log::info!("Range: {} - {}", min_rank, max_rank);
Ok(())
}

View file

@ -22,7 +22,7 @@ use std::time::Instant;
/// Basic usage example demonstrating core functionality
pub fn basic_usage_example() -> Result<(), EvaluatorError> {
println!("=== Basic Usage Example ===");
log::info!("=== Basic Usage Example ===");
// Create a 7-card poker hand
let cards = [
@ -35,7 +35,7 @@ pub fn basic_usage_example() -> Result<(), EvaluatorError> {
Card::from_str("6d").unwrap(),
];
println!(
log::info!(
"Evaluating hand: {:?}",
cards.iter().map(|c| c.to_string()).collect::<Vec<_>>()
);
@ -44,7 +44,7 @@ pub fn basic_usage_example() -> Result<(), EvaluatorError> {
let mut math_evaluator = MathEvaluator::new()?;
let result1 = math_evaluator.evaluate_7_card(&cards);
println!("MathEvaluator result: {:?}", result1);
log::info!("MathEvaluator result: {:?}", result1);
// Method 2: Using JumpTable directly (for advanced use cases)
let mut table = JumpTable::with_target_memory();
@ -54,18 +54,18 @@ pub fn basic_usage_example() -> Result<(), EvaluatorError> {
let packed_array: [PackedCard; 7] = packed_cards.try_into().unwrap();
let result2 = table.evaluate_7_card(&packed_array);
println!("JumpTable result: {:?}", result2);
log::info!("JumpTable result: {:?}", result2);
// Results should be identical
assert_eq!(result1, result2.unwrap());
println!("✓ Basic evaluation complete\n");
log::info!("✓ Basic evaluation complete\n");
Ok(())
}
/// Performance benchmarking example
pub fn performance_example() -> Result<(), EvaluatorError> {
println!("=== Performance Benchmarking Example ===");
log::info!("=== Performance Benchmarking Example ===");
// Create test hands of different types
let test_hands = vec![
@ -121,7 +121,7 @@ pub fn performance_example() -> Result<(), EvaluatorError> {
let mut math_evaluator = MathEvaluator::new()?;
println!("Benchmarking {} hand types...", test_hands.len());
log::info!("Benchmarking {} hand types...", test_hands.len());
for (hand_type, cards) in test_hands {
let iterations = 10000;
@ -135,23 +135,23 @@ pub fn performance_example() -> Result<(), EvaluatorError> {
let elapsed = start_time.elapsed();
let avg_time = elapsed / iterations;
println!(" {}: {:?}", hand_type, avg_time);
log::info!(" {}: {:?}", hand_type, avg_time);
}
// Display evaluator statistics
let stats = math_evaluator.stats();
println!("\nEvaluator Statistics:");
println!(" Total evaluations: {}", stats.total_evaluations);
println!(" Average time: {} ns", stats.average_time_ns);
println!(" Total time: {} ms", stats.total_time_ns / 1_000_000);
log::info!("\nEvaluator Statistics:");
log::info!(" Total evaluations: {}", stats.total_evaluations);
log::info!(" Average time: {} ns", stats.average_time_ns);
log::info!(" Total time: {} ms", stats.total_time_ns / 1_000_000);
println!("✓ Performance benchmarking complete\n");
log::info!("✓ Performance benchmarking complete\n");
Ok(())
}
/// Integration example showing compatibility with holdem_core
pub fn integration_example() -> Result<(), EvaluatorError> {
println!("=== Integration Example ===");
log::info!("=== Integration Example ===");
// Create hands using holdem_core
let hole_cards = crate::HoleCards::from_notation("AKs")
@ -160,10 +160,10 @@ pub fn integration_example() -> Result<(), EvaluatorError> {
let hand = Hand::from_hole_cards_and_board(&hole_cards, &board)
.map_err(|_| EvaluatorError::table_init_failed("Invalid hand"))?;
println!("Evaluating complete hand:");
println!(" Hole cards: {}", hole_cards);
println!(" Board: {}", board);
println!(" Complete hand: {}", hand);
log::info!("Evaluating complete hand:");
log::info!(" Hole cards: {}", hole_cards);
log::info!(" Board: {}", board);
log::info!(" Complete hand: {}", hand);
// Compare results between math and holdem_core evaluators
let mut comparison = EvaluatorComparison::new()?;
@ -171,40 +171,40 @@ pub fn integration_example() -> Result<(), EvaluatorError> {
let math_result = comparison.math_evaluator.evaluate_hand(&hand);
let core_result = comparison.core_evaluator.evaluate_hand(&hand);
println!("\nEvaluation Results:");
println!(" Math evaluator: {:?}", math_result);
println!(" Core evaluator: {:?}", core_result);
println!(" Results match: {}", math_result == core_result);
log::info!("\nEvaluation Results:");
log::info!(" Math evaluator: {:?}", math_result);
log::info!(" Core evaluator: {:?}", core_result);
log::info!(" Results match: {}", math_result == core_result);
// Test type conversion utilities
let cards = hand.cards();
let packed_cards = super::integration::convert_cards(&cards);
let converted_back = super::integration::convert_cards_back(&packed_cards)?;
println!("\nType Conversion Test:");
println!(
log::info!("\nType Conversion Test:");
log::info!(
" Original: {:?}",
cards.iter().map(|c| c.to_string()).collect::<Vec<_>>()
);
println!(
log::info!(
" Converted back: {:?}",
converted_back
.iter()
.map(|c| c.to_string())
.collect::<Vec<_>>()
);
println!(
log::info!(
" Conversion successful: {}",
cards.len() == converted_back.len()
);
println!("✓ Integration example complete\n");
log::info!("✓ Integration example complete\n");
Ok(())
}
/// Canonical mapping example demonstrating suit canonicalization
pub fn canonicalization_example() -> Result<(), EvaluatorError> {
println!("=== Canonical Mapping Example ===");
log::info!("=== Canonical Mapping Example ===");
// Create cards with different suit patterns
let test_cases = vec![
@ -240,12 +240,12 @@ pub fn canonicalization_example() -> Result<(), EvaluatorError> {
];
for (description, cards) in test_cases {
println!("Testing: {}", description);
log::info!("Testing: {}", description);
let mapping = CanonicalMapping::from_cards(&cards);
println!(" Original suits: {:?}", extract_suits(&cards));
println!(
log::info!(" Original suits: {:?}", extract_suits(&cards));
log::info!(
" Canonical suits: {:?}",
mapping
.canonical_cards
@ -253,46 +253,46 @@ pub fn canonicalization_example() -> Result<(), EvaluatorError> {
.map(|&c| c & 0x03)
.collect::<Vec<_>>()
);
println!(" Suit mapping: {:?}", mapping.suit_map);
println!(" Reverse mapping: {:?}", mapping.reverse_map);
log::info!(" Suit mapping: {:?}", mapping.suit_map);
log::info!(" Reverse mapping: {:?}", mapping.reverse_map);
// Verify round-trip conversion
let original_suits = mapping.to_original_suits(&mapping.canonical_cards);
println!(
log::info!(
" Round-trip successful: {}",
original_suits.len() == cards.len()
);
println!();
log::info!("");
}
println!("✓ Canonical mapping example complete\n");
log::info!("✓ Canonical mapping example complete\n");
Ok(())
}
/// Advanced configuration example
pub fn advanced_configuration_example() -> Result<(), EvaluatorError> {
println!("=== Advanced Configuration Example ===");
log::info!("=== Advanced Configuration Example ===");
// Create custom jump table with specific size
let custom_size = 25_000_000; // 25M entries
let mut table = JumpTable::new(custom_size);
println!("Creating custom jump table with {} entries...", custom_size);
log::info!("Creating custom jump table with {} entries...", custom_size);
let build_start = Instant::now();
table.build()?;
let build_time = build_start.elapsed();
println!("Table built in {:?}", build_time);
println!(
log::info!("Table built in {:?}", build_time);
log::info!(
"Memory usage: {:.2} MB",
table.memory_usage() as f64 / 1_000_000.0
);
// Validate table integrity
table.validate()?;
println!("Table validation: ✓ Passed");
log::info!("Table validation: ✓ Passed");
// Test evaluation with custom table
let test_cards = [
@ -309,27 +309,27 @@ pub fn advanced_configuration_example() -> Result<(), EvaluatorError> {
let packed_array: [PackedCard; 7] = packed_cards.try_into().unwrap();
let result = table.evaluate_7_card(&packed_array);
println!("Custom table evaluation result: {:?}", result);
log::info!("Custom table evaluation result: {:?}", result);
// Display table metadata
println!("\nTable Metadata:");
println!(" Version: {}", table.metadata.version);
println!(" Created: {}", table.metadata.created_at);
println!(
log::info!("\nTable Metadata:");
log::info!(" Version: {}", table.metadata.version);
log::info!(" Created: {}", table.metadata.created_at);
log::info!(
" Total combinations: {}",
table.metadata.total_combinations
);
println!(" Level 5 nodes: {}", table.metadata.stats.level5_nodes);
println!(" Level 6 nodes: {}", table.metadata.stats.level6_nodes);
println!(" Level 7 nodes: {}", table.metadata.stats.level7_nodes);
log::info!(" Level 5 nodes: {}", table.metadata.stats.level5_nodes);
log::info!(" Level 6 nodes: {}", table.metadata.stats.level6_nodes);
log::info!(" Level 7 nodes: {}", table.metadata.stats.level7_nodes);
println!("✓ Advanced configuration example complete\n");
log::info!("✓ Advanced configuration example complete\n");
Ok(())
}
/// Memory optimization example
pub fn memory_optimization_example() -> Result<(), EvaluatorError> {
println!("=== Memory Optimization Example ===");
log::info!("=== Memory Optimization Example ===");
// Compare different table sizes
let sizes = vec![10_000_000, 20_000_000, 30_000_000, 34_000_000];
@ -343,7 +343,7 @@ pub fn memory_optimization_example() -> Result<(), EvaluatorError> {
let memory_mb = table.memory_usage() as f64 / 1_000_000.0;
println!(
log::info!(
"Size: {:8} entries | Memory: {:6.1} MB | Build time: {:?}",
size, memory_mb, build_time
);
@ -356,22 +356,22 @@ pub fn memory_optimization_example() -> Result<(), EvaluatorError> {
let target_table = JumpTable::with_target_memory();
let target_memory = target_table.memory_usage() as f64 / 1_000_000.0;
println!("\nTarget configuration:");
println!(" Memory usage: {:.1} MB", target_memory);
println!(" Recommended for production use: ✓");
log::info!("\nTarget configuration:");
log::info!(" Memory usage: {:.1} MB", target_memory);
log::info!(" Recommended for production use: ✓");
println!("✓ Memory optimization example complete\n");
log::info!("✓ Memory optimization example complete\n");
Ok(())
}
/// Error handling example
pub fn error_handling_example() -> Result<(), EvaluatorError> {
println!("=== Error Handling Example ===");
log::info!("=== Error Handling Example ===");
// Demonstrate proper error handling
match MathEvaluator::new() {
Ok(mut evaluator) => {
println!("✓ MathEvaluator created successfully");
log::info!("✓ MathEvaluator created successfully");
// Test with valid input
let valid_cards = [
@ -385,10 +385,10 @@ pub fn error_handling_example() -> Result<(), EvaluatorError> {
];
let result = evaluator.evaluate_7_card(&valid_cards);
println!("✓ Evaluation successful: {:?}", result);
log::info!("✓ Evaluation successful: {:?}", result);
}
Err(e) => {
println!("✗ Failed to create MathEvaluator: {:?}", e);
log::info!("✗ Failed to create MathEvaluator: {:?}", e);
return Err(e);
}
}
@ -405,23 +405,23 @@ pub fn error_handling_example() -> Result<(), EvaluatorError> {
}
match table.validate() {
Ok(()) => println!("✓ Table validation passed"),
Err(e) => println!("✗ Table validation failed: {:?}", e),
Ok(()) => log::info!("✓ Table validation passed"),
Err(e) => log::info!("✗ Table validation failed: {:?}", e),
}
// Test invalid table access
match table.get(2000) {
Some(_) => println!("✗ Should not find entry beyond table size"),
None => println!("✓ Properly handles out-of-bounds access"),
Some(_) => log::info!("✗ Should not find entry beyond table size"),
None => log::info!("✓ Properly handles out-of-bounds access"),
}
println!("✓ Error handling example complete\n");
log::info!("✓ Error handling example complete\n");
Ok(())
}
/// Comprehensive demonstration of all features
pub fn comprehensive_demo() -> Result<(), EvaluatorError> {
println!("=== Comprehensive Math Evaluator Demo ===\n");
log::info!("=== Comprehensive Math Evaluator Demo ===\n");
// Run all examples
basic_usage_example()?;
@ -432,8 +432,8 @@ pub fn comprehensive_demo() -> Result<(), EvaluatorError> {
memory_optimization_example()?;
error_handling_example()?;
println!("🎉 All examples completed successfully!");
println!("The math evaluator is ready for production use.");
log::info!("🎉 All examples completed successfully!");
log::info!("The math evaluator is ready for production use.");
Ok(())
}

View file

@ -39,7 +39,7 @@
//!
//! let packed_array: [PackedCard; 7] = math_cards.try_into().unwrap();
//! let result = table.evaluate_7_card(&packed_array);
//! println!("Hand evaluation: {:?}", result);
//! log::info!("Hand evaluation: {:?}", result);
//! ```
//!
//! ### Performance Comparison
@ -58,8 +58,8 @@
//! core_eval.evaluate_7_card(cards)
//! });
//!
//! println!("Math evaluator: {:?}", math_time);
//! println!("Core evaluator: {:?}", core_time);
//! log::info!("Math evaluator: {:?}", math_time);
//! log::info!("Core evaluator: {:?}", core_time);
//! ```
use super::errors::EvaluatorError;
@ -494,7 +494,7 @@ pub mod utils {
}
}
println!("All evaluator compatibility tests passed!");
log::info!("All evaluator compatibility tests passed!");
Ok(())
}

View file

@ -0,0 +1,4 @@
//! Integration utilities for the hand evaluator.
//!
//! This module provides helper functions and data structures for integration
//! testing and cross-validation of hand evaluation results.

View file

@ -71,7 +71,7 @@ impl EvaluatorTestSuite {
pub fn run_all_tests(&mut self) -> Result<TestStats, EvaluatorError> {
let start_time = std::time::Instant::now();
println!("Running comprehensive math evaluator test suite...");
log::info!("Running comprehensive math evaluator test suite...");
// Run correctness tests
self.run_correctness_tests()?;
@ -91,13 +91,13 @@ impl EvaluatorTestSuite {
let elapsed = start_time.elapsed();
self.stats.execution_time_ms = elapsed.as_millis() as u64;
println!("Test suite completed: {:?}", self.stats);
log::info!("Test suite completed: {:?}", self.stats);
Ok(self.stats.clone())
}
/// Test that math evaluator produces correct results
fn run_correctness_tests(&mut self) -> Result<(), EvaluatorError> {
println!("Running correctness tests...");
log::info!("Running correctness tests...");
let test_hands = generate_comprehensive_test_hands();
let mut passed = 0;
@ -130,7 +130,7 @@ impl EvaluatorTestSuite {
passed += 1;
} else {
failed += 1;
println!(
log::info!(
"5-card mismatch at test {}: math={:?}, core={:?}",
i, math_result_5, core_result_5
);
@ -150,7 +150,7 @@ impl EvaluatorTestSuite {
passed += 1;
} else {
failed += 1;
println!(
log::info!(
"6-card mismatch at test {}: math={:?}, core={:?}",
i, math_result_6, core_result_6
);
@ -167,7 +167,7 @@ impl EvaluatorTestSuite {
passed += 1;
} else {
failed += 1;
println!(
log::info!(
"7-card mismatch at test {}: math={:?}, core={:?}",
i, math_result_7, core_result_7
);
@ -182,13 +182,13 @@ impl EvaluatorTestSuite {
self.stats.passed_tests += passed;
self.stats.failed_tests += failed;
println!("Correctness tests: {} passed, {} failed", passed, failed);
log::info!("Correctness tests: {} passed, {} failed", passed, failed);
Ok(())
}
/// Test performance characteristics
fn run_performance_tests(&mut self) -> Result<(), EvaluatorError> {
println!("Running performance tests...");
log::info!("Running performance tests...");
// Test 5-card performance
let time_5 = benchmark_evaluation(|cards| {
@ -209,33 +209,33 @@ impl EvaluatorTestSuite {
let max_7_card_time = std::time::Duration::from_nanos(1000);
if time_7 > max_7_card_time {
println!("Warning: 7-card evaluation too slow: {:?}", time_7);
log::info!("Warning: 7-card evaluation too slow: {:?}", time_7);
} else {
println!("Performance tests passed");
log::info!("Performance tests passed");
}
println!("Performance results:");
println!(" 5-card: {:?}", time_5);
println!(" 6-card: {:?}", time_6);
println!(" 7-card: {:?}", time_7);
log::info!("Performance results:");
log::info!(" 5-card: {:?}", time_5);
log::info!(" 6-card: {:?}", time_6);
log::info!(" 7-card: {:?}", time_7);
Ok(())
}
/// Test memory usage targets
fn run_memory_tests(&mut self) -> Result<(), EvaluatorError> {
println!("Running memory tests...");
log::info!("Running memory tests...");
let memory_usage = self.math_evaluator.get_jump_table().memory_usage();
let target_max_memory = 150_000_000; // 150MB target
if memory_usage > target_max_memory {
println!(
log::info!(
"Warning: Memory usage exceeds target: {} bytes",
memory_usage
);
} else {
println!("Memory usage within target: {} bytes", memory_usage);
log::info!("Memory usage within target: {} bytes", memory_usage);
}
// Test that jump table is properly sized
@ -245,13 +245,13 @@ impl EvaluatorTestSuite {
assert!(table.size > 0);
assert!(table.memory_usage() > 0);
println!("Memory tests passed");
log::info!("Memory tests passed");
Ok(())
}
/// Test integration between systems
fn run_integration_tests(&mut self) -> Result<(), EvaluatorError> {
println!("Running integration tests...");
log::info!("Running integration tests...");
// Test type conversions
let cards = [
@ -272,13 +272,13 @@ impl EvaluatorTestSuite {
// Should handle empty input gracefully
assert_eq!(results.len(), 0);
println!("Integration tests passed");
log::info!("Integration tests passed");
Ok(())
}
/// Test for known bugs and regressions
fn run_regression_tests(&mut self) -> Result<(), EvaluatorError> {
println!("Running regression tests...");
log::info!("Running regression tests...");
// Test edge cases that have caused issues in the past
let edge_cases = vec![
@ -325,10 +325,10 @@ impl EvaluatorTestSuite {
assert!(result_7.rank as u8 <= HandRank::RoyalFlush as u8);
}
println!("Regression test {} passed", i);
log::info!("Regression test {} passed", i);
}
println!("Regression tests passed");
log::info!("Regression tests passed");
Ok(())
}
}
@ -441,7 +441,7 @@ fn generate_comprehensive_test_hands() -> Vec<[Card; 7]> {
/// Property-based tests for canonicalization
pub fn test_canonicalization_properties() -> Result<(), EvaluatorError> {
println!("Testing canonicalization properties...");
log::info!("Testing canonicalization properties...");
// Test that canonicalization is deterministic
let cards = vec![
@ -468,13 +468,13 @@ pub fn test_canonicalization_properties() -> Result<(), EvaluatorError> {
let original_suits = mapping1.to_original_suits(&mapping1.canonical_cards);
assert_eq!(original_suits.len(), mapping1.canonical_cards.len());
println!("Canonicalization property tests passed");
log::info!("Canonicalization property tests passed");
Ok(())
}
/// Property-based tests for jump table
pub fn test_jump_table_properties() -> Result<(), EvaluatorError> {
println!("Testing jump table properties...");
log::info!("Testing jump table properties...");
let mut table = JumpTable::with_target_memory();
@ -509,13 +509,13 @@ pub fn test_jump_table_properties() -> Result<(), EvaluatorError> {
assert!(entry.is_some());
}
println!("Jump table property tests passed");
log::info!("Jump table property tests passed");
Ok(())
}
/// Stress tests for performance under load
pub fn run_stress_tests() -> Result<(), EvaluatorError> {
println!("Running stress tests...");
log::info!("Running stress tests...");
let mut evaluator = MathEvaluator::new()?;
let num_hands = 1000;
@ -586,7 +586,7 @@ pub fn run_stress_tests() -> Result<(), EvaluatorError> {
eval_pb.finish_with_message("Stress test completed");
println!(
log::info!(
"Stress test: {} hands in {:?}, average: {:?}",
test_hands.len(),
elapsed,
@ -599,7 +599,7 @@ pub fn run_stress_tests() -> Result<(), EvaluatorError> {
"Stress test too slow"
);
println!("Stress tests passed");
log::info!("Stress tests passed");
Ok(())
}

View file

@ -468,11 +468,11 @@ impl JumpTable {
let total_size = level5_size + level6_size + level7_size;
println!("Creating jump table with optimal memory layout:");
println!(" Level 5 (5-card): {} entries", level5_size);
println!(" Level 6 (6-card): {} entries", level6_size);
println!(" Level 7 (7-card): {} entries", level7_size);
println!(
log::info!("Creating jump table with optimal memory layout:");
log::info!(" Level 5 (5-card): {} entries", level5_size);
log::info!(" Level 6 (6-card): {} entries", level6_size);
log::info!(" Level 7 (7-card): {} entries", level7_size);
log::info!(
" Total: {} entries (~{} MB)",
total_size,
total_size * 16 / 1024 / 1024
@ -574,7 +574,7 @@ impl JumpTable {
EvaluatorError::table_init_failed(&format!("Failed to write to file: {}", e))
})?;
println!("Jump table saved to: {}", path.display());
log::info!("Jump table saved to: {}", path.display());
Ok(())
}
@ -621,7 +621,7 @@ impl JumpTable {
// Validate the loaded table
table.validate()?;
println!("Jump table loaded from: {}", path.display());
log::info!("Jump table loaded from: {}", path.display());
Ok(table)
}
@ -655,7 +655,7 @@ impl JumpTable {
// Generate all C(52,7) combinations - this is a large number (133M+)
// For practical purposes, we'll generate a representative subset
// In production, this would be done in batches or with streaming
println!("Generating canonical 7-card combinations...");
log::info!("Generating canonical 7-card combinations...");
// For now, generate a smaller subset for testing and development
let max_combinations = if cfg!(test) { 1000 } else { 100_000 };
@ -680,7 +680,7 @@ impl JumpTable {
combinations.push(combo);
if combinations.len() >= max_combinations {
println!(
log::info!(
"Generated {} combinations for testing",
combinations.len()
);
@ -694,31 +694,31 @@ impl JumpTable {
}
}
println!("Generated {} canonical combinations", combinations.len());
log::info!("Generated {} canonical combinations", combinations.len());
Ok(combinations)
}
/// Build the jump table using bottom-up trie construction
pub fn build(&mut self) -> Result<(), EvaluatorError> {
println!("Building jump table with {} entries...", self.size);
log::info!("Building jump table with {} entries...", self.size);
// Step 1: Generate canonical mappings for all 7-card combinations
let combinations = self.generate_canonical_combinations()?;
// Step 2: Build Level 5 (terminal nodes) - 5-card hand evaluations
println!("Building Level 5 terminal nodes...");
log::info!("Building Level 5 terminal nodes...");
self.build_level_5()?;
// Step 3: Build Level 6 (intermediate nodes) - 6-card combinations
println!("Building Level 6 intermediate nodes...");
log::info!("Building Level 6 intermediate nodes...");
self.build_level_6(&combinations)?;
// Step 4: Build Level 7 (root nodes) - 7-card combinations
println!("Building Level 7 root nodes...");
log::info!("Building Level 7 root nodes...");
self.build_level_7(&combinations)?;
// Step 5: Flatten the trie into contiguous array
println!("Flattening trie structure...");
log::info!("Flattening trie structure...");
// Temporary placeholder for flatten_trie method
// self.flatten_trie()?;
@ -726,13 +726,13 @@ impl JumpTable {
self.metadata.total_combinations = combinations.len();
self.metadata.memory_usage = self.memory_usage();
println!("Jump table construction complete!");
log::info!("Jump table construction complete!");
Ok(())
}
/// Build Level 5 terminal nodes (5-card hand evaluations) using proper combinatorial indexing
fn build_level_5(&mut self) -> Result<(), EvaluatorError> {
println!(
log::info!(
"Building Level 5: Terminal nodes for 5-card hands using combinatorial indexing..."
);
@ -743,7 +743,7 @@ impl JumpTable {
// This is more efficient than generating from 7-card combinations
let unique_5_card_hands = self.generate_all_unique_5_card_hands();
println!(
log::info!(
"Generated {} unique 5-card combinations using combinatorial indexing",
unique_5_card_hands.len()
);
@ -763,7 +763,7 @@ impl JumpTable {
let card_array: [Card; 5] = cards.try_into().unwrap();
// Evaluate the hand using the algorithmic evaluator for accuracy
let evaluator = AlgorithmicEvaluator::new();
let evaluator = AlgorithmicEvaluator::new()?;
let hand_value = evaluator.evaluate_5_card_direct(&card_array);
// Use proper combinatorial indexing instead of simple hashing
@ -776,12 +776,12 @@ impl JumpTable {
}
if hand_index % 10000 == 0 && hand_index > 0 {
println!("Processed {} Level 5 entries", hand_index);
log::info!("Processed {} Level 5 entries", hand_index);
}
}
let elapsed = start_time.elapsed();
println!(
log::info!(
"Level 5 construction complete: {} nodes in {:?}",
level5_count, elapsed
);
@ -890,7 +890,7 @@ impl JumpTable {
/// Build Level 6 intermediate nodes (6-card combinations)
fn build_level_6(&mut self, combinations: &[Vec<PackedCard>]) -> Result<(), EvaluatorError> {
println!("Building Level 6: Intermediate nodes for 6-card hands...");
log::info!("Building Level 6: Intermediate nodes for 6-card hands...");
let mut level6_count = 0;
let start_time = std::time::Instant::now();
@ -907,7 +907,7 @@ impl JumpTable {
}
// Find the best 5-card hand from this 6-card hand using algorithmic evaluator
let mut evaluator = AlgorithmicEvaluator::new();
let evaluator = AlgorithmicEvaluator::new()?;
// Convert 6 PackedCards to 6-element Card array
let mut cards = Vec::new();
for &packed in &six_cards {
@ -927,12 +927,12 @@ impl JumpTable {
}
if combo_index % 1000 == 0 && combo_index > 0 {
println!("Processed {} 7-card combinations for Level 6", combo_index);
log::info!("Processed {} 7-card combinations for Level 6", combo_index);
}
}
let elapsed = start_time.elapsed();
println!(
log::info!(
"Level 6 construction complete: {} nodes in {:?}",
level6_count, elapsed
);
@ -945,7 +945,7 @@ impl JumpTable {
/// Build Level 7 root nodes (7-card combinations)
fn build_level_7(&mut self, combinations: &[Vec<PackedCard>]) -> Result<(), EvaluatorError> {
println!("Building Level 7: Root nodes for 7-card hands...");
log::info!("Building Level 7: Root nodes for 7-card hands...");
let mut level7_count = 0;
let start_time = std::time::Instant::now();
@ -954,7 +954,7 @@ impl JumpTable {
for (combo_index, combo) in combinations.iter().enumerate() {
if combo.len() >= 6 {
// Find the best 5-card hand from this 7-card hand using algorithmic evaluator
let mut evaluator = AlgorithmicEvaluator::new();
let evaluator = AlgorithmicEvaluator::new()?;
// Convert 7 PackedCards to 7-element Card array
let mut cards = Vec::new();
for &packed in combo {
@ -973,12 +973,12 @@ impl JumpTable {
}
if combo_index % 1000 == 0 && combo_index > 0 {
println!("Processed {} 7-card combinations for Level 7", combo_index);
log::info!("Processed {} 7-card combinations for Level 7", combo_index);
}
}
let elapsed = start_time.elapsed();
println!(
log::info!(
"Level 7 construction complete: {} nodes in {:?}",
level7_count, elapsed
);
@ -1294,7 +1294,7 @@ impl JumpTable {
// TEMPORARY FIX: Use algorithmic evaluator instead of jump table
// This bypasses hash function issues and ensures correct results
use super::algorithmic_evaluator::AlgorithmicEvaluator;
let evaluator = AlgorithmicEvaluator::new();
let evaluator = AlgorithmicEvaluator::new()?;
Ok(evaluator.evaluate_5_card_direct(&card_array))
}
@ -1351,7 +1351,7 @@ impl JumpTable {
// TEMPORARY FIX: Use algorithmic evaluator directly to bypass hash function issues
use super::algorithmic_evaluator::AlgorithmicEvaluator;
let mut evaluator = AlgorithmicEvaluator::new();
let evaluator = AlgorithmicEvaluator::new()?;
// Convert PackedCards to Cards for algorithmic evaluator
let card_array: Vec<Card> = cards
@ -1493,11 +1493,11 @@ pub fn calculate_optimal_table_size() -> JumpTable {
let total_size = level5_size + level6_size + level7_size;
println!("Creating jump table with optimal memory layout:");
println!(" Level 5 (5-card): {} entries", level5_size);
println!(" Level 6 (6-card): {} entries", level6_size);
println!(" Level 7 (7-card): {} entries", level7_size);
println!(
log::info!("Creating jump table with optimal memory layout:");
log::info!(" Level 5 (5-card): {} entries", level5_size);
log::info!(" Level 6 (6-card): {} entries", level6_size);
log::info!(" Level 7 (7-card): {} entries", level7_size);
log::info!(
" Total: {} entries (~{} MB)",
total_size,
total_size * 16 / 1024 / 1024

View file

@ -0,0 +1,4 @@
//! New table implementations for the hand evaluator.
//!
//! This module is a placeholder for future jump table implementations
//! and optimized lookup structures.

View file

@ -0,0 +1,4 @@
//! Test utilities for the hand evaluator.
//!
//! This module provides helper functions and data structures for testing
//! hand evaluation correctness.