use super::{call, insert_uncontested};
use crate::bidding::constraint::{balanced, len, points};
use crate::bidding::{Alert, Rules, Trie};
use contract_bridge::auction::Call;
use contract_bridge::{Bid, Strain, Suit};
use std::cell::Cell;
const XYZ_RELAY: Alert = Alert("xyz-relay");
const XYZ_FORCE: Alert = Alert("xyz-game-force");
const XYZ_COMPLETION: Alert = Alert("xyz-completion");
std::thread_local! {
static XYZ: Cell<bool> = const { Cell::new(true) };
}
pub fn set_xyz(on: bool) {
XYZ.with(|cell| cell.set(on));
}
fn xyz() -> bool {
XYZ.with(Cell::get)
}
fn xyz_responder(response: Suit, rebid: Strain) -> Rules {
let mut rules = Rules::new()
.rule(
Bid::new(2, Strain::Clubs),
1.5,
points(10..=12) | (len(Suit::Diamonds, 6..) & points(..=9)),
)
.alert(XYZ_RELAY)
.rule(Bid::new(2, Strain::Diamonds), 1.4, points(13..))
.alert(XYZ_FORCE);
if let Some(second) = rebid.suit() {
rules = rules.rule(Bid::new(2, rebid), 1.15, len(second, 4..) & points(6..=9));
}
if response != Suit::Diamonds {
rules = rules.rule(
Bid::new(2, Strain::from(response)),
1.1,
len(response, 5..) & points(..=9),
);
}
if response == Suit::Spades && rebid == Strain::Notrump {
rules = rules.rule(
Bid::new(2, Strain::Hearts),
1.05,
len(Suit::Hearts, 4..) & points(..=9),
);
}
rules.rule(Call::Pass, 0.0, points(..=9))
}
fn xyz_completion() -> Rules {
Rules::new()
.rule(Bid::new(2, Strain::Diamonds), 0.0, points(0..))
.alert(XYZ_COMPLETION)
}
fn xyz_after_relay(opening: Suit, response: Suit, rebid: Strain) -> Rules {
let mut rules = Rules::new();
if let Some(second) = rebid.suit() {
rules = rules.rule(Bid::new(2, rebid), 1.3, len(second, 4..) & points(10..=12));
}
if response != Suit::Diamonds {
rules = rules.rule(
Bid::new(2, Strain::from(response)),
1.2,
len(response, 5..) & points(10..=12),
);
}
if response == Suit::Spades && rebid == Strain::Notrump {
rules = rules.rule(
Bid::new(2, Strain::Hearts),
1.1,
len(Suit::Hearts, 4..) & points(10..=12),
);
}
for minor in [Suit::Clubs, Suit::Diamonds] {
let long = if minor == opening { 5 } else { 6 };
rules = rules.rule(
Bid::new(3, Strain::from(minor)),
1.0,
len(minor, long..) & points(10..=12),
);
}
rules
.rule(Bid::new(2, Strain::Notrump), 0.2, points(10..))
.rule(Call::Pass, 0.0, points(..=9))
}
fn accept_or_decline(game: Bid) -> Rules {
Rules::new()
.rule(game, 1.0, points(14..))
.rule(Call::Pass, 0.0, points(0..))
}
fn xyz_gf_answers(opening: Suit, response: Suit, rebid: Strain) -> Rules {
let mut rules = Rules::new();
if response != Suit::Diamonds {
rules = rules.rule(Bid::new(2, Strain::from(response)), 1.3, len(response, 3..));
}
if rebid == Strain::Hearts {
rules = rules.rule(Bid::new(2, Strain::Spades), 1.2, len(Suit::Spades, 4..));
}
if opening == Suit::Hearts {
rules = rules.rule(Bid::new(2, Strain::Hearts), 1.2, len(Suit::Hearts, 6..));
}
if response == Suit::Diamonds {
rules = rules.rule(Bid::new(3, Strain::Diamonds), 1.1, len(Suit::Diamonds, 4..));
}
if opening != Suit::Hearts {
rules = rules.rule(Bid::new(3, Strain::from(opening)), 0.8, len(opening, 6..));
}
rules
.rule(
Bid::new(2, Strain::Notrump),
1.0,
points(12..=14) & balanced(),
)
.rule(Bid::new(2, Strain::Notrump), 0.1, points(0..))
}
fn register_prefix(book: &mut Trie, opening: Suit, response: Suit, rebid: Strain) {
let prefix = [
call(1, Strain::from(opening)),
call(1, Strain::from(response)),
call(1, rebid),
];
let two_c = call(2, Strain::Clubs);
let two_d = call(2, Strain::Diamonds);
insert_uncontested(book, &prefix, xyz_responder(response, rebid));
insert_uncontested(
book,
&[prefix[0], prefix[1], prefix[2], two_c],
xyz_completion(),
);
insert_uncontested(
book,
&[prefix[0], prefix[1], prefix[2], two_d],
xyz_gf_answers(opening, response, rebid),
);
let relay = [prefix[0], prefix[1], prefix[2], two_c, two_d];
insert_uncontested(book, &relay, xyz_after_relay(opening, response, rebid));
let mut accept = |invite: Call, table: Rules| {
let key = [relay[0], relay[1], relay[2], relay[3], relay[4], invite];
insert_uncontested(book, &key, table);
};
if rebid.suit().is_some() {
accept(call(2, rebid), accept_or_decline(Bid::new(4, rebid)));
}
if response != Suit::Diamonds {
let major = Strain::from(response);
accept(
call(2, major),
Rules::new()
.rule(Bid::new(4, major), 1.2, len(response, 3..) & points(14..))
.rule(Bid::new(3, Strain::Notrump), 1.0, points(14..))
.rule(Call::Pass, 0.0, points(0..)),
);
}
if response == Suit::Spades && rebid == Strain::Notrump {
accept(
call(2, Strain::Hearts),
Rules::new()
.rule(
Bid::new(4, Strain::Spades),
1.3,
len(Suit::Spades, 3..) & points(14..),
)
.rule(
Bid::new(4, Strain::Hearts),
1.2,
len(Suit::Hearts, 4..) & points(14..),
)
.rule(Bid::new(3, Strain::Notrump), 1.0, points(14..))
.rule(Call::Pass, 0.0, points(0..)),
);
}
for minor in [Suit::Clubs, Suit::Diamonds] {
accept(
call(3, Strain::from(minor)),
accept_or_decline(Bid::new(3, Strain::Notrump)),
);
}
accept(
call(2, Strain::Notrump),
accept_or_decline(Bid::new(3, Strain::Notrump)),
);
}
pub(super) fn register(book: &mut Trie) {
if !xyz() {
return;
}
for opening in [Suit::Clubs, Suit::Diamonds, Suit::Hearts] {
for response in [Suit::Diamonds, Suit::Hearts, Suit::Spades] {
if Strain::from(response) <= Strain::from(opening) {
continue;
}
for higher in Suit::ASC {
if Strain::from(higher) > Strain::from(response) {
register_prefix(book, opening, response, Strain::from(higher));
}
}
register_prefix(book, opening, response, Strain::Notrump);
}
}
}