trictrac/bot/src/lib.rs

188 lines
6.4 KiB
Rust
Raw Normal View History

2025-08-01 20:45:57 +02:00
pub mod dqn;
2025-05-26 20:44:35 +02:00
pub mod strategy;
2024-03-24 18:37:35 +01:00
2025-08-08 16:24:12 +02:00
use log::{debug, error};
2025-01-26 17:52:57 +01:00
use store::{CheckerMove, Color, GameEvent, GameState, PlayerId, PointsRules, Stage, TurnStage};
2024-11-07 16:51:33 +01:00
pub use strategy::default::DefaultStrategy;
pub use strategy::dqn::DqnStrategy;
2025-08-08 21:31:38 +02:00
pub use strategy::dqnburn::DqnBurnStrategy;
2025-03-18 21:19:57 +01:00
pub use strategy::erroneous_moves::ErroneousStrategy;
2025-08-08 16:24:12 +02:00
pub use strategy::random::RandomStrategy;
2025-03-02 15:20:24 +01:00
pub use strategy::stable_baselines3::StableBaselines3Strategy;
2024-03-24 18:37:35 +01:00
2024-11-04 17:37:36 +01:00
pub trait BotStrategy: std::fmt::Debug {
2024-10-17 17:35:07 +02:00
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);
2025-01-09 21:27:24 +01:00
fn choose_go(&self) -> bool;
2024-10-17 17:35:07 +02:00
fn set_player_id(&mut self, player_id: PlayerId);
2025-01-06 20:27:16 +01:00
fn set_color(&mut self, color: Color);
2024-10-17 17:35:07 +02:00
fn init_players(&mut self) {
self.get_mut_game().init_player("p1");
self.get_mut_game().init_player("p2");
}
}
#[derive(Debug)]
2024-11-04 17:37:36 +01:00
pub struct Bot {
2024-10-17 17:35:07 +02:00
pub player_id: PlayerId,
2024-11-04 17:37:36 +01:00
strategy: Box<dyn BotStrategy>,
2025-08-04 18:04:40 +02:00
color: Color,
2024-11-04 17:37:36 +01:00
// schools_enabled: bool,
2024-03-24 18:37:35 +01:00
}
2024-11-04 17:37:36 +01:00
impl Default for Bot {
2024-10-17 17:35:07 +02:00
fn default() -> Self {
2024-11-04 17:37:36 +01:00
let strategy = DefaultStrategy::default();
2024-10-17 17:35:07 +02:00
Self {
2025-08-04 18:04:40 +02:00
player_id: 1,
2024-11-04 17:37:36 +01:00
strategy: Box::new(strategy),
2025-08-04 18:04:40 +02:00
color: Color::White,
2024-11-04 17:37:36 +01:00
// schools_enabled: false,
2024-03-24 18:37:35 +01:00
}
}
}
2024-11-04 17:37:36 +01:00
impl Bot {
2024-03-24 18:37:35 +01:00
/// new initialize a bot
2024-11-04 17:37:36 +01:00
// pub fn new(mut strategy: Box<dyn BotStrategy>, color: Color, schools_enabled: bool) -> Self {
pub fn new(mut strategy: Box<dyn BotStrategy>, color: Color) -> Self {
// let game = strategy.get_mut_game();
2024-10-17 17:35:07 +02:00
strategy.init_players();
2024-03-24 18:37:35 +01:00
let player_id = match color {
Color::White => 1,
Color::Black => 2,
};
2025-08-04 18:04:40 +02:00
// strategy.set_player_id(player_id);
// strategy.set_color(color);
2024-03-24 18:37:35 +01:00
Self {
player_id,
2024-10-17 17:35:07 +02:00
strategy,
2025-08-04 18:04:40 +02:00
color,
2024-11-04 17:37:36 +01:00
// schools_enabled: false,
2024-03-24 18:37:35 +01:00
}
}
2024-03-30 16:10:53 +01:00
pub fn handle_event(&mut self, event: &GameEvent) -> Option<GameEvent> {
2025-08-08 16:24:12 +02:00
debug!(">>>> {:?} BOT handle", self.color);
2024-10-17 17:35:07 +02:00
let game = self.strategy.get_mut_game();
2025-08-04 18:04:40 +02:00
let internal_event = if self.color == Color::Black {
&event.get_mirror()
} else {
event
};
let init_player_points = game.who_plays().map(|p| (p.points, p.holes));
let turn_stage = game.turn_stage;
game.consume(internal_event);
2025-01-26 17:52:57 +01:00
if game.stage == Stage::Ended {
2025-08-08 16:24:12 +02:00
debug!("<<<< end {:?} BOT handle", self.color);
2025-01-26 17:52:57 +01:00
return None;
}
2025-08-04 18:04:40 +02:00
let active_player_id = if self.color == Color::Black {
if game.active_player_id == 1 {
2
} else {
1
}
} else {
game.active_player_id
};
if active_player_id == self.player_id {
let player_points = game.who_plays().map(|p| (p.points, p.holes));
if self.color == Color::Black {
2025-08-08 16:24:12 +02:00
debug!( " input (internal) evt : {internal_event:?}, points : {init_player_points:?}, stage : {turn_stage:?}");
2025-08-04 18:04:40 +02:00
}
let internal_event = match game.turn_stage {
TurnStage::MarkAdvPoints => Some(GameEvent::Mark {
2025-08-04 18:04:40 +02:00
player_id: 1,
2024-10-17 17:35:07 +02:00
points: self.strategy.calculate_adv_points(),
}),
2025-08-04 18:04:40 +02:00
TurnStage::RollDice => Some(GameEvent::Roll { player_id: 1 }),
2024-03-24 18:37:35 +01:00
TurnStage::MarkPoints => Some(GameEvent::Mark {
2025-08-04 18:04:40 +02:00
player_id: 1,
2024-10-17 17:35:07 +02:00
points: self.strategy.calculate_points(),
2024-03-24 18:37:35 +01:00
}),
TurnStage::Move => Some(GameEvent::Move {
2025-08-04 18:04:40 +02:00
player_id: 1,
2024-10-17 17:35:07 +02:00
moves: self.strategy.choose_move(),
2024-03-24 18:37:35 +01:00
}),
2025-01-09 21:27:24 +01:00
TurnStage::HoldOrGoChoice => {
if self.strategy.choose_go() {
2025-08-04 18:04:40 +02:00
Some(GameEvent::Go { player_id: 1 })
2025-01-09 21:27:24 +01:00
} else {
Some(GameEvent::Move {
2025-08-04 18:04:40 +02:00
player_id: 1,
2025-01-09 21:27:24 +01:00
moves: self.strategy.choose_move(),
})
}
}
2024-03-27 21:10:15 +01:00
_ => None,
2024-03-24 18:37:35 +01:00
};
2025-08-04 18:04:40 +02:00
return if self.color == Color::Black {
2025-08-08 16:24:12 +02:00
debug!(" bot (internal) evt : {internal_event:?} ; points : {player_points:?}");
debug!("<<<< end {:?} BOT handle", self.color);
2025-08-04 18:04:40 +02:00
internal_event.map(|evt| evt.get_mirror())
} else {
2025-08-08 16:24:12 +02:00
debug!("<<<< end {:?} BOT handle", self.color);
2025-08-04 18:04:40 +02:00
internal_event
};
2024-03-24 18:37:35 +01:00
}
2025-08-08 16:24:12 +02:00
debug!("<<<< end {:?} BOT handle", self.color);
2024-03-24 18:37:35 +01:00
None
}
2025-08-04 18:04:40 +02:00
// Only used in tests below
2024-10-17 17:35:07 +02:00
pub fn get_state(&self) -> &GameState {
self.strategy.get_game()
2024-03-24 18:37:35 +01:00
}
}
#[cfg(test)]
mod tests {
use super::*;
2024-11-04 17:37:36 +01:00
use store::{Dice, Stage};
2024-03-24 18:37:35 +01:00
#[test]
fn test_new() {
2024-11-04 17:37:36 +01:00
let bot = Bot::new(Box::new(DefaultStrategy::default()), Color::Black);
// let bot = Bot::new(Box::new(DefaultStrategy::default()), Color::Black, false);
2024-10-17 17:35:07 +02:00
assert_eq!(bot.get_state().stage, Stage::PreGame);
2024-03-24 18:37:35 +01:00
}
#[test]
2025-08-04 18:04:40 +02:00
fn test_handle_event() {
2024-11-04 17:37:36 +01:00
let mut bot = Bot::new(Box::new(DefaultStrategy::default()), Color::Black);
// let mut bot = Bot::new(Box::new(DefaultStrategy::default()), Color::Black, false);
2024-03-30 16:10:53 +01:00
let mut event = bot.handle_event(&GameEvent::BeginGame { goes_first: 2 });
2024-03-24 18:37:35 +01:00
assert_eq!(event, Some(GameEvent::Roll { player_id: 2 }));
2025-08-04 18:04:40 +02:00
assert_eq!(bot.get_state().active_player_id, 1); // bot internal active_player_id for black
event = bot.handle_event(&GameEvent::RollResult {
player_id: 2,
dice: Dice { values: (2, 3) },
});
assert_eq!(
event,
Some(GameEvent::Move {
player_id: 2,
moves: (
CheckerMove::new(24, 21).unwrap(),
CheckerMove::new(24, 22).unwrap()
)
})
);
2024-03-24 18:37:35 +01:00
2024-03-30 16:10:53 +01:00
event = bot.handle_event(&GameEvent::BeginGame { goes_first: 1 });
2024-03-24 18:37:35 +01:00
assert_eq!(event, None);
2025-08-04 18:04:40 +02:00
assert_eq!(bot.get_state().active_player_id, 2); //internal active_player_id
2024-03-30 16:10:53 +01:00
bot.handle_event(&GameEvent::RollResult {
2024-10-17 17:35:07 +02:00
player_id: 1,
2024-03-24 18:37:35 +01:00
dice: Dice { values: (2, 3) },
});
2024-10-17 17:35:07 +02:00
assert_eq!(bot.get_state().turn_stage, TurnStage::Move);
2024-03-24 18:37:35 +01:00
}
}