#hold-em #poker #variant #player #plo

bin+lib chironaut

A poker game library for Texas Hold'em and other poker variants

1 unstable release

new 0.1.0 May 2, 2025

#91 in Games

Download history 100/week @ 2025-04-28

100 downloads per month

MIT/Apache

92KB
2K SLoC

Chironaut

A complete and flexible Texas Hold'em and Omaha poker engine written in Rust.

Features

  • ๐Ÿƒ Support for Texas Hold'em and multiple Omaha variants (4, 5, and 6-card)
  • ๐Ÿ’ฐ Fully functional betting logic with blinds, antes, raises, and all-in situations
  • ๐Ÿ”„ Side pot calculation and distribution with multiple all-in players
  • ๐ŸŽฎ Simple action model for easy integration with user interfaces
  • ๐Ÿคฒ Hand evaluation for all poker hand rankings
  • ๐Ÿ”€ Support for running it multiple times (2 or 3 runs)
  • ๐Ÿ’พ Game state serialization for saving and loading games
  • ๐Ÿงช Thoroughly tested with comprehensive test suite

Usage

Basic Game Setup

use chironaut::{
    action::Action,
    game::Game,
    rules::GameRules,
    game::GameStage,
};

// Create a No-Limit Hold'em game with 10/20 blinds
let rules = GameRules::nlhe(10, 20);
let mut game = Game::new(rules);

// Add players
game.add_player("Player 1".to_string(), 1000).unwrap();
game.add_player("Player 2".to_string(), 1000).unwrap();
game.add_player("Player 3".to_string(), 1000).unwrap();

// Start a hand
game.start_hand().unwrap();

// Player actions are handled with the simplified Action model
// Player 1 calls the big blind
game.handle_action(Action::Bet(20)).unwrap();

// Player 2 raises to 60
game.handle_action(Action::Bet(60)).unwrap();

// Player 3 folds
game.handle_action(Action::Fold).unwrap();

// Player 1 calls the raise
game.handle_action(Action::Bet(40)).unwrap();

// Move to the flop
game.next_street().unwrap();
println!("Community cards: {:?}", game.community_cards);

Action Model

Chironaut uses a simplified action model to represent player actions:

  • Action::Fold - Player folds their hand and gives up claim to the pot
  • Action::Bet(amount) - A unified betting action that covers:
    • Check: Bet(0) when current_bet = player's current bet
    • Call: Bet(amount) to match the current bet
    • Raise: Bet(amount) higher than the amount needed to call
    • All-in: Bet(player.chips) to go all-in with remaining chips

This unified model simplifies integration with user interfaces while maintaining all poker functionality.

All-in and Side Pots

The engine automatically handles all-in scenarios and side pot calculations:

// Player goes all-in with their remaining chips
let all_in_amount = game.players[current_position].chips;
game.handle_action(Action::Bet(all_in_amount)).unwrap();

// When a round is complete with all-in players, side pots are calculated
if game.is_round_complete() {
    game.calculate_side_pots();
    println!("Side pots: {:?}", game.side_pots);
}

// At showdown, pots are awarded to winners automatically
if game.stage == GameStage::Showdown {
    game.evaluate_winner();
}

Running Multiple Times

The engine supports running the community cards multiple times:

// After players are all-in, you can run the board multiple times
let run_count = 2; // Can be 2 or 3
let multi_run = game.run_multiple_times(run_count).unwrap();

// Pot is distributed proportionally based on run wins
game.complete_with_multiple_runs(run_count).unwrap();

Game State Serialization

Save and load game states:

// Save the current game state
let snapshot = game.get_snapshot();
let json = serde_json::to_string(&snapshot).unwrap();

// Load a game from a saved state
let loaded_snapshot: GameSnapshot = serde_json::from_json(&json).unwrap();
let game = Game::from_snapshot(loaded_snapshot);

Seating and Blind Structure

In heads-up (2 player) games, the button posts the small blind and acts first preflop. After the flop, the big blind acts first.

For 3+ player games, the standard poker seating and blind structure applies:

  • Button (dealer) is last to act
  • Small blind is to the left of the button
  • Big blind is to the left of the small blind
  • First to act (UTG) is to the left of the big blind

Seat positions can be explicitly assigned:

// Add players to specific seats (0-based index)
game.add_player_to_seat("Button".to_string(), 1000, 0).unwrap();
game.add_player_to_seat("Small Blind".to_string(), 1000, 1).unwrap();
game.add_player_to_seat("Big Blind".to_string(), 1000, 2).unwrap();

Testing

The library includes comprehensive tests for all functionality:

cargo test

Example CLI Application

A simple command-line poker game is included as an example:

use chironaut::{
    action::Action,
    game::Game,
    rules::GameRules,
};

fn main() {
    // Create a game with 10/20 blinds
    let rules = GameRules::nlhe(10, 20);
    let mut game = Game::new(rules);
    
    // Add players
    game.add_player("Alice".to_string(), 1000).unwrap();
    game.add_player("Bob".to_string(), 1000).unwrap();
    
    // Start the hand
    game.start_hand().unwrap();
    
    // Play through a simple hand
    println!("Starting the hand");
    println!("Alice's cards: {}", game.players[0].hand);
    println!("Bob's cards: {}", game.players[1].hand);
    
    // Alice calls
    game.handle_action(Action::Bet(10)).unwrap();
    // Bob checks
    game.handle_action(Action::Bet(0)).unwrap();
    
    // Deal the flop
    game.next_street().unwrap();
    println!("Flop: {:?}", game.community_cards);
    
    // Complete the hand
    // ...
}

License

MIT

Dependencies

~0.9โ€“1.8MB
~38K SLoC