use super::super::constraint::{
balanced, hcp, len, min_level_is, pred, short_in_their_suits, stopper_in_their_suits, support,
top_honors,
};
use super::super::context::Context;
use super::super::{Defensive, Rules};
use super::{call, insert_all_seats};
use contract_bridge::auction::Call;
use contract_bridge::{Bid, Hand, Strain, Suit};
#[must_use]
pub fn defense_to_suit(their_opening: Bid) -> Rules {
let theirs = their_opening.strain;
let t = theirs.suit().expect("their opening is always a suit bid");
let mut rules = Rules::new()
.rule(
Bid::new(1, Strain::Notrump),
1.5,
hcp(15..=18) & balanced() & stopper_in_their_suits(),
)
.rule(Call::Double, 1.3, hcp(12..) & short_in_their_suits())
.rule(Call::Double, 1.2, hcp(17..))
.rule(Call::Pass, 0.0, hcp(0..));
for suit in [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades] {
let strain = Strain::from(suit);
if strain != theirs {
let level = if strain > theirs { 1 } else { 2 };
let weight = if level == 1 { 1.4 } else { 1.0 };
rules = rules.rule(
Bid::new(level, strain),
weight,
len(suit, 5..) & hcp(8..=16),
);
}
}
rules = match t {
Suit::Clubs | Suit::Diamonds => rules.rule(
Bid::new(2, theirs),
2.0,
len(Suit::Hearts, 5..) & len(Suit::Spades, 5..) & hcp(8..),
),
Suit::Hearts => rules.rule(
Bid::new(2, theirs),
2.0,
len(Suit::Spades, 5..) & (len(Suit::Clubs, 5..) | len(Suit::Diamonds, 5..)) & hcp(8..),
),
Suit::Spades => rules.rule(
Bid::new(2, theirs),
2.0,
len(Suit::Hearts, 5..) & (len(Suit::Clubs, 5..) | len(Suit::Diamonds, 5..)) & hcp(8..),
),
};
match t {
Suit::Clubs => rules.rule(
Bid::new(2, Strain::Notrump),
1.9,
len(Suit::Diamonds, 5..) & len(Suit::Hearts, 5..) & hcp(8..),
),
Suit::Diamonds => rules.rule(
Bid::new(2, Strain::Notrump),
1.9,
len(Suit::Clubs, 5..) & len(Suit::Hearts, 5..) & hcp(8..),
),
Suit::Hearts | Suit::Spades => rules.rule(
Bid::new(2, Strain::Notrump),
1.9,
len(Suit::Clubs, 5..) & len(Suit::Diamonds, 5..) & hcp(8..),
),
}
}
#[must_use]
pub fn defense_to_weak_two(their_opening: Bid) -> Rules {
let theirs = their_opening.strain;
let level = their_opening.level.get();
let mut rules = Rules::new()
.rule(
Bid::new(2, Strain::Notrump),
1.5,
hcp(15..=18) & balanced() & stopper_in_their_suits(),
)
.rule(Call::Double, 1.3, hcp(12..) & short_in_their_suits())
.rule(Call::Double, 1.2, hcp(17..))
.rule(Call::Pass, 0.0, hcp(0..));
for suit in [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades] {
let strain = Strain::from(suit);
if strain != theirs {
let overcall_level = if strain > theirs { level } else { level + 1 };
rules = rules.rule(
Bid::new(overcall_level, strain),
1.0,
len(suit, 5..) & hcp(10..=16),
);
}
}
rules
}
pub fn defense_to_notrump() -> Rules {
let mut rules = Rules::new()
.rule(Call::Double, 1.3, hcp(15..) & balanced())
.rule(Call::Pass, 0.0, hcp(0..));
for suit in [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades] {
rules = rules.rule(
Bid::new(2, Strain::from(suit)),
1.0,
len(suit, 5..) & hcp(8..=14),
);
}
rules
}
pub fn advances(our_suit: Suit) -> Rules {
let s = Strain::from(our_suit);
Rules::new()
.rule(Bid::new(4, s), 1.6, support(5..) & hcp(..6))
.rule(Bid::new(3, s), 1.4, support(3..) & hcp(11..=12))
.rule(Bid::new(2, s), 1.0, support(3..) & hcp(6..=10))
.rule(Call::Pass, 0.0, hcp(..6))
}
#[must_use]
pub fn advance_double(their_opening: Bid) -> Rules {
let theirs = their_opening.strain;
let t = theirs.suit().expect("their opening is always a suit bid");
let level = their_opening.level.get();
let mut rules = Rules::new()
.rule(Call::Pass, 1.5, len(t, 4..) & top_honors(t, 2..) & hcp(6..))
.rule(
Bid::new(3, Strain::Notrump),
1.3,
hcp(13..) & stopper_in_their_suits(),
)
.rule(Bid::new(level, Strain::Notrump), 0.3, hcp(0..))
.rule(Call::Pass, 0.0, hcp(0..));
for suit in [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades] {
let strain = Strain::from(suit);
if strain == theirs {
continue;
}
let bid_level = if strain > theirs { level } else { level + 1 };
rules = rules.rule(Bid::new(bid_level, strain), 1.0, len(suit, 4..));
if matches!(suit, Suit::Hearts | Suit::Spades) {
rules = rules.rule(Bid::new(4, strain), 1.4, len(suit, 4..) & hcp(11..));
}
}
rules
}
fn michaels_advances(t: Suit) -> Rules {
match t {
Suit::Clubs | Suit::Diamonds => {
let hearts_longer = pred(|hand: Hand, _: &Context<'_>| {
hand[Suit::Hearts].len() >= hand[Suit::Spades].len()
});
let spades_longer = pred(|hand: Hand, _: &Context<'_>| {
hand[Suit::Spades].len() > hand[Suit::Hearts].len()
});
Rules::new()
.rule(
Bid::new(4, Strain::Hearts),
1.3,
hcp(10..) & len(Suit::Hearts, 3..) & hearts_longer.clone(),
)
.rule(
Bid::new(4, Strain::Spades),
1.3,
hcp(10..) & len(Suit::Spades, 3..) & spades_longer.clone(),
)
.rule(Bid::new(2, Strain::Hearts), 1.0, hearts_longer)
.rule(Bid::new(2, Strain::Spades), 1.0, spades_longer)
}
Suit::Hearts => Rules::new()
.rule(
Bid::new(4, Strain::Spades),
1.3,
hcp(10..) & len(Suit::Spades, 3..),
)
.rule(Bid::new(2, Strain::Spades), 0.5, hcp(0..)),
Suit::Spades => Rules::new()
.rule(
Bid::new(4, Strain::Hearts),
1.3,
hcp(10..) & len(Suit::Hearts, 3..),
)
.rule(Bid::new(3, Strain::Hearts), 0.5, hcp(0..)),
}
}
const fn unusual_suits(t: Suit) -> (Suit, Suit) {
match t {
Suit::Clubs => (Suit::Diamonds, Suit::Hearts),
Suit::Diamonds => (Suit::Clubs, Suit::Hearts),
Suit::Hearts | Suit::Spades => (Suit::Clubs, Suit::Diamonds),
}
}
fn unusual_nt_advances(t: Suit) -> Rules {
let (a, b) = unusual_suits(t);
let a_longer = pred(move |hand: Hand, _: &Context<'_>| hand[a].len() >= hand[b].len());
let b_longer = pred(move |hand: Hand, _: &Context<'_>| hand[b].len() > hand[a].len());
Rules::new()
.rule(Bid::new(3, Strain::from(a)), 1.0, a_longer)
.rule(Bid::new(3, Strain::from(b)), 1.0, b_longer)
}
fn responsive_doubles(t: Suit, _raise_lvl: u8) -> Rules {
let mut rules = if matches!(t, Suit::Hearts | Suit::Spades) {
Rules::new().rule(
Call::Double,
1.5,
len(Suit::Clubs, 4..) & len(Suit::Diamonds, 4..) & hcp(8..),
)
} else {
Rules::new().rule(
Call::Double,
1.5,
len(Suit::Hearts, 4..) & len(Suit::Spades, 4..) & hcp(8..),
)
};
rules = rules.rule(Call::Pass, 0.0, hcp(0..));
for suit in [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades] {
if suit == t {
continue;
}
let strain = Strain::from(suit);
for bid_lvl in 2u8..=3 {
rules = rules.rule(
Bid::new(bid_lvl, strain),
1.0,
min_level_is(bid_lvl, strain) & len(suit, 5..) & hcp(8..),
);
}
}
rules
}
#[must_use]
pub fn defensive() -> Defensive {
let mut d = Defensive::new();
for suit in [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades] {
let theirs = Strain::from(suit);
let opening = Bid::new(1, theirs);
insert_all_seats(&mut d, &[Call::Bid(opening)], 3, defense_to_suit(opening));
insert_all_seats(
&mut d,
&[Call::Bid(opening), Call::Double, Call::Pass],
3,
advance_double(opening),
);
for our in [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades] {
let strain = Strain::from(our);
if strain != theirs {
let level = if strain > theirs { 1 } else { 2 };
let overcall = call(level, strain);
insert_all_seats(
&mut d,
&[Call::Bid(opening), overcall, Call::Pass],
3,
advances(our),
);
}
}
let michaels_bid = call(2, theirs);
insert_all_seats(
&mut d,
&[Call::Bid(opening), michaels_bid, Call::Pass],
3,
michaels_advances(suit),
);
let unusual_bid = call(2, Strain::Notrump);
insert_all_seats(
&mut d,
&[Call::Bid(opening), unusual_bid, Call::Pass],
3,
unusual_nt_advances(suit),
);
for raise_lvl in [2u8, 3] {
let raise = call(raise_lvl, theirs);
insert_all_seats(
&mut d,
&[Call::Bid(opening), Call::Double, raise],
3,
responsive_doubles(suit, raise_lvl),
);
}
}
for suit in [Suit::Diamonds, Suit::Hearts, Suit::Spades] {
let theirs = Strain::from(suit);
let opening = Bid::new(2, theirs);
insert_all_seats(
&mut d,
&[Call::Bid(opening)],
3,
defense_to_weak_two(opening),
);
insert_all_seats(
&mut d,
&[Call::Bid(opening), Call::Double, Call::Pass],
3,
advance_double(opening),
);
}
insert_all_seats(&mut d, &[call(1, Strain::Notrump)], 3, defense_to_notrump());
d
}