diff options
author | Kjetil Orbekk <kj@orbekk.com> | 2022-12-22 08:31:35 -0500 |
---|---|---|
committer | Kjetil Orbekk <kj@orbekk.com> | 2022-12-22 08:31:35 -0500 |
commit | 42f6ef6d44e893b47e5e2a49496b5dd2122df232 (patch) | |
tree | dff2f84bbc41295b27deb2397071cb16bb742058 /protocol/src/simple_bots.rs | |
parent | 10ecb9e30568bf20287b053a620252d7a80dbd6b (diff) |
Add a simple bot that plays random (legal) cards
- Leads random cards
- Follows suit if possible
Diffstat (limited to 'protocol/src/simple_bots.rs')
-rw-r--r-- | protocol/src/simple_bots.rs | 123 |
1 files changed, 119 insertions, 4 deletions
diff --git a/protocol/src/simple_bots.rs b/protocol/src/simple_bots.rs index b7986b6..7cc2ce8 100644 --- a/protocol/src/simple_bots.rs +++ b/protocol/src/simple_bots.rs @@ -1,6 +1,14 @@ use async_trait::async_trait; +use rand::{ + prelude::{IteratorRandom, SliceRandom}, + thread_rng, +}; -use crate::{bot::BiddingBot, bridge_engine::{BiddingStatePlayerView, Bid}}; +use crate::{ + bot::{BiddingBot, PlayingBot}, + bridge_engine::{Bid, BiddingStatePlayerView, PlayStatePlayerView}, + card::Card, +}; pub struct AlwaysPassBiddingBot {} @@ -11,12 +19,113 @@ impl BiddingBot for AlwaysPassBiddingBot { } } +pub struct RandomPlayingBot {} + +#[async_trait] +impl PlayingBot for RandomPlayingBot { + async fn play(&self, state: &PlayStatePlayerView) -> Card { + let mut rng = thread_rng(); + if let Some(suit) = state.current_trick.suit() { + if let Some(card) = state + .hand + .iter() + .filter(|c| c.suit() == suit) + .choose(&mut rng) + { + return *card; + } + } + + *state + .hand + .choose(&mut rng) + .expect("must have at least one card") + } +} + #[cfg(test)] mod tests { + use std::str::FromStr; + use super::*; + use crate::{ + bridge_engine::{ + Bidding, BiddingState, BiddingStatePlayerView, Contract, + ContractLevel, ContractModifier, Deal, PlayState, PlayStateResult, + Player, Raise, + }, + card::Suit, + }; use log::info; use rand::random; - use crate::bridge_engine::{BiddingState, Bidding, BiddingStatePlayerView}; + + #[tokio::test] + async fn random_playing_bot() { + crate::tests::test_setup(); + let play_state = example_play_state(); + info!("Play state: {play_state:#?}"); + + let south_state = + PlayStatePlayerView::from_play_state(&play_state, Player::South); + let card1 = (RandomPlayingBot {}).play(&south_state).await; + info!("South state: {south_state:#?}"); + + let play_state = match play_state.play(card1).unwrap() { + PlayStateResult::InProgress(p) => p, + PlayStateResult::PlayFinished(_) => { + panic!("game should not be over") + } + }; + + let west_state = + PlayStatePlayerView::from_play_state(&play_state, Player::West); + let card2 = (RandomPlayingBot {}).play(&west_state).await; + info!("West state: {west_state:#?}"); + + assert_eq!(card1.suit(), card2.suit()); + + let play_state = match play_state.play(card2).unwrap() { + PlayStateResult::InProgress(p) => p, + PlayStateResult::PlayFinished(_) => { + panic!("game should not be over") + } + }; + } + + fn mkcard(s: &str) -> Card { + Card::from_str(s).unwrap() + } + + fn mkcards(s: &str) -> Vec<Card> { + s.split(" ").map(mkcard).collect() + } + + fn example_deal() -> Deal { + Deal { + 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"), + } + } + + fn example_play_state() -> PlayState { + let deal = example_deal(); + let raise1c = Raise { + level: ContractLevel::One, + suit: Some(Suit::Club), + }; + let contract = Contract { + declarer: Player::West, + 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) + } #[tokio::test] async fn always_passing_bot_passes() { @@ -28,8 +137,14 @@ mod tests { deal: random(), bidding: Bidding::new(dealer), }; - let player_view = BiddingStatePlayerView::from_bidding_state(&bidding_state, player_position); + let player_view = BiddingStatePlayerView::from_bidding_state( + &bidding_state, + player_position, + ); info!("Bidding state: {bidding_state:#?}"); - assert_eq!(Bid::Pass, (AlwaysPassBiddingBot {}).bid(&player_view).await); + assert_eq!( + Bid::Pass, + (AlwaysPassBiddingBot {}).bid(&player_view).await + ); } } |