use contract_bridge::auction::{Auction, Call};
use contract_bridge::{AbsoluteVulnerability, Bid, Contract, Penalty, Seat};
use ddss::TrickCountTable;
const fn seat_at(dealer: Seat, index: usize) -> Seat {
Seat::ALL[(dealer as usize + index) % 4]
}
#[must_use]
pub fn final_contract(auction: &Auction, dealer: Seat) -> Option<(Contract, Seat)> {
let mut last_bid: Option<Bid> = None;
let mut penalty = Penalty::Undoubled;
for &call in auction.iter() {
match call {
Call::Bid(bid) => {
last_bid = Some(bid);
penalty = Penalty::Undoubled;
}
Call::Double => penalty = Penalty::Doubled,
Call::Redouble => penalty = Penalty::Redoubled,
Call::Pass => {}
}
}
let bid = last_bid?;
let index = auction.declarer()?;
Some((Contract { bid, penalty }, seat_at(dealer, index)))
}
#[must_use]
pub fn ns_score(
result: Option<(Contract, Seat)>,
table: &TrickCountTable,
vul: AbsoluteVulnerability,
) -> i64 {
let Some((contract, declarer)) = result else {
return 0;
};
let tricks = u8::from(table[contract.bid.strain].get(declarer));
let declarer_vul = vul.contains(match declarer {
Seat::North | Seat::South => AbsoluteVulnerability::NS,
Seat::East | Seat::West => AbsoluteVulnerability::EW,
});
let score = i64::from(contract.score(tricks, declarer_vul));
match declarer {
Seat::North | Seat::South => score,
Seat::East | Seat::West => -score,
}
}
const IMP_BOUNDS: [i64; 24] = [
20, 50, 90, 130, 170, 220, 270, 320, 370, 430, 500, 600, 750, 900, 1100, 1300, 1500, 1750,
2000, 2250, 2500, 3000, 3500, 4000,
];
#[must_use]
pub fn imps(diff: i64) -> i64 {
let magnitude = IMP_BOUNDS
.iter()
.take_while(|&&bound| diff.abs() >= bound)
.count();
i64::try_from(magnitude).expect("at most 24 IMPs") * diff.signum()
}