use super::fallback_all_seats;
use super::uncontested;
use super::{Trie, call};
use crate::bidding::Rules;
use crate::bidding::constraint::{
balanced, described, fifths, hcp, len, partner_suit_is, points, support,
};
use crate::bidding::fallback::{Fallback, Undisturbed};
use contract_bridge::auction::Call;
use contract_bridge::{Bid, Level, Strain, Suit};
use std::cell::Cell;
use std::sync::Arc;
std::thread_local! {
static SECOND_SUIT_AGREEMENT: Cell<bool> = const { Cell::new(true) };
}
pub fn set_second_suit_agreement(on: bool) {
SECOND_SUIT_AGREEMENT.with(|cell| cell.set(on));
}
fn second_suit_agreement() -> bool {
SECOND_SUIT_AGREEMENT.with(Cell::get)
}
fn opener_rebid(major: Suit, resp: Suit) -> Rules {
let major_strain = Strain::from(major);
let resp_strain = Strain::from(resp);
let mut rules = Rules::new()
.rule(call(3, major_strain), 1.7, len(major, 6..) & points(15..))
.rule(call(3, resp_strain), 1.6, support(4..))
.rule(call(2, major_strain), 1.4, len(major, 6..))
.rule(
call(2, Strain::Notrump),
1.2,
balanced() & (fifths(12.0..15.0) | fifths(18.0..20.0)),
);
let other_suits: Vec<Suit> = [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades]
.into_iter()
.filter(|&x| x != major && x != resp)
.collect();
let mut two_level_weight = 1.0_f32;
for &x in &other_suits {
let x_strain = Strain::from(x);
if x_strain > resp_strain {
rules = rules.rule(call(2, x_strain), two_level_weight, len(x, 4..));
two_level_weight -= 0.05;
}
}
for &x in &other_suits {
let x_strain = Strain::from(x);
if x_strain < resp_strain {
rules = rules.rule(call(3, x_strain), 0.9, len(x, 4..));
}
}
rules.rule(call(2, major_strain), 0.3, len(major, 5..))
}
fn responder_rebid(major: Suit, resp: Suit) -> Rules {
let major_strain = Strain::from(major);
let resp_strain = Strain::from(resp);
let mut rules = Rules::new()
.rule(call(3, major_strain), 2.0, len(major, 3..))
.rule(call(3, resp_strain), 1.2, len(resp, 6..))
.rule(
call(4, major_strain),
1.0,
partner_suit_is(major) & len(major, 2..),
)
.rule(call(3, Strain::Notrump), 0.8, hcp(13..));
for &x in &[Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades] {
if x != major && x != resp {
let x_strain = Strain::from(x);
rules = rules.rule(call(3, x_strain), 1.4, partner_suit_is(x) & support(4..));
}
}
rules
}
fn opener_third(major: Suit) -> Rules {
let major_strain = Strain::from(major);
Rules::new()
.rule(call(4, Strain::Notrump), 1.0, points(15..))
.alert(super::slam::RKCB)
.rule(call(4, major_strain), 0.5, hcp(0..))
}
fn opener_third_agree(agreed: Suit) -> Rules {
let strain = Strain::from(agreed);
let rules = Rules::new()
.rule(call(4, Strain::Notrump), 1.0, points(15..))
.alert(super::slam::RKCB);
if matches!(agreed, Suit::Hearts | Suit::Spades) {
rules.rule(call(4, strain), 0.5, hcp(0..))
} else {
rules
.rule(call(3, Strain::Notrump), 0.5, hcp(0..))
.rule(call(5, strain), 0.3, hcp(0..))
}
}
fn opener_rebid_1d_2c() -> Rules {
Rules::new()
.rule(call(3, Strain::Clubs), 1.6, support(4..))
.rule(
call(2, Strain::Notrump),
1.2,
balanced() & (fifths(12.0..15.0) | fifths(18.0..20.0)),
)
.rule(call(2, Strain::Hearts), 1.0, len(Suit::Hearts, 4..))
.rule(call(2, Strain::Spades), 0.95, len(Suit::Spades, 4..))
.rule(call(2, Strain::Diamonds), 1.0, len(Suit::Diamonds, 6..))
.rule(call(2, Strain::Notrump), 0.2, hcp(0..))
}
fn responder_rebid_1d_2c() -> Rules {
Rules::new()
.rule(
call(3, Strain::Diamonds),
1.2,
partner_suit_is(Suit::Diamonds) & len(Suit::Diamonds, 4..),
)
.rule(call(3, Strain::Clubs), 1.1, len(Suit::Clubs, 6..))
.rule(call(3, Strain::Notrump), 0.8, hcp(13..))
}
fn game_backstop() -> Rules {
Rules::new()
.rule(
call(4, Strain::Hearts),
0.7,
described("our side bid ♥", |_, ctx| ctx.we_bid(Strain::Hearts))
& len(Suit::Hearts, 3..),
)
.rule(
call(4, Strain::Spades),
0.7,
described("our side bid ♠", |_, ctx| ctx.we_bid(Strain::Spades))
& len(Suit::Spades, 3..),
)
.rule(call(3, Strain::Notrump), 0.5, hcp(0..))
}
pub(super) fn register(book: &mut Trie) {
for &major in &[Suit::Spades, Suit::Hearts] {
for &resp in &[Suit::Clubs, Suit::Diamonds, Suit::Hearts] {
if Strain::from(resp) >= Strain::from(major) {
continue;
}
register_major(book, major, resp);
}
}
register_minor(book);
}
fn register_major(book: &mut Trie, major: Suit, resp: Suit) {
let major_strain = Strain::from(major);
let resp_strain = Strain::from(resp);
let opener_calls = &[call(1, major_strain), call(2, resp_strain)];
let rebid = opener_rebid(major, resp);
let rebid_calls: Vec<Call> = {
let mut seen = std::collections::HashSet::new();
rebid
.rules()
.iter()
.filter_map(|r| {
let c = r.call();
if seen.insert(c) { Some(c) } else { None }
})
.collect()
};
super::insert_uncontested(book, opener_calls, rebid);
let three_m_bid = Bid::new(3, major_strain);
for &r_call in &rebid_calls {
let resp_calls = &[call(1, major_strain), call(2, resp_strain), r_call];
super::insert_uncontested(book, resp_calls, responder_rebid(major, resp));
if let Call::Bid(r_bid) = r_call
&& r_bid < three_m_bid
{
let third_calls = &[
call(1, major_strain),
call(2, resp_strain),
r_call,
call(3, major_strain),
];
super::insert_uncontested(book, third_calls, opener_third(major));
super::slam::install_rkcb(book, third_calls, major);
}
if second_suit_agreement()
&& let Call::Bid(r_bid) = r_call
&& r_bid.level == Level::new(2)
&& let Ok(x) = Suit::try_from(r_bid.strain)
&& x != major
&& x != resp
{
let agree_calls = &[
call(1, major_strain),
call(2, resp_strain),
r_call,
call(3, Strain::from(x)),
];
super::insert_uncontested(book, agree_calls, opener_third_agree(x));
super::slam::install_rkcb(book, agree_calls, x);
}
}
let anchor = uncontested(opener_calls);
fallback_all_seats(
book,
&anchor,
2,
Arc::new(Undisturbed),
Fallback::classify(game_backstop()),
);
}
fn register_minor(book: &mut Trie) {
let opener_calls = &[call(1, Strain::Diamonds), call(2, Strain::Clubs)];
let rebid = opener_rebid_1d_2c();
let rebid_calls: Vec<Call> = {
let mut seen = std::collections::HashSet::new();
rebid
.rules()
.iter()
.filter_map(|r| {
let c = r.call();
if seen.insert(c) { Some(c) } else { None }
})
.collect()
};
super::insert_uncontested(book, opener_calls, rebid);
for &r_call in &rebid_calls {
let resp_calls = &[call(1, Strain::Diamonds), call(2, Strain::Clubs), r_call];
super::insert_uncontested(book, resp_calls, responder_rebid_1d_2c());
}
let anchor = uncontested(opener_calls);
fallback_all_seats(
book,
&anchor,
2,
Arc::new(Undisturbed),
Fallback::classify(game_backstop()),
);
}