feat(sim): cash_out_threshold — rathole stack excess to bankroll
After each hand, stack above the threshold moves to the bankroll. Bounds effective depth while keeping natural short-stack dynamics (losses, rebuy cycles). Needed for realistic deep-stack-safe benchmarks: the oracle hybrid's EV collapses with stack depth (+6.9 at 75BB reset vs -33.8 natural), so unbounded natural-stack snowballs distort candidate evaluation.
This commit is contained in:
parent
a589acbbc7
commit
b9963bfbb1
2 changed files with 41 additions and 1 deletions
|
|
@ -354,6 +354,12 @@ pub struct GameConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub bankroll_refill_threshold: ChipCount,
|
pub bankroll_refill_threshold: ChipCount,
|
||||||
|
|
||||||
|
/// Cash-out (rathole) threshold: after each hand, stack excess above
|
||||||
|
/// this amount is moved to the player's bankroll. 0 = disabled.
|
||||||
|
/// Keeps natural stack dynamics (losses, rebuys) while bounding depth.
|
||||||
|
#[serde(default)]
|
||||||
|
pub cash_out_threshold: ChipCount,
|
||||||
|
|
||||||
/// If true, player seat assignments are randomized before each hand
|
/// If true, player seat assignments are randomized before each hand
|
||||||
/// (cash game) or before tournament start.
|
/// (cash game) or before tournament start.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
@ -430,6 +436,7 @@ impl Default for GameConfig {
|
||||||
rake_cap: 0,
|
rake_cap: 0,
|
||||||
reset_bankroll_each_hand: false,
|
reset_bankroll_each_hand: false,
|
||||||
bankroll_refill_threshold: 0,
|
bankroll_refill_threshold: 0,
|
||||||
|
cash_out_threshold: 0,
|
||||||
randomize_seats: false,
|
randomize_seats: false,
|
||||||
auto_rebuy: false,
|
auto_rebuy: false,
|
||||||
rebuy_threshold_percentage: default_rebuy_threshold(),
|
rebuy_threshold_percentage: default_rebuy_threshold(),
|
||||||
|
|
@ -1069,6 +1076,7 @@ min_raise = 50
|
||||||
rake_cap: 0,
|
rake_cap: 0,
|
||||||
reset_bankroll_each_hand: false,
|
reset_bankroll_each_hand: false,
|
||||||
bankroll_refill_threshold: 0,
|
bankroll_refill_threshold: 0,
|
||||||
|
cash_out_threshold: 0,
|
||||||
randomize_seats: false,
|
randomize_seats: false,
|
||||||
auto_rebuy: false,
|
auto_rebuy: false,
|
||||||
rebuy_threshold_percentage: 25,
|
rebuy_threshold_percentage: 25,
|
||||||
|
|
@ -1158,6 +1166,7 @@ min_raise = 50
|
||||||
rake_cap: 0,
|
rake_cap: 0,
|
||||||
reset_bankroll_each_hand: false,
|
reset_bankroll_each_hand: false,
|
||||||
bankroll_refill_threshold: 0,
|
bankroll_refill_threshold: 0,
|
||||||
|
cash_out_threshold: 0,
|
||||||
randomize_seats: false,
|
randomize_seats: false,
|
||||||
auto_rebuy: false,
|
auto_rebuy: false,
|
||||||
rebuy_threshold_percentage: 25,
|
rebuy_threshold_percentage: 25,
|
||||||
|
|
@ -1197,6 +1206,7 @@ min_raise = 50
|
||||||
rake_cap: 0,
|
rake_cap: 0,
|
||||||
reset_bankroll_each_hand: false,
|
reset_bankroll_each_hand: false,
|
||||||
bankroll_refill_threshold: 0,
|
bankroll_refill_threshold: 0,
|
||||||
|
cash_out_threshold: 0,
|
||||||
randomize_seats: false,
|
randomize_seats: false,
|
||||||
auto_rebuy: false,
|
auto_rebuy: false,
|
||||||
rebuy_threshold_percentage: 25,
|
rebuy_threshold_percentage: 25,
|
||||||
|
|
@ -1252,6 +1262,7 @@ min_raise = 50
|
||||||
rake_cap: 0,
|
rake_cap: 0,
|
||||||
reset_bankroll_each_hand: false,
|
reset_bankroll_each_hand: false,
|
||||||
bankroll_refill_threshold: 0,
|
bankroll_refill_threshold: 0,
|
||||||
|
cash_out_threshold: 0,
|
||||||
randomize_seats: false,
|
randomize_seats: false,
|
||||||
auto_rebuy: false,
|
auto_rebuy: false,
|
||||||
rebuy_threshold_percentage: 25,
|
rebuy_threshold_percentage: 25,
|
||||||
|
|
@ -1309,6 +1320,7 @@ min_raise = 50
|
||||||
rake_cap: 0,
|
rake_cap: 0,
|
||||||
reset_bankroll_each_hand: false,
|
reset_bankroll_each_hand: false,
|
||||||
bankroll_refill_threshold: 0,
|
bankroll_refill_threshold: 0,
|
||||||
|
cash_out_threshold: 0,
|
||||||
randomize_seats: false,
|
randomize_seats: false,
|
||||||
auto_rebuy: false,
|
auto_rebuy: false,
|
||||||
rebuy_threshold_percentage: 25,
|
rebuy_threshold_percentage: 25,
|
||||||
|
|
@ -1360,6 +1372,7 @@ min_raise = 50
|
||||||
rake_cap: 0,
|
rake_cap: 0,
|
||||||
reset_bankroll_each_hand: false,
|
reset_bankroll_each_hand: false,
|
||||||
bankroll_refill_threshold: 0,
|
bankroll_refill_threshold: 0,
|
||||||
|
cash_out_threshold: 0,
|
||||||
randomize_seats: false,
|
randomize_seats: false,
|
||||||
auto_rebuy: false,
|
auto_rebuy: false,
|
||||||
rebuy_threshold_percentage: 25,
|
rebuy_threshold_percentage: 25,
|
||||||
|
|
@ -1422,6 +1435,7 @@ min_raise = 50
|
||||||
rake_cap: 0,
|
rake_cap: 0,
|
||||||
reset_bankroll_each_hand: false,
|
reset_bankroll_each_hand: false,
|
||||||
bankroll_refill_threshold: 0,
|
bankroll_refill_threshold: 0,
|
||||||
|
cash_out_threshold: 0,
|
||||||
randomize_seats: false,
|
randomize_seats: false,
|
||||||
auto_rebuy: false,
|
auto_rebuy: false,
|
||||||
rebuy_threshold_percentage: 25,
|
rebuy_threshold_percentage: 25,
|
||||||
|
|
@ -1475,6 +1489,7 @@ min_raise = 50
|
||||||
rake_cap: 0,
|
rake_cap: 0,
|
||||||
reset_bankroll_each_hand: false,
|
reset_bankroll_each_hand: false,
|
||||||
bankroll_refill_threshold: 0,
|
bankroll_refill_threshold: 0,
|
||||||
|
cash_out_threshold: 0,
|
||||||
randomize_seats: false,
|
randomize_seats: false,
|
||||||
auto_rebuy: false,
|
auto_rebuy: false,
|
||||||
rebuy_threshold_percentage: 25,
|
rebuy_threshold_percentage: 25,
|
||||||
|
|
@ -1594,6 +1609,7 @@ min_raise = 20
|
||||||
rake_cap: 0,
|
rake_cap: 0,
|
||||||
reset_bankroll_each_hand: false,
|
reset_bankroll_each_hand: false,
|
||||||
bankroll_refill_threshold: 0,
|
bankroll_refill_threshold: 0,
|
||||||
|
cash_out_threshold: 0,
|
||||||
randomize_seats: false,
|
randomize_seats: false,
|
||||||
auto_rebuy: false,
|
auto_rebuy: false,
|
||||||
rebuy_threshold_percentage: 25,
|
rebuy_threshold_percentage: 25,
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,28 @@ pub fn run_cash_game(config: &GameConfig) -> Result<(SimulationStats, Table), Ga
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Cash-out (rathole): move stack excess above the threshold
|
||||||
|
// into the bankroll after each hand. Bounds effective depth
|
||||||
|
// while keeping natural short-stack dynamics intact.
|
||||||
|
if config.cash_out_threshold > 0 {
|
||||||
|
for slot in table.seats_mut().iter_mut() {
|
||||||
|
if let Some(seat) = slot {
|
||||||
|
if seat.stack > config.cash_out_threshold {
|
||||||
|
let excess =
|
||||||
|
seat.stack - config.cash_out_threshold;
|
||||||
|
log::debug!(
|
||||||
|
"Cash-out: {} ratholes {} (stack {} -> {})",
|
||||||
|
seat.player.name(),
|
||||||
|
excess,
|
||||||
|
seat.stack,
|
||||||
|
config.cash_out_threshold
|
||||||
|
);
|
||||||
|
seat.bankroll += excess as i64;
|
||||||
|
seat.stack = config.cash_out_threshold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(GameError::NotEnoughPlayers(count)) => {
|
Err(GameError::NotEnoughPlayers(count)) => {
|
||||||
// Not enough active players to continue. This should only
|
// Not enough active players to continue. This should only
|
||||||
|
|
@ -175,7 +197,9 @@ pub fn run_cash_game(config: &GameConfig) -> Result<(SimulationStats, Table), Ga
|
||||||
.map(|seat| (seat.player_id, seat.bankroll))
|
.map(|seat| (seat.player_id, seat.bankroll))
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let bankroll_mode = config.reset_bankroll_each_hand || config.bankroll_refill_threshold > 0;
|
let bankroll_mode = config.reset_bankroll_each_hand
|
||||||
|
|| config.bankroll_refill_threshold > 0
|
||||||
|
|| config.cash_out_threshold > 0;
|
||||||
stats.record_final_stacks(&final_stacks, &starting_stacks, &bankrolls, bankroll_mode, config.auto_rebuy);
|
stats.record_final_stacks(&final_stacks, &starting_stacks, &bankrolls, bankroll_mode, config.auto_rebuy);
|
||||||
|
|
||||||
Ok((stats, table))
|
Ok((stats, table))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue