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
This commit is contained in:
Jan-Peter von Hunnius 2026-05-25 14:30:16 +02:00
parent 2da48d43fb
commit 912445d992
3 changed files with 82 additions and 94 deletions

View file

@ -161,26 +161,27 @@ The evaluator recognizes all standard poker hand types:
### Texas Hold'em Bot Analysis ### Texas Hold'em Bot Analysis
```rust ```rust
use holdem_core::hand_evaluator::LookupHandEvaluator; use holdem_core::{Card, Hand, HoleCards, Board, Evaluator};
use holdem_core::api::{card::Card, hand::Hand}; use std::str::FromStr;
let evaluator = LookupHandEvaluator::new().unwrap(); let evaluator = Evaluator::new().unwrap();
// Bot analyzes hole cards + flop for decision making // Bot analyzes hole cards + flop for decision making
let hole_card1 = Card::from_string("As").unwrap().index() as u32; let hole_cards = HoleCards::from_notation("AKs").unwrap();
let hole_card2 = Card::from_string("Ks").unwrap().index() as u32; let board = Board::new()
.with_flop([
let mut board = Hand::new(); Card::from_str("Td").unwrap(),
board.add_card(Card::from_string("Td").unwrap()).unwrap(); Card::from_str("Jc").unwrap(),
board.add_card(Card::from_string("Jc").unwrap()).unwrap(); Card::from_str("Qh").unwrap(),
board.add_card(Card::from_string("Qh").unwrap()).unwrap(); ]).unwrap();
// Bot evaluates complete hand strength // Bot evaluates complete hand strength
let hand_strength = evaluator.rank_hand_with_hole_cards(hole_card1, hole_card2, &board); let hand = Hand::from_hole_cards_and_board(&hole_cards, &board).unwrap();
println!("Hand strength after flop: {}", hand_strength); let hand_value = evaluator.evaluate_hand(&hand);
println!("Hand strength after flop: {:?}", hand_value.rank);
// Bot uses this for post-flop decision making // 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"); println!("Bot: Continuing with strong hand");
} }
``` ```
@ -188,25 +189,22 @@ if hand_strength < 2000 { // Strong hand
### Bot Range Analysis ### Bot Range Analysis
```rust ```rust
use holdem_core::hand_evaluator::LookupHandEvaluator; use holdem_core::{Card, Hand, Evaluator};
use holdem_core::api::card::Card; use std::str::FromStr;
let evaluator = LookupHandEvaluator::new().unwrap(); let evaluator = Evaluator::new().unwrap();
// Bot analyzes range of starting hands for pre-flop strategy // Bot analyzes range of starting hands for pre-flop strategy
let premium_hands = vec![ let premium_hands = vec![
(Card::from_string("AA").unwrap(), Card::from_string("KK").unwrap()), Hand::from_notation("As Ah").unwrap(),
(Card::from_string("QQ").unwrap(), Card::from_string("JJ").unwrap()), Hand::from_notation("Ks Kh").unwrap(),
(Card::from_string("AK").unwrap(), Card::from_string("AQ").unwrap()), Hand::from_notation("Qs Qh").unwrap(),
Hand::from_notation("As Ks").unwrap(),
]; ];
for (card1, card2) in premium_hands { for hand in premium_hands {
let mut hand = Hand::new(); let hand_value = evaluator.evaluate_hand(&hand);
hand.add_card(card1).unwrap(); println!("Bot range analysis - {}: {:?}", hand, hand_value.rank);
hand.add_card(card2).unwrap();
let strength = evaluator.rank_hand(&hand);
println!("Bot range analysis - {}: {}", hand.to_string(), strength);
} }
``` ```
@ -215,29 +213,26 @@ for (card1, card2) in premium_hands {
### 7-Card Hand Evaluation for Bot Testing ### 7-Card Hand Evaluation for Bot Testing
```rust ```rust
use holdem_core::hand_evaluator::LookupHandEvaluator; use holdem_core::{Card, Hand, HoleCards, Board, Evaluator};
use holdem_core::api::card::Card; 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 // Texas Hold'em complete hand: 2 hole cards + 5 community cards
let hole_cards = [ let hole_cards = HoleCards::from_notation("AKs").unwrap();
Card::from_string("As").unwrap().index(), let board = Board::new()
Card::from_string("Ks").unwrap().index(), .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 = [ // Bot evaluates best possible hand from all 7 cards
Card::from_string("Td").unwrap().index(), let hand = Hand::from_hole_cards_and_board(&hole_cards, &board).unwrap();
Card::from_string("Jc").unwrap().index(), let hand_value = evaluator.evaluate_hand(&hand);
Card::from_string("Qh").unwrap().index(), println!("Bot best hand rank: {:?}", hand_value.rank);
Card::from_string("9s").unwrap().index(),
Card::from_string("2d").unwrap().index(),
];
// Bot evaluates best possible hand from all cards
let all_cards: Vec<u32> = hole_cards.iter().chain(community_cards.iter()).cloned().collect();
let best_rank = evaluator.rank_hand7(&all_cards);
println!("Bot best hand rank: {}", best_rank);
``` ```
## Testing ## Testing

View file

@ -53,8 +53,7 @@ Key modules:
### Basic Usage ### Basic Usage
```rust ```rust
use math::{Evaluator, HandRank}; use holdem_core::{Card, Hand, Evaluator, evaluator::HandRank};
use holdem_core::{Card, Hand};
use std::str::FromStr; use std::str::FromStr;
// Get the singleton evaluator instance // 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); let hand_value = evaluator.evaluate_hand(&hand);
println!("Hand rank: {:?}", hand_value.rank); println!("Hand rank: {:?}", hand_value.rank);
println!("Hand value: {}", hand_value.value);
``` ```
### Advanced Usage with Integration ### Advanced Usage with Integration
```rust ```rust
use math::evaluator::integration::HandEvaluator; use holdem_core::{HoleCards, Board, Hand, Evaluator};
use holdem_core::{HoleCards, Board}; use std::str::FromStr;
// Evaluate hole cards with a board // Evaluate hole cards with a board
let hole_cards = HoleCards::from_strings(&["As", "Ks"]).unwrap(); let hole_cards = HoleCards::from_notation("AKs").unwrap();
let board = Board::from_strings(&["Qs", "Js", "Ts", "7h", "3d"]).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 ## 🎯 Hand Ranking System
@ -170,7 +177,7 @@ Lookup tables are automatically managed:
### Poker Server Implementation ### Poker Server Implementation
```rust ```rust
use math::Evaluator; use holdem_core::Evaluator;
use std::sync::Arc; use std::sync::Arc;
struct PokerServer { struct PokerServer {
@ -183,10 +190,6 @@ impl PokerServer {
Ok(Self { evaluator }) 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 { fn process_game_state(&self, game_state: GameState) -> GameResult {
// High-performance hand evaluation for poker server // High-performance hand evaluation for poker server
// Can handle 100,000+ hands per second // Can handle 100,000+ hands per second
@ -197,11 +200,10 @@ impl PokerServer {
### Poker Analysis Tool ### Poker Analysis Tool
```rust ```rust
use math::evaluator::integration::{HandEvaluator, HandEvaluation}; use holdem_core::{Hand, HoleCards, Board, Evaluator};
use holdem_core::{Hand, HoleCards, Board};
struct PokerAnalyzer { struct PokerAnalyzer {
evaluator: math::Evaluator, evaluator: Evaluator,
} }
impl PokerAnalyzer { impl PokerAnalyzer {
@ -209,9 +211,9 @@ impl PokerAnalyzer {
let mut analysis = RangeAnalysis::default(); let mut analysis = RangeAnalysis::default();
for notation in hole_cards_list { 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 = 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 // Analyze hand strength distribution
analysis.total_hands += 1; analysis.total_hands += 1;
@ -228,18 +230,16 @@ impl PokerAnalyzer {
Built-in performance monitoring and diagnostics: Built-in performance monitoring and diagnostics:
```rust ```rust
use math::Evaluator; use holdem_core::Evaluator;
// Monitor evaluation performance // Monitor evaluation performance
let evaluator = Evaluator::instance(); let evaluator = Evaluator::instance();
// Check table file status // Evaluate a hand to verify the evaluator is working
let table_info = evaluator.get_table_info().unwrap(); use holdem_core::Hand;
println!("Table files: {:?}", table_info); let hand = Hand::from_notation("As Ks Qs Js Ts").unwrap();
let hand_value = evaluator.evaluate_hand(&hand);
// Validate table integrity println!("Hand rank: {:?}", hand_value.rank);
let is_valid = evaluator.validate_table_files().unwrap();
println!("Tables valid: {}", is_valid);
``` ```
## 🚨 Error Handling ## 🚨 Error Handling
@ -247,7 +247,8 @@ println!("Tables valid: {}", is_valid);
Comprehensive error handling for all failure modes: Comprehensive error handling for all failure modes:
```rust ```rust
use math::{Evaluator, evaluator::errors::EvaluatorError}; use holdem_core::Evaluator;
use holdem_core::evaluator::errors::EvaluatorError;
match Evaluator::new() { match Evaluator::new() {
Ok(evaluator) => println!("Evaluator ready"), Ok(evaluator) => println!("Evaluator ready"),

View file

@ -200,35 +200,28 @@ impl SupersonicEvaluator {
} }
Err(e) => { Err(e) => {
log::warn!( log::warn!(
"Failed to load Supersonic table ({}), regenerating...", "Failed to load Supersonic table ({}), falling back to Cactus Kev",
e 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!( 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() 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
} }
}
}
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Evaluation // Evaluation
@ -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", "Invalid hand category {} in packed value {}, falling back to HighCard",
category, category,
packed packed
); );
debug_assert!(false, "Invalid hand category: {} (packed: {})", category, packed);
HandRank::HighCard HandRank::HighCard
} }
}; };