use clap::Parser;
use contract_bridge::AbsoluteVulnerability;
use pons::scoring::{final_contract, ns_score_contract, ns_score_pd};
#[path = "../common/mod.rs"]
#[allow(dead_code)]
mod common;
use common::{Dump, mean_with_ci, score_boards};
#[derive(Parser)]
struct Args {
on: String,
off: String,
#[arg(short, long)]
vulnerability: Option<AbsoluteVulnerability>,
#[arg(long, default_value_t = 8)]
show: usize,
#[arg(long, default_value = "plain")]
score: String,
}
fn main() {
let args = Args::parse();
let on: Dump = serde_json::from_reader(std::io::BufReader::new(
std::fs::File::open(&args.on).expect("open ON dump"),
))
.expect("parse ON dump");
let off: Dump = serde_json::from_reader(std::io::BufReader::new(
std::fs::File::open(&args.off).expect("open OFF dump"),
))
.expect("parse OFF dump");
assert_eq!(on.boards.len(), off.boards.len(), "dumps must be aligned");
let vul = args.vulnerability.unwrap_or(on.vulnerability);
let mut deals = Vec::with_capacity(on.boards.len());
let contracts: Vec<_> = on
.boards
.iter()
.zip(&off.boards)
.map(|(a, b)| {
assert_eq!(a.deal, b.deal, "dumps not seed-aligned");
deals.push(a.deal);
(
final_contract(&a.table_a, a.dealer),
final_contract(&b.table_a, b.dealer),
)
})
.collect();
let scorer = match args.score.as_str() {
"plain" => ns_score_contract,
"pd" => ns_score_pd,
other => panic!("--score must be plain|pd, got {other:?}"),
};
let scored = score_boards(&contracts, &deals, vul, scorer);
let (mean, ci) = mean_with_ci(&scored.board_imps);
let n = on.boards.len();
let d = scored.divergent.len();
println!(
"ON {} vs OFF {} ({} boards, vul {vul}): {} fired ({:.2}%)",
on.our_label,
off.our_label,
n,
d,
100.0 * d as f64 / n.max(1) as f64,
);
println!(
"Delta (run − sit): {:+} IMPs, {:+.4} IMPs/board [95% CI ±{:.4}], {:+.3} IMPs/fired",
scored.total_imps,
mean,
ci,
scored.total_imps as f64 / d.max(1) as f64,
);
let mut swings = scored.swings.clone();
swings.sort_by_key(|&(_, _, imp)| imp);
let show = args.show.min(swings.len());
if show > 0 {
println!("--- {show} worst (for the feature) ---");
for &(i, _, imp) in swings.iter().take(show) {
let b = &on.boards[i];
println!(
"[{imp:+} IMP] {}\n on: {}\n off: {}",
b.deal, b.table_a, off.boards[i].table_a
);
}
}
}