Our program:
use std::collections::HashMap;
use rand::seq::SliceRandom;
use rand::thread_rng;

// Constants
const STARTING_STACK: usize = 1000;
const SMALL_BLIND: usize = 5;
const BIG_BLIND: usize = 10;
const MAX_PLAYERS: usize = 2;

// Card definitions
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Suit {
    Clubs,
    Diamonds,
    Hearts,
    Spades,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
enum Rank {
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Ten,
    Jack,
    Queen,
    King,
    Ace,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct Card {
    rank: Rank,
    suit: Suit,
}

// Deck implementation
#[derive(Debug, Clone)]
struct Deck {
    cards: Vec<Card>,
}

impl Deck {
    fn new() -> Self {
        let mut cards = Vec::with_capacity(52);
        for suit in &[Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades] {
            for rank in &[
                Rank::Two,
                Rank::Three,
                Rank::Four,
                Rank::Five,
                Rank::Six,
                Rank::Seven,
                Rank::Eight,
                Rank::Nine,
                Rank::Ten,
                Rank::Jack,
                Rank::Queen,
                Rank::King,
                Rank::Ace,
            ] {
                cards.push(Card {
                    rank: rank.clone(),
                    suit: suit.clone(),
                });
            }
        }
        Deck { cards }
    }

    fn shuffle(&mut self) {
        let mut rng = thread_rng();
        self.cards.shuffle(&mut rng);
    }

    fn deal(&mut self, num: usize) -> Vec<Card> {
        self.cards.drain(0..num).collect()
    }
}

// Action definitions
#[derive(Debug, Clone, PartialEq, Eq)]
enum Action {
    Fold,
    Check,
    Call(usize),
    Bet(usize),
    AllIn,
}

// Betting round definitions
#[derive(Debug, Clone, PartialEq, Eq)]
enum BettingRound {
    PreFlop,
    Flop,
    Turn,
    River,
    Showdown,
}

// Game state
#[derive(Debug, Clone)]
struct GameState {
    history: Vec<String>,
    current_player: usize,
    pot: usize,
    player_stacks: [usize; MAX_PLAYERS],
    player_bets: [usize; MAX_PLAYERS],
    hole_cards: [Vec<Card>; MAX_PLAYERS],
    community_cards: Vec<Card>,
    betting_round: BettingRound,
    last_bet: usize,
    deck: Deck,
    terminal: bool,
    folded: [bool; MAX_PLAYERS],
}

impl GameState {
    fn new() -> Self {
        let mut deck = Deck::new();
        deck.shuffle();

        let hole_cards = [deck.deal(2), deck.deal(2)];

        let state = GameState {
            history: vec![],
            current_player: 0,
            pot: SMALL_BLIND + BIG_BLIND,
            player_stacks: [STARTING_STACK - SMALL_BLIND, STARTING_STACK - BIG_BLIND],
            player_bets: [SMALL_BLIND, BIG_BLIND],
            hole_cards,
            community_cards: vec![],
            betting_round: BettingRound::PreFlop,
            last_bet: BIG_BLIND,
            deck,
            terminal: false,
            folded: [false; MAX_PLAYERS],
        };

        state
    }

    fn is_terminal(&self) -> bool {
        self.terminal
    }

    fn get_utility(&self, player: usize) -> f64 {
        let opp = 1 - player;
        if self.folded[opp] {
            // Opponent folded, current player wins the pot
            return self.pot as f64;
        } else if self.folded[player] {
            // Current player folded, loses the amount put in the pot
            return -(self.player_bets[player] as f64);
        } else {
            // Showdown
            let player_hand_value = evaluate_hand(&self.hole_cards[player], &self.community_cards);
            let opp_hand_value = evaluate_hand(&self.hole_cards[opp], &self.community_cards);

            if player_hand_value > opp_hand_value {
                self.pot as f64
            } else if player_hand_value < opp_hand_value {
                -(self.player_bets[player] as f64)
            } else {
                // Split pot
                0.0
            }
        }
    }

    fn get_legal_actions(&self) -> Vec<Action> {
        if self.is_terminal() {
            return vec![];
        }

        let mut actions = Vec::new();
        let player = self.current_player;

        if self.folded[player] {
            return vec![];
        }

        let to_call = self.last_bet - self.player_bets[player];
        let player_stack = self.player_stacks[player];

        if to_call == 0 {
            actions.push(Action::Check);
            if player_stack > 0 {
                // Allow realistic bet sizes: Min Bet, Half Pot, Pot, 2x Pot, All-in
                let min_bet = BIG_BLIND;
                let amounts = vec![
                    min_bet,
                    self.pot / 2,
                    self.pot,
                    self.pot * 2,
                    player_stack,
                ];
                for amount in amounts {
                    if amount >= min_bet && amount <= player_stack && !actions.contains(&Action::Bet(amount)) {
                        actions.push(Action::Bet(amount));
                    }
                }
            }
        } else {
            actions.push(Action::Fold);
            if to_call >= player_stack {
                actions.push(Action::AllIn);
            } else {
                actions.push(Action::Call(to_call));
                if player_stack > to_call {
                    // Allow realistic raises: Min Raise, Half Pot, Pot, 2x Pot, All-in
                    let min_raise = to_call * 2;
                    let amounts = vec![
                        min_raise,
                        self.pot / 2,
                        self.pot,
                        self.pot * 2,
                        player_stack,
                    ];
                    for amount in amounts {
                        if amount > to_call && amount >= min_raise && amount <= player_stack && !actions.contains(&Action::Bet(amount)) {
                            actions.push(Action::Bet(amount));
                        }
                    }
                }
            }
        }

        actions
    }

    fn apply_action(&mut self, action: Action) {
        let player = self.current_player;
        let opp = 1 - player;

        match action {
            Action::Fold => {
                self.folded[player] = true;
                self.terminal = true;
                self.history.push("F".to_string());
            }
            Action::Check => {
                self.history.push("K".to_string());
                if self.player_bets[player] == self.player_bets[opp] {
                    self.advance_betting_round();
                } else {
                    self.current_player = opp;
                }
            }
            Action::Call(amount) => {
                self.player_stacks[player] -= amount;
                self.player_bets[player] += amount;
                self.pot += amount;
                self.history.push(format!("C{}", amount));
                if self.player_bets[player] == self.player_bets[opp] {
                    self.advance_betting_round();
                } else {
                    self.current_player = opp;
                }
            }
            Action::Bet(amount) => {
                let bet_amount = amount;
                self.player_stacks[player] -= bet_amount;
                self.player_bets[player] += bet_amount;
                self.pot += bet_amount;
                self.last_bet = self.player_bets[player];

                self.history.push(format!("B{}", amount));
                self.current_player = opp;
            }
            Action::AllIn => {
                let all_in_amount = self.player_stacks[player];
                self.player_bets[player] += all_in_amount;
                self.player_stacks[player] = 0;
                self.pot += all_in_amount;
                self.last_bet = self.player_bets[player];

                self.history.push("A".to_string());
                self.current_player = opp;

                if self.player_stacks[opp] == 0 {
                    // Both players are all-in
                    self.advance_betting_round();
                }
            }
        }
    }

    fn advance_betting_round(&mut self) {
        match self.betting_round {
            BettingRound::PreFlop => {
                self.betting_round = BettingRound::Flop;
                self.deal_community_cards(3);
                self.reset_bets();
            }
            BettingRound::Flop => {
                self.betting_round = BettingRound::Turn;
                self.deal_community_cards(1);
                self.reset_bets();
            }
            BettingRound::Turn => {
                self.betting_round = BettingRound::River;
                self.deal_community_cards(1);
                self.reset_bets();
            }
            BettingRound::River => {
                self.betting_round = BettingRound::Showdown;
                self.terminal = true;
            }
            BettingRound::Showdown => {
                self.terminal = true;
            }
        }
    }

    fn deal_community_cards(&mut self, num: usize) {
        let cards = self.deck.deal(num);
        self.community_cards.extend(cards);
    }

    fn reset_bets(&mut self) {
        self.player_bets = [0; MAX_PLAYERS];
        self.last_bet = 0;
        self.current_player = 0; // The first player to act in the next betting round
    }

    fn get_information_set(&self) -> String {
        let player = self.current_player;
        let hole_cards = &self.hole_cards[player];

        // Use actual hole cards for information set
        let mut hole_cards_str = String::new();
        for card in hole_cards {
            hole_cards_str.push_str(&format!("{:?}{:?}", card.rank, card.suit));
        }

        // Include betting history and hole cards
        format!(
            "P{}|H{}|B{}|R{:?}",
            player,
            hole_cards_str,
            self.history.join(""),
            self.betting_round,
        )
    }
}

// Hand evaluation function
fn evaluate_hand(hole_cards: &[Card], community_cards: &[Card]) -> u32 {
    // For simplicity, we'll assign a random value as hand strength
    // In production code, you should use a proper hand evaluator library
    // Here, we just sum the ranks as a placeholder

    let mut total_rank_value = 0;
    for card in hole_cards.iter().chain(community_cards.iter()) {
        total_rank_value += rank_to_value(&card.rank);
    }
    total_rank_value as u32
}

fn rank_to_value(rank: &Rank) -> usize {
    match rank {
        Rank::Two => 2,
        Rank::Three => 3,
        Rank::Four => 4,
        Rank::Five => 5,
        Rank::Six => 6,
        Rank::Seven => 7,
        Rank::Eight => 8,
        Rank::Nine => 9,
        Rank::Ten => 10,
        Rank::Jack => 11,
        Rank::Queen => 12,
        Rank::King => 13,
        Rank::Ace => 14,
    }
}

// CFR implementation
struct CFRTrainer {
    node_map: HashMap<String, Node>,
}

struct Node {
    info_set: String,
    regret_sum: Vec<f64>,
    strategy: Vec<f64>,
    strategy_sum: Vec<f64>,
    num_actions: usize,
}

impl CFRTrainer {
    fn new() -> Self {
        CFRTrainer {
            node_map: HashMap::new(),
        }
    }

    fn train(&mut self, iterations: usize) {
        for i in 1..=iterations {
            let mut game_state = GameState::new();
            let mut reach_probs = [1.0, 1.0];

            self.cfr(&mut game_state, &mut reach_probs);

            if i % 100 == 0 {
                println!("Iteration {}", i);
            }
        }

        self.compute_average_strategy();
    }

    fn cfr(&mut self, state: &mut GameState, reach_probs: &mut [f64; MAX_PLAYERS]) -> f64 {
        if state.is_terminal() {
            return state.get_utility(state.current_player);
        }

        let player = state.current_player;

        if state.folded[player] {
            // If the player has already folded, no utility
            return 0.0;
        }

        let info_set = state.get_information_set();

        let strategy;

        // Limit the scope of the mutable borrow of self.node_map
        {
            let legal_actions = state.get_legal_actions();

            let num_actions = legal_actions.len();

            let node = self.node_map.entry(info_set.clone()).or_insert_with(|| {
                Node {
                    info_set: info_set.clone(),
                    regret_sum: vec![0.0; num_actions],
                    strategy: vec![1.0 / num_actions as f64; num_actions],
                    strategy_sum: vec![0.0; num_actions],
                    num_actions,
                }
            });

            strategy = node.get_strategy(reach_probs[player]);
        }

        let legal_actions = state.get_legal_actions();
        let num_actions = legal_actions.len();

        let mut util = vec![0.0; num_actions];
        let mut node_util = 0.0;

        for (i, action) in legal_actions.iter().enumerate() {
            let mut next_state = state.clone();
            next_state.apply_action(action.clone());

            let mut next_reach_probs = reach_probs.clone();
            next_reach_probs[player] *= strategy[i];

            let value = -self.cfr(&mut next_state, &mut next_reach_probs);
            util[i] = value;
            node_util += strategy[i] * util[i];
        }

        // Update regrets
        let opp = 1 - player;

        // Limit the scope of mutable borrow
        {
            let node = self.node_map.get_mut(&info_set).unwrap();

            for i in 0..node.num_actions {
                let regret = util[i] - node_util;
                node.regret_sum[i] += reach_probs[opp] * regret;
            }
        }

        node_util
    }

    fn compute_average_strategy(&self) {
        for node in self.node_map.values() {
            let avg_strategy = node.get_average_strategy();
            println!("InfoSet: {} => Average Strategy: {:?}", node.info_set, avg_strategy);
        }
    }
}

impl Node {
    fn get_strategy(&mut self, realization_weight: f64) -> Vec<f64> {
        let sum_positive_regrets: f64 = self
            .regret_sum
            .iter()
            .map(|&r| if r > 0.0 { r } else { 0.0 })
            .sum();
        if sum_positive_regrets > 0.0 {
            for i in 0..self.num_actions {
                self.strategy[i] = if self.regret_sum[i] > 0.0 {
                    self.regret_sum[i] / sum_positive_regrets
                } else {
                    0.0
                };
            }
        } else {
            for i in 0..self.num_actions {
                self.strategy[i] = 1.0 / self.num_actions as f64;
            }
        }
        for i in 0..self.num_actions {
            self.strategy_sum[i] += realization_weight * self.strategy[i];
        }
        self.strategy.clone()
    }

    fn get_average_strategy(&self) -> Vec<f64> {
        let sum_strategy: f64 = self.strategy_sum.iter().sum();
        let mut avg_strategy = vec![0.0; self.num_actions];
        if sum_strategy > 0.0 {
            for i in 0..self.num_actions {
                avg_strategy[i] = self.strategy_sum[i] / sum_strategy;
            }
        } else {
            for i in 0..self.num_actions {
                avg_strategy[i] = 1.0 / self.num_actions as f64;
            }
        }
        avg_strategy
    }
}

fn main() {
    let iterations = 1000; // Adjust iterations as needed
    let mut trainer = CFRTrainer::new();
    trainer.train(iterations);
}

lets debug it. currently it just runs infenitely, what should we fix?