use std::fmt;
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum GameVariant {
TexasHoldem,
OmahaHoldem4, OmahaHoldem5, OmahaHoldem6, }
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 AnteStructure {
None,
Fixed(u32),
BigBlindOnly(u32),
ButtonOnly(u32),
BigBlindPercentage(u8),
}
impl fmt::Display for AnteStructure {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AnteStructure::None => write!(f, "No Ante"),
AnteStructure::Fixed(amount) => write!(f, "Fixed Ante: {}", amount),
AnteStructure::BigBlindOnly(amount) => write!(f, "BB Ante: {}", amount),
AnteStructure::ButtonOnly(amount) => write!(f, "Button Ante: {}", amount),
AnteStructure::BigBlindPercentage(percent) => write!(f, "{}% of BB Ante", percent),
}
}
}
#[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_structure: AnteStructure,
pub min_players: usize,
pub max_players: usize,
pub hole_cards: usize,
pub is_pot_limit: bool,
pub allow_straddles: bool,
}
impl GameRules {
pub fn max_players_for_variant(variant: GameVariant) -> usize {
const DECK_SIZE: usize = 52;
const COMMUNITY_CARDS: usize = 5;
let cards_per_player = match variant {
GameVariant::TexasHoldem => 2,
GameVariant::OmahaHoldem4 => 4,
GameVariant::OmahaHoldem5 => 5,
GameVariant::OmahaHoldem6 => 6,
};
(DECK_SIZE - COMMUNITY_CARDS) / cards_per_player
}
pub fn nlhe(small_blind: u32, big_blind: u32) -> Self {
let variant = GameVariant::TexasHoldem;
let max_players = 10.min(Self::max_players_for_variant(variant));
Self {
variant,
betting_structure: BettingStructure::NoLimit,
small_blind,
big_blind,
ante_structure: AnteStructure::None,
min_players: 2,
max_players,
hole_cards: 2,
is_pot_limit: false,
allow_straddles: false,
}
}
pub fn plo4(small_blind: u32, big_blind: u32) -> Self {
let variant = GameVariant::OmahaHoldem4;
let max_players = 10.min(Self::max_players_for_variant(variant));
Self {
variant,
betting_structure: BettingStructure::PotLimit,
small_blind,
big_blind,
ante_structure: AnteStructure::None,
min_players: 2,
max_players,
hole_cards: 4,
is_pot_limit: true,
allow_straddles: false,
}
}
pub fn plo5(small_blind: u32, big_blind: u32) -> Self {
let variant = GameVariant::OmahaHoldem5;
let max_players = Self::max_players_for_variant(variant);
Self {
variant,
betting_structure: BettingStructure::PotLimit,
small_blind,
big_blind,
ante_structure: AnteStructure::None,
min_players: 2,
max_players,
hole_cards: 5,
is_pot_limit: true,
allow_straddles: false,
}
}
pub fn plo6(small_blind: u32, big_blind: u32) -> Self {
let variant = GameVariant::OmahaHoldem6;
let max_players = Self::max_players_for_variant(variant);
Self {
variant,
betting_structure: BettingStructure::PotLimit,
small_blind,
big_blind,
ante_structure: AnteStructure::None,
min_players: 2,
max_players,
hole_cards: 6,
is_pot_limit: true,
allow_straddles: false,
}
}
pub fn plo(small_blind: u32, big_blind: u32) -> Self {
Self::plo4(small_blind, big_blind)
}
pub fn with_ante(mut self, ante_amount: u32) -> Self {
self.ante_structure = AnteStructure::Fixed(ante_amount);
self
}
pub fn with_bb_ante(mut self, ante_amount: u32) -> Self {
self.ante_structure = AnteStructure::BigBlindOnly(ante_amount);
self
}
pub fn with_button_ante(mut self, ante_amount: u32) -> Self {
self.ante_structure = AnteStructure::ButtonOnly(ante_amount);
self
}
pub fn with_percentage_ante(mut self, percent: u8) -> Self {
let clamped_percent = percent.clamp(1, 100);
self.ante_structure = AnteStructure::BigBlindPercentage(clamped_percent);
self
}
pub fn calculate_ante_amount(&self) -> u32 {
match self.ante_structure {
AnteStructure::None => 0,
AnteStructure::Fixed(amount) => amount,
AnteStructure::BigBlindOnly(amount) => amount,
AnteStructure::ButtonOnly(amount) => amount,
AnteStructure::BigBlindPercentage(percent) => {
let amount = (self.big_blind as u64 * percent as u64) / 100;
amount as u32
}
}
}
pub fn with_straddles(mut self) -> Self {
self.allow_straddles = true;
self
}
pub fn with_player_limits(mut self, min: usize, max: usize) -> Self {
let deck_max = Self::max_players_for_variant(self.variant);
let adjusted_max = max.min(deck_max);
self.min_players = min.min(adjusted_max);
self.max_players = adjusted_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_to >= current_bet + self.min_raise()
},
BettingStructure::PotLimit => {
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 => {
raise_to == current_bet + self.big_blind
},
}
}
}