summaryrefslogtreecommitdiff
path: root/protocol/src/bridge_engine.rs
diff options
context:
space:
mode:
authorKjetil Orbekk <kj@orbekk.com>2022-11-27 16:45:07 -0500
committerKjetil Orbekk <kj@orbekk.com>2022-11-27 17:23:11 -0500
commitd8de16a7187d2a05fd043946cf4cb32449a5aa3b (patch)
tree6c1a212232203dbb6a9da48c192e10bef5d3b932 /protocol/src/bridge_engine.rs
parent854f247b6b7bf1106f31d7f23a326c0904d4f87e (diff)
Add basic bot trait for bot bidding
Diffstat (limited to 'protocol/src/bridge_engine.rs')
-rw-r--r--protocol/src/bridge_engine.rs151
1 files changed, 95 insertions, 56 deletions
diff --git a/protocol/src/bridge_engine.rs b/protocol/src/bridge_engine.rs
index 5c7ce4d..6b3cf7d 100644
--- a/protocol/src/bridge_engine.rs
+++ b/protocol/src/bridge_engine.rs
@@ -1,6 +1,7 @@
use crate::card::{Card, Deal, RankOrder, Suit};
use anyhow::{anyhow, bail};
use log::{error, info};
+use rand::{prelude::Distribution, distributions::Standard};
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
@@ -59,6 +60,15 @@ impl Player {
}
}
+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,
@@ -484,47 +494,78 @@ impl BiddingResult {
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
+pub struct BiddingState {
+ pub dealer: Player,
+ pub deal: Deal,
+ pub bidding: Bidding,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
+pub struct BiddingStatePlayerView {
+ pub dealer: Player,
+ pub player_position: Player,
+ pub hand: Vec<Card>,
+ pub bidding: Bidding,
+}
+
+impl BiddingStatePlayerView {
+ pub fn from_bidding_state(bidding_state: &BiddingState, player_position: Player) -> Self {
+ let BiddingState {
+ dealer,
+ deal,
+ bidding,
+ } = bidding_state;
+ Self {
+ dealer: *dealer,
+ player_position,
+ hand: player_position.get_cards(deal).clone(),
+ bidding: bidding.clone(),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
+pub struct PlayState {
+ dealer: Player,
+ deal: Deal,
+ contract: Contract,
+ bidding: Bidding,
+ playing_deal: DealInPlay,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum GameState {
- Bidding {
- dealer: Player,
- deal: Deal,
- bidding: Bidding,
- },
+ Bidding(BiddingState),
+ Play(PlayState),
PassedOut {
dealer: Player,
deal: Deal,
bidding: Bidding,
},
- Play {
- dealer: Player,
- playing_deal: DealInPlay,
- contract: Contract,
- bidding: Bidding,
- },
}
impl GameState {
pub fn new(deal: Deal, dealer: Player) -> Self {
- Self::Bidding {
+ Self::Bidding(BiddingState {
dealer,
deal,
bidding: Bidding::new(dealer),
- }
+ })
}
pub fn deal(&self) -> &Deal {
match self {
- Self::Bidding { deal, .. } => deal,
+ Self::Bidding(BiddingState { deal, .. }) => deal,
Self::PassedOut { deal, .. } => deal,
- Self::Play { playing_deal, .. } => &playing_deal.deal(),
+ Self::Play(PlayState { playing_deal, .. }) => &playing_deal.deal(),
}
}
pub fn dealer(&self) -> Player {
match *self {
- Self::Bidding { dealer, .. } => dealer,
+ Self::Bidding(BiddingState { dealer, .. }) => dealer,
Self::PassedOut { dealer, .. } => dealer,
- Self::Play { dealer, .. } => dealer,
+ Self::Play(PlayState { dealer, .. }) => dealer,
}
}
@@ -546,30 +587,31 @@ impl GameState {
pub fn bid(self, bid: Bid) -> Result<Self, anyhow::Error> {
let (dealer, deal, bidding) = match self {
- GameState::Bidding {
+ GameState::Bidding(BiddingState {
dealer,
deal,
bidding,
- } => (dealer, deal, bidding),
+ }) => (dealer, deal, bidding),
_ => bail!("not currently bidding: {self:?}"),
};
Ok(match bidding.bid(bid)? {
- BiddingResult::InProgress(bidding) => GameState::Bidding {
+ BiddingResult::InProgress(bidding) => GameState::Bidding(BiddingState {
dealer,
deal,
bidding,
- },
+ }),
BiddingResult::Contract(None, bidding) => GameState::PassedOut {
dealer,
deal,
bidding,
},
- BiddingResult::Contract(Some(contract), bidding) => GameState::Play {
+ BiddingResult::Contract(Some(contract), bidding) => GameState::Play(PlayState {
+ deal: deal.clone(),
dealer,
playing_deal: DealInPlay::new(contract.declarer, deal),
contract,
bidding,
- },
+ }),
})
}
}
@@ -583,12 +625,7 @@ pub fn deal() -> Deal {
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
pub enum GameStatePlayerView {
- Bidding {
- dealer: Player,
- player_position: Player,
- hand: Vec<Card>,
- bidding: Bidding,
- },
+ Bidding(BiddingStatePlayerView),
PassedOut {
dealer: Player,
player_position: Player,
@@ -614,33 +651,17 @@ pub enum GameStatePlayerView {
impl GameStatePlayerView {
pub fn from_game_state(game_state: &GameState, player_position: Player) -> Self {
match game_state {
- GameState::Bidding {
- dealer,
- deal,
- bidding,
- } => GameStatePlayerView::Bidding {
- dealer: *dealer,
- player_position,
- bidding: bidding.clone(),
- hand: player_position.get_cards(deal).clone(),
- },
- GameState::PassedOut {
- dealer,
- deal,
- bidding,
- } => todo!(),
- GameState::Play {
- dealer,
- playing_deal,
- contract,
- bidding,
- } => todo!(),
+ GameState::Bidding(bidding_state) => GameStatePlayerView::Bidding(
+ BiddingStatePlayerView::from_bidding_state(bidding_state, player_position),
+ ),
+ GameState::PassedOut { .. } => todo!(),
+ GameState::Play { .. } => todo!(),
}
}
pub fn hand(&self) -> &Vec<Card> {
match self {
- GameStatePlayerView::Bidding { hand, .. } => hand,
+ GameStatePlayerView::Bidding(BiddingStatePlayerView { hand, .. }) => hand,
GameStatePlayerView::PassedOut {
deal,
player_position,
@@ -840,10 +861,10 @@ mod tests {
fn example_deal() -> Deal {
Deal {
- west: mkcards("♠5 ♦10 ♥K ♣4 ♥J ♣5 ♦5 ♠9 ♦3 ♠2 ♣2 ♥4 ♠Q"),
- north: mkcards("♦Q ♥9 ♠7 ♠8 ♠A ♥A ♥5 ♠6 ♦9 ♣3 ♥3 ♣9 ♦J"),
- east: mkcards("♣10 ♥7 A ♣6 ♥8 ♣Q ♠K ♥10 ♣K ♠3 ♥Q ♣J ♦4"),
- south: mkcards("♦K ♥6 ♣8 ♦6 ♦7 ♦8 ♣A ♥2 ♣7 ♠10 ♠4 ♠J ♦2"),
+ west: mkcards("♠Q ♠9 ♠5 ♠2 ♥K ♥J ♥4 ♣5 ♣4 ♣2 ♦10 ♦5 ♦3"),
+ north: mkcards("♠A ♠8 ♠7 ♠6 ♥A ♥9 ♥5 ♥3 ♣9 ♣3 ♦Q ♦J ♦9"),
+ east: mkcards("♠K ♠3 ♥Q ♥10 ♥8 ♥7 ♣K ♣Q ♣J ♣10 ♣6 ♦A ♦4"),
+ south: mkcards("♠J ♠10 ♠4 ♥6 ♥2 ♣A ♣8 ♣7 ♦K ♦8 ♦7 ♦6 ♦2"),
}
}
@@ -857,6 +878,24 @@ mod tests {
}
#[test]
+ fn example_deal_is_sorted() {
+ crate::tests::test_setup();
+ let mut sorted = example_deal();
+ sorted.sort(&SUIT_DISPLAY_ORDER, RankOrder::Descending);
+ let pp = |hand: &[Card]| {
+ hand.iter()
+ .map(|c| format!("{}", c))
+ .collect::<Vec<_>>()
+ .join(" ")
+ };
+ info!("{}", pp(&sorted.west));
+ info!("{}", pp(&sorted.north));
+ info!("{}", pp(&sorted.east));
+ info!("{}", pp(&sorted.south));
+ assert_eq!(example_deal(), sorted);
+ }
+
+ #[test]
fn game_state() {
crate::tests::test_setup();
let game_state = GameState::new(mini_deal(), Player::North);
@@ -889,7 +928,7 @@ mod tests {
info!("Testing view for {p:?}");
let view = GameStatePlayerView::from_game_state(&game_state, p);
match view {
- GameStatePlayerView::Bidding { dealer, hand, .. } => {
+ GameStatePlayerView::Bidding(BiddingStatePlayerView { dealer, hand, .. }) => {
assert_eq!(dealer, Player::East);
assert_eq!(&hand, p.get_cards(&mini_deal()));
}