summaryrefslogtreecommitdiff
path: root/protocol/src/contract.rs
diff options
context:
space:
mode:
authorKjetil Orbekk <kj@orbekk.com>2023-01-01 20:34:09 -0500
committerKjetil Orbekk <kj@orbekk.com>2023-01-01 20:34:09 -0500
commitd3fbefad9cf25786fb5f28f96eeceb65d0a8b35b (patch)
tree156a23b5c04b93d746ecf592971aefbcc127cfd2 /protocol/src/contract.rs
parentbb2ed3a2926384df063e476d10613fa310cd7ffa (diff)
Split bridge_engine into a few separate modules
Diffstat (limited to 'protocol/src/contract.rs')
-rw-r--r--protocol/src/contract.rs210
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 })
+ }
+}