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/bridge_engine.rs | |
parent | bb2ed3a2926384df063e476d10613fa310cd7ffa (diff) |
Split bridge_engine into a few separate modules
Diffstat (limited to 'protocol/src/bridge_engine.rs')
-rw-r--r-- | protocol/src/bridge_engine.rs | 406 |
1 files changed, 17 insertions, 389 deletions
diff --git a/protocol/src/bridge_engine.rs b/protocol/src/bridge_engine.rs index d04bdf0..74a8262 100644 --- a/protocol/src/bridge_engine.rs +++ b/protocol/src/bridge_engine.rs @@ -1,90 +1,16 @@ use crate::{ - card::{make_deck, sort_cards, Card, RankOrder, Suit}, + card::{Card, Suit}, move_result::MoveResult, + core::{Player, Deal}, actions::Bid, contract::{LevelAndSuit, Contract, ContractModifier} }; use anyhow::{anyhow, bail}; use log::info; -use rand::{ - distributions::Standard, - prelude::{Distribution, SliceRandom}, -}; -use regex::Regex; use serde::{Deserialize, Serialize}; -use std::{cmp::Ordering, borrow::Cow}; -use std::fmt; -use std::str::FromStr; -use strum::{EnumCount, IntoEnumIterator}; -use strum_macros::{EnumCount as EnumCountMacro, EnumIter, FromRepr}; +use std::borrow::Cow; pub const SUIT_DISPLAY_ORDER: [Suit; 4] = [Suit::Diamond, Suit::Club, Suit::Heart, Suit::Spade]; -#[derive( - PartialEq, - Eq, - Clone, - Copy, - Debug, - FromRepr, - EnumCountMacro, - Serialize, - Deserialize, - EnumIter, -)] -#[repr(u8)] -pub enum Player { - West = 0, - North, - East, - South, -} - -impl Player { - pub fn next(&self) -> Self { - self.many_next(1) - } - - pub fn many_next(self, i: usize) -> Self { - Player::from_repr(((self as usize + i) % Player::COUNT) as u8).unwrap() - } - - pub fn short_str(&self) -> &str { - match self { - Self::West => "W", - Self::North => "N", - Self::East => "E", - Self::South => "W", - } - } - - pub fn get_cards<'a>(&self, deal: &'a Deal) -> &'a Vec<Card> { - match self { - Self::West => &deal.west, - Self::North => &deal.north, - Self::East => &deal.east, - Self::South => &deal.south, - } - } - - pub fn get_cards_mut<'a>(&self, deal: &'a mut Deal) -> &'a mut Vec<Card> { - match self { - Self::West => &mut deal.west, - Self::North => &mut deal.north, - Self::East => &mut deal.east, - Self::South => &mut deal.south, - } - } -} - -impl Distribution<Player> for Standard { - fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Player { - let min = Player::West as u8; - let max = Player::South as u8; - let v = rng.gen_range(min..=max); - Player::from_repr(v).unwrap() - } -} - #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] pub struct Trick { pub leader: Player, @@ -255,266 +181,6 @@ impl DealInPlay { } } -#[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(PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] -pub enum Bid { - Pass, - Double, - Redouble, - Raise(Raise), -} - -impl Bid { - pub fn as_raise(&self) -> Option<Raise> { - match self { - Bid::Raise(raise) => Some(*raise), - _ => None, - } - } -} - -impl fmt::Display for Bid { - fn fmt( - &self, - f: &mut std::fmt::Formatter<'_>, - ) -> std::result::Result<(), std::fmt::Error> { - match self { - Bid::Pass => write!(f, "Pass"), - Bid::Double => write!(f, "Double"), - Bid::Redouble => write!(f, "Redouble"), - Bid::Raise(x) => write!(f, "{}", x), - } - } -} - -impl fmt::Debug for Bid { - fn fmt( - &self, - f: &mut std::fmt::Formatter<'_>, - ) -> std::result::Result<(), std::fmt::Error> { - match self { - Bid::Pass => write!(f, "Pass"), - Bid::Double => write!(f, "Double"), - Bid::Redouble => write!(f, "Redouble"), - Bid::Raise(x) => write!(f, "Raise({})", x), - } - } -} - -impl FromStr for Bid { - type Err = anyhow::Error; - fn from_str( - s: &str, - ) -> std::result::Result<Self, <Self as std::str::FromStr>::Err> { - match s.trim().to_ascii_lowercase().as_str() { - "pass" => Ok(Bid::Pass), - "double" => Ok(Bid::Double), - "redouble" => Ok(Bid::Redouble), - x => Ok(Bid::Raise(x.parse()?)), - } - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] -pub struct Raise { - pub level: ContractLevel, - pub suit: Option<Suit>, -} - -impl Raise { - pub fn all_raises() -> Vec<Raise> { - let mut result = Vec::with_capacity(7 * 5); - for level in ContractLevel::iter() { - for suit in Suit::iter() { - result.push(Raise { - level, - suit: Some(suit), - }); - } - result.push(Raise { level, suit: None }); - } - result - } -} - -impl PartialOrd<Self> for Raise { - 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 Raise { - fn cmp(&self, o: &Self) -> std::cmp::Ordering { - self.partial_cmp(o).unwrap() - } -} - -impl fmt::Display for Raise { - 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 Raise { - fn fmt( - &self, - f: &mut std::fmt::Formatter<'_>, - ) -> std::result::Result<(), std::fmt::Error> { - write!(f, "{}", self) - } -} - -impl FromStr for Raise { - 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))?; - info!("caps: {:?}", caps); - let level = caps[1].parse()?; - let suit = match caps[2].to_ascii_uppercase().as_str() { - "NT" => None, - x => Some(x.parse()?), - }; - Ok(Raise { level, suit }) - } -} - -#[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: Raise, - 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(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct Bidding { pub dealer: Player, @@ -538,7 +204,7 @@ impl Bidding { self.dealer.many_next(self.bids.len()) } - pub fn highest_bid(&self) -> Option<Raise> { + pub fn highest_bid(&self) -> Option<LevelAndSuit> { for bid in self.bids.iter().rev() { if let Some(raise) = bid.as_raise() { return Some(raise); @@ -856,49 +522,6 @@ impl GameState { } } -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -pub struct Deal { - pub north: Vec<Card>, - pub west: Vec<Card>, - pub south: Vec<Card>, - pub east: Vec<Card>, -} - -impl Deal { - pub fn empty() -> Self { - Self { - north: Vec::with_capacity(13), - west: Vec::with_capacity(13), - south: Vec::with_capacity(13), - east: Vec::with_capacity(13), - } - } - - pub fn sort(&mut self, suits: &[Suit; 4], ord: RankOrder) { - sort_cards(suits, ord, self.north.as_mut_slice()); - sort_cards(suits, ord, self.west.as_mut_slice()); - sort_cards(suits, ord, self.south.as_mut_slice()); - sort_cards(suits, ord, self.east.as_mut_slice()); - } -} - -impl Distribution<Deal> for Standard { - fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Deal { - let mut deck = make_deck(); - deck.shuffle(rng); - let mut deck = deck.iter(); - let north = deck.by_ref().take(13).cloned().collect(); - let west = deck.by_ref().take(13).cloned().collect(); - let south = deck.by_ref().take(13).cloned().collect(); - let east = deck.by_ref().take(13).cloned().collect(); - Deal { - north, - west, - south, - east, - } - } -} #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] pub struct PlayStatePlayerView { @@ -1059,9 +682,14 @@ impl TableStatePlayerView { #[cfg(test)] mod tests { + use std::str::FromStr; + + use crate::{contract::ContractLevel, card::RankOrder}; + use super::*; use log::info; use rand::random; + use strum::IntoEnumIterator; fn as_bidding(r: BiddingResult) -> Bidding { match r { @@ -1105,21 +733,21 @@ mod tests { #[test] fn bid_conversion() { crate::tests::test_setup(); - let bid1d = Raise { + let bid1d = LevelAndSuit { level: ContractLevel::One, suit: Some(Suit::Diamond), }; assert_eq!("1♢", format!("{}", bid1d)); assert_eq!("1♢", format!("{:?}", bid1d)); - assert_eq!(bid1d, Raise::from_str("1D").unwrap()); + assert_eq!(bid1d, LevelAndSuit::from_str("1D").unwrap()); assert_eq!(Bid::Pass, Bid::from_str("pass").unwrap()); let mut checked_raises = 0; - for bid in Raise::all_raises() { + for bid in LevelAndSuit::all_raises() { assert_eq!( bid, - Raise::from_str(format!("{}", bid).as_str()).unwrap() + LevelAndSuit::from_str(format!("{}", bid).as_str()).unwrap() ); assert_eq!( Bid::Raise(bid), @@ -1159,12 +787,12 @@ mod tests { #[test] fn bid_ord() { - let bid = |s| Raise::from_str(s).unwrap(); + let bid = |s| LevelAndSuit::from_str(s).unwrap(); assert!(bid("2♦") < bid("3♦")); assert!(bid("3♦") < bid("3♥")); assert!(bid("1♠") < bid("2♣")); assert!(bid("1♠") < bid("1NT")); - for bid in Raise::all_raises() { + for bid in LevelAndSuit::all_raises() { assert_eq!(bid, bid); } } @@ -1283,7 +911,7 @@ mod tests { assert!(game_state.is_bidding()); info!("Start bidding with game state {game_state:#?}"); - let raise = |s| Bid::Raise(Raise::from_str(s).unwrap()); + let raise = |s| Bid::Raise(LevelAndSuit::from_str(s).unwrap()); let game_state = game_state.bid(raise("1H")).unwrap().current().unwrap(); let game_state = game_state @@ -1329,7 +957,7 @@ mod tests { fn some_play_state() -> PlayState { crate::tests::test_setup(); let deal = random(); - let raise1c = Raise { + let raise1c = LevelAndSuit { level: ContractLevel::One, suit: Some(Suit::Club), }; |