From 7848bfcbcac40564d7f6e00cd0b1a36c6da39d97 Mon Sep 17 00:00:00 2001 From: Henri Bourcereau Date: Thu, 17 Oct 2024 17:35:07 +0200 Subject: [PATCH] refact: bot strategy trait --- bot/src/lib.rs | 195 ++++++++++++++++++++++------------ client_cli/src/app.rs | 1 + client_cli/src/game_runner.rs | 21 +++- 3 files changed, 144 insertions(+), 73 deletions(-) diff --git a/bot/src/lib.rs b/bot/src/lib.rs index 5a02197..5106212 100644 --- a/bot/src/lib.rs +++ b/bot/src/lib.rs @@ -4,81 +4,59 @@ use store::{ CheckerMove, Color, Dice, GameEvent, GameState, Player, PlayerId, PointsRules, Stage, TurnStage, }; +pub trait BotStrategy { + fn get_game(&self) -> &GameState; + fn get_mut_game(&mut self) -> &mut GameState; + fn calculate_points(&self) -> u8; + fn calculate_adv_points(&self) -> u8; + fn choose_move(&self) -> (CheckerMove, CheckerMove); + fn set_player_id(&mut self, player_id: PlayerId); + fn init_players(&mut self) { + self.get_mut_game().init_player("p1"); + self.get_mut_game().init_player("p2"); + } +} + #[derive(Debug)] -pub struct Bot { +pub struct DefaultStrategy { pub game: GameState, pub player_id: PlayerId, - color: Color, - schools_enabled: bool, + pub color: Color, } -impl Default for Bot { - fn default() -> Bot { - Bot { - game: GameState::default(), - player_id: 1, - color: Color::Black, - schools_enabled: false, - } - } -} - -// impl PlayerEngine for Bot {} - -impl Bot { - /// new initialize a bot - /// # Examples - /// ```let mut bot = Bot::new(Color::Black); - /// assert_eq!(bot.game.stage, Stage::PreGame); - /// ``` - pub fn new(color: Color, schools_enabled: bool) -> Self { - let mut game = GameState::default(); - game.init_player("p1"); - game.init_player("p2"); - - let player_id = match color { - Color::White => 1, - Color::Black => 2, - }; - - Self { +impl Default for DefaultStrategy { + fn default() -> Self { + let game = GameState::default(); + let mut strategy = Self { game, - player_id, - color, - schools_enabled: false, - } + player_id: 2, + color: Color::Black, + }; + strategy + } +} + +impl DefaultStrategy { + fn new() -> Self { + Self::default() + } +} + +impl BotStrategy for DefaultStrategy { + fn get_game(&self) -> &GameState { + &self.game + } + fn get_mut_game(&mut self) -> &mut GameState { + &mut self.game } - pub fn handle_event(&mut self, event: &GameEvent) -> Option { - self.game.consume(event); - // println!("bot game {:?}", self.game); - // println!("bot player_id {:?}", self.player_id); - if self.game.active_player_id == self.player_id { - return match self.game.turn_stage { - TurnStage::MarkAdvPoints => Some(GameEvent::Mark { - player_id: self.player_id, - points: self.calculate_adv_points(), - }), - TurnStage::RollDice => Some(GameEvent::Roll { - player_id: self.player_id, - }), - TurnStage::MarkPoints => Some(GameEvent::Mark { - player_id: self.player_id, - points: self.calculate_points(), - }), - TurnStage::Move => Some(GameEvent::Move { - player_id: self.player_id, - moves: self.choose_move(), - }), - _ => None, - }; - } - None + fn set_player_id(&mut self, player_id: PlayerId) { + self.player_id = player_id; } fn calculate_points(&self) -> u8 { let dice_roll_count = self - .game + .get_game() .players .get(&self.player_id) .unwrap() @@ -89,7 +67,7 @@ impl Bot { fn calculate_adv_points(&self) -> u8 { let dice_roll_count = self - .game + .get_game() .players .get(&self.player_id) .unwrap() @@ -116,29 +94,108 @@ impl Bot { } } +#[derive(Debug)] +pub struct Bot { + pub player_id: PlayerId, + strategy: BotStrategy, + color: Color, + schools_enabled: bool, +} + +impl Default for Bot { + fn default() -> Self { + Self { + player_id: 2, + strategy: DefaultStrategy::default(), + color: Color::Black, + schools_enabled: false, + } + } +} + +impl Bot +where + BS: BotStrategy, +{ + /// new initialize a bot + /// # Examples + /// ```let mut bot = Bot::new(Color::Black); + /// assert_eq!(bot.game.stage, Stage::PreGame); + /// ``` + pub fn new(mut strategy: BS, color: Color, schools_enabled: bool) -> Self { + let game = strategy.get_mut_game(); + strategy.init_players(); + let player_id = match color { + Color::White => 1, + Color::Black => 2, + }; + strategy.set_player_id(player_id); + Self { + player_id, + strategy, + color, + schools_enabled: false, + } + } + + pub fn handle_event(&mut self, event: &GameEvent) -> Option { + let game = self.strategy.get_mut_game(); + game.consume(event); + // println!("bot game {:?}", self.game); + // println!("bot player_id {:?}", self.player_id); + if game.active_player_id == self.player_id { + return match game.turn_stage { + TurnStage::MarkAdvPoints => Some(GameEvent::Mark { + player_id: self.player_id, + points: self.strategy.calculate_adv_points(), + }), + TurnStage::RollDice => Some(GameEvent::Roll { + player_id: self.player_id, + }), + TurnStage::MarkPoints => Some(GameEvent::Mark { + player_id: self.player_id, + points: self.strategy.calculate_points(), + }), + TurnStage::Move => Some(GameEvent::Move { + player_id: self.player_id, + moves: self.strategy.choose_move(), + }), + _ => None, + }; + } + None + } + + pub fn get_state(&self) -> &GameState { + self.strategy.get_game() + } +} + #[cfg(test)] mod tests { use super::*; #[test] fn test_new() { - let bot = Bot::new(Color::Black, false); - assert_eq!(bot.game.stage, Stage::PreGame); + let bot = Bot::new(DefaultStrategy::new(), Color::Black, false); + assert_eq!(bot.get_state().stage, Stage::PreGame); } #[test] fn test_consume() { - let mut bot = Bot::new(Color::Black, false); + let mut bot = Bot::new(DefaultStrategy::new(), Color::Black, false); let mut event = bot.handle_event(&GameEvent::BeginGame { goes_first: 2 }); assert_eq!(event, Some(GameEvent::Roll { player_id: 2 })); + assert_eq!(bot.get_state().active_player_id, 2); event = bot.handle_event(&GameEvent::BeginGame { goes_first: 1 }); assert_eq!(event, None); + assert_eq!(bot.get_state().active_player_id, 1); bot.handle_event(&GameEvent::RollResult { - player_id: 2, + player_id: 1, dice: Dice { values: (2, 3) }, }); - assert_eq!(bot.game.turn_stage, TurnStage::Move); + assert_eq!(bot.get_state().turn_stage, TurnStage::Move); } } diff --git a/client_cli/src/app.rs b/client_cli/src/app.rs index 186375f..2599532 100644 --- a/client_cli/src/app.rs +++ b/client_cli/src/app.rs @@ -1,6 +1,7 @@ use itertools::Itertools; use crate::game_runner::Game; +use bot::BotStrategy; use store::{CheckerMove, GameEvent, GameState, PointsRules, Stage, TurnStage}; #[derive(Debug, Default)] diff --git a/client_cli/src/game_runner.rs b/client_cli/src/game_runner.rs index 1a6a290..0d37f36 100644 --- a/client_cli/src/game_runner.rs +++ b/client_cli/src/game_runner.rs @@ -1,14 +1,26 @@ -use bot::Bot; +use bot::{Bot, BotStrategy, DefaultStrategy}; use store::{CheckerMove, DiceRoller, GameEvent, GameState, PlayerId, TurnStage}; // Application Game -#[derive(Debug, Default)] +#[derive(Debug)] pub struct Game { pub state: GameState, pub dice_roller: DiceRoller, pub first_move: Option, pub player_id: Option, - bot: Bot, + bot: Bot, +} + +impl Default for Game { + fn default() -> Self { + Self { + state: GameState::default(), + dice_roller: DiceRoller::default(), + first_move: None, + player_id: None, + bot: Bot::default(), + } + } } impl Game { @@ -20,7 +32,8 @@ impl Game { // bot let bot_id: PlayerId = state.init_player("bot").unwrap(); let bot_color = state.player_color_by_id(&bot_id).unwrap(); - let bot: Bot = Bot::new(bot_color, schools_enabled); + let bot_strategy = DefaultStrategy::default(); + let bot: Bot = Bot::new(bot_strategy, bot_color, schools_enabled); let mut game = Self { state,