use super::fallback::{Always, Fallback, Guard};
use super::instinct::instinct;
use super::trie::Classifier;
use super::{Constructive, Family, Pair, Trie};
use contract_bridge::auction::Call;
use contract_bridge::{Bid, Strain};
use std::sync::Arc;
mod competition;
mod defense;
mod game_force;
mod notrump;
mod openings;
mod raises;
mod rebids;
mod responses;
pub(in crate::bidding) mod slam;
mod strong_two;
mod weak_twos;
mod xyz;
pub use competition::{
Competitive4333, DoubleStyle, LebensohlStyle, NegativeDoubleShape, competition,
set_competition_over_diamond_transfer, set_competition_over_minor_transfer,
set_competition_over_stayman, set_competition_over_transfer, set_competitive_4333,
set_cue_minor_raise_answer, set_cue_raise_answer, set_defense_to_2d_multi, set_delayed_cue,
set_direct_3nt_stopper, set_double_override, set_double_style, set_free_bid_floor,
set_free_bids, set_high_overcall_responses, set_jordan_truscott, set_lebensohl,
set_lebensohl_style, set_major_support_double, set_natural_floor, set_negative_double_shape,
set_penalty_double_leave_in, set_penalty_pass, set_splinter_doubled,
set_strong_two_competition, set_trap_pass, set_uvu, set_uvu_cue_floor, set_uvu_natural_floor,
set_uvu_over_majors, set_uvu_x_floor, set_weak_two_competition,
};
pub(crate) use competition::uvu_over_majors;
pub use defense::{
DoubleShape, NotrumpDefense, TakeoutSupport, advance_double, defense_to_suit,
defense_to_weak_two, set_advance_rubens, set_advance_sohl_style, set_always_pass_defense,
set_diamond_transfer_defense, set_direct_dont, set_direct_dont_four_four,
set_direct_dont_one_suiter_min, set_direct_dont_x_floor, set_direct_landy_double,
set_direct_landy_double_floor, set_direct_landy_penalty_pass, set_doubled_landy_escape,
set_landy, set_landy_hcp, set_leaping_michaels, set_meckwell, set_meckwell_minor_major_44,
set_meckwell_x_floor, set_meckwell_x_four_four, set_minor_transfer_defense,
set_natural_defense, set_natural_double_floor, set_natural_double_shape,
set_natural_double_weight, set_natural_overcall_points, set_notrump_balancing,
set_notrump_defense, set_nt_overcall_gladiator, set_nt_overcall_no_major,
set_nt_overcall_systems_on, set_overcall_discipline, set_passed_hand_overcall,
set_responsive_overcall, set_responsive_takeout, set_rich_advance_double, set_stayman_defense,
set_stayman_defense_overcall, set_takeout_support, set_transfer_defense,
set_two_level_minor_overcall_tight, set_unusual_notrump_defense, set_woolsey,
set_woolsey_double_floor, set_woolsey_points,
};
pub(crate) use defense::{
direct_dont_enabled, direct_landy_double, landy_range, meckwell_enabled,
natural_defense_enabled, natural_double_floor, natural_overcall_points, nt_overcall_gladiator,
nt_overcall_systems_on, woolsey_double_floor, woolsey_enabled, woolsey_points,
};
pub use game_force::set_second_suit_agreement;
pub use notrump::{
EUROPEAN, PUPPET, notrump_responses, set_crawling_stayman, set_garbage_stayman,
set_invitational_5card_majors, set_long_minor_force, set_minor_min_to_3nt, set_notrump_minors,
set_sixcard_accept_floor, set_sixcard_invite_floor, set_stayman_5card_max,
set_stayman_both_majors, set_stayman_cue_continuation, set_stayman_minor_slam_try,
set_texas_game_floor, set_texas_slam_drive, set_transfer_gf_hearts, set_transfer_gf_majors,
set_transfer_longer_major, set_transfer_slam_try, set_transfer_super_accept,
};
pub(crate) use notrump::{crawling_stayman, garbage_stayman, notrump_minors};
pub(crate) use openings::rule_of_20_enabled;
pub use openings::{
NotrumpShape, openings, openings_with, set_one_notrump_fifths, set_open_one_notrump,
set_rule_of_20,
};
pub use raises::{set_limit_raise_acceptance, set_major_game_tries};
pub(crate) use rebids::{opener_extras_ladder, opener_major_jump_rebid};
pub use rebids::{
set_balanced_1nt_rebid, set_fourth_suit_forcing, set_major_rebid_tails, set_meckstroth_adjunct,
set_opener_extras_ladder, set_opener_major_jump_rebid,
};
pub(crate) use responses::longer_major_response;
pub use responses::{major_responses, minor_responses, set_longer_major_response, set_up_the_line};
pub use xyz::set_xyz;
const fn call(level: u8, strain: Strain) -> Call {
Call::Bid(Bid::new(level, strain))
}
fn insert_all_seats(
book: &mut Trie,
suffix: &[Call],
max_passes: usize,
rules: impl Classifier + 'static,
) {
let shared: Arc<dyn Classifier> = Arc::new(rules);
for n in 0..=max_passes {
let key: Vec<Call> = core::iter::repeat_n(Call::Pass, n)
.chain(suffix.iter().copied())
.collect();
book.insert_arc(&key, Arc::clone(&shared));
}
}
fn uncontested(our_calls: &[Call]) -> Vec<Call> {
our_calls
.iter()
.flat_map(|&call| [call, Call::Pass])
.collect()
}
fn insert_uncontested(book: &mut Trie, our_calls: &[Call], rules: impl Classifier + 'static) {
insert_all_seats(book, &uncontested(our_calls), 3, rules);
}
#[allow(clippy::needless_pass_by_value)]
fn fallback_all_seats(
book: &mut Trie,
suffix: &[Call],
max_passes: usize,
guard: Arc<dyn Guard>,
fallback: Fallback,
) {
for n in 0..=max_passes {
let key: Vec<Call> = core::iter::repeat_n(Call::Pass, n)
.chain(suffix.iter().copied())
.collect();
book.fallback_arc_at(&key, Arc::clone(&guard), fallback.clone());
}
}
#[must_use]
pub fn american() -> Pair {
with_instinct_floor(bare_american())
}
#[must_use]
pub fn american_classic() -> Pair {
with_instinct_floor(bare_american_with(NotrumpShape::Balanced))
}
#[must_use]
pub fn american_wide_6322() -> Pair {
with_instinct_floor(bare_american_with(NotrumpShape::Wide6322))
}
#[cfg(feature = "neural-floor")]
#[must_use]
pub fn american_neural() -> Pair {
with_floor(bare_american(), super::neural_floor::NeuralFloor)
}
#[cfg(feature = "neural-floor")]
#[must_use]
pub fn american_neural_v2() -> Pair {
with_floor(bare_american(), super::neural_floor::NeuralFloorV2)
}
#[cfg(feature = "neural-floor")]
#[must_use]
pub fn american_neural_v3() -> Pair {
with_floor(bare_american(), super::neural_floor::NeuralFloorV3)
}
#[cfg(feature = "neural-floor")]
#[must_use]
pub fn american_neural_search() -> Pair {
with_floor(bare_american(), super::neural_floor::NeuralFloorSearch)
}
#[cfg(feature = "search")]
#[must_use]
pub fn american_search() -> Pair {
american_search_with(super::search_floor::SearchFloor::default())
}
#[cfg(feature = "search")]
#[must_use]
pub fn american_search_with(floor: super::search_floor::SearchFloor) -> Pair {
with_floor(bare_american(), floor)
}
#[cfg(feature = "neural-floor")]
#[must_use]
pub fn american_constructive_floor<C: Classifier + 'static>(floor: C) -> Pair {
let mut pair = bare_american();
pair.constructive
.fallback_at(&[], Always, Fallback::classify(floor));
pair
}
fn with_floor<C: Classifier + 'static>(mut pair: Pair, floor: C) -> Pair {
let floor = Fallback::classify(floor);
pair.competitive.fallback_at(&[], Always, floor.clone());
pair.defensive.fallback_at(&[], Always, floor);
pair.constructive
.fallback_at(&[], Always, Fallback::classify(instinct()));
pair
}
fn with_instinct_floor(pair: Pair) -> Pair {
with_floor(pair, instinct())
}
#[must_use]
pub fn bare_american() -> Pair {
bare_american_with(NotrumpShape::Wide)
}
#[must_use]
fn bare_american_with(shape: NotrumpShape) -> Pair {
let mut c = Constructive::new();
openings::register(&mut c, shape);
responses::register(&mut c);
notrump::register(&mut c);
rebids::register(&mut c);
xyz::register(&mut c);
game_force::register(&mut c);
raises::register(&mut c);
strong_two::register(&mut c);
weak_twos::register(&mut c);
Pair::new(
Family::NATURAL,
c,
competition::competition(),
defense::defensive(),
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bidding::Rules;
use crate::bidding::context::Context;
use contract_bridge::auction::RelativeVulnerability;
use contract_bridge::{Hand, Suit};
fn best(rules: &Rules, auction: &[Call], hand: &str) -> Call {
let hand: Hand = hand.parse().expect("valid test hand");
let context = Context::new(RelativeVulnerability::NONE, auction);
let logits = rules.classify(hand, &context);
(&logits.0)
.into_iter()
.max_by(|(_, a), (_, b)| a.partial_cmp(b).expect("logits are never NaN"))
.map(|(call, _)| call)
.expect("array is never empty")
}
#[test]
fn openings_pick_the_descriptive_bid() {
let o = openings();
assert_eq!(best(&o, &[], "AQ32.K53.QJ4.A92"), call(1, Strain::Notrump));
assert_eq!(best(&o, &[], "AKQ2.AKJ.KQ4.932"), call(2, Strain::Clubs));
assert_eq!(best(&o, &[], "A2.KQJ53.Q42.J92"), call(1, Strain::Hearts));
assert_eq!(best(&o, &[], "KQJ732.53.842.92"), call(2, Strain::Spades));
}
#[test]
fn openings_suppress_weak_twos_in_fourth_seat() {
let o = openings();
assert_eq!(best(&o, &[], "KQJ732.53.842.92"), call(2, Strain::Spades));
assert_eq!(best(&o, &[Call::Pass; 3], "KQJ732.53.842.92"), Call::Pass,);
}
#[test]
fn major_responses_run_the_2_over_1_ladder() {
let r = major_responses(Suit::Hearts);
let a = [call(1, Strain::Hearts), Call::Pass];
assert_eq!(best(&r, &a, "K2.KQ54.A964.Q92"), call(2, Strain::Notrump));
assert_eq!(best(&r, &a, "Q32.J53.A964.Q92"), call(2, Strain::Hearts));
assert_eq!(best(&r, &a, "A2.K3.Q543.KJ85"), call(2, Strain::Clubs));
}
#[test]
fn notrump_responses_transfer_and_stayman() {
let r = notrump_responses();
let a = [call(1, Strain::Notrump), Call::Pass];
assert_eq!(best(&r, &a, "KJ542.Q32.K43.92"), call(2, Strain::Hearts));
assert_eq!(best(&r, &a, "KJ54.KQ32.43.Q92"), call(2, Strain::Clubs));
}
#[test]
fn defense_doubles_with_strength() {
let r = defense_to_suit(Bid::new(1, Strain::Diamonds));
let a = [call(1, Strain::Diamonds)];
assert_eq!(best(&r, &a, "A.Q6.KJ852.AKJ42"), Call::Double);
assert_eq!(best(&r, &a, "AQJ32.853.42.K92"), call(1, Strain::Spades));
}
}