use super::System;
use super::array::Logits;
use super::book::{Pair, Stance};
use super::context::relative;
use contract_bridge::auction::{Auction, Call};
use contract_bridge::{AbsoluteVulnerability, FullDeal, Hand, Seat};
#[derive(Clone, Debug)]
pub struct Table<N, E> {
north_south: N,
east_west: E,
dealer: Seat,
vul: AbsoluteVulnerability,
}
impl<N: System, E: System> Table<N, E> {
#[must_use]
pub const fn new(
north_south: N,
east_west: E,
dealer: Seat,
vul: AbsoluteVulnerability,
) -> Self {
Self {
north_south,
east_west,
dealer,
vul,
}
}
#[must_use]
pub const fn seat_to_act(&self, len: usize) -> Seat {
Seat::ALL[(self.dealer as usize + len) % 4]
}
#[must_use]
pub fn classify(&self, hand: Hand, auction: &[Call]) -> Option<Logits> {
let seat = self.seat_to_act(auction.len());
let vul = relative(self.vul, seat);
match seat {
Seat::North | Seat::South => self.north_south.classify(hand, vul, auction),
Seat::East | Seat::West => self.east_west.classify(hand, vul, auction),
}
}
#[must_use]
pub fn next_call(&self, hand: Hand, auction: &Auction) -> Call {
let Some(logits) = self.classify(hand, auction) else {
return Call::Pass;
};
let mut scored: Vec<(Call, f32)> = logits
.iter()
.map(|(call, &logit)| (call, logit))
.filter(|&(_, logit)| logit.is_finite())
.collect();
scored.sort_by(|a, b| b.1.partial_cmp(&a.1).expect("logits are never NaN"));
scored
.into_iter()
.map(|(call, _)| call)
.find(|&call| auction.can_push(call).is_ok())
.unwrap_or(Call::Pass)
}
#[must_use]
pub fn bid_out_from(&self, deal: &FullDeal, mut auction: Auction) -> Auction {
while !auction.has_ended() {
let seat = self.seat_to_act(auction.len());
auction.push(self.next_call(deal[seat], &auction));
}
auction
}
#[must_use]
pub fn bid_out(&self, deal: &FullDeal) -> Auction {
self.bid_out_from(deal, Auction::new())
}
}
impl Table<Stance, Stance> {
#[must_use]
pub fn of_pairs(ns: &Pair, ew: &Pair, dealer: Seat, vul: AbsoluteVulnerability) -> Self {
Self::new(ns.against(ew.family), ew.against(ns.family), dealer, vul)
}
}