feat: calcul automatique des points #3

This commit is contained in:
Henri Bourcereau 2024-09-22 16:11:42 +02:00
parent 08fd067a95
commit 17605efe76
4 changed files with 87 additions and 18 deletions

View file

@ -9,6 +9,7 @@ pub struct Bot {
pub game: GameState, pub game: GameState,
pub player_id: PlayerId, pub player_id: PlayerId,
color: Color, color: Color,
schools_enabled: bool,
} }
impl Default for Bot { impl Default for Bot {
@ -17,6 +18,7 @@ impl Default for Bot {
game: GameState::default(), game: GameState::default(),
player_id: 1, player_id: 1,
color: Color::Black, color: Color::Black,
schools_enabled: false,
} }
} }
} }
@ -29,7 +31,7 @@ impl Bot {
/// ```let mut bot = Bot::new(Color::Black); /// ```let mut bot = Bot::new(Color::Black);
/// assert_eq!(bot.game.stage, Stage::PreGame); /// assert_eq!(bot.game.stage, Stage::PreGame);
/// ``` /// ```
pub fn new(color: Color) -> Self { pub fn new(color: Color, schools_enabled: bool) -> Self {
let mut game = GameState::default(); let mut game = GameState::default();
game.init_player("p1"); game.init_player("p1");
game.init_player("p2"); game.init_player("p2");
@ -43,6 +45,7 @@ impl Bot {
game, game,
player_id, player_id,
color, color,
schools_enabled: false,
} }
} }
@ -107,13 +110,13 @@ mod tests {
#[test] #[test]
fn test_new() { fn test_new() {
let bot = Bot::new(Color::Black); let bot = Bot::new(Color::Black, false);
assert_eq!(bot.game.stage, Stage::PreGame); assert_eq!(bot.game.stage, Stage::PreGame);
} }
#[test] #[test]
fn test_consume() { fn test_consume() {
let mut bot = Bot::new(Color::Black); let mut bot = Bot::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 }));
@ -124,6 +127,6 @@ mod tests {
player_id: 2, player_id: 2,
dice: Dice { values: (2, 3) }, dice: Dice { values: (2, 3) },
}); });
assert_eq!(bot.game.turn_stage, TurnStage::MarkPoints); assert_eq!(bot.game.turn_stage, TurnStage::Move);
} }
} }

View file

