chironaut 0.1.0

A poker game library for Texas Hold'em and other poker variants
Documentation
use std::fmt;
use serde::{Serialize, Deserialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum GameVariant {
    TexasHoldem,
    OmahaHoldem4,  // Classic 4-card PLO
    OmahaHoldem5,  // 5-card PLO
    OmahaHoldem6,  // 6-card PLO
}

impl fmt::Display for GameVariant {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            GameVariant::TexasHoldem => write!(f, "Texas Hold'em"),
            GameVariant::OmahaHoldem4 => write!(f, "Pot-Limit Omaha (4-card)"),
            GameVariant::OmahaHoldem5 => write!(f, "Pot-Limit Omaha (5-card)"),
            GameVariant::OmahaHoldem6 => write!(f, "Pot-Limit Omaha (6-card)"),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BettingStructure {
    NoLimit,
    PotLimit,
    FixedLimit,
}

impl fmt::Display for BettingStructure {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            BettingStructure::NoLimit => write!(f, "No-Limit"),
            BettingStructure::PotLimit => write!(f, "Pot-Limit"),
            BettingStructure::FixedLimit => write!(f, "Fixed-Limit"),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GameRules {
    pub variant: GameVariant,
    pub betting_structure: BettingStructure,
    pub small_blind: u32,
    pub big_blind: u32,
    pub ante: u32,
    pub min_players: usize,
    pub max_players: usize,
    pub hole_cards: usize,
    pub is_pot_limit: bool,
}

impl GameRules {
    pub fn nlhe(small_blind: u32, big_blind: u32) -> Self {
        Self {
            variant: GameVariant::TexasHoldem,
            betting_structure: BettingStructure::NoLimit,
            small_blind,
            big_blind,
            ante: 0,
            min_players: 2,
            max_players: 10,
            hole_cards: 2,
            is_pot_limit: false,
        }
    }
    
    pub fn plo4(small_blind: u32, big_blind: u32) -> Self {
        Self {
            variant: GameVariant::OmahaHoldem4,
            betting_structure: BettingStructure::PotLimit,
            small_blind,
            big_blind,
            ante: 0,
            min_players: 2,
            max_players: 10,
            hole_cards: 4,
            is_pot_limit: true,
        }
    }
    
    pub fn plo5(small_blind: u32, big_blind: u32) -> Self {
        Self {
            variant: GameVariant::OmahaHoldem5,
            betting_structure: BettingStructure::PotLimit,
            small_blind,
            big_blind,
            ante: 0,
            min_players: 2,
            max_players: 10,
            hole_cards: 5,
            is_pot_limit: true,
        }
    }
    
    pub fn plo6(small_blind: u32, big_blind: u32) -> Self {
        Self {
            variant: GameVariant::OmahaHoldem6,
            betting_structure: BettingStructure::PotLimit,
            small_blind,
            big_blind,
            ante: 0,
            min_players: 2,
            max_players: 9,  // Fewer players due to more cards per player
            hole_cards: 6,
            is_pot_limit: true,
        }
    }
    
    // Rename old plo to plo4 for backward compatibility
    pub fn plo(small_blind: u32, big_blind: u32) -> Self {
        Self::plo4(small_blind, big_blind)
    }
    
    pub fn with_ante(mut self, ante: u32) -> Self {
        self.ante = ante;
        self
    }
    
    pub fn with_player_limits(mut self, min: usize, max: usize) -> Self {
        self.min_players = min;
        self.max_players = max;
        self
    }
    
    pub fn min_raise(&self) -> u32 {
        self.big_blind
    }
    
    pub fn is_valid_raise(&self, current_bet: u32, raise_to: u32, pot_size: u32) -> bool {
        match self.betting_structure {
            BettingStructure::NoLimit => {
                // Raise must be at least min raise
                raise_to >= current_bet + self.min_raise()
            },
            BettingStructure::PotLimit => {
                // Raise must be at least min raise and at most pot-sized
                let min_valid = current_bet + self.min_raise();
                let max_valid = current_bet + pot_size;
                raise_to >= min_valid && raise_to <= max_valid
            },
            BettingStructure::FixedLimit => {
                // Fixed raises
                raise_to == current_bet + self.big_blind
            },
        }
    }
}