trictrac/bot/src/lib.rs

202 lines
5.8 KiB
Rust
Raw Normal View History

2024-03-24 18:37:35 +01:00
mod bot;
2024-05-20 19:04:46 +02:00
use store::{
CheckerMove, Color, Dice, GameEvent, GameState, Player, PlayerId, PointsRules, Stage, TurnStage,
};
2024-03-24 18:37:35 +01:00
2024-10-17 17:35:07 +02:00
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");
}
}
2024-03-24 18:37:35 +01:00
#[derive(Debug)]
2024-10-17 17:35:07 +02:00
pub struct DefaultStrategy {
2024-03-24 18:37:35 +01:00
pub game: GameState,
2024-03-27 21:10:15 +01:00
pub player_id: PlayerId,
2024-10-17 17:35:07 +02:00
pub color: Color,
}
impl Default for DefaultStrategy {
fn default() -> Self {
let game = GameState::default();
let mut strategy = Self {
game,
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
}
fn set_player_id(&mut self, player_id: PlayerId) {
self.player_id = player_id;
}
fn calculate_points(&self) -> u8 {
let dice_roll_count = self
.get_game()
.players
.get(&self.player_id)
.unwrap()
.dice_roll_count;
let points_rules = PointsRules::new(&Color::White, &self.game.board, self.game.dice);
points_rules.get_points(dice_roll_count).0
}
fn calculate_adv_points(&self) -> u8 {
let dice_roll_count = self
.get_game()
.players
.get(&self.player_id)
.unwrap()
.dice_roll_count;
let points_rules = PointsRules::new(&Color::White, &self.game.board, self.game.dice);
points_rules.get_points(dice_roll_count).0
}
fn choose_move(&self) -> (CheckerMove, CheckerMove) {
let (dice1, dice2) = match self.color {
Color::White => (self.game.dice.values.0 as i8, self.game.dice.values.1 as i8),
Color::Black => (
0 - self.game.dice.values.0 as i8,
0 - self.game.dice.values.1 as i8,
),
};
let fields = self.game.board.get_color_fields(self.color);
let first_field = fields.first().unwrap();
(
CheckerMove::new(first_field.0, (first_field.0 as i8 + dice1) as usize).unwrap(),
CheckerMove::new(first_field.0, (first_field.0 as i8 + dice2) as usize).unwrap(),
)
}
}
#[derive(Debug)]
pub struct Bot<BotStrategy> {
pub player_id: PlayerId,
strategy: BotStrategy,
2024-03-24 18:37:35 +01:00
color: Color,
2024-09-22 16:11:42 +02:00
schools_enabled: bool,
2024-03-24 18:37:35 +01:00
}
2024-10-17 17:35:07 +02:00
impl Default for Bot<DefaultStrategy> {
fn default() -> Self {
Self {
player_id: 2,
strategy: DefaultStrategy::default(),
2024-03-24 18:37:35 +01:00
color: Color::Black,
2024-09-22 16:11:42 +02:00
schools_enabled: false,
2024-03-24 18:37:35 +01:00
}
}
}
2024-10-17 17:35:07 +02:00
impl<BS> Bot<BS>
where
BS: BotStrategy,
{
2024-03-24 18:37:35 +01:00
/// new initialize a bot
/// # Examples
/// ```let mut bot = Bot::new(Color::Black);
/// assert_eq!(bot.game.stage, Stage::PreGame);
/// ```
2024-10-17 17:35:07 +02:00
pub fn new(mut strategy: BS, color: Color, schools_enabled: bool) -> Self {
let game = strategy.get_mut_game();
strategy.init_players();
2024-03-24 18:37:35 +01:00
let player_id = match color {
Color::White => 1,
Color::Black => 2,
};
2024-10-17 17:35:07 +02:00
strategy.set_player_id(player_id);
2024-03-24 18:37:35 +01:00
Self {
player_id,
2024-10-17 17:35:07 +02:00
strategy,
2024-03-24 18:37:35 +01:00
color,
2024-09-22 16:11:42 +02: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> {
2024-10-17 17:35:07 +02:00
let game = self.strategy.get_mut_game();
game.consume(event);
2024-03-29 21:04:58 +01:00
// println!("bot game {:?}", self.game);
// println!("bot player_id {:?}", self.player_id);
2024-10-17 17:35:07 +02:00
if game.active_player_id == self.player_id {
return match game.turn_stage {
TurnStage::MarkAdvPoints => Some(GameEvent::Mark {
player_id: self.player_id,
2024-10-17 17:35:07 +02:00
points: self.strategy.calculate_adv_points(),
}),
2024-03-24 18:37:35 +01:00
TurnStage::RollDice => Some(GameEvent::Roll {
player_id: self.player_id,
}),
TurnStage::MarkPoints => Some(GameEvent::Mark {
player_id: self.player_id,
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 {
player_id: self.player_id,
2024-10-17 17:35:07 +02:00
moves: self.strategy.choose_move(),
2024-03-24 18:37:35 +01:00
}),
2024-03-27 21:10:15 +01:00
_ => None,
2024-03-24 18:37:35 +01:00
};
}
None
}
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::*;
#[test]
fn test_new() {
2024-10-17 17:35:07 +02:00
let bot = Bot::new(DefaultStrategy::new(), Color::Black, false);
assert_eq!(bot.get_state().stage, Stage::PreGame);
2024-03-24 18:37:35 +01:00
}
#[test]
fn test_consume() {
2024-10-17 17:35:07 +02:00
let mut bot = Bot::new(DefaultStrategy::new(), 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 }));
2024-10-17 17:35:07 +02:00
assert_eq!(bot.get_state().active_player_id, 2);
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);
2024-10-17 17:35:07 +02:00
assert_eq!(bot.get_state().active_player_id, 1);
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
}
}