diff options
author | Kjetil Orbekk <kj@orbekk.com> | 2023-01-01 20:34:09 -0500 |
---|---|---|
committer | Kjetil Orbekk <kj@orbekk.com> | 2023-01-01 20:34:09 -0500 |
commit | d3fbefad9cf25786fb5f28f96eeceb65d0a8b35b (patch) | |
tree | 156a23b5c04b93d746ecf592971aefbcc127cfd2 /protocol/src/contract.rs | |
parent | bb2ed3a2926384df063e476d10613fa310cd7ffa (diff) |
Split bridge_engine into a few separate modules
Diffstat (limited to 'protocol/src/contract.rs')
-rw-r--r-- | protocol/src/contract.rs | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/protocol/src/contract.rs b/protocol/src/contract.rs new file mode 100644 index 0000000..4c1d2f3 --- /dev/null +++ b/protocol/src/contract.rs @@ -0,0 +1,210 @@ +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<Self, <Self as std::str::FromStr>::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<Suit>, +} + +impl LevelAndSuit { + pub fn all_raises() -> Vec<LevelAndSuit> { + 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<Self> for LevelAndSuit { + fn partial_cmp(&self, o: &Self) -> Option<Ordering> { + 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<Self, <Self as std::str::FromStr>::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 }) + } +} |