summaryrefslogtreecommitdiff
path: root/protocol/src/bridge_engine.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/bridge_engine.rs
parentbb2ed3a2926384df063e476d10613fa310cd7ffa (diff)
Split bridge_engine into a few separate modules
Diffstat (limited to 'protocol/src/bridge_engine.rs')
-rw-r--r--protocol/src/bridge_engine.rs406
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),
};