Added Documentation
Added Test Cases Added Comments
This commit is contained in:
parent
ac25a1caea
commit
5c271b279f
42 changed files with 5279 additions and 52 deletions
389
docs/api.md
Normal file
389
docs/api.md
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
# API Documentation
|
||||
|
||||
## Core Types
|
||||
|
||||
### `LookupHandEvaluator`
|
||||
|
||||
The main hand evaluation engine that uses precomputed lookup tables for O(1) hand evaluation.
|
||||
|
||||
#### Constructor
|
||||
|
||||
```rust
|
||||
pub fn new() -> io::Result<Self>
|
||||
```
|
||||
|
||||
Creates a new evaluator instance. On first use, generates evaluation tables if they don't exist.
|
||||
|
||||
**Returns:**
|
||||
- `Ok(LookupHandEvaluator)` - Successfully initialized evaluator
|
||||
- `Err(io::Error)` - Failed to load or generate tables
|
||||
|
||||
#### Methods
|
||||
|
||||
##### `rank_hand(&self, hand: &Hand) -> u32`
|
||||
|
||||
Evaluates any poker hand (5, 6, or 7 cards) and returns its rank value.
|
||||
|
||||
```rust
|
||||
let rank = evaluator.rank_hand(&hand);
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `hand: &Hand` - Poker hand containing 5-7 cards
|
||||
|
||||
**Returns:**
|
||||
- `u32` - Hand rank (higher values = stronger hands)
|
||||
|
||||
##### `rank_hand5(&self, hand: &Hand) -> u32`
|
||||
|
||||
Optimized evaluation for exactly 5-card hands.
|
||||
|
||||
```rust
|
||||
let rank = evaluator.rank_hand5(&hand);
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `hand: &Hand` - Poker hand containing exactly 5 cards
|
||||
|
||||
**Returns:**
|
||||
- `u32` - Hand rank value
|
||||
|
||||
##### `rank_hand6(&self, cards: &[u32]) -> u32`
|
||||
|
||||
Evaluates a 6-card hand by finding the best 5-card combination.
|
||||
|
||||
```rust
|
||||
let rank = evaluator.rank_hand6(&card_indices);
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `cards: &[u32]` - Array of 6 card indices
|
||||
|
||||
**Returns:**
|
||||
- `u32` - Best possible 5-card hand rank
|
||||
|
||||
##### `rank_hand7(&self, cards: &[u32]) -> u32`
|
||||
|
||||
Evaluates a 7-card hand by finding the best 5-card combination.
|
||||
|
||||
```rust
|
||||
let rank = evaluator.rank_hand7(&card_indices);
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `cards: &[u32]` - Array of 7 card indices
|
||||
|
||||
**Returns:**
|
||||
- `u32` - Best possible 5-card hand rank
|
||||
|
||||
### `HandEval` Trait
|
||||
|
||||
Standardized interface for poker hand evaluators.
|
||||
|
||||
#### Constants
|
||||
|
||||
```rust
|
||||
const HAND_CATEGORY_SHIFT: u32 = 24;
|
||||
const HAND_CATEGORY_MASK: u32 = 0xF << Self::HAND_CATEGORY_SHIFT;
|
||||
const VALUE_MASK: u32 = 0x000FFFFF;
|
||||
```
|
||||
|
||||
#### Methods
|
||||
|
||||
##### `rank_hand(&self, hand: &Hand) -> u32`
|
||||
|
||||
Evaluates a poker hand of any supported size.
|
||||
|
||||
##### `rank_hand7(&self, cards: &[Card; 7]) -> u32`
|
||||
|
||||
Evaluates exactly 7 cards.
|
||||
|
||||
##### `hand_description(&self, hand_value: u32) -> String`
|
||||
|
||||
Returns human-readable description of a hand rank.
|
||||
|
||||
##### `hand_description_hand(&self, hand: &Hand) -> String`
|
||||
|
||||
Describes a hand by evaluating it first.
|
||||
|
||||
##### `hand_description7(&self, cards: &[Card; 7]) -> String`
|
||||
|
||||
Describes the best 5-card hand from 7 cards.
|
||||
|
||||
## Supporting Types
|
||||
|
||||
### `Hand`
|
||||
|
||||
Collection of cards that can be evaluated.
|
||||
|
||||
#### Key Methods
|
||||
|
||||
```rust
|
||||
// Construction
|
||||
let mut hand = Hand::new();
|
||||
hand.add_card(card).unwrap();
|
||||
|
||||
// Information
|
||||
let size = hand.size();
|
||||
let is_valid = hand.size() >= 5;
|
||||
|
||||
// String representation
|
||||
println!("{}", hand.to_string()); // "As Ks Qs Js Ts"
|
||||
```
|
||||
|
||||
### `Card`
|
||||
|
||||
Individual playing card representation.
|
||||
|
||||
#### Construction
|
||||
|
||||
```rust
|
||||
// From string notation
|
||||
let card = Card::from_string("As").unwrap();
|
||||
let card = Card::from_string("Kh").unwrap();
|
||||
|
||||
// String representation
|
||||
assert_eq!(card.to_string(), "As");
|
||||
```
|
||||
|
||||
#### Properties
|
||||
|
||||
```rust
|
||||
let rank = card.rank(); // Card rank (0-12)
|
||||
let suit = card.suit(); // Card suit (0-3)
|
||||
let index = card.index(); // Unique card index (0-51)
|
||||
```
|
||||
|
||||
## Hand Ranking Values
|
||||
|
||||
### Category Extraction
|
||||
|
||||
```rust
|
||||
// Extract hand category (bits 24-27)
|
||||
let category = (hand_value & HandEval::HAND_CATEGORY_MASK) >> HandEval::HAND_CATEGORY_SHIFT;
|
||||
|
||||
// Extract relative value (bits 0-23)
|
||||
let value = hand_value & HandEval::VALUE_MASK;
|
||||
```
|
||||
|
||||
### Hand Categories
|
||||
|
||||
| Category | Value | Description |
|
||||
|----------|-------|-------------|
|
||||
| Straight Flush | 9 | Five cards in sequence, same suit |
|
||||
| Four of a Kind | 8 | Four cards of same rank |
|
||||
| Full House | 7 | Three of a kind + pair |
|
||||
| Flush | 6 | Five cards, same suit |
|
||||
| Straight | 5 | Five cards in sequence |
|
||||
| Three of a Kind | 4 | Three cards of same rank |
|
||||
| Two Pair | 3 | Two different pairs |
|
||||
| One Pair | 2 | Two cards of same rank |
|
||||
| High Card | 1 | No matching cards |
|
||||
|
||||
### Rank Value Ranges
|
||||
|
||||
| Hand Type | Rank Range | Count |
|
||||
|-----------|------------|-------|
|
||||
| Straight Flush | 368,793,253 - 371,292,191 | 40 |
|
||||
| Four of a Kind | 184,396,629 - 368,793,252 | 624 |
|
||||
| Full House | 92,198,315 - 184,396,628 | 3,744 |
|
||||
| Flush | 46,099,158 - 92,198,314 | 5,108 |
|
||||
| Straight | 23,049,579 - 46,099,157 | 10,200 |
|
||||
| Three of a Kind | 11,524,790 - 23,049,578 | 54,912 |
|
||||
| Two Pair | 5,762,395 - 11,524,789 | 123,552 |
|
||||
| One Pair | 2,881,198 - 5,762,394 | 1,098,240 |
|
||||
| High Card | 1 - 2,881,197 | 1,302,540 |
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
### Time Complexity
|
||||
|
||||
| Operation | Time Complexity | Notes |
|
||||
|-----------|----------------|-------|
|
||||
| 5-Card evaluation | O(1) | Direct table lookup |
|
||||
| 6-Card evaluation | O(1) | 6 table lookups |
|
||||
| 7-Card evaluation | O(1) | 21 table lookups |
|
||||
| Table generation | O(N) | N = 32M combinations |
|
||||
|
||||
### Memory Usage
|
||||
|
||||
| Component | Size | Description |
|
||||
|-----------|------|-------------|
|
||||
| Hand ranks table | ~128 MB | Complete evaluation table |
|
||||
| Working tables | ~2.4 MB | Generation workspace |
|
||||
| Code size | ~50 KB | Compiled binary |
|
||||
|
||||
### Cache Performance
|
||||
|
||||
The lookup table design provides excellent cache performance:
|
||||
|
||||
- **Sequential Access**: Table generation processes data sequentially
|
||||
- **Compact Encoding**: Cards encoded in 8 bits each
|
||||
- **Prefetch Friendly**: Linear table layout enables hardware prefetching
|
||||
- **Low Branch Misprediction**: Perfect hash eliminates conditional logic
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Common Error Cases
|
||||
|
||||
```rust
|
||||
// Invalid hand size
|
||||
let rank = evaluator.rank_hand(&hand); // Returns 0 for < 5 cards
|
||||
|
||||
// Card parsing errors
|
||||
let card = Card::from_string("XX"); // Returns Err
|
||||
|
||||
// Table loading errors
|
||||
let evaluator = LookupHandEvaluator::new(); // May fail if disk full
|
||||
```
|
||||
|
||||
### Error Types
|
||||
|
||||
- `io::Error` - File system errors during table loading/generation
|
||||
- `CardParseError` - Invalid card string format
|
||||
- `HandFullError` - Attempting to add cards to full hand
|
||||
|
||||
## Thread Safety
|
||||
|
||||
The `LookupHandEvaluator` is designed for read-only usage after construction:
|
||||
|
||||
```rust
|
||||
// Safe: Read-only evaluator
|
||||
let evaluator = LookupHandEvaluator::new().unwrap();
|
||||
|
||||
// Multiple threads can use the same evaluator
|
||||
let rank1 = evaluator.rank_hand(&hand1);
|
||||
let rank2 = evaluator.rank_hand(&hand2);
|
||||
|
||||
// NOT safe: Modifying evaluator during use
|
||||
// (evaluator is not Sync + Send for mutation)
|
||||
```
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### Texas Hold'em Engine
|
||||
|
||||
```rust
|
||||
use poker_api::hand_evaluator::LookupHandEvaluator;
|
||||
use poker_api::api::{hand::Hand, card::Card};
|
||||
|
||||
struct HoldemEngine {
|
||||
evaluator: LookupHandEvaluator,
|
||||
}
|
||||
|
||||
impl HoldemEngine {
|
||||
fn evaluate_board(&self, hole_cards: &[Card; 2], board: &[Card; 5]) -> u32 {
|
||||
let mut all_cards = Vec::new();
|
||||
all_cards.extend_from_slice(hole_cards);
|
||||
all_cards.extend_from_slice(board);
|
||||
|
||||
let mut hand = Hand::new();
|
||||
for card in all_cards {
|
||||
hand.add_card(card).unwrap();
|
||||
}
|
||||
|
||||
self.evaluator.rank_hand(&hand)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Hand Range Analysis
|
||||
|
||||
```rust
|
||||
use poker_api::hand_evaluator::LookupHandEvaluator;
|
||||
|
||||
struct RangeAnalyzer {
|
||||
evaluator: LookupHandEvaluator,
|
||||
}
|
||||
|
||||
impl RangeAnalyzer {
|
||||
fn compare_hands(&self, hand1: &Hand, hand2: &Hand) -> std::cmp::Ordering {
|
||||
let rank1 = self.evaluator.rank_hand(hand1);
|
||||
let rank2 = self.evaluator.rank_hand(hand2);
|
||||
rank1.cmp(&rank2)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Integration
|
||||
|
||||
### Unit Tests
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use poker_api::hand_evaluator::LookupHandEvaluator;
|
||||
|
||||
#[test]
|
||||
fn test_royal_flush() {
|
||||
let evaluator = LookupHandEvaluator::new().unwrap();
|
||||
let mut hand = Hand::new();
|
||||
// Add A,K,Q,J,10 of same suit
|
||||
let rank = evaluator.rank_hand(&hand);
|
||||
assert!(rank > 368_793_253); // Royal flush range
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Benchmark Tests
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod benchmarks {
|
||||
use super::*;
|
||||
use std::time::Instant;
|
||||
|
||||
#[test]
|
||||
fn benchmark_evaluation() {
|
||||
let evaluator = LookupHandEvaluator::new().unwrap();
|
||||
let hand = create_test_hand();
|
||||
|
||||
let start = Instant::now();
|
||||
for _ in 0..1_000_000 {
|
||||
let _rank = evaluator.rank_hand(&hand);
|
||||
}
|
||||
let duration = start.elapsed();
|
||||
|
||||
println!("1M evaluations in {:?}", duration);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Custom Table Paths
|
||||
|
||||
The evaluator uses default paths, but this can be customized by modifying the source:
|
||||
|
||||
```rust
|
||||
// In hand_evaluator.rs
|
||||
const FILE_NAME: &'static str = "custom/path/HandRanks.dat";
|
||||
```
|
||||
|
||||
### Memory Management
|
||||
|
||||
For systems with limited memory, consider:
|
||||
|
||||
```rust
|
||||
// Use Box<[u32]> for heap allocation control
|
||||
let hand_ranks: Box<[u32]> = vec![0; SIZE].into_boxed_slice();
|
||||
|
||||
// Monitor memory usage
|
||||
println!("Table memory usage: {} MB",
|
||||
std::mem::size_of_val(&*hand_ranks) / 1_048_576);
|
||||
```
|
||||
|
||||
### Performance Tuning
|
||||
|
||||
For maximum performance:
|
||||
|
||||
```rust
|
||||
// Use release mode
|
||||
cargo build --release
|
||||
|
||||
// Consider SIMD optimizations for batch evaluation
|
||||
// (requires custom implementation)
|
||||
```
|
||||
|
||||
This API documentation covers the complete public interface for poker hand evaluation. For implementation details, see the source code comments and the developer guide.
|
||||
Loading…
Add table
Add a link
Reference in a new issue