157 lines
5.6 KiB
Rust
157 lines
5.6 KiB
Rust
|
|
//! Every board, in every mode, at every seat band (CB-WP-0047).
|
||
|
|
//!
|
||
|
|
//! The engine dealt `SCN_01` and only `SCN_01` for the whole life of this
|
||
|
|
//! repo: `deal` took a scenario id, `setup` passed a literal, and **15 of
|
||
|
|
//! the 20 Problem cards had never been dealt by anything**. The three
|
||
|
|
//! scoring modes were reachable from the driver but no measurement had
|
||
|
|
//! ever compared them.
|
||
|
|
//!
|
||
|
|
//! This plays all of it. It is a **characterisation panel**, not a
|
||
|
|
//! hypothesis test: nothing here predicts an outcome, and the numbers
|
||
|
|
//! exist so that a later change to a deck or a mode is visible as a
|
||
|
|
//! change rather than discovered by a player.
|
||
|
|
//!
|
||
|
|
//! **SCN_01 and SCN_02 are the same board.** Identical suits and values
|
||
|
|
//! at every priority; they differ only in prose. They are both played
|
||
|
|
//! anyway — a panel that dropped one would hide the day they diverge —
|
||
|
|
//! and the column is expected to match, which is itself a control.
|
||
|
|
|
||
|
|
use cb_game_runtime::{ScenarioGame, Setup};
|
||
|
|
use games_ground::bot::{play, GreedyPolicy, Policy};
|
||
|
|
use games_ground::{GroundState, ScoringMode};
|
||
|
|
|
||
|
|
/// Games per cell. Named once so the banner and the assertion cannot
|
||
|
|
/// disagree — a previous panel printed "200 games per cell" over
|
||
|
|
/// 196-game columns (CB-REV-0002 #1).
|
||
|
|
const GAMES: u32 = 100;
|
||
|
|
|
||
|
|
struct Cell {
|
||
|
|
games: u32,
|
||
|
|
/// Games that reached an outcome over the full five rounds. A game
|
||
|
|
/// that RAN is not a game that was PLAYED (CB-REV-0003 #2).
|
||
|
|
played: u32,
|
||
|
|
group_success: u32,
|
||
|
|
/// Seats named a winner. Under SHARED GROUND that is everyone or
|
||
|
|
/// nobody; under the other two it is the point of the mode.
|
||
|
|
winners: u32,
|
||
|
|
/// Claimed value summed over games, so the threshold has something
|
||
|
|
/// to be compared against.
|
||
|
|
total: u32,
|
||
|
|
threshold: u32,
|
||
|
|
setup_fails: u32,
|
||
|
|
}
|
||
|
|
|
||
|
|
fn sweep(scenario: &str, mode: ScoringMode, players: u8) -> Cell {
|
||
|
|
let mut c = Cell {
|
||
|
|
games: 0,
|
||
|
|
played: 0,
|
||
|
|
group_success: 0,
|
||
|
|
winners: 0,
|
||
|
|
total: 0,
|
||
|
|
threshold: 0,
|
||
|
|
setup_fails: 0,
|
||
|
|
};
|
||
|
|
let preset = if scenario == "SCN_01" {
|
||
|
|
format!("standard-{players}p")
|
||
|
|
} else {
|
||
|
|
format!("scn-{}-{players}p", scenario.rsplit('_').next().unwrap())
|
||
|
|
};
|
||
|
|
for seed in 0..GAMES as u64 {
|
||
|
|
let Ok(mut st) = GroundState::setup(
|
||
|
|
&Setup {
|
||
|
|
players,
|
||
|
|
preset: preset.clone(),
|
||
|
|
patch: Default::default(),
|
||
|
|
},
|
||
|
|
seed,
|
||
|
|
) else {
|
||
|
|
// Counted, not skipped (CB-REV-0001 #11).
|
||
|
|
c.setup_fails += 1;
|
||
|
|
continue;
|
||
|
|
};
|
||
|
|
st.mode = mode;
|
||
|
|
// The board actually dealt, so the panel cannot report a
|
||
|
|
// scenario it did not play.
|
||
|
|
assert_eq!(st.scenario, scenario, "{preset} dealt {}", st.scenario);
|
||
|
|
let mut ps: Vec<Box<dyn Policy>> = (0..players)
|
||
|
|
.map(|_| Box::new(GreedyPolicy) as Box<dyn Policy>)
|
||
|
|
.collect();
|
||
|
|
let g = match play(st, &mut ps) {
|
||
|
|
Ok(g) => g,
|
||
|
|
Err(e) => {
|
||
|
|
eprintln!(" !! {scenario} {mode:?} {players}p seed {seed}: {e:?}");
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
c.games += 1;
|
||
|
|
if g.state.outcome.is_some() && g.rounds == 5 {
|
||
|
|
c.played += 1;
|
||
|
|
}
|
||
|
|
if let Some(o) = &g.state.outcome {
|
||
|
|
if o.group_success {
|
||
|
|
c.group_success += 1;
|
||
|
|
}
|
||
|
|
c.winners += o.winners.len() as u32;
|
||
|
|
c.total += o.total;
|
||
|
|
c.threshold = o.threshold;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
assert_eq!(
|
||
|
|
c.games, GAMES,
|
||
|
|
"{scenario} {mode:?} {players}p: only {} of {GAMES} games ran \
|
||
|
|
({} setups refused) — every number in the cell is over a sample \
|
||
|
|
nobody chose",
|
||
|
|
c.games, c.setup_fails
|
||
|
|
);
|
||
|
|
assert_eq!(
|
||
|
|
c.played, GAMES,
|
||
|
|
"{scenario} {mode:?} {players}p: {} of {} games reached an outcome \
|
||
|
|
over five rounds",
|
||
|
|
c.played, c.games
|
||
|
|
);
|
||
|
|
c
|
||
|
|
}
|
||
|
|
|
||
|
|
fn main() {
|
||
|
|
println!("CB-WP-0047 — the four Scenarios, the three Modes\n");
|
||
|
|
println!("Greedy throughout; {GAMES} games per cell; seeds 0..{GAMES}.");
|
||
|
|
println!("`won` is group success; `win/g` is winning SEATS per game,");
|
||
|
|
println!("which is what separates the modes — SHARED GROUND names all");
|
||
|
|
println!("or none, the other two name a subset.\n");
|
||
|
|
|
||
|
|
let scenarios = games_ground::edition::scenarios().expect("Scenarios.csv");
|
||
|
|
// Every mode the kernel has. Adding a fourth without extending the
|
||
|
|
// panel is the omission this list exists to make loud.
|
||
|
|
let modes = [
|
||
|
|
("SHARED GROUND ", ScoringMode::SharedGround),
|
||
|
|
("COMMON PROBLEM ", ScoringMode::CommonProblem),
|
||
|
|
("BONDED COALITIONS ", ScoringMode::BondedCoalitions),
|
||
|
|
];
|
||
|
|
|
||
|
|
for players in [2u8, 4, 6] {
|
||
|
|
println!("{players} players");
|
||
|
|
println!(
|
||
|
|
" {:<32} {:<18} {:>5} {:>7} {:>6} {:>9}",
|
||
|
|
"scenario", "mode", "won", "win/g", "pts", "threshold"
|
||
|
|
);
|
||
|
|
for s in &scenarios {
|
||
|
|
for (label, mode) in &modes {
|
||
|
|
let c = sweep(&s.id, *mode, players);
|
||
|
|
println!(
|
||
|
|
" {:<32} {label} {:>5} {:>7.2} {:>6.1} {:>9}",
|
||
|
|
format!("{} {}", s.id, s.title),
|
||
|
|
c.group_success,
|
||
|
|
f64::from(c.winners) / f64::from(c.games),
|
||
|
|
f64::from(c.total) / f64::from(c.games),
|
||
|
|
c.threshold,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
println!();
|
||
|
|
}
|
||
|
|
|
||
|
|
println!("SCN_01 and SCN_02 are the same board — identical suits and");
|
||
|
|
println!("values at every priority. Their rows are expected to MATCH,");
|
||
|
|
println!("and a divergence means one of the two decks was edited.");
|
||
|
|
}
|