pub mod array;
pub mod map;
pub mod trie;
pub use array::Array;
pub use map::Map;
pub use trie::Trie;
use core::ops::{Deref, Index};
use dds_bridge::contract::{Bid, Call, Penalty, Strain};
use dds_bridge::deal::Hand;
use thiserror::Error;
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Vulnerability: u8 {
const WE = 1;
const THEY = 2;
}
}
#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
pub enum IllegalCall {
#[error("Law 27: insufficient bid")]
InsufficientBid {
this: Bid,
last: Option<Bid>,
},
#[error("Law 36: inadmissible doubles and redoubles")]
InadmissibleDouble(Penalty),
#[error("Law 38: bid of more than seven")]
BidOfMoreThanSeven(Bid),
#[error("Law 39: call after the final pass")]
AfterFinalPass,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Auction(Vec<Call>);
impl Deref for Auction {
type Target = [Call];
fn deref(&self) -> &[Call] {
&self.0
}
}
impl From<Auction> for Vec<Call> {
fn from(auction: Auction) -> Self {
auction.0
}
}
impl Auction {
#[must_use]
pub const fn new() -> Self {
Self(Vec::new())
}
#[must_use]
pub fn has_ended(&self) -> bool {
self.len() >= 4 && self[self.len() - 3..] == [Call::Pass; 3]
}
fn can_double(&self) -> Result<(), IllegalCall> {
let admissible = self
.iter()
.rev()
.copied()
.enumerate()
.find(|&(_, call)| call != Call::Pass)
.is_some_and(|(index, call)| index & 1 == 0 && matches!(call, Call::Bid(_)));
if !admissible {
return Err(IllegalCall::InadmissibleDouble(Penalty::Doubled));
}
Ok(())
}
fn can_redouble(&self) -> Result<(), IllegalCall> {
let admissible = self
.iter()
.rev()
.copied()
.enumerate()
.find(|&(_, call)| call != Call::Pass)
.is_some_and(|(index, call)| index & 1 == 0 && call == Call::Double);
if !admissible {
return Err(IllegalCall::InadmissibleDouble(Penalty::Redoubled));
}
Ok(())
}
fn can_bid(&self, bid: Bid) -> Result<(), IllegalCall> {
if bid.level < 1 {
return Err(IllegalCall::InsufficientBid {
this: bid,
last: None,
});
}
if bid.level > 7 {
return Err(IllegalCall::BidOfMoreThanSeven(bid));
}
let last = self.iter().rev().find_map(|&call| match call {
Call::Bid(bid) => Some(bid),
_ => None,
});
if last >= Some(bid) {
return Err(IllegalCall::InsufficientBid { this: bid, last });
}
Ok(())
}
fn can_push(&self, call: Call) -> Result<(), IllegalCall> {
if self.has_ended() {
return Err(IllegalCall::AfterFinalPass);
}
match call {
Call::Pass => Ok(()),
Call::Double => self.can_double(),
Call::Redouble => self.can_redouble(),
Call::Bid(bid) => self.can_bid(bid),
}
}
pub fn try_push(&mut self, call: Call) -> Result<(), IllegalCall> {
self.can_push(call)?;
self.0.push(call);
Ok(())
}
pub fn force_push(&mut self, mut call: Call) -> Result<(), IllegalCall> {
if call == Call::Double && self.can_redouble().is_ok() {
call = Call::Redouble;
}
let report = self.can_push(call);
self.0.push(call);
report
}
pub fn try_extend(&mut self, iter: impl IntoIterator<Item = Call>) -> Result<(), IllegalCall> {
let iter = iter.into_iter();
if let Some(size) = iter.size_hint().1 {
self.0.reserve(size);
}
for call in iter {
self.try_push(call)?;
}
Ok(())
}
pub fn pop(&mut self) -> Option<Call> {
self.0.pop()
}
pub fn truncate(&mut self, len: usize) {
self.0.truncate(len);
}
#[must_use]
pub fn declarer(&self) -> Option<usize> {
let (parity, strain) =
self.iter()
.copied()
.enumerate()
.rev()
.find_map(|(index, call)| match call {
Call::Bid(bid) => Some((index & 1, bid.strain)),
_ => None,
})?;
self.iter()
.skip(parity)
.step_by(2)
.position(|call| match call {
Call::Bid(bid) => bid.strain == strain,
_ => false,
})
.map(|position| position << 1 | parity)
}
}
pub trait System: Index<Vulnerability, Output = Trie> {}
impl System for Trie {}
impl System for trie::Forest {}