use clap::Parser;
use contract_bridge::deck::full_deal;
use contract_bridge::{AbsoluteVulnerability, FullDeal, Seat};
use ddss::{NonEmptyStrainFlags, Solver};
use pons::bidding::Family;
use pons::bidding::american::american_constructive_floor;
use pons::bidding::neural_floor::NeuralFloorSearch;
use pons::bidding::search_floor::SearchFloor;
use pons::instinct;
use pons::scoring::{final_contract, imps, ns_score_contract};
use rayon::prelude::*;
#[path = "../common/mod.rs"]
#[allow(dead_code)]
mod common;
use common::bid_uncontested;
#[derive(Parser)]
struct Args {
#[arg(short, long, default_value = "200")]
count: usize,
#[arg(short, long, default_value = "none")]
vulnerability: AbsoluteVulnerability,
#[arg(short, long, default_value = "128")]
layouts: usize,
#[arg(short, long, default_value = "8")]
shortlist: usize,
}
#[allow(clippy::cast_precision_loss)]
fn main() {
let args = Args::parse();
let mut rng = rand::rng();
let names = ["instinct", "search", "neural"];
let stances = [
american_constructive_floor(instinct()).against(Family::NATURAL),
american_constructive_floor(SearchFloor {
layouts: args.layouts,
shortlist: args.shortlist,
..SearchFloor::default()
})
.against(Family::NATURAL),
american_constructive_floor(NeuralFloorSearch).against(Family::NATURAL),
];
let deals: Vec<FullDeal> = (0..args.count).map(|_| full_deal(&mut rng)).collect();
let vul = args.vulnerability;
let contracts: Vec<[_; 3]> = deals
.par_iter()
.enumerate()
.map(|(index, deal)| {
let dealer = Seat::ALL[index % 4];
std::array::from_fn(|arm| {
let auction = bid_uncontested(&stances[arm], dealer, vul, deal);
final_contract(&auction, dealer)
})
})
.collect();
let divergent: Vec<usize> = (0..args.count)
.filter(|&i| {
let c = &contracts[i];
c[0] != c[1] || c[1] != c[2]
})
.collect();
let solve_deals: Vec<FullDeal> = divergent.iter().map(|&i| deals[i]).collect();
let tables = Solver::lock().solve_deals(&solve_deals, NonEmptyStrainFlags::ALL);
let pairings = [(0, 1), (0, 2), (1, 2)];
let mut points = [0i64; 3];
let mut imp_totals = [0i64; 3];
for (&i, table) in divergent.iter().zip(tables.iter()) {
let scores: [i64; 3] = std::array::from_fn(|arm| {
ns_score_contract(contracts[i][arm], table, args.vulnerability)
});
for (p, &(x, y)) in pairings.iter().enumerate() {
let swing = scores[x] - scores[y];
points[p] += swing;
imp_totals[p] += imps(swing);
}
}
println!(
"=== Constructive-only floor A/B/C: {} boards, vulnerability {} ===",
args.count, args.vulnerability,
);
println!("(opponents silenced — every auction is constructive)");
println!(
"SearchFloor knobs: layouts={}, shortlist={}",
args.layouts, args.shortlist,
);
println!(
"Divergent boards: {} of {} ({:.0}%)",
divergent.len(),
args.count,
100.0 * divergent.len() as f64 / args.count.max(1) as f64,
);
println!(
"\n {:>8} vs {:<8} points IMPs IMPs/board",
"first", "second"
);
for (p, &(x, y)) in pairings.iter().enumerate() {
println!(
" {:>8} vs {:<8} {:+6} {:+6} {:+.3}",
names[x],
names[y],
points[p],
imp_totals[p],
imp_totals[p] as f64 / args.count.max(1) as f64,
);
}
println!("\n(a positive row favors the first-named floor)");
}