- Delete ARCHITECTURE_HAND_LOGGING_TOURNAMENT_IMPROVEMENTS.md (implemented) - Delete ARCHITECTURE_SUPERSONIC_EVALUATOR.md (implemented) - Delete REFERENCES.md (no longer needed) - Simplify ROADMAP.md: remove Phases 4-7 (live server, advanced bots) - Simplify VISION.md: focus on simulation testbed only - Update README.md: dual evaluator architecture, fix broken links - Update holdem_core/README.md: fix title, add Supersonic evaluator docs
392 lines
14 KiB
Markdown
392 lines
14 KiB
Markdown
# Poker Simulation Testbed
|
||
|
||
[](https://crates.io/crates/holdem_core)
|
||
[](https://docs.rs/holdem_core)
|
||
[](LICENSE)
|
||
|
||
A comprehensive poker simulation testbed and unit test framework for bot development. Features two Rust-native hand evaluators — the **Cactus Kev** perfect-hash evaluator (~100 KB tables) and the **Supersonic** LUT trie evaluator (~124 MB tables) — designed for automated poker bot testing, strategy analysis, and simulation at scale.
|
||
|
||
## Recent Updates
|
||
|
||
- **Supersonic Evaluator**: New high-throughput LUT trie evaluator (~124 MB) for O(n) hand evaluation
|
||
- **Dual Evaluator Architecture**: Choose between Cactus Kev (compact) and Supersonic (fast) per use case
|
||
- **Simulation Testbed**: Run cash games, SNGs, and MTTs with configurable bots via TOML
|
||
- **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
|
||
- **Dual Evaluators**: Cactus Kev (compact ~100 KB) and Supersonic LUT trie (~124 MB) hand evaluators
|
||
- **Blazing Fast**: Sub-microsecond hand evaluation using perfect hash and trie-based lookup
|
||
- **Memory Efficient**: Choose between compact Cactus Kev tables or high-throughput Supersonic trie
|
||
- **Standards Compliant**: Based on proven poker evaluation algorithms
|
||
- **Multi-Hand Support**: Evaluate 5, 6, or 7-card poker hands
|
||
- **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
|
||
- **Simulation Engine**: Run cash games, SNGs, and MTTs with configurable bots via TOML
|
||
|
||
## Performance
|
||
|
||
### Cactus Kev Evaluator
|
||
|
||
| Hand Type | Evaluation Time | Method |
|
||
|-----------|----------------|--------|
|
||
| 5-Card | ~50-100ns | Single prime-product hash → table lookup |
|
||
| 6-Card | ~300-500ns | Six 5-card lookups |
|
||
| 7-Card | ~1-2µs | 21 enumeration + HashMap cache |
|
||
|
||
### Supersonic Evaluator
|
||
|
||
| Hand Type | Evaluation Time | Method |
|
||
|-----------|----------------|--------|
|
||
| 5-Card | O(5) array lookups | Direct trie traversal |
|
||
| 6-Card | O(6) array lookups | Direct trie traversal |
|
||
| 7-Card | O(7) array lookups | Direct trie traversal |
|
||
|
||
## Documentation
|
||
|
||
For detailed project information, see:
|
||
|
||
- **[VISION.md](docs/VISION.md)** - Project goals and architectural vision
|
||
- **[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**: Consistent hand evaluation across both Cactus Kev and Supersonic evaluators
|
||
- **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
|
||
|
||
### Dual Evaluator Support
|
||
- **Cactus Kev**: Compact ~100 KB tables with prime-product hashing — ideal for memory-constrained environments
|
||
- **Supersonic**: High-throughput ~124 MB LUT trie with O(n) array lookups — fastest for bulk evaluation
|
||
- **Algorithmic Fallback**: Rule-based evaluator serves as correctness oracle for validation
|
||
|
||
### 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 provides **two hand evaluators** with different performance/memory tradeoffs:
|
||
|
||
### Cactus Kev Evaluator (compact)
|
||
Uses prime-product hashing into two small lookup tables (~100 KB total):
|
||
1. **Prime Product Hashing**: Each rank maps to a unique prime; the product of 5 primes uniquely identifies the hand
|
||
2. **Dual Lookup Tables**: 1,287 flush entries + 6,175 unsuited entries = 7,462 total (~30 KB)
|
||
3. **7-card Cache**: C(7,5)=21 enumeration with HashMap cache for amortized O(1) lookups
|
||
4. **Zero Collisions**: Validated against all 2,598,960 five-card hands
|
||
|
||
### Supersonic Evaluator (high-throughput)
|
||
Uses a pre-computed LUT trie (~124 MB) for O(n) array lookups:
|
||
1. **Trie Structure**: 32,487,834 `u32` entries organized in pages of 53 (one per card + base)
|
||
2. **Direct Lookup**: Follow `hand_ranks[p + card_index + 1]` for each card — no enumeration needed
|
||
3. **Packed Hand Rank**: 16-bit format (category + equivalency class) for compact result representation
|
||
4. **Best for Bulk**: Fastest evaluator for large-scale simulations and batch evaluation
|
||
|
||
### Table Generation
|
||
|
||
Lookup tables are generated automatically on first use and persisted to disk:
|
||
|
||
- **Cactus Kev**: Small tables (~100 KB) generated in-memory from prime products
|
||
- **Supersonic**: Large LUT trie (~124 MB) loaded from pre-generated binary file
|
||
|
||
## 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);
|
||
```
|
||
|
||
## Testing
|
||
|
||
Run the comprehensive test suite:
|
||
|
||
```bash
|
||
cargo test
|
||
```
|
||
|
||
Run performance benchmarks:
|
||
|
||
```bash
|
||
cargo test --release hand_eval_integration_test -- --nocapture
|
||
```
|
||
|
||
## Simulation Testbed
|
||
|
||
The `holdem_sim` binary provides a CLI for running poker simulations with configurable bots, game types, and formats. It supports cash games, Sit & Go (SNG) tournaments, and Multi-Table Tournaments (MTT).
|
||
|
||
### Running Simulations
|
||
|
||
```bash
|
||
# Run a cash game simulation
|
||
cargo run --bin holdem_sim -- holdem_core/examples/configs/cash_game.toml
|
||
|
||
# Run with a specific seed for reproducibility
|
||
cargo run --bin holdem_sim -- holdem_core/examples/configs/cash_game.toml --seed 42
|
||
|
||
# Override the number of hands
|
||
cargo run --bin holdem_sim -- holdem_core/examples/configs/cash_game.toml --hands 5000
|
||
```
|
||
|
||
### Example Configuration (Cash Game)
|
||
|
||
```toml
|
||
[game]
|
||
game_type = "Cash"
|
||
num_seats = 6
|
||
small_blind = 10
|
||
big_blind = 20
|
||
ante = 0
|
||
starting_chips = 10000
|
||
num_hands = 1000
|
||
|
||
[game.bet_limit]
|
||
type = "no_limit"
|
||
min_raise = 20
|
||
|
||
[[game.bots]]
|
||
bot_type = "call_bot"
|
||
name = "Alice"
|
||
|
||
[[game.bots]]
|
||
bot_type = "fold_bot"
|
||
name = "Bob"
|
||
|
||
[[game.bots]]
|
||
bot_type = "random_bot"
|
||
name = "Charlie"
|
||
```
|
||
|
||
Example configs are available in [`holdem_core/examples/configs/`](holdem_core/examples/configs/):
|
||
|
||
- [`cash_game.toml`](holdem_core/examples/configs/cash_game.toml) — 6-max NLHE cash game
|
||
- [`sng_tournament.toml`](holdem_core/examples/configs/sng_tournament.toml) — 9-seat Sit & Go
|
||
- [`mtt_tournament.toml`](holdem_core/examples/configs/mtt_tournament.toml) — Multi-table tournament
|
||
|
||
### Built-in Bots
|
||
|
||
| Bot | Description |
|
||
|-----|-------------|
|
||
| `call_bot` | Calls every bet, never raises or folds |
|
||
| `fold_bot` | Folds whenever facing a bet, otherwise checks |
|
||
| `raise_bot` | Raises with configurable aggression thresholds |
|
||
| `random_bot` | Takes random legal actions with configurable tendencies |
|
||
|
||
### Example Output
|
||
|
||
```
|
||
=== Simulation Results ===
|
||
Hands played: 1000
|
||
|
||
Player Statistics:
|
||
Alice (call_bot)
|
||
Hands: 1000 | VPIP: 100.0% | PFR: 0.0% | Win: $45,230
|
||
Bob (fold_bot)
|
||
Hands: 1000 | VPIP: 0.0% | PFR: 0.0% | Win: -$32,100
|
||
Charlie (random_bot)
|
||
Hands: 1000 | VPIP: 67.3% | PFR: 22.1% | Win: -$13,130
|
||
```
|
||
|
||
## Mathematical Foundation
|
||
|
||
### Cactus Kev — Perfect Hash Algorithm
|
||
|
||
The Cactus Kev evaluator uses prime-product hashing:
|
||
|
||
```
|
||
Hash = prime(rank₁) × prime(rank₂) × ... × prime(rank₅)
|
||
Index = Hash % TABLE_SIZE
|
||
Rank = TABLE[Index]
|
||
```
|
||
|
||
Each rank maps to a unique prime (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41), so the product of 5 primes uniquely identifies the hand pattern.
|
||
|
||
### Supersonic — LUT Trie Algorithm
|
||
|
||
The Supersonic evaluator uses a pre-computed trie of 32.5M `u32` entries:
|
||
|
||
```
|
||
p = INITIAL_STATE (page 1 base)
|
||
for each card:
|
||
p = hand_ranks[p + card_index + 1]
|
||
rank = p // (for 7-card hands)
|
||
```
|
||
|
||
Each step is a single array lookup — no hashing, no enumeration, no branching.
|
||
|
||
## References
|
||
|
||
- **Cactus Kev**: Perfect hash algorithm for 5-card poker hand evaluation
|
||
- **Supersonic**: LUT trie evaluator for high-throughput hand evaluation
|
||
- **Poker Standards**: Standard poker hand ranking rules
|
||
- **Poker Testbed**: Framework designed for automated bot testing and analysis
|
||
|
||
## 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**: The Cactus Kev evaluator generates its small lookup tables (~100 KB) in-memory on first use. The Supersonic evaluator requires a pre-generated ~124 MB LUT file loaded at startup.
|