refact: bot strategy trait

This commit is contained in:
Henri Bourcereau 2024-10-17 17:35:07 +02:00
parent acab0b0593
commit 7848bfcbca
3 changed files with 144 additions and 73 deletions

View file

@ -4,81 +4,59 @@ use store::{
CheckerMove, Color, Dice, GameEvent, GameState, Player, PlayerId, PointsRules, Stage, TurnStage, 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)] #[derive(Debug)]
pub struct Bot { pub struct DefaultStrategy {
pub game: GameState, pub game: GameState,
pub player_id: PlayerId, pub player_id: PlayerId,
color: Color, pub color: Color,
schools_enabled: bool,
} }
impl Default for Bot { impl Default for DefaultStrategy {
fn default() -> Bot { fn default() -> Self {
Bot { let game = GameState::default();
game: GameState::default(), let mut strategy = Self {
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 {
game, game,
player_id, player_id: 2,
color, color: Color::Black,
schools_enabled: false, };
} 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<GameEvent> { fn set_player_id(&mut self, player_id: PlayerId) {
self.game.consume(event); self.player_id = player_id;
// 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 calculate_points(&self) -> u8 { fn calculate_points(&self) -> u8 {
let dice_roll_count = self let dice_roll_count = self
.game .get_game()
.players .players
.get(&self.player_id) .get(&self.player_id)
.unwrap() .unwrap()
@ -89,7 +67,7 @@ impl Bot {
fn calculate_adv_points(&self) -> u8 { fn calculate_adv_points(&self) -> u8 {
let dice_roll_count = self let dice_roll_count = self
.game .get_game()
.players .players
.get(&self.player_id) .get(&self.player_id)
.unwrap() .unwrap()
@ -116,29 +94,108 @@ impl Bot {
} }
} }
#[derive(Debug)]
pub struct Bot<BotStrategy> {
pub player_id: PlayerId,
strategy: BotStrategy,
color: Color,
schools_enabled: bool,
}
impl Default for Bot<DefaultStrategy> {
fn default() -> Self {
Self {
player_id: 2,
strategy: DefaultStrategy::default(),
color: Color::Black,
schools_enabled: false,
}
}
}
impl<BS> Bot<BS>
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<GameEvent> {
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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[test] #[test]
fn test_new() { fn test_new() {
let bot = Bot::new(Color::Black, false); let bot = Bot::new(DefaultStrategy::new(), Color::Black, false);
assert_eq!(bot.game.stage, Stage::PreGame); assert_eq!(bot.get_state().stage, Stage::PreGame);
} }
#[test] #[test]
fn test_consume() { 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 }); let mut event = bot.handle_event(&GameEvent::BeginGame { goes_first: 2 });
assert_eq!(event, Some(GameEvent::Roll { player_id: 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 }); event = bot.handle_event(&GameEvent::BeginGame { goes_first: 1 });
assert_eq!(event, None); assert_eq!(event, None);
assert_eq!(bot.get_state().active_player_id, 1);
bot.handle_event(&GameEvent::RollResult { bot.handle_event(&GameEvent::RollResult {
player_id: 2, player_id: 1,
dice: Dice { values: (2, 3) }, dice: Dice { values: (2, 3) },
}); });
assert_eq!(bot.game.turn_stage, TurnStage::Move); assert_eq!(bot.get_state().turn_stage, TurnStage::Move);
} }
} }

View file

@ -1,6 +1,7 @@
use itertools::Itertools; use itertools::Itertools;
use crate::game_runner::Game; use crate::game_runner::Game;
use bot::BotStrategy;
use store::{CheckerMove, GameEvent, GameState, PointsRules, Stage, TurnStage}; use store::{CheckerMove, GameEvent, GameState, PointsRules, Stage, TurnStage};
#[derive(Debug, Default)] #[derive(Debug, Default)]

View file

@ -1,14 +1,26 @@
use bot::Bot; use bot::{Bot, BotStrategy, DefaultStrategy};
use store::{CheckerMove, DiceRoller, GameEvent, GameState, PlayerId, TurnStage}; use store::{CheckerMove, DiceRoller, GameEvent, GameState, PlayerId, TurnStage};
// Application Game // Application Game
#[derive(Debug, Default)] #[derive(Debug)]
pub struct Game { pub struct Game {
pub state: GameState, pub state: GameState,
pub dice_roller: DiceRoller, pub dice_roller: DiceRoller,
pub first_move: Option<CheckerMove>, pub first_move: Option<CheckerMove>,
pub player_id: Option<PlayerId>, pub player_id: Option<PlayerId>,
bot: Bot, bot: Bot<DefaultStrategy>,
}
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 { impl Game {
@ -20,7 +32,8 @@ impl Game {
// bot // bot
let bot_id: PlayerId = state.init_player("bot").unwrap(); let bot_id: PlayerId = state.init_player("bot").unwrap();
let bot_color = state.player_color_by_id(&bot_id).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<DefaultStrategy> = Bot::new(bot_strategy, bot_color, schools_enabled);
let mut game = Self { let mut game = Self {
state, state,