- 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)
348 lines
No EOL
13 KiB
Markdown
348 lines
No EOL
13 KiB
Markdown
# Poker Testbed - Java-Compatible Hand Evaluator for Bot Testing
|
|
|
|
[](https://crates.io/crates/holdem_core)
|
|
[](https://docs.rs/holdem_core)
|
|
[](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.
|
|
|
|
## Recent Updates
|
|
|
|
- **🚀 Perfect Hash Jump Table**: Revolutionary 79% memory reduction (625MB → ~130MB) with enhanced performance
|
|
- **Suit Canonicalization**: Advanced isomorphic hand reduction for optimal memory usage
|
|
- **Bottom-up Trie Construction**: Cache-optimized memory layout for improved evaluation speed
|
|
- **Input Validation**: Added comprehensive validation to `Card::new()` ensuring rank (0-12) and suit (0-3) are within valid ranges
|
|
- **Enhanced Documentation**: All public methods now include working code examples and panic condition documentation
|
|
- **Error Handling**: Improved error handling throughout the codebase with proper `Result` types
|
|
|
|
## Features
|
|
|
|
- **Bot Testing Framework**: Specifically designed for automated poker bot testing and development
|
|
- **Blazing Fast**: O(1) hand evaluation using perfect hash jump tables
|
|
- **Memory Efficient**: ~130MB for complete evaluation tables (79% reduction from 625MB)
|
|
- **Perfect Hash Jump Tables**: Revolutionary memory optimization with suit canonicalization
|
|
- **Standards Compliant**: Based on proven poker evaluation algorithms with Java compatibility
|
|
- **Multi-Hand Support**: Evaluate 5, 6, or 7-card poker hands with Java-compatible results
|
|
- **Texas Hold'em Ready**: Optimized for Texas Hold'em bot analysis and testing
|
|
- **Comprehensive Testing**: Extensive test suite with millions of verification cases
|
|
- **Deterministic Results**: Consistent hand evaluation for reproducible bot testing
|
|
- **Tool Integration**: Compatible with existing Java poker tools and frameworks
|
|
|
|
## Performance
|
|
|
|
| Hand Type | Evaluation Time | Combinations Evaluated |
|
|
|-----------|----------------|----------------------|
|
|
| 5-Card | ~10-20ns | Direct lookup |
|
|
| 6-Card | ~50-80ns | 6 combinations |
|
|
| 7-Card | ~150-250ns | 21 combinations |
|
|
|
|
## Documentation
|
|
|
|
For detailed project information, see:
|
|
|
|
- **[VISION.md](docs/VISION.md)** - Project goals and architectural vision
|
|
- **[ACHIEVEMENTS.md](docs/ACHIEVEMENTS.md)** - Completed milestones and success metrics
|
|
- **[ROADMAP.md](docs/ROADMAP.md)** - Future development phases and implementation plan
|
|
|
|
## Installation
|
|
|
|
Add this to your `Cargo.toml`:
|
|
|
|
```toml
|
|
[dependencies]
|
|
holdem_core = "0.1.0"
|
|
```
|
|
|
|
## Quick Start - Bot Testing
|
|
|
|
```rust
|
|
use holdem_core::{Card, Hand, HoleCards, Board, Evaluator};
|
|
use std::str::FromStr;
|
|
|
|
// Create cards from string notation
|
|
let ace_spades = Card::from_str("As").unwrap();
|
|
let king_spades = Card::from_str("Ks").unwrap();
|
|
|
|
// Create hole cards from notation
|
|
let hole_cards = HoleCards::from_notation("AKs").unwrap();
|
|
|
|
// 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();
|
|
|
|
// 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
|
|
|
|
This framework provides specialized capabilities for poker bot testing and development:
|
|
|
|
### Automated Bot Testing
|
|
- **Standardized Evaluation**: Java-compatible hand evaluation for consistent bot testing
|
|
- **Performance Benchmarking**: Fast evaluation for large-scale bot performance testing
|
|
- **Range Analysis**: Efficient hand range evaluation for bot strategy analysis
|
|
- **Decision Tree Testing**: Quick hand strength calculation for bot decision trees
|
|
|
|
### Java Tool Integration
|
|
- **Cross-Platform Compatibility**: Works seamlessly with existing Java poker tools
|
|
- **Consistent Results**: Identical hand rankings to Java Meerkat API
|
|
- **Tool Chain Integration**: Compatible with Java-based poker analysis frameworks
|
|
- **Bot Framework Support**: Integrates with popular Java poker bot frameworks
|
|
|
|
### Bot Development Features
|
|
- **Hole Card Analysis**: Fast evaluation of starting hand strength for pre-flop bots
|
|
- **Post-Flop Analysis**: Efficient board texture and hand strength analysis
|
|
- **Incremental Evaluation**: Multi-street hand evaluation for complex bot scenarios
|
|
- **Memory Efficient**: Optimized for bot testing environments with limited resources
|
|
|
|
## Algorithm Overview
|
|
|
|
This library implements the **Perfect Hash Jump Table algorithm**, a revolutionary memory-optimized approach with advanced suit canonicalization:
|
|
|
|
1. **Suit Canonicalization**: Cards are mapped to lexicographically smallest suit permutation for isomorphic reduction
|
|
2. **Perfect Hash Jump Table**: Three-level trie structure (Level 5/6/7) for optimal memory layout and cache performance
|
|
3. **Bottom-up Construction**: Cache-optimized table construction using streaming algorithms
|
|
4. **Memory Efficiency**: 79% memory reduction (625MB → ~130MB) while maintaining O(1) lookup performance
|
|
5. **Best Hand Selection**: For 6-7 card hands, finds optimal 5-card combination using jump table optimization
|
|
|
|
### Advanced Architecture
|
|
|
|
#### Jump Table System
|
|
The evaluator uses a sophisticated three-level jump table architecture:
|
|
|
|
- **Level 5 (Terminal)**: Direct hand values for all canonical 5-card combinations
|
|
- **Level 6 (Intermediate)**: Jump offsets pointing to best Level 5 combinations
|
|
- **Level 7 (Root)**: Jump offsets pointing to best Level 6 combinations
|
|
|
|
Each level is optimized for memory efficiency and cache performance with sequential access patterns.
|
|
|
|
#### Suit Canonicalization Algorithm
|
|
The canonicalization process reduces isomorphic hand variations:
|
|
|
|
1. **Suit Analysis**: Identify unique suits in the hand
|
|
2. **Permutation Generation**: Generate all possible suit assignments
|
|
3. **Lexicographic Optimization**: Find lexicographically smallest representation
|
|
4. **Mapping Creation**: Create bidirectional suit mapping for conversion
|
|
|
|
#### Memory Layout Strategy
|
|
The jump table uses advanced memory optimization:
|
|
|
|
- **Target Size**: Exactly ~130MB (32-35 million u32 entries)
|
|
- **Trie-based Construction**: Bottom-up building for optimal memory usage
|
|
- **Table Flattening**: Reorganized layout for cache-friendly access
|
|
- **Streaming Construction**: Handles large datasets without excessive memory allocation
|
|
|
|
### Table Generation
|
|
|
|
The first time an evaluator is created, it generates the Perfect Hash Jump Table:
|
|
|
|
```rust
|
|
// Jump table is generated automatically on first use
|
|
let evaluator = LookupHandEvaluator::new().unwrap();
|
|
// Output: "Generating jump table..." (first run only)
|
|
// Output: "Hand evaluation tables saved to math/data/"
|
|
```
|
|
|
|
The jump table is saved to `math/data/` directory as a single optimized file and reused for subsequent runs:
|
|
- `jump_table.bin` (~130 MB) - Complete evaluation table with 79% memory reduction
|
|
|
|
## Hand Types
|
|
|
|
The evaluator recognizes all standard poker hand types:
|
|
|
|
| Hand Type | Description | Example |
|
|
|-----------|-------------|---------|
|
|
| Straight Flush | Five cards in sequence, same suit | A♠ K♠ Q♠ J♠ T♠ |
|
|
| Four of a Kind | Four cards of same rank | A♠ A♥ A♦ A♣ K♦ |
|
|
| Full House | Three of a kind + one pair | K♠ K♥ K♦ Q♠ Q♥ |
|
|
| Flush | Five cards of same suit | A♠ J♠ 8♠ 5♠ 2♠ |
|
|
| Straight | Five cards in sequence | A♦ K♣ Q♥ J♠ T♦ |
|
|
| Three of a Kind | Three cards of same rank | Q♠ Q♥ Q♦ A♣ 7♦ |
|
|
| Two Pair | Two different pairs | J♠ J♥ 7♦ 7♣ A♦ |
|
|
| One Pair | Two cards of same rank | A♠ A♥ K♦ Q♣ 7♦ |
|
|
| High Card | No matching cards | A♠ K♥ Q♦ J♣ 9♦ |
|
|
|
|
## Bot Testing Examples
|
|
|
|
### Texas Hold'em Bot Analysis
|
|
|
|
```rust
|
|
use holdem_core::hand_evaluator::LookupHandEvaluator;
|
|
use holdem_core::api::{card::Card, hand::Hand};
|
|
|
|
let evaluator = LookupHandEvaluator::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();
|
|
|
|
// 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);
|
|
|
|
// Bot uses this for post-flop decision making
|
|
if hand_strength < 2000 { // Strong hand
|
|
println!("Bot: Continuing with strong hand");
|
|
}
|
|
```
|
|
|
|
### Bot Range Analysis
|
|
|
|
```rust
|
|
use holdem_core::hand_evaluator::LookupHandEvaluator;
|
|
use holdem_core::api::card::Card;
|
|
|
|
let evaluator = LookupHandEvaluator::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()),
|
|
];
|
|
|
|
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);
|
|
}
|
|
```
|
|
|
|
## Advanced Usage
|
|
|
|
### 7-Card Hand Evaluation for Bot Testing
|
|
|
|
```rust
|
|
use holdem_core::hand_evaluator::LookupHandEvaluator;
|
|
use holdem_core::api::card::Card;
|
|
|
|
let evaluator = LookupHandEvaluator::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 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<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);
|
|
```
|
|
|
|
### Custom Table Generation
|
|
|
|
```rust
|
|
use holdem_core::evaluator_generator::state_table_generator::StateTableGenerator;
|
|
|
|
let mut generator = StateTableGenerator::new();
|
|
generator.generate_tables(); // ~1-2 seconds
|
|
generator.save_tables().unwrap(); // Save to disk
|
|
```
|
|
|
|
## Testing
|
|
|
|
Run the comprehensive test suite:
|
|
|
|
```bash
|
|
cargo test
|
|
```
|
|
|
|
Run performance benchmarks:
|
|
|
|
```bash
|
|
cargo test --release hand_eval_integration_test -- --nocapture
|
|
```
|
|
|
|
## Mathematical Foundation
|
|
|
|
### Perfect Hash Algorithm
|
|
|
|
The evaluator uses a perfect hash function that maps each possible card combination to a unique index:
|
|
|
|
```
|
|
Hash = f(card₁, card₂, ..., cardₙ)
|
|
Index = Hash % TABLE_SIZE
|
|
Rank = TABLE[Index]
|
|
```
|
|
|
|
### Card Encoding
|
|
|
|
Each card is encoded using multiple techniques:
|
|
|
|
- **Prime Numbers**: Unique primes for rank identification (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41)
|
|
- **Bit Patterns**: Suit detection using bit manipulation
|
|
- **Binary Representation**: Compact 8-bit card encoding
|
|
|
|
### Hand Strength Calculation
|
|
|
|
Hand strength is calculated using:
|
|
|
|
1. **Flush Detection**: Bitwise suit comparison
|
|
2. **Rank Analysis**: Prime product uniqueness
|
|
3. **Pattern Matching**: Specialized algorithms per hand type
|
|
4. **Relative Ranking**: Comparison against all possible hands
|
|
|
|
## References
|
|
|
|
- **Perfect Hash Jump Tables**: Revolutionary memory optimization with suit canonicalization
|
|
- **Suit Canonicalization**: Lexicographically smallest permutation algorithm for isomorphic reduction
|
|
- **Bottom-up Trie Construction**: Cache-optimized memory layout for modern CPU architectures
|
|
- **Poker Standards**: 2+2 Poker hand ranking standards (ranking reference)
|
|
- **Poker Testbed**: Framework designed for automated bot testing and analysis
|
|
- **Java Integration**: Compatible with existing Java poker tools and frameworks
|
|
|
|
## License
|
|
|
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
|
|
## Contributing
|
|
|
|
Contributions are welcome! Areas for improvement:
|
|
|
|
- Hardware acceleration (SIMD optimizations)
|
|
- Additional poker variants (Omaha, Stud, etc.)
|
|
- Memory-mapped table loading
|
|
- Distributed table generation
|
|
- Advanced benchmarking tools
|
|
|
|
## Support
|
|
|
|
For questions, issues, or contributions, please:
|
|
|
|
1. Check existing documentation
|
|
2. Review test cases for usage examples
|
|
3. Open GitHub issues for bugs or feature requests
|
|
4. Submit pull requests with comprehensive tests
|
|
|
|
---
|
|
|
|
**Performance Note**: First run includes ~1-2 second table generation. Subsequent runs load precomputed tables in ~100-200ms. |