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:
parent
2da48d43fb
commit
912445d992
3 changed files with 82 additions and 94 deletions
85
README.md
85
README.md
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"),
|
||||||
|
|
|
||||||
|
|
@ -200,34 +200,27 @@ 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();
|
None
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue