//! Does H1 do nothing, or does it do nothing **to a seat that already //! manages its Stress**? (CB-WP-0039) //! //! [`CB-EV-0030`] measured ground-game's H1 with a panel that was //! greedy-family throughout, and found DARVO never arms. But //! `GreedyPolicy` ranks `Action::Ground if gated => 100`: the moment the //! stress gate bites it grounds, Stress plateaus at 3, and the arm at 5 //! is unreachable **by construction**. //! //! H1 was written for the seat that does *not* do that. So this varies //! **exactly one preference** — GROUND demoted below ATTACK — and holds //! every other ranking identical to greedy's. //! //! [`CB-EV-0030`]: ../../../evidence/CB-EV-0030-h1-measured.md use cb_game_runtime::{ScenarioGame, Setup}; use cb_kernel::PlayerId; use games_ground::bot::{play, Choice, GreedyPolicy, Policy}; use games_ground::{Action, GroundCommand, GroundState, ScoringMode, Variant}; /// Greedy, with GROUND demoted below ATTACK. Nothing else differs. /// /// **The copy is deliberate.** `GreedyPolicy::rank` is private, and the /// point is to change one line of it while holding the rest identical — /// calling into it would make that impossible to demonstrate. struct Reactive; impl Policy for Reactive { fn name(&self) -> &'static str { "reactive" } fn choose( &mut self, state: &GroundState, seat: PlayerId, legal: &[GroundCommand], _may_pass: bool, ) -> Choice { let gated = state .players .get(&seat) .is_some_and(|p| p.stress >= 4 && !p.freedom_gate_lifted); let rank = |c: &GroundCommand| -> i32 { match c { GroundCommand::SelectAction { action, problem, .. } => match action { // THE ONE LINE. Greedy has `Ground if gated => 100`. Action::Ground if gated => 5, Action::Solve if problem .and_then(|p| state.problems.get(&p)) .is_some_and(|p| p.claimed_by.is_some()) => { 20 } Action::Solve => 90, Action::Investigate => 80, Action::Support => 70, Action::Ground => 5, Action::Attack => 10, }, GroundCommand::SpendFreedom => 95, GroundCommand::ChooseGroundMode { .. } => 50, GroundCommand::RespondToSupport { .. } => 90, GroundCommand::ChooseDarvoTarget { .. } => 50, GroundCommand::Reveal | GroundCommand::Resolve | GroundCommand::EndRound => -1, } }; let mut best = 0; for (i, c) in legal.iter().enumerate() { if rank(c) > rank(&legal[best]) { best = i; } } Choice::Command(best) } } struct Cell { games: u32, won: u32, atk: u32, darvo: u32, peak_stress: u8, } fn sweep(mode: ScoringMode, variant: Variant, players: u8, reactive: bool) -> Cell { let mut c = Cell { games: 0, won: 0, atk: 0, darvo: 0, peak_stress: 0, }; for seed in 0..200u64 { let Ok(mut st) = GroundState::setup( &Setup { players, preset: format!("standard-{players}p"), patch: Default::default(), }, seed, ) else { continue; }; st.mode = mode; st.variant = variant; let mut ps: Vec> = (0..players) .map(|_| { if reactive { Box::new(Reactive) as Box } else { Box::new(GreedyPolicy) as Box } }) .collect(); // A refused game is not a lost game (CB-EV-0030 §3). let g = match play(st, &mut ps) { Ok(g) => g, Err(e) => { eprintln!(" !! {players}p seed {seed}: {e:?}"); continue; } }; c.games += 1; if g.state.outcome.as_ref().is_some_and(|o| o.group_success) { c.won += 1; } for (_, cmd) in &g.steps { if let GroundCommand::SelectAction { action: Action::Attack, .. } = cmd { c.atk += 1; } } for e in &g.events { if matches!(e, games_ground::GroundEvent::DarvoTriggered { .. }) { c.darvo += 1; } // How close does Stress actually get to the arm at 5? That is // the number H1's whole mechanism turns on, and CB-EV-0030 // had to infer it from a single game. if let games_ground::GroundEvent::StressSet { stress, .. } = e { c.peak_stress = c.peak_stress.max(*stress); } } } c } fn main() { println!("CB-WP-0039 — does H1 reach a seat that does not regulate?\n"); println!("`reactive` is greedy with ONE preference changed: GROUND is"); println!("demoted below ATTACK, so the seat never grounds to shed Stress."); println!("SHARED GROUND; `won` is group success; 200 games per cell.\n"); for (vlabel, variant) in [ ("BASELINE ground-darvo-r0", Variant::Baseline), ("H1 h1-problem-stress", Variant::H1ProblemStress), ] { println!("{vlabel}"); println!(" greedy (regulates) reactive (does not)"); println!("seats games won atk darvo peak games won atk darvo peak"); for players in [2u8, 3, 4, 6] { let g = sweep(ScoringMode::SharedGround, variant, players, false); let r = sweep(ScoringMode::SharedGround, variant, players, true); println!( " {players}p {:>4} {:>4} {:>4} {:>5} {:>4} {:>4} {:>4} {:>4} {:>5} {:>4}", g.games, g.won, g.atk, g.darvo, g.peak_stress, r.games, r.won, r.atk, r.darvo, r.peak_stress ); } println!(); } }