use core::fmt; use std::{str::FromStr, cmp::Ordering}; use anyhow::anyhow; use regex::Regex; use serde::{Serialize, Deserialize}; use strum::IntoEnumIterator; use strum_macros::EnumIter; use crate::{core::Player, card::Suit}; #[derive( PartialEq, Eq, PartialOrd, Ord, Clone, Copy, EnumIter, Serialize, Deserialize, )] pub enum ContractLevel { One = 1, Two, Three, Four, Five, Six, Seven, } impl fmt::Display for ContractLevel { fn fmt( &self, f: &mut std::fmt::Formatter<'_>, ) -> std::result::Result<(), std::fmt::Error> { write!(f, "{}", *self as u8) } } impl fmt::Debug for ContractLevel { fn fmt( &self, f: &mut std::fmt::Formatter<'_>, ) -> std::result::Result<(), std::fmt::Error> { write!(f, "{}", self) } } impl FromStr for ContractLevel { type Err = anyhow::Error; fn from_str( s: &str, ) -> std::result::Result::Err> { match s { "1" => Ok(ContractLevel::One), "2" => Ok(ContractLevel::Two), "3" => Ok(ContractLevel::Three), "4" => Ok(ContractLevel::Four), "5" => Ok(ContractLevel::Five), "6" => Ok(ContractLevel::Six), "7" => Ok(ContractLevel::Seven), _ => Err(anyhow!("invalid string: {}", s)), } } } #[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub enum ContractModifier { None, Doubled, Redoubled, } impl fmt::Display for ContractModifier { fn fmt( &self, f: &mut fmt::Formatter, ) -> std::result::Result<(), std::fmt::Error> { match self { ContractModifier::None => Ok(()), ContractModifier::Doubled => write!(f, "x"), ContractModifier::Redoubled => write!(f, "xx"), } } } #[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub struct Contract { pub declarer: Player, pub highest_bid: LevelAndSuit, pub modifier: ContractModifier, } impl Contract { pub fn dummy(&self) -> Player { self.declarer.many_next(2) } pub fn leader(&self) -> Player { self.declarer.many_next(3) } } impl fmt::Display for Contract { fn fmt( &self, f: &mut fmt::Formatter<'_>, ) -> std::result::Result<(), fmt::Error> { write!( f, "{}{}{}", self.highest_bid, self.declarer.short_str(), self.modifier ) } } #[derive(PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] pub struct LevelAndSuit { pub level: ContractLevel, pub suit: Option, } impl LevelAndSuit { pub fn all_raises() -> Vec { let mut result = Vec::with_capacity(7 * 5); for level in ContractLevel::iter() { for suit in Suit::iter() { result.push(LevelAndSuit { level, suit: Some(suit), }); } result.push(LevelAndSuit { level, suit: None }); } result } } impl PartialOrd for LevelAndSuit { fn partial_cmp(&self, o: &Self) -> Option { if self.level != o.level { return self.level.partial_cmp(&o.level); } if self.suit != o.suit { if self.suit.is_none() { return Some(Ordering::Greater); } if o.suit.is_none() { return Some(Ordering::Less); } return self.suit.partial_cmp(&o.suit); } Some(Ordering::Equal) } } impl Ord for LevelAndSuit { fn cmp(&self, o: &Self) -> std::cmp::Ordering { self.partial_cmp(o).unwrap() } } impl fmt::Display for LevelAndSuit { fn fmt( &self, f: &mut std::fmt::Formatter<'_>, ) -> std::result::Result<(), std::fmt::Error> { write!( f, "{}{}", self.level, self.suit .map_or("NT".to_string(), |suit| format!("{}", suit)) ) } } impl fmt::Debug for LevelAndSuit { fn fmt( &self, f: &mut std::fmt::Formatter<'_>, ) -> std::result::Result<(), std::fmt::Error> { write!(f, "{}", self) } } impl FromStr for LevelAndSuit { type Err = anyhow::Error; fn from_str( s: &str, ) -> std::result::Result::Err> { lazy_static::lazy_static! { static ref RE: Regex = Regex::new(r#"\s*(.[0-9]*)\s*(.*)"#).unwrap(); }; let caps = RE .captures(s) .ok_or_else(|| anyhow!("invalid raise: {}", s))?; let level = caps[1].parse()?; let suit = match caps[2].to_ascii_uppercase().as_str() { "NT" => None, x => Some(x.parse()?), }; Ok(LevelAndSuit { level, suit }) } }