//! 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); // ONE ARM OVERRIDDEN, THE REST DELEGATED. // // The first version re-typed greedy's ranking and changed one // line of the copy. It differed in five places, and the review // found it: `SpendFreedom` ranked 95 unconditionally instead of // greedy's `95 if gated else 0`, so this seat spent its Freedom // token in round one of every game — a second change to the very // mechanism under study. Delegating makes "exactly one preference // differs" structurally true instead of a claim in a comment. let rank = |c: &GroundCommand| -> i32 { match c { GroundCommand::SelectAction { action: Action::Ground, .. } if gated => 5, other => GreedyPolicy::rank(state, seat, other), } }; let mut best = 0; for (i, c) in legal.iter().enumerate() { if rank(c) > rank(&legal[best]) { best = i; } } Choice::Command(best) } } /// `Scenarios.csv`: "All players start at Stress 2." Named rather than /// inlined so the peak metric cannot silently disagree with setup. const START_STRESS: u8 = 2; 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; } } // Peak Stress **held**, not peak Stress *assigned*. // // The first version took a maximum over `StressSet` PAYLOADS. // Starting Stress is 2 and is written by `setup`, never by an // event, so a table that sat at 2 all game reported **1**, and a // table with no `StressSet` at all would report 0. CB-EV-0031 §2 // built its headline claim on that number. Wrong subject: the // metric answered "highest value ever assigned", the prose said // "highest Stress reached". let mut held: std::collections::BTreeMap = g.state.players.keys().map(|s| (*s, START_STRESS)).collect(); c.peak_stress = c.peak_stress.max(START_STRESS); for e in &g.events { if matches!(e, games_ground::GroundEvent::DarvoTriggered { .. }) { c.darvo += 1; } if let games_ground::GroundEvent::StressSet { player, stress } = e { held.insert(*player, *stress); c.peak_stress = c.peak_stress.max(*stress); } } let _ = held; } 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!(); } }