@ -2,7 +2,9 @@ use itertools::Itertools;
use bot::Bot; use bot::Bot;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use store::{CheckerMove, DiceRoller, GameEvent, GameState, PlayerId, Stage, TurnStage}; use store::{
CheckerMove, DiceRoller, GameEvent, GameState, PlayerId, PointsRules, Stage, TurnStage,
};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct AppArgs { pub struct AppArgs {
@ -21,14 +23,14 @@ pub struct Game {
impl Game { impl Game {
// Constructs a new instance of [`App`]. // Constructs a new instance of [`App`].
pub fn new(seed: Option<u64>) -> Self { pub fn new(schools_enabled: bool, seed: Option<u64>) -> Self {
let mut state = GameState::default(); let mut state = GameState::new(schools_enabled);
// local : player // local : player
let player_id: Option<PlayerId> = state.init_player("myself"); let player_id: Option<PlayerId> = state.init_player("myself");
// 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); let bot: Bot = Bot::new(bot_color, schools_enabled);
let mut game = Self { let mut game = Self {
state, state,
@ -77,20 +79,23 @@ impl Game {
pub struct App { pub struct App {
// should the application exit? // should the application exit?
pub should_quit: bool, pub should_quit: bool,
pub schools_enabled: bool,
pub game: Game, pub game: Game,
} }
impl App { impl App {
// Constructs a new instance of [`App`]. // Constructs a new instance of [`App`].
pub fn new(args: AppArgs) -> Self { pub fn new(args: AppArgs) -> Self {
let schools_enabled = false;
Self { Self {
game: Game::new(args.seed.map(|s| s as u64)), game: Game::new(schools_enabled, args.seed.map(|s| s as u64)),
should_quit: false, should_quit: false,
schools_enabled,
} }
} }
pub fn start(&mut self) { pub fn start(&mut self) {
self.game.state = GameState::new(); self.game.state = GameState::new(self.schools_enabled);
} }
pub fn input(&mut self, input: &str) { pub fn input(&mut self, input: &str) {
@ -130,6 +135,17 @@ impl App {
return; return;
} }
let dice = self.game.dice_roller.roll(); let dice = self.game.dice_roller.roll();
// get correct points for these board and dice
let points_rules = PointsRules::new(
&self
.game
.state
.player_color_by_id(&self.game.player_id.unwrap())
.unwrap(),
&self.game.state.board,
dice,
);
self.game.handle_event(&GameEvent::RollResult { self.game.handle_event(&GameEvent::RollResult {
player_id: self.game.player_id.unwrap(), player_id: self.game.player_id.unwrap(),
dice, dice,
@ -247,7 +263,7 @@ Rolled dice : 0 & 0
#[test] #[test]
fn test_move() { fn test_move() {
let expected = "------------------------------- let expected = "-------------------------------
InGame > myself > RollDice InGame > myself > MarkAdvPoints
Rolled dice : 4 & 6 Rolled dice : 4 & 6
Player :: holes :: points Player :: holes :: points

View file

@ -42,8 +42,12 @@ pub struct GameState {
pub history: Vec<GameEvent>, pub history: Vec<GameEvent>,
/// last dice pair rolled /// last dice pair rolled
pub dice: Dice, pub dice: Dice,
/// players points computed for the last dice pair rolled
dice_points: (u8, u8),
/// true if player needs to roll first /// true if player needs to roll first
roll_first: bool, roll_first: bool,
// NOTE: add to a Setting struct if other fields needed
pub schools_enabled: bool,
} }
// implement Display trait // implement Display trait
@ -71,15 +75,33 @@ impl Default for GameState {
players: HashMap::new(), players: HashMap::new(),
history: Vec::new(), history: Vec::new(),
dice: Dice::default(), dice: Dice::default(),
dice_points: (0, 0),
roll_first: true, roll_first: true,
schools_enabled: false,
} }
} }
} }
impl GameState { impl GameState {
/// Create a new default game /// Create a new default game
pub fn new() -> Self { pub fn new(schools_enabled: bool) -> Self {
GameState::default() let mut gs = GameState::default();
gs.set_schools_enabled(schools_enabled);
gs
}
fn set_schools_enabled(&mut self, schools_enabled: bool) {
self.schools_enabled = schools_enabled;
}
fn get_opponent_id(&self) -> Option<PlayerId> {
self.players
.keys()
.map(|k| *k)
.filter(|k| k != &self.active_player_id)
.collect::<Vec<PlayerId>>()
.first()
.copied()
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
@ -358,11 +380,30 @@ impl GameState {
self.players.remove(player_id); self.players.remove(player_id);
} }
Roll { player_id: _ } => { Roll { player_id: _ } => {
self.turn_stage = TurnStage::RollWaiting; // Opponent has moved, we can mark pending points earned during opponent's turn
self.mark_points(self.active_player_id, self.dice_points.1);
if self.stage != Stage::Ended {
self.turn_stage = TurnStage::RollWaiting;
}
} }
RollResult { player_id: _, dice } => { RollResult { player_id, dice } => {
self.dice = *dice; self.dice = *dice;
self.turn_stage = TurnStage::MarkPoints; self.turn_stage = TurnStage::MarkPoints;
// We compute points for the move
let points_rules = PointsRules::new(
&self.player_color_by_id(&self.active_player_id).unwrap(),
&self.board,
*dice,
);
self.dice_points = points_rules.get_points();
if !self.schools_enabled {
// Schools are not enabled. We mark points automatically
// the points earned by the opponent will be marked on its turn
self.mark_points(self.active_player_id, self.dice_points.0);
if self.stage != Stage::Ended {
self.turn_stage = TurnStage::Move;
}
}
} }
Mark { player_id, points } => { Mark { player_id, points } => {
self.mark_points(*player_id, *points); self.mark_points(*player_id, *points);
@ -379,7 +420,11 @@ impl GameState {
self.board.move_checker(&player.color, moves.0).unwrap(); self.board.move_checker(&player.color, moves.0).unwrap();
self.board.move_checker(&player.color, moves.1).unwrap(); self.board.move_checker(&player.color, moves.1).unwrap();
self.active_player_id = *self.players.keys().find(|id| *id != player_id).unwrap(); self.active_player_id = *self.players.keys().find(|id| *id != player_id).unwrap();
self.turn_stage = TurnStage::MarkAdvPoints; self.turn_stage = if self.schools_enabled {
TurnStage::MarkAdvPoints
} else {
TurnStage::RollDice
};
} }
} }
@ -393,7 +438,9 @@ impl GameState {
fn mark_points(&mut self, player_id: PlayerId, points: u8) { fn mark_points(&mut self, player_id: PlayerId, points: u8) {
self.players.get_mut(&player_id).map(|p| { self.players.get_mut(&player_id).map(|p| {
p.points += points; let sum_points = p.points + points;
p.points = sum_points % 12;
p.holes += sum_points / 12;
p p
}); });
} }

View file

@ -443,10 +443,13 @@ impl PointsRules {
pub fn get_points(&self) -> (u8, u8) { pub fn get_points(&self) -> (u8, u8) {
let jans = self.get_jans(&self.board); let jans = self.get_jans(&self.board);
// if !jans.is_empty() {
// println!("get points : {:?}", jans);
// }
let (points, adv_points) = jans let (points, adv_points) = jans
.into_iter() .into_iter()
.fold((0, 0), |acc: (i8, i8), (jan, moves)| { .fold((0, 0), |acc: (i8, i8), (jan, moves)| {
println!("get_points : {:?}", jan); // println!("get_points : {:?}", jan);
let is_double = if jan == Jan::HelplessMan { let is_double = if jan == Jan::HelplessMan {
moves[0] == (CheckerMove::default(), CheckerMove::default()) moves[0] == (CheckerMove::default(), CheckerMove::default())
} else { } else {