summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKjetil Orbekk <kj@orbekk.com>2022-12-21 08:10:38 -0500
committerKjetil Orbekk <kj@orbekk.com>2022-12-21 08:10:38 -0500
commit10ecb9e30568bf20287b053a620252d7a80dbd6b (patch)
treec6a82f7cb4d24071234a1429dbb91815709052be
parent27f74d8c366be675e7ab64ca746496a66b3cf024 (diff)
Add struct for the player view of a hand in play
-rw-r--r--protocol/src/bot.rs9
-rw-r--r--protocol/src/bridge_engine.rs289
-rw-r--r--server/src/play.rs116
3 files changed, 323 insertions, 91 deletions
diff --git a/protocol/src/bot.rs b/protocol/src/bot.rs
index 3bae7db..cc676df 100644
--- a/protocol/src/bot.rs
+++ b/protocol/src/bot.rs
@@ -1,8 +1,15 @@
use async_trait::async_trait;
-use crate::bridge_engine::{BiddingStatePlayerView, Bid};
+use crate::{bridge_engine::{BiddingStatePlayerView, Bid}, card::Card};
#[async_trait]
pub trait BiddingBot {
async fn bid(&self, bidding: &BiddingStatePlayerView) -> Bid;
}
+
+#[async_trait]
+pub trait PlayingBot {
+ async fn play(&self) -> Card {
+ todo!()
+ }
+}
diff --git a/protocol/src/bridge_engine.rs b/protocol/src/bridge_engine.rs
index 925676e..50f619b 100644
--- a/protocol/src/bridge_engine.rs
+++ b/protocol/src/bridge_engine.rs
@@ -1,7 +1,10 @@
-use crate::card::{Card, RankOrder, Suit, sort_cards, make_deck};
+use crate::card::{make_deck, sort_cards, Card, RankOrder, Suit};
use anyhow::{anyhow, bail};
use log::{error, info};
-use rand::{prelude::{Distribution, SliceRandom}, distributions::Standard};
+use rand::{
+ distributions::Standard,
+ prelude::{Distribution, SliceRandom}, random,
+};
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
@@ -10,10 +13,20 @@ use std::str::FromStr;
use strum::{EnumCount, IntoEnumIterator};
use strum_macros::{EnumCount as EnumCountMacro, EnumIter, FromRepr};
-pub const SUIT_DISPLAY_ORDER: [Suit; 4] = [Suit::Diamond, Suit::Club, Suit::Heart, Suit::Spade];
+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,
+ PartialEq,
+ Eq,
+ Clone,
+ Copy,
+ Debug,
+ FromRepr,
+ EnumCountMacro,
+ Serialize,
+ Deserialize,
+ EnumIter,
)]
#[repr(u8)]
pub enum Player {
@@ -166,7 +179,10 @@ impl DealInPlay {
&self.deal
}
- pub fn play(mut self: Self, card: Card) -> Result<DealInPlayResult, anyhow::Error> {
+ pub fn play(
+ mut self: Self,
+ card: Card,
+ ) -> Result<DealInPlayResult, anyhow::Error> {
let player = self.in_progress.next_player();
let player_cards = player.get_cards_mut(&mut self.deal);
@@ -182,24 +198,38 @@ impl DealInPlay {
player_cards.remove(i);
Ok(match self.in_progress.play(card) {
- TurnInPlayResult::InProgress(turn) => DealInPlayResult::InProgress(Self {
- in_progress: turn,
- ..self
- }),
- TurnInPlayResult::Trick(trick) => DealInPlayResult::InProgress(Self {
- in_progress: TurnInPlay::new(trick.winner()),
- tricks_played: {
- let mut tricks = self.tricks_played;
- tricks.push(trick);
- tricks
- },
- deal: self.deal,
- }),
+ TurnInPlayResult::InProgress(turn) => {
+ DealInPlayResult::InProgress(Self {
+ in_progress: turn,
+ ..self
+ })
+ }
+ TurnInPlayResult::Trick(trick) => {
+ DealInPlayResult::InProgress(Self {
+ in_progress: TurnInPlay::new(trick.winner()),
+ tricks_played: {
+ let mut tricks = self.tricks_played;
+ tricks.push(trick);
+ tricks
+ },
+ deal: self.deal,
+ })
+ }
})
}
}
-#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, EnumIter, Serialize, Deserialize)]
+#[derive(
+ PartialEq,
+ Eq,
+ PartialOrd,
+ Ord,
+ Clone,
+ Copy,
+ EnumIter,
+ Serialize,
+ Deserialize,
+)]
pub enum ContractLevel {
One = 1,
Two,
@@ -211,13 +241,19 @@ pub enum ContractLevel {
}
impl fmt::Display for ContractLevel {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
+ 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> {
+ fn fmt(
+ &self,
+ f: &mut std::fmt::Formatter<'_>,
+ ) -> std::result::Result<(), std::fmt::Error> {
write!(f, "{}", self)
}
}
@@ -225,7 +261,9 @@ impl fmt::Debug for ContractLevel {
impl FromStr for ContractLevel {
type Err = anyhow::Error;
- fn from_str(s: &str) -> std::result::Result<Self, <Self as std::str::FromStr>::Err> {
+ 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),
@@ -257,7 +295,10 @@ impl Bid {
}
impl fmt::Display for Bid {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
+ 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"),
@@ -268,7 +309,10 @@ impl fmt::Display for Bid {
}
impl fmt::Debug for Bid {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
+ 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"),
@@ -280,7 +324,9 @@ impl fmt::Debug for Bid {
impl FromStr for Bid {
type Err = anyhow::Error;
- fn from_str(s: &str) -> std::result::Result<Self, <Self as std::str::FromStr>::Err> {
+ 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),
@@ -337,7 +383,10 @@ impl Ord for Raise {
}
impl fmt::Display for Raise {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
+ fn fmt(
+ &self,
+ f: &mut std::fmt::Formatter<'_>,
+ ) -> std::result::Result<(), std::fmt::Error> {
write!(
f,
"{}{}",
@@ -349,14 +398,19 @@ impl fmt::Display for Raise {
}
impl fmt::Debug for Raise {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
+ 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> {
+ 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();
};
@@ -379,7 +433,10 @@ pub enum ContractModifier {
}
impl fmt::Display for ContractModifier {
- fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
+ fn fmt(
+ &self,
+ f: &mut fmt::Formatter,
+ ) -> std::result::Result<(), std::fmt::Error> {
match self {
ContractModifier::None => Ok(()),
ContractModifier::Doubled => write!(f, "x"),
@@ -396,7 +453,10 @@ pub struct Contract {
}
impl fmt::Display for Contract {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
+ fn fmt(
+ &self,
+ f: &mut fmt::Formatter<'_>,
+ ) -> std::result::Result<(), fmt::Error> {
write!(
f,
"{}{}{}",
@@ -440,7 +500,9 @@ impl Bidding {
}
fn passed_out(&self) -> bool {
- if self.bids.len() < 4 { return false; }
+ if self.bids.len() < 4 {
+ return false;
+ }
let mut passes = 0;
for b in self.bids.iter().rev().take(3) {
if b == &Bid::Pass {
@@ -514,7 +576,10 @@ pub struct BiddingStatePlayerView {
}
impl BiddingStatePlayerView {
- pub fn from_bidding_state(bidding_state: &BiddingState, player_position: Player) -> Self {
+ pub fn from_bidding_state(
+ bidding_state: &BiddingState,
+ player_position: Player,
+ ) -> Self {
let BiddingState {
dealer,
deal,
@@ -531,13 +596,29 @@ impl BiddingStatePlayerView {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct PlayState {
- pub dealer: Player,
pub deal: Deal,
pub contract: Contract,
pub bidding: Bidding,
pub playing_deal: DealInPlay,
}
+impl PlayState {
+ pub fn new(deal: Deal, contract: Contract, bidding: Bidding) -> Self {
+ let playing_deal = DealInPlay::new(contract.declarer.many_next(3),
+ deal.clone());
+ Self {
+ deal,
+ contract,
+ bidding,
+ playing_deal,
+ }
+ }
+
+ pub fn dealer(&self) -> Player {
+ self.bidding.dealer
+ }
+}
+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum GameState {
Bidding(BiddingState),
@@ -567,18 +648,24 @@ impl GameState {
}
pub fn dealer(&self) -> Player {
- match *self {
- Self::Bidding(BiddingState { dealer, .. }) => dealer,
- Self::PassedOut { dealer, .. } => dealer,
- Self::Play(PlayState { dealer, .. }) => dealer,
+ match self {
+ Self::Bidding(BiddingState { dealer, .. }) => *dealer,
+ Self::PassedOut { dealer, .. } => *dealer,
+ Self::Play(play_state) => play_state.dealer(),
}
}
pub fn current_player(&self) -> Option<Player> {
match self {
- GameState::Bidding(bidding) => Some(bidding.bidding.current_bidder()),
+ GameState::Bidding(bidding) => {
+ Some(bidding.bidding.current_bidder())
+ }
GameState::Play(_) => todo!(),
- GameState::PassedOut { dealer, deal, bidding } => None,
+ GameState::PassedOut {
+ dealer,
+ deal,
+ bidding,
+ } => None,
}
}
@@ -608,23 +695,22 @@ impl GameState {
_ => bail!("not currently bidding: {self:?}"),
};
Ok(match bidding.bid(bid)? {
- BiddingResult::InProgress(bidding) => GameState::Bidding(BiddingState {
- dealer,
- deal,
- 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(PlayState {
- deal: deal.clone(),
- dealer,
- playing_deal: DealInPlay::new(contract.declarer, deal),
- contract,
- bidding,
- }),
+ BiddingResult::Contract(Some(contract), bidding) => {
+ GameState::Play(
+ PlayState::new(deal, contract, bidding))
+ }
})
}
}
@@ -665,6 +751,37 @@ impl Distribution<Deal> for Standard {
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
+pub struct PlayStatePlayerView {
+ player_position: Player,
+ bidding: Bidding,
+ contract: Contract,
+ // If None, the lead has not been played.
+ dummy: Option<Vec<Card>>,
+ declarer_tricks: u8,
+ hand: Vec<Card>,
+ previous_trick: Trick,
+ current_trick: TurnInPlay,
+}
+
+impl PlayStatePlayerView {
+ pub fn from_play_state(
+ play_state: &PlayState,
+ player_position: Player,
+ ) -> Self {
+ Self {
+ player_position,
+ bidding: play_state.bidding.clone(),
+ contract: play_state.contract,
+ dummy: None,
+ declarer_tricks: 0,
+ hand: vec!(),
+ previous_trick: Trick { leader: random(), cards_played: vec!() },
+ current_trick: TurnInPlay::new(random()),
+ }
+ }
+}
+
+#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
pub enum GameStatePlayerView {
Bidding(BiddingStatePlayerView),
PassedOut {
@@ -690,10 +807,16 @@ pub enum GameStatePlayerView {
}
impl GameStatePlayerView {
- pub fn from_game_state(game_state: &GameState, player_position: Player) -> Self {
+ pub fn from_game_state(
+ game_state: &GameState,
+ player_position: Player,
+ ) -> Self {
match game_state {
GameState::Bidding(bidding_state) => GameStatePlayerView::Bidding(
- BiddingStatePlayerView::from_bidding_state(bidding_state, player_position),
+ BiddingStatePlayerView::from_bidding_state(
+ bidding_state,
+ player_position,
+ ),
),
GameState::PassedOut { .. } => todo!(),
GameState::Play { .. } => todo!(),
@@ -702,7 +825,10 @@ impl GameStatePlayerView {
pub fn hand(&self) -> &Vec<Card> {
match self {
- GameStatePlayerView::Bidding(BiddingStatePlayerView { hand, .. }) => hand,
+ GameStatePlayerView::Bidding(BiddingStatePlayerView {
+ hand,
+ ..
+ }) => hand,
GameStatePlayerView::PassedOut {
deal,
player_position,
@@ -741,7 +867,8 @@ mod tests {
assert_eq!(bidding.current_bidder(), Player::South);
let bidding = as_bidding(bidding.bid(Bid::Pass).unwrap());
assert_eq!(bidding.current_bidder(), Player::West);
- let bidding = as_bidding(bidding.bid(Bid::Raise("1♦".parse().unwrap())).unwrap());
+ let bidding =
+ as_bidding(bidding.bid(Bid::Raise("1♦".parse().unwrap())).unwrap());
assert_eq!(bidding.current_bidder(), Player::North);
let bidding = as_bidding(bidding.bid(Bid::Pass).unwrap());
assert_eq!(bidding.current_bidder(), Player::East);
@@ -773,7 +900,10 @@ mod tests {
let mut checked_raises = 0;
for bid in Raise::all_raises() {
- assert_eq!(bid, Raise::from_str(format!("{}", bid).as_str()).unwrap());
+ assert_eq!(
+ bid,
+ Raise::from_str(format!("{}", bid).as_str()).unwrap()
+ );
assert_eq!(
Bid::Raise(bid),
Bid::from_str(format!("{}", bid).as_str()).unwrap()
@@ -836,10 +966,11 @@ mod tests {
#[test]
fn next_player() {
- let next_players = vec![Player::North, Player::East, Player::South, Player::West]
- .iter()
- .map(Player::next)
- .collect::<Vec<_>>();
+ let next_players =
+ vec![Player::North, Player::East, Player::South, Player::West]
+ .iter()
+ .map(Player::next)
+ .collect::<Vec<_>>();
assert_eq!(
next_players,
vec![Player::East, Player::South, Player::West, Player::North]
@@ -975,7 +1106,11 @@ mod tests {
info!("Testing view for {p:?}");
let view = GameStatePlayerView::from_game_state(&game_state, p);
match view {
- GameStatePlayerView::Bidding(BiddingStatePlayerView { dealer, hand, .. }) => {
+ GameStatePlayerView::Bidding(BiddingStatePlayerView {
+ dealer,
+ hand,
+ ..
+ }) => {
assert_eq!(dealer, Player::East);
assert_eq!(&hand, p.get_cards(&mini_deal()));
}
@@ -984,6 +1119,38 @@ mod tests {
}
}
+ fn some_play_state() -> PlayState {
+ crate::tests::test_setup();
+ let deal = random();
+ let raise1c = Raise {
+ level: ContractLevel::One,
+ suit: Some(Suit::Club),
+ };
+ let contract = Contract {
+ declarer: random(),
+ highest_bid: raise1c,
+ modifier: ContractModifier::Doubled,
+ };
+ let bidding = Bidding {
+ dealer: random(),
+ bids: vec![Bid::Raise(raise1c), Bid::Pass, Bid::Pass, Bid::Pass],
+ };
+ PlayState::new(deal, contract, bidding)
+ }
+
+ #[test]
+ fn play_state() {
+ some_play_state();
+ }
+
+ #[test]
+ fn play_state_player_view() {
+ crate::tests::test_setup();
+ let play_state = some_play_state();
+ let player_deal =
+ PlayStatePlayerView::from_play_state(&play_state, random());
+ }
+
fn as_playing_hand(result: DealInPlayResult) -> DealInPlay {
match result {
DealInPlayResult::InProgress(r) => r,
diff --git a/server/src/play.rs b/server/src/play.rs
index 79d241c..86a4e06 100644
--- a/server/src/play.rs
+++ b/server/src/play.rs
@@ -1,5 +1,9 @@
use async_trait::async_trait;
-use protocol::{bridge_engine::{GameState, Player, Bid, Deal, BiddingStatePlayerView}, simple_bots::AlwaysPassBiddingBot, bot::BiddingBot};
+use protocol::{
+ bot::BiddingBot,
+ bridge_engine::{Bid, BiddingStatePlayerView, Deal, GameState, Player},
+ simple_bots::AlwaysPassBiddingBot,
+};
use rand::random;
use serde::{Deserialize, Serialize};
use serde_json::json;
@@ -15,16 +19,23 @@ pub trait Journal {
fn next(&self) -> i64;
// Append payload to the journal at sequence number `seq`.
- async fn append(&mut self, seq: i64, payload: serde_json::Value) -> Result<(), BridgeError>;
+ async fn append(
+ &mut self,
+ seq: i64,
+ payload: serde_json::Value,
+ ) -> Result<(), BridgeError>;
// Fetch all journal entries with sequence number greater or equal to `seq`.
- async fn replay(&mut self, seq: i64) -> Result<Vec<serde_json::Value>, BridgeError>;
+ async fn replay(
+ &mut self,
+ seq: i64,
+ ) -> Result<Vec<serde_json::Value>, BridgeError>;
}
pub struct DbJournal {
db: PgPool,
id: Uuid,
- seq: i64
+ seq: i64,
}
impl DbJournal {
@@ -35,7 +46,11 @@ impl DbJournal {
#[async_trait]
impl Journal for DbJournal {
- async fn append(&mut self, seq: i64, payload: serde_json::Value) -> Result<(), BridgeError> {
+ async fn append(
+ &mut self,
+ seq: i64,
+ payload: serde_json::Value,
+ ) -> Result<(), BridgeError> {
let result = query!(
r#"
insert into object_journal (id, seq, payload)
@@ -46,18 +61,24 @@ impl Journal for DbJournal {
payload,
)
.execute(&self.db)
- .await;
+ .await;
if let Err(sqlx::Error::Database(e)) = result {
if e.constraint() == Some("journal_entry") {
- return Err(BridgeError::JournalConflict(format!("{}", self.id), seq));
+ return Err(BridgeError::JournalConflict(
+ format!("{}", self.id),
+ seq,
+ ));
}
}
self.seq += 1;
Ok(())
}
- async fn replay(&mut self, seq: i64) -> Result<Vec<serde_json::Value>, BridgeError> {
- let rows =query!(
+ async fn replay(
+ &mut self,
+ seq: i64,
+ ) -> Result<Vec<serde_json::Value>, BridgeError> {
+ let rows = query!(
r#"
select seq, payload from object_journal
where id = $1 and seq >= $2
@@ -67,8 +88,8 @@ impl Journal for DbJournal {
seq
)
.fetch_all(&self.db)
- .await?;
- let mut payloads = vec!();
+ .await?;
+ let mut payloads = vec![];
for v in rows {
payloads.push(v.payload);
self.seq = v.seq;
@@ -106,13 +127,19 @@ where
}
impl<J: Journal> Table<J> {
- pub fn game(&self) -> &GameState { &self.game }
+ pub fn game(&self) -> &GameState {
+ &self.game
+ }
}
impl<J: Journal> Table<J> {
pub async fn new(mut journal: J) -> Result<Self, BridgeError> {
let game = Self::init(&mut journal).await?;
- Ok(Table { journal, game, settings: Default::default() })
+ Ok(Table {
+ journal,
+ game,
+ settings: Default::default(),
+ })
}
async fn init(journal: &mut J) -> Result<GameState, BridgeError> {
@@ -123,7 +150,9 @@ impl<J: Journal> Table<J> {
pub async fn bid(&mut self, bid: Bid) -> Result<(), BridgeError> {
let game = self.game.clone().bid(bid)?;
- self.journal.append(self.journal.next(), json!(game)).await?;
+ self.journal
+ .append(self.journal.next(), json!(game))
+ .await?;
self.game = game;
Ok(())
}
@@ -131,10 +160,16 @@ impl<J: Journal> Table<J> {
pub async fn replay(mut journal: J) -> Result<Self, BridgeError> {
let games = journal.replay(0).await?;
if games.is_empty() {
- return Err(BridgeError::NotFound("table journal missing".to_string()));
+ return Err(BridgeError::NotFound(
+ "table journal missing".to_string(),
+ ));
}
let game = serde_json::from_value(games[games.len() - 1].clone())?;
- Ok(Table { journal, game, settings: Default::default() } )
+ Ok(Table {
+ journal,
+ game,
+ settings: Default::default(),
+ })
}
pub async fn new_or_replay(mut journal: J) -> Result<Self, BridgeError> {
@@ -142,29 +177,43 @@ impl<J: Journal> Table<J> {
if let Err(BridgeError::JournalConflict(..)) = game {
return Self::replay(journal).await;
}
- Ok(Self { journal, game: game?, settings: Default::default() } )
+ Ok(Self {
+ journal,
+ game: game?,
+ settings: Default::default(),
+ })
}
}
-
-pub async fn advance_play<J: Journal>(table: &mut Table<J>) -> Result<(), BridgeError> {
+pub async fn advance_play<J: Journal>(
+ table: &mut Table<J>,
+) -> Result<(), BridgeError> {
let current_player = match table.game().current_player() {
Some(player) => player,
None => {
info!("Could not make play. Game: {:#?}", table.game());
- return Err(BridgeError::InvalidRequest(format!("No play to make for game")));
- },
+ return Err(BridgeError::InvalidRequest(format!(
+ "No play to make for game"
+ )));
+ }
};
match table.game() {
GameState::Bidding(bidding) => {
- let player_view = BiddingStatePlayerView::from_bidding_state(bidding, current_player);
+ let player_view = BiddingStatePlayerView::from_bidding_state(
+ bidding,
+ current_player,
+ );
let bot = AlwaysPassBiddingBot {};
let bid = bot.bid(&player_view).await;
table.bid(bid).await?;
Ok(())
- },
+ }
GameState::Play(_) => todo!(),
- GameState::PassedOut { dealer, deal, bidding } => todo!(),
+ GameState::PassedOut {
+ dealer,
+ deal,
+ bidding,
+ } => Err(BridgeError::InvalidRequest(format!("The game is over"))),
}
}
@@ -185,13 +234,19 @@ mod test {
payload: serde_json::Value,
) -> Result<(), BridgeError> {
if seq != self.log.len() as i64 {
- return Err(BridgeError::UpdateConflict(self.log.len() as i64, seq));
+ return Err(BridgeError::UpdateConflict(
+ self.log.len() as i64,
+ seq,
+ ));
}
self.log.push(Some(payload));
Ok(())
}
- async fn replay(&mut self, seq: i64) -> Result<Vec<serde_json::Value>, BridgeError> {
+ async fn replay(
+ &mut self,
+ seq: i64,
+ ) -> Result<Vec<serde_json::Value>, BridgeError> {
Ok(self.log[seq as usize..]
.into_iter()
.filter_map(|e| e.clone())
@@ -218,7 +273,8 @@ mod test {
#[tokio::test]
async fn test_new_table() {
- let t1: Table<TestJournal> = Table::new(Default::default()).await.unwrap();
+ let t1: Table<TestJournal> =
+ Table::new(Default::default()).await.unwrap();
match t1.game {
GameState::Bidding { .. } => (),
_ => panic!("should be Bidding"),
@@ -227,7 +283,8 @@ mod test {
#[tokio::test]
async fn test_replay_table() {
- let t1: Table<TestJournal> = Table::new(Default::default()).await.unwrap();
+ let t1: Table<TestJournal> =
+ Table::new(Default::default()).await.unwrap();
let game = t1.game;
let journal = t1.journal;
@@ -237,7 +294,8 @@ mod test {
#[tokio::test]
async fn test_advance_play() {
- let mut t1: Table<TestJournal> = Table::new(Default::default()).await.unwrap();
+ let mut t1: Table<TestJournal> =
+ Table::new(Default::default()).await.unwrap();
let player = t1.game().current_player();
advance_play(&mut t1).await.unwrap();
assert_ne!(player, t1.game().current_player());