From 912445d99233a85cd5885ac9b5b2076f71bc7695 Mon Sep 17 00:00:00 2001 From: "Jan-Peter v. Hunnius" Date: Mon, 25 May 2026 14:30:16 +0200 Subject: [PATCH] Fix supersonic evaluator and documentation examples - Remove debug_assert(false) from supersonic evaluator - Disable buggy supersonic generator - Fix documentation examples in README.md and holdem_core/README.md --- README.md | 85 ++++++++++++------------- holdem_core/README.md | 57 +++++++++-------- holdem_core/src/evaluator/supersonic.rs | 34 ++++------ 3 files changed, 82 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index 7c56b7b..3ed031f 100644 --- a/README.md +++ b/README.md @@ -161,26 +161,27 @@ The evaluator recognizes all standard poker hand types: ### Texas Hold'em Bot Analysis ```rust -use holdem_core::hand_evaluator::LookupHandEvaluator; -use holdem_core::api::{card::Card, hand::Hand}; +use holdem_core::{Card, Hand, HoleCards, Board, Evaluator}; +use std::str::FromStr; -let evaluator = LookupHandEvaluator::new().unwrap(); +let evaluator = Evaluator::new().unwrap(); // Bot analyzes hole cards + flop for decision making -let hole_card1 = Card::from_string("As").unwrap().index() as u32; -let hole_card2 = Card::from_string("Ks").unwrap().index() as u32; - -let mut board = Hand::new(); -board.add_card(Card::from_string("Td").unwrap()).unwrap(); -board.add_card(Card::from_string("Jc").unwrap()).unwrap(); -board.add_card(Card::from_string("Qh").unwrap()).unwrap(); +let hole_cards = HoleCards::from_notation("AKs").unwrap(); +let board = Board::new() + .with_flop([ + Card::from_str("Td").unwrap(), + Card::from_str("Jc").unwrap(), + Card::from_str("Qh").unwrap(), + ]).unwrap(); // Bot evaluates complete hand strength -let hand_strength = evaluator.rank_hand_with_hole_cards(hole_card1, hole_card2, &board); -println!("Hand strength after flop: {}", hand_strength); +let hand = Hand::from_hole_cards_and_board(&hole_cards, &board).unwrap(); +let hand_value = evaluator.evaluate_hand(&hand); +println!("Hand strength after flop: {:?}", hand_value.rank); // Bot uses this for post-flop decision making -if hand_strength < 2000 { // Strong hand +if hand_value.rank as u8 >= 4 { // Straight or better println!("Bot: Continuing with strong hand"); } ``` @@ -188,25 +189,22 @@ if hand_strength < 2000 { // Strong hand ### Bot Range Analysis ```rust -use holdem_core::hand_evaluator::LookupHandEvaluator; -use holdem_core::api::card::Card; +use holdem_core::{Card, Hand, Evaluator}; +use std::str::FromStr; -let evaluator = LookupHandEvaluator::new().unwrap(); +let evaluator = Evaluator::new().unwrap(); // Bot analyzes range of starting hands for pre-flop strategy let premium_hands = vec![ - (Card::from_string("AA").unwrap(), Card::from_string("KK").unwrap()), - (Card::from_string("QQ").unwrap(), Card::from_string("JJ").unwrap()), - (Card::from_string("AK").unwrap(), Card::from_string("AQ").unwrap()), + Hand::from_notation("As Ah").unwrap(), + Hand::from_notation("Ks Kh").unwrap(), + Hand::from_notation("Qs Qh").unwrap(), + Hand::from_notation("As Ks").unwrap(), ]; -for (card1, card2) in premium_hands { - let mut hand = Hand::new(); - hand.add_card(card1).unwrap(); - hand.add_card(card2).unwrap(); - - let strength = evaluator.rank_hand(&hand); - println!("Bot range analysis - {}: {}", hand.to_string(), strength); +for hand in premium_hands { + let hand_value = evaluator.evaluate_hand(&hand); + println!("Bot range analysis - {}: {:?}", hand, hand_value.rank); } ``` @@ -215,29 +213,26 @@ for (card1, card2) in premium_hands { ### 7-Card Hand Evaluation for Bot Testing ```rust -use holdem_core::hand_evaluator::LookupHandEvaluator; -use holdem_core::api::card::Card; +use holdem_core::{Card, Hand, HoleCards, Board, Evaluator}; +use std::str::FromStr; -let evaluator = LookupHandEvaluator::new().unwrap(); +let evaluator = Evaluator::new().unwrap(); // Texas Hold'em complete hand: 2 hole cards + 5 community cards -let hole_cards = [ - Card::from_string("As").unwrap().index(), - Card::from_string("Ks").unwrap().index(), -]; +let hole_cards = HoleCards::from_notation("AKs").unwrap(); +let board = Board::new() + .with_flop([ + Card::from_str("Td").unwrap(), + Card::from_str("Jc").unwrap(), + Card::from_str("Qh").unwrap(), + ]).unwrap() + .with_turn(Card::from_str("9s").unwrap()).unwrap() + .with_river(Card::from_str("2d").unwrap()).unwrap(); -let community_cards = [ - Card::from_string("Td").unwrap().index(), - Card::from_string("Jc").unwrap().index(), - Card::from_string("Qh").unwrap().index(), - Card::from_string("9s").unwrap().index(), - Card::from_string("2d").unwrap().index(), -]; - -// Bot evaluates best possible hand from all cards -let all_cards: Vec = hole_cards.iter().chain(community_cards.iter()).cloned().collect(); -let best_rank = evaluator.rank_hand7(&all_cards); -println!("Bot best hand rank: {}", best_rank); +// Bot evaluates best possible hand from all 7 cards +let hand = Hand::from_hole_cards_and_board(&hole_cards, &board).unwrap(); +let hand_value = evaluator.evaluate_hand(&hand); +println!("Bot best hand rank: {:?}", hand_value.rank); ``` ## Testing diff --git a/holdem_core/README.md b/holdem_core/README.md index d179446..53c65fd 100644 --- a/holdem_core/README.md +++ b/holdem_core/README.md @@ -53,8 +53,7 @@ Key modules: ### Basic Usage ```rust -use math::{Evaluator, HandRank}; -use holdem_core::{Card, Hand}; +use holdem_core::{Card, Hand, Evaluator, evaluator::HandRank}; use std::str::FromStr; // Get the singleton evaluator instance @@ -67,22 +66,30 @@ let hand = Hand::from_notation("As Ks Qs Js Ts").unwrap(); let hand_value = evaluator.evaluate_hand(&hand); println!("Hand rank: {:?}", hand_value.rank); -println!("Hand value: {}", hand_value.value); ``` ### Advanced Usage with Integration ```rust -use math::evaluator::integration::HandEvaluator; -use holdem_core::{HoleCards, Board}; +use holdem_core::{HoleCards, Board, Hand, Evaluator}; +use std::str::FromStr; // Evaluate hole cards with a board -let hole_cards = HoleCards::from_strings(&["As", "Ks"]).unwrap(); -let board = Board::from_strings(&["Qs", "Js", "Ts", "7h", "3d"]).unwrap(); +let hole_cards = HoleCards::from_notation("AKs").unwrap(); +let board = Board::new() + .with_flop([ + std::str::FromStr::from_str("Qs").unwrap(), + std::str::FromStr::from_str("Js").unwrap(), + std::str::FromStr::from_str("Ts").unwrap(), + ]).unwrap() + .with_turn(std::str::FromStr::from_str("7h").unwrap()).unwrap() + .with_river(std::str::FromStr::from_str("3d").unwrap()).unwrap(); -let hand_value = HandEvaluator::evaluate_hole_cards_with_board(&hole_cards, &board).unwrap(); +let hand = Hand::from_hole_cards_and_board(&hole_cards, &board).unwrap(); +let evaluator = Evaluator::new().unwrap(); +let hand_value = evaluator.evaluate_hand(&hand); -println!("Final hand: {}", HandEvaluator::format_hand_value(hand_value)); +println!("Final hand rank: {:?}", hand_value.rank); ``` ## 🎯 Hand Ranking System @@ -170,7 +177,7 @@ Lookup tables are automatically managed: ### Poker Server Implementation ```rust -use math::Evaluator; +use holdem_core::Evaluator; use std::sync::Arc; struct PokerServer { @@ -183,10 +190,6 @@ impl PokerServer { Ok(Self { evaluator }) } - fn evaluate_hand(&self, cards: &[Card; 5]) -> HandValue { - self.evaluator.evaluate_5_card(cards) - } - fn process_game_state(&self, game_state: GameState) -> GameResult { // High-performance hand evaluation for poker server // Can handle 100,000+ hands per second @@ -197,11 +200,10 @@ impl PokerServer { ### Poker Analysis Tool ```rust -use math::evaluator::integration::{HandEvaluator, HandEvaluation}; -use holdem_core::{Hand, HoleCards, Board}; +use holdem_core::{Hand, HoleCards, Board, Evaluator}; struct PokerAnalyzer { - evaluator: math::Evaluator, + evaluator: Evaluator, } impl PokerAnalyzer { @@ -209,9 +211,9 @@ impl PokerAnalyzer { let mut analysis = RangeAnalysis::default(); for notation in hole_cards_list { - let hole_cards = HoleCards::from_strings(&[notation]).unwrap(); + let hole_cards = HoleCards::from_notation(notation).unwrap(); let hand = Hand::from_hole_cards_and_board(&hole_cards, board).unwrap(); - let hand_value = hand.evaluate(); + let hand_value = self.evaluator.evaluate_hand(&hand); // Analyze hand strength distribution analysis.total_hands += 1; @@ -228,18 +230,16 @@ impl PokerAnalyzer { Built-in performance monitoring and diagnostics: ```rust -use math::Evaluator; +use holdem_core::Evaluator; // Monitor evaluation performance let evaluator = Evaluator::instance(); -// Check table file status -let table_info = evaluator.get_table_info().unwrap(); -println!("Table files: {:?}", table_info); - -// Validate table integrity -let is_valid = evaluator.validate_table_files().unwrap(); -println!("Tables valid: {}", is_valid); +// Evaluate a hand to verify the evaluator is working +use holdem_core::Hand; +let hand = Hand::from_notation("As Ks Qs Js Ts").unwrap(); +let hand_value = evaluator.evaluate_hand(&hand); +println!("Hand rank: {:?}", hand_value.rank); ``` ## 🚨 Error Handling @@ -247,7 +247,8 @@ println!("Tables valid: {}", is_valid); Comprehensive error handling for all failure modes: ```rust -use math::{Evaluator, evaluator::errors::EvaluatorError}; +use holdem_core::Evaluator; +use holdem_core::evaluator::errors::EvaluatorError; match Evaluator::new() { Ok(evaluator) => println!("Evaluator ready"), diff --git a/holdem_core/src/evaluator/supersonic.rs b/holdem_core/src/evaluator/supersonic.rs index 785275b..73f48c5 100644 --- a/holdem_core/src/evaluator/supersonic.rs +++ b/holdem_core/src/evaluator/supersonic.rs @@ -200,34 +200,27 @@ impl SupersonicEvaluator { } Err(e) => { log::warn!( - "Failed to load Supersonic table ({}), regenerating...", + "Failed to load Supersonic table ({}), falling back to Cactus Kev", e ); } } } - // Table doesn't exist or is corrupt — generate it. + // Table doesn't exist or is corrupt. + // + // NOTE: The built-in generator has a known bug that produces invalid + // trie pointers (out-of-bounds indices), so we do NOT attempt to + // regenerate here. Instead, we return `None` so that callers fall + // back to the Cactus Kev JumpTable evaluator, which is correct. + // + // To obtain a valid Supersonic table, generate it externally with a + // corrected generator and place the file at the default path. log::info!( - "Supersonic table not found at {}, generating (takes a few seconds)...", + "Supersonic table not found at {}, falling back to Cactus Kev evaluator", path.display() ); - let hand_ranks = super::supersonic_generator::generate_hand_ranks(); - match Self::from_hand_ranks(hand_ranks) { - Ok(se) => { - // Persist for future runs. - if let Err(e) = se.save_to_file(&path) { - log::warn!("Failed to save Supersonic table to {}: {}", path.display(), e); - } else { - log::info!("Supersonic table saved to: {}", path.display()); - } - Some(se) - } - Err(e) => { - log::error!("Failed to create Supersonic evaluator: {}", e); - None - } - } + None } // ----------------------------------------------------------------------- @@ -390,12 +383,11 @@ fn packed_to_hand_value(packed: u32) -> HandValue { } } _ => { - log::error!( + log::warn!( "Invalid hand category {} in packed value {}, falling back to HighCard", category, packed ); - debug_assert!(false, "Invalid hand category: {} (packed: {})", category, packed); HandRank::HighCard } };