feat: add rake, bankroll management, seat randomization, FlockBot & ChumpBot

- Add RakeConfig with percentage-based rake and per-pot cap
- Add bankroll tracking (reset per hand or auto-refill threshold)
- Add seat randomization with seeded RNG for reproducibility
- Add FlockBot (tight-aggressive) and ChumpBot (loose-passive) bots
- Refactor HandRunner: extract finalize_result, compute_chip_movements, credit_winnings
- Add num_raises to GameInfo for authoritative raise counting
- Record SB/BB as Raise actions (Meerkat convention)
- Update all existing bot tests for new GameInfo fields
- Add ARCHITECTURE_RAKE_BANKROLL_SEATS.md documentation
This commit is contained in:
Jan-Peter von Hunnius 2026-05-18 20:44:08 +02:00
parent 17d0440c86
commit 9dfd9c3ee3
20 changed files with 2880 additions and 173 deletions

View file

@ -84,7 +84,7 @@ pub fn run_cash_game(config: &GameConfig) -> Result<(SimulationStats, Table), Ga
}
}
// 5. Record final stacks for accurate profit calculation.
// 5. Record final stacks and bankrolls for accurate profit calculation.
let final_stacks: HashMap<PlayerId, ChipCount> = table
.seats()
.iter()
@ -93,7 +93,16 @@ pub fn run_cash_game(config: &GameConfig) -> Result<(SimulationStats, Table), Ga
.map(|seat| (seat.player_id, seat.stack))
})
.collect();
stats.record_final_stacks(&final_stacks, &starting_stacks);
let bankrolls: HashMap<PlayerId, i64> = table
.seats()
.iter()
.filter_map(|slot| {
slot.as_ref()
.map(|seat| (seat.player_id, seat.bankroll))
})
.collect();
let bankroll_mode = config.reset_bankroll_each_hand || config.bankroll_refill_threshold > 0;
stats.record_final_stacks(&final_stacks, &starting_stacks, &bankrolls, bankroll_mode);
Ok((stats, table))
}
@ -124,6 +133,11 @@ mod tests {
num_blind_levels: 15,
num_hands,
seed,
rake_percentage: 0.0,
rake_cap: 0,
reset_bankroll_each_hand: false,
bankroll_refill_threshold: 0,
randomize_seats: false,
bots: vec![
BotConfig {
bot_type: "call_bot".to_string(),
@ -225,6 +239,12 @@ mod tests {
num_blind_levels: 15,
num_hands: 10,
seed: None,
rake_percentage: 0.0,
rake_cap: 0,
reset_bankroll_each_hand: false,
bankroll_refill_threshold: 0,
randomize_seats: false,
bots: vec![BotConfig {
bot_type: "call_bot".to_string(),
name: Some("Lonely".to_string()),
@ -253,6 +273,12 @@ mod tests {
num_blind_levels: 15,
num_hands: 10,
seed: None,
rake_percentage: 0.0,
rake_cap: 0,
reset_bankroll_each_hand: false,
bankroll_refill_threshold: 0,
randomize_seats: false,
bots: vec![
BotConfig {
bot_type: "call_bot".to_string(),
@ -289,6 +315,12 @@ mod tests {
num_blind_levels: 15,
num_hands: 5,
seed: Some(99),
rake_percentage: 0.0,
rake_cap: 0,
reset_bankroll_each_hand: false,
bankroll_refill_threshold: 0,
randomize_seats: false,
bots: vec![
BotConfig {
bot_type: "call_bot".to_string(),