rusty_marvin/docs/user_guide.md
Jan-Peter von Hunnius 5c271b279f Added Documentation
Added Test Cases
Added Comments
2025-10-02 18:33:31 +02:00

441 lines
No EOL
10 KiB
Markdown

# User Guide
## Introduction
This guide explains how to use the Poker Hand Evaluator library for evaluating poker hands in your applications. Whether you're building a poker game, analysis tool, or AI bot, this library provides the fast and accurate hand evaluation you need.
## Quick Start
### Basic Setup
1. **Add dependency** to your `Cargo.toml`:
```toml
[dependencies]
poker-api = "0.1.0"
```
2. **Import and use** in your code:
```rust
use poker_api::hand_evaluator::LookupHandEvaluator;
use poker_api::api::hand::Hand;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create the evaluator (generates tables on first run)
let evaluator = LookupHandEvaluator::new()?;
// Create a poker hand
let mut hand = Hand::new();
// ... add cards
// Evaluate the hand
let rank = evaluator.rank_hand(&hand);
println!("Hand strength: {}", rank);
Ok(())
}
```
### First Run Experience
On the first run, you'll see:
```
Evaluation tables do not exist, generating them...
Generating state table...
Hand evaluation tables saved to math/HandRanks.dat
Evaluation tables loaded.
```
This generates the lookup tables (~1-2 seconds) and saves them for future use.
## Working with Cards
### Card Creation
Cards can be created from string notation:
```rust
use poker_api::api::card::Card;
// Face cards
let ace_spades = Card::from_string("As")?;
let king_hearts = Card::from_string("Kh")?;
// Number cards
let five_clubs = Card::from_string("5c")?;
let ten_diamonds = Card::from_string("Td")?;
// All suits: s (spades), h (hearts), d (diamonds), c (clubs)
// All ranks: A, K, Q, J, T, 9, 8, 7, 6, 5, 4, 3, 2
```
### Card Properties
```rust
let card = Card::from_string("As")?;
println!("Rank: {}", card.rank()); // 12 (Ace)
println!("Suit: {}", card.suit()); // 0 (Spades)
println!("Index: {}", card.index()); // 51 (unique card ID)
println!("String: {}", card.to_string()); // "As"
```
## Working with Hands
### Creating Hands
```rust
use poker_api::api::hand::Hand;
use poker_api::api::card::Card;
// Create empty hand
let mut hand = Hand::new();
// Add cards one by one
hand.add_card(Card::from_string("As")?).unwrap();
hand.add_card(Card::from_string("Ks")?).unwrap();
hand.add_card(Card::from_string("Qs")?).unwrap();
hand.add_card(Card::from_string("Js")?).unwrap();
hand.add_card(Card::from_string("Ts")?).unwrap();
println!("Hand size: {}", hand.size()); // 5
println!("Hand: {}", hand.to_string()); // "As Ks Qs Js Ts"
```
### Hand Evaluation
```rust
// 5-card hand evaluation
let rank = evaluator.rank_hand(&hand);
println!("Royal flush rank: {}", rank);
// 6-card hand (finds best 5-card combination)
let mut six_card_hand = Hand::new();
six_card_hand.add_card(Card::from_string("As")?).unwrap();
six_card_hand.add_card(Card::from_string("Ks")?).unwrap();
// ... add 4 more cards
let rank = evaluator.rank_hand(&six_card_hand);
// 7-card hand (Texas Hold'em)
let mut seven_card_hand = Hand::new();
// ... add 7 cards
let rank = evaluator.rank_hand(&seven_card_hand);
```
## Understanding Hand Ranks
### Rank Values
Higher rank values indicate stronger hands:
```rust
// Compare hand strengths
let hand1_rank = evaluator.rank_hand(&hand1);
let hand2_rank = evaluator.rank_hand(&hand2);
if hand1_rank > hand2_rank {
println!("Hand 1 is stronger");
} else if hand1_rank < hand2_rank {
println!("Hand 2 is stronger");
} else {
println!("Hands are equal");
}
```
### Hand Categories
Extract hand category from rank:
```rust
use poker_api::api::hand_eval::HandEval;
fn get_hand_category(rank: u32) -> u32 {
(rank & HandEval::HAND_CATEGORY_MASK) >> HandEval::HAND_CATEGORY_SHIFT
}
let category = get_hand_category(rank);
match category {
9 => println!("Straight Flush"),
8 => println!("Four of a Kind"),
7 => println!("Full House"),
6 => println!("Flush"),
5 => println!("Straight"),
4 => println!("Three of a Kind"),
3 => println!("Two Pair"),
2 => println!("One Pair"),
1 => println!("High Card"),
_ => println!("Unknown"),
}
```
## Texas Hold'em Example
Here's a complete example of evaluating Texas Hold'em hands:
```rust
use poker_api::hand_evaluator::LookupHandEvaluator;
use poker_api::api::hand::Hand;
use poker_api::api::card::Card;
struct PokerGame {
evaluator: LookupHandEvaluator,
}
impl PokerGame {
fn new() -> Result<Self, Box<dyn std::error::Error>> {
Ok(Self {
evaluator: LookupHandEvaluator::new()?,
})
}
fn evaluate_texas_holdem(
&self,
hole_cards: &[Card; 2],
board: &[Card; 5]
) -> u32 {
let mut hand = Hand::new();
// Add hole cards
hand.add_card(hole_cards[0]).unwrap();
hand.add_card(hole_cards[1]).unwrap();
// Add community cards
for &card in board {
hand.add_card(card).unwrap();
}
self.evaluator.rank_hand(&hand)
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let game = PokerGame::new()?;
// Example: Royal Flush
let hole_cards = [
Card::from_string("As").unwrap(),
Card::from_string("Ks").unwrap(),
];
let board = [
Card::from_string("Qs").unwrap(),
Card::from_string("Js").unwrap(),
Card::from_string("Ts").unwrap(),
Card::from_string("2d").unwrap(),
Card::from_string("3c").unwrap(),
];
let rank = game.evaluate_texas_holdem(&hole_cards, &board);
println!("Royal flush rank: {}", rank);
Ok(())
}
```
## Performance Optimization
### Table Preloading
For applications that need immediate response:
```rust
// Warm up the evaluator before use
fn preload_evaluator() -> LookupHandEvaluator {
println!("Loading poker evaluator...");
let start = std::time::Instant::now();
let evaluator = LookupHandEvaluator::new().unwrap();
let duration = start.elapsed();
println!("Evaluator ready in {:?}", duration);
evaluator
}
```
### Batch Evaluation
For evaluating many hands:
```rust
fn evaluate_multiple_hands(evaluator: &LookupHandEvaluator, hands: &[Hand]) -> Vec<u32> {
hands.iter()
.map(|hand| evaluator.rank_hand(hand))
.collect()
}
```
## Error Handling
### Common Errors
```rust
use poker_api::api::card::Card;
// Invalid card strings
let invalid = Card::from_string("XX"); // Err
let invalid = Card::from_string("53"); // Err
// Hand capacity exceeded
let mut hand = Hand::new();
// Adding more than 7 cards will fail
// Table loading failures
let evaluator = LookupHandEvaluator::new();
// May fail if disk is full or permissions are insufficient
```
### Proper Error Handling
```rust
use std::io;
fn create_evaluator() -> Result<LookupHandEvaluator, io::Error> {
match LookupHandEvaluator::new() {
Ok(evaluator) => {
println!("Evaluator created successfully");
Ok(evaluator)
}
Err(e) => {
eprintln!("Failed to create evaluator: {}", e);
Err(e)
}
}
}
```
## Best Practices
### 1. Reuse Evaluators
```rust
// Good: Reuse the same evaluator
struct GameEngine {
evaluator: LookupHandEvaluator,
}
impl GameEngine {
fn new() -> Result<Self, io::Error> {
Ok(Self {
evaluator: LookupHandEvaluator::new()?,
})
}
fn evaluate_hand(&self, hand: &Hand) -> u32 {
self.evaluator.rank_hand(hand)
}
}
```
### 2. Validate Input Data
```rust
fn safe_evaluate_hand(evaluator: &LookupHandEvaluator, hand: &Hand) -> Option<u32> {
if hand.size() >= 5 && hand.size() <= 7 {
Some(evaluator.rank_hand(hand))
} else {
None
}
}
```
### 3. Handle Edge Cases
```rust
// Check for invalid hands
let rank = evaluator.rank_hand(&hand);
if rank == 0 {
println!("Invalid hand: must have 5-7 cards");
}
```
## Integration Examples
### Poker Bot
```rust
use poker_api::hand_evaluator::LookupHandEvaluator;
struct PokerBot {
evaluator: LookupHandEvaluator,
}
impl PokerBot {
fn should_raise(&self, my_hand: &Hand, pot_odds: f32) -> bool {
let hand_strength = self.evaluator.rank_hand(my_hand);
// Simple strategy: raise with strong hands
match hand_strength {
rank if rank > 300_000_000 => true, // Very strong
rank if rank > 100_000_000 => pot_odds > 0.1, // Medium strength
_ => false, // Weak hands
}
}
}
```
### Hand Range Analysis
```rust
use poker_api::hand_evaluator::LookupHandEvaluator;
use poker_api::api::hand::Hand;
struct RangeAnalyzer {
evaluator: LookupHandEvaluator,
}
impl RangeAnalyzer {
fn analyze_range(&self, hands: &[Hand]) -> HandStrengthStats {
let ranks: Vec<u32> = hands.iter()
.map(|hand| self.evaluator.rank_hand(hand))
.collect();
HandStrengthStats {
average_rank: ranks.iter().sum::<u32>() / ranks.len() as u32,
median_rank: self.calculate_median(&ranks),
strongest_hand: ranks.iter().max().copied().unwrap_or(0),
weakest_hand: ranks.iter().min().copied().unwrap_or(0),
}
}
}
```
## Troubleshooting
### Common Issues
**Problem**: "Evaluation tables do not exist, generating them..." every time
**Solution**: Ensure write permissions in the current directory, or specify a custom path
**Problem**: Slow first startup
**Solution**: Expected behavior - table generation takes 1-2 seconds
**Problem**: High memory usage
**Solution**: The 128MB lookup table is required for performance. Consider if this fits your deployment constraints
**Problem**: Card parsing fails
**Solution**: Verify card strings use correct format: rank + suit (e.g., "As", "Kh", "Td")
### Performance Issues
If evaluation seems slow:
1. **Check build mode**: Use `cargo build --release`
2. **Verify table loading**: Ensure tables are generated and cached
3. **Monitor memory**: Ensure sufficient RAM for table storage
4. **Consider batching**: Evaluate multiple hands together when possible
## Support
For additional help:
1. Check the [API Documentation](api.md) for detailed reference
2. Review test files for usage examples
3. Open issues on GitHub for bugs or feature requests
4. Study the source code comments for implementation details
This user guide covers the most common use cases. For advanced usage or customization, see the developer guide and source code documentation.