use super::context::Context;
use super::trie::Classifier;
use contract_bridge::Bid;
use contract_bridge::auction::Call;
use core::fmt;
use std::sync::Arc;
pub trait Guard: Send + Sync {
fn admits(&self, context: &Context<'_>, suffix: &[Call]) -> bool;
}
impl fmt::Debug for dyn Guard {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Guard({:p})", &self)
}
}
impl<F> Guard for F
where
F: Fn(&Context<'_>, &[Call]) -> bool + Send + Sync,
{
fn admits(&self, context: &Context<'_>, suffix: &[Call]) -> bool {
self(context, suffix)
}
}
pub const fn guard<F>(f: F) -> F
where
F: Fn(&Context<'_>, &[Call]) -> bool + Send + Sync,
{
f
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Always;
impl Guard for Always {
fn admits(&self, _: &Context<'_>, _: &[Call]) -> bool {
true
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Undisturbed;
impl Guard for Undisturbed {
fn admits(&self, context: &Context<'_>, _: &[Call]) -> bool {
context.undisturbed()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FirstIs(
pub Call,
);
impl Guard for FirstIs {
fn admits(&self, _: &Context<'_>, suffix: &[Call]) -> bool {
suffix.first() == Some(&self.0)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct OvercallAtMost(
pub Bid,
);
impl Guard for OvercallAtMost {
fn admits(&self, _: &Context<'_>, suffix: &[Call]) -> bool {
matches!(*suffix, [Call::Bid(bid)] if bid <= self.0)
}
}
pub trait Rewrite: Send + Sync {
fn rewrite(&self, auction: &[Call], depth: usize) -> Option<Vec<Call>>;
}
impl fmt::Debug for dyn Rewrite {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Rewrite({:p})", &self)
}
}
impl<F> Rewrite for F
where
F: Fn(&[Call], usize) -> Option<Vec<Call>> + Send + Sync,
{
fn rewrite(&self, auction: &[Call], depth: usize) -> Option<Vec<Call>> {
self(auction, depth)
}
}
pub const fn rewriter<F>(f: F) -> F
where
F: Fn(&[Call], usize) -> Option<Vec<Call>> + Send + Sync,
{
f
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ReplaceNext(
pub Call,
);
impl Rewrite for ReplaceNext {
fn rewrite(&self, auction: &[Call], depth: usize) -> Option<Vec<Call>> {
(depth < auction.len()).then(|| {
let mut rewritten = auction.to_vec();
rewritten[depth] = self.0;
rewritten
})
}
}
#[derive(Clone, Debug)]
pub enum Fallback {
Classify(Arc<dyn Classifier>),
Rebase(Arc<dyn Rewrite>),
}
impl Fallback {
pub fn classify(classifier: impl Classifier + 'static) -> Self {
Self::Classify(Arc::new(classifier))
}
pub fn rebase(rewrite: impl Rewrite + 'static) -> Self {
Self::Rebase(Arc::new(rewrite))
}
}
#[cfg(test)]
mod tests {
use super::*;
use contract_bridge::Strain;
use contract_bridge::auction::RelativeVulnerability;
const fn bid(level: u8, strain: Strain) -> Call {
Call::Bid(Bid {
level: contract_bridge::Level::new(level),
strain,
})
}
fn empty_context() -> Context<'static> {
Context::new(RelativeVulnerability::NONE, &[])
}
#[test]
fn test_first_is() {
let guard = FirstIs(Call::Double);
let context = empty_context();
assert!(guard.admits(&context, &[Call::Double]));
assert!(guard.admits(&context, &[Call::Double, Call::Pass]));
assert!(!guard.admits(&context, &[Call::Pass, Call::Double]));
assert!(!guard.admits(&context, &[]));
}
#[test]
fn test_overcall_at_most() {
let guard = OvercallAtMost(Bid::new(2, Strain::Spades));
let context = empty_context();
assert!(guard.admits(&context, &[bid(1, Strain::Spades)]));
assert!(guard.admits(&context, &[bid(2, Strain::Spades)]));
assert!(!guard.admits(&context, &[bid(2, Strain::Notrump)]));
assert!(!guard.admits(&context, &[Call::Double]));
assert!(!guard.admits(&context, &[bid(1, Strain::Spades), Call::Pass]));
}
#[test]
fn test_replace_next() {
let rewrite = ReplaceNext(Call::Pass);
let auction = [bid(1, Strain::Notrump), Call::Double, Call::Pass];
assert_eq!(
rewrite.rewrite(&auction, 1),
Some(vec![bid(1, Strain::Notrump), Call::Pass, Call::Pass]),
);
assert_eq!(rewrite.rewrite(&auction, 3), None);
}
}