#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
use core::fmt::{self, Write as _};
use thiserror::Error;
pub mod contract;
pub mod deal;
pub mod solver;
pub use contract::{Bid, Contract, Level, Penalty};
pub use deal::{Card, Deal, Hand, Holding, Rank, Seat, SeatFlags};
pub use solver::Solver;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum Strain {
Clubs,
Diamonds,
Hearts,
Spades,
Notrump,
}
impl Strain {
#[must_use]
#[inline]
pub const fn is_minor(self) -> bool {
matches!(self, Self::Clubs | Self::Diamonds)
}
#[must_use]
#[inline]
pub const fn is_major(self) -> bool {
matches!(self, Self::Hearts | Self::Spades)
}
#[must_use]
#[inline]
pub const fn is_suit(self) -> bool {
!matches!(self, Self::Notrump)
}
#[must_use]
#[inline]
pub const fn is_notrump(self) -> bool {
matches!(self, Self::Notrump)
}
#[must_use]
#[inline]
pub const fn suit(self) -> Option<Suit> {
match self {
Self::Clubs => Some(Suit::Clubs),
Self::Diamonds => Some(Suit::Diamonds),
Self::Hearts => Some(Suit::Hearts),
Self::Spades => Some(Suit::Spades),
Self::Notrump => None,
}
}
#[must_use]
#[inline]
pub const fn letter(self) -> char {
match self {
Self::Clubs => 'C',
Self::Diamonds => 'D',
Self::Hearts => 'H',
Self::Spades => 'S',
Self::Notrump => 'N',
}
}
}
impl fmt::Display for Strain {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Clubs => f.write_char('♣'),
Self::Diamonds => f.write_char('♦'),
Self::Hearts => f.write_char('♥'),
Self::Spades => f.write_char('♠'),
Self::Notrump => f.write_str("NT"),
}
}
}
impl Strain {
pub const ASC: [Self; 5] = [
Self::Clubs,
Self::Diamonds,
Self::Hearts,
Self::Spades,
Self::Notrump,
];
pub const DESC: [Self; 5] = [
Self::Notrump,
Self::Spades,
Self::Hearts,
Self::Diamonds,
Self::Clubs,
];
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum Suit {
Clubs,
Diamonds,
Hearts,
Spades,
}
impl Suit {
pub const ASC: [Self; 4] = [Self::Clubs, Self::Diamonds, Self::Hearts, Self::Spades];
pub const DESC: [Self; 4] = [Self::Spades, Self::Hearts, Self::Diamonds, Self::Clubs];
#[must_use]
#[inline]
pub const fn letter(self) -> char {
match self {
Self::Clubs => 'C',
Self::Diamonds => 'D',
Self::Hearts => 'H',
Self::Spades => 'S',
}
}
}
impl fmt::Display for Suit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_char(match self {
Self::Clubs => '♣',
Self::Diamonds => '♦',
Self::Hearts => '♥',
Self::Spades => '♠',
})
}
}
impl From<Suit> for Strain {
fn from(suit: Suit) -> Self {
match suit {
Suit::Clubs => Self::Clubs,
Suit::Diamonds => Self::Diamonds,
Suit::Hearts => Self::Hearts,
Suit::Spades => Self::Spades,
}
}
}
#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
#[error("Notrump is not a suit")]
pub struct SuitFromNotrumpError;
impl TryFrom<Strain> for Suit {
type Error = SuitFromNotrumpError;
fn try_from(strain: Strain) -> Result<Self, Self::Error> {
match strain {
Strain::Clubs => Ok(Self::Clubs),
Strain::Diamonds => Ok(Self::Diamonds),
Strain::Hearts => Ok(Self::Hearts),
Strain::Spades => Ok(Self::Spades),
Strain::Notrump => Err(SuitFromNotrumpError),
}
}
}