diff --git a/bot/src/lib.rs b/bot/src/lib.rs index a585dc7..5a02197 100644 --- a/bot/src/lib.rs +++ b/bot/src/lib.rs @@ -77,13 +77,25 @@ impl Bot { } fn calculate_points(&self) -> u8 { + let dice_roll_count = self + .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().0 + points_rules.get_points(dice_roll_count).0 } fn calculate_adv_points(&self) -> u8 { + let dice_roll_count = self + .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().1 + points_rules.get_points(dice_roll_count).0 } fn choose_move(&self) -> (CheckerMove, CheckerMove) { diff --git a/client_cli/src/app.rs b/client_cli/src/app.rs index 0dfe5da..b1eb184 100644 --- a/client_cli/src/app.rs +++ b/client_cli/src/app.rs @@ -209,7 +209,7 @@ impl App { let player = &self.game.state.players[player_id]; output += format!( "\n{}. {:<8} :: {:<5} :: {}", - &player_id, &player.name, &player.holes, &player.points + &player_id, &player.name, &player.holes, &player.points, ) .as_str(); } @@ -263,7 +263,7 @@ Rolled dice : 0 & 0 #[test] fn test_move() { let expected = "------------------------------- -InGame > myself > MarkAdvPoints +InGame > myself > RollDice Rolled dice : 4 & 6 Player :: holes :: points diff --git a/store/src/game.rs b/store/src/game.rs index 8b5cc1b..f36cf61 100644 --- a/store/src/game.rs +++ b/store/src/game.rs @@ -373,6 +373,7 @@ impl GameState { points: 0, can_bredouille: true, can_big_bredouille: true, + dice_roll_count: 0, }, ); } @@ -388,14 +389,9 @@ impl GameState { } RollResult { player_id, dice } => { self.dice = *dice; + self.inc_roll_count(self.active_player_id); 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(); + self.dice_points = self.get_rollresult_points(dice); if !self.schools_enabled { // Schools are not enabled. We mark points automatically // the points earned by the opponent will be marked on its turn @@ -431,11 +427,24 @@ impl GameState { self.history.push(valid_event.clone()); } + fn get_rollresult_points(&self, dice: &Dice) -> (u8, u8) { + let player = &self.players.get(&self.active_player_id).unwrap(); + let points_rules = PointsRules::new(&player.color, &self.board, *dice); + points_rules.get_points(player.dice_roll_count) + } + /// Determines if someone has won the game pub fn determine_winner(&self) -> Option { None } + fn inc_roll_count(&mut self, player_id: PlayerId) { + self.players.get_mut(&player_id).map(|p| { + p.dice_roll_count += 1; + p + }); + } + fn mark_points(&mut self, player_id: PlayerId, points: u8) { self.players.get_mut(&player_id).map(|p| { let sum_points = p.points + points; diff --git a/store/src/game_rules_points.rs b/store/src/game_rules_points.rs index b1f10e0..feb2b91 100644 --- a/store/src/game_rules_points.rs +++ b/store/src/game_rules_points.rs @@ -2,14 +2,14 @@ use std::cmp; use std::collections::HashMap; use crate::board::{Board, Field, EMPTY_MOVE}; -use crate::dice::Dice; +use crate::dice::{self, Dice}; use crate::game_rules_moves::MoveRules; use crate::player::Color; use crate::CheckerMove; use crate::Error; #[derive(PartialEq, Eq, Hash, Clone, Debug)] -enum Jan { +pub enum Jan { FilledQuarter, TrueHitSmallJan, TrueHitBigJan, @@ -161,7 +161,7 @@ impl PointsRules { self.move_rules.board.set_positions(positions); } - fn get_jans(&self, board_ini: &Board) -> PossibleJans { + fn get_jans(&self, board_ini: &Board, dice_rolls_count: u8) -> PossibleJans { let dices = &vec![self.dice.values.0, self.dice.values.1]; let dices_reversed = &vec![self.dice.values.1, self.dice.values.0]; let dice1 = self.dice.values.0 as usize; @@ -248,31 +248,33 @@ impl PointsRules { } // « JANS RARES » - // Jan de 6 tables - // on devrait avoir 4 cases occupées par une dame chacune - let fields_with_single: Vec<&(usize, i8)> = - checkers.iter().filter(|(f, c)| c == &1).collect(); - if fields_with_single.len() == 4 { - let checkers_fields: Vec = checkers.iter().map(|(f, c)| *f).collect(); - let mut missing_for_6tables: Vec = Vec::from([2, 3, 4, 5, 6, 7]) - .into_iter() - .filter(|f| !checkers_fields.contains(f)) - .collect(); - if missing_for_6tables.len() == 2 { - // Les dés doivent permettre le mouvement de deux dames du talon vers les 2 cases - // vides - let mut dice_to: Vec = vec![ - 1 + self.dice.values.0 as usize, - 1 + self.dice.values.1 as usize, - ]; - missing_for_6tables.sort(); - dice_to.sort(); - if dice_to == missing_for_6tables { - let moves = vec![( - CheckerMove::new(1, missing_for_6tables[0]).unwrap(), - CheckerMove::new(1, missing_for_6tables[1]).unwrap(), - )]; - jans.insert(Jan::SixTables, moves); + // Jan de 3 coups ou de 6 tables + if dice_rolls_count == 3 { + // on devrait avoir 4 cases occupées par une dame chacune + let fields_with_single: Vec<&(usize, i8)> = + checkers.iter().filter(|(f, c)| c == &1).collect(); + if fields_with_single.len() == 4 { + let checkers_fields: Vec = checkers.iter().map(|(f, c)| *f).collect(); + let mut missing_for_6tables: Vec = Vec::from([2, 3, 4, 5, 6, 7]) + .into_iter() + .filter(|f| !checkers_fields.contains(f)) + .collect(); + if missing_for_6tables.len() == 2 { + // Les dés doivent permettre le mouvement de deux dames du talon vers les 2 cases + // vides + let mut dice_to: Vec = vec![ + 1 + self.dice.values.0 as usize, + 1 + self.dice.values.1 as usize, + ]; + missing_for_6tables.sort(); + dice_to.sort(); + if dice_to == missing_for_6tables { + let moves = vec![( + CheckerMove::new(1, missing_for_6tables[0]).unwrap(), + CheckerMove::new(1, missing_for_6tables[1]).unwrap(), + )]; + jans.insert(Jan::SixTables, moves); + } } } } @@ -441,11 +443,7 @@ impl PointsRules { jans } - pub fn get_points(&self) -> (u8, u8) { - let jans = self.get_jans(&self.board); - // if !jans.is_empty() { - // println!("get points : {:?}", jans); - // } + pub fn get_jans_points(&self, jans: HashMap>) -> (u8, u8) { let (points, adv_points) = jans .into_iter() .fold((0, 0), |acc: (i8, i8), (jan, moves)| { @@ -465,6 +463,11 @@ impl PointsRules { (points as u8, adv_points as u8) } + + pub fn get_points(&self, dice_rolls_count: u8) -> (u8, u8) { + let jans = self.get_jans(&self.board, dice_rolls_count); + self.get_jans_points(jans) + } } #[cfg(test)] @@ -567,7 +570,7 @@ mod tests { 2, 0, -1, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); rules.set_dice(Dice { values: (2, 3) }); - assert_eq!(12, rules.get_points().0); + assert_eq!(12, rules.get_points(5).0); // Battre à vrai une dame située dans la table des grands jans : 2 + 2 = 4 let mut rules = PointsRules::default(); @@ -575,28 +578,28 @@ mod tests { 2, 0, 0, -1, 2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); rules.set_dice(Dice { values: (2, 4) }); - assert_eq!(4, rules.get_points().0); + assert_eq!(4, rules.get_points(5).0); // Battre à vrai une dame située dans la table des grands jans : 2 let mut rules = PointsRules::default(); rules.update_positions([ 2, 0, -2, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); rules.set_dice(Dice { values: (2, 4) }); - assert_eq!((2, 2), rules.get_points()); + assert_eq!((2, 2), rules.get_points(5)); // Battre à vrai le coin adverse par doublet : 6 rules.update_positions([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); rules.set_dice(Dice { values: (2, 2) }); - assert_eq!(6, rules.get_points().0); + assert_eq!(6, rules.get_points(5).0); // Cas de battage du coin de repos adverse impossible rules.update_positions([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); rules.set_dice(Dice { values: (1, 1) }); - assert_eq!(0, rules.get_points().0); + assert_eq!(0, rules.get_points(5).0); // ---- Jan de remplissage // Faire un petit jan : 4 @@ -604,29 +607,29 @@ mod tests { 3, 1, 2, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); rules.set_dice(Dice { values: (2, 1) }); - assert_eq!(1, rules.get_jans(&rules.board).len()); - assert_eq!(4, rules.get_points().0); + assert_eq!(1, rules.get_jans(&rules.board, 5).len()); + assert_eq!(4, rules.get_points(5).0); // Faire un petit jan avec un doublet : 6 rules.update_positions([ 2, 3, 1, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); rules.set_dice(Dice { values: (1, 1) }); - assert_eq!(6, rules.get_points().0); + assert_eq!(6, rules.get_points(5).0); // Faire un petit jan avec 2 moyens : 6 + 6 = 12 rules.update_positions([ 3, 3, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); rules.set_dice(Dice { values: (1, 1) }); - assert_eq!(12, rules.get_points().0); + assert_eq!(12, rules.get_points(5).0); // Conserver un jan avec un doublet : 6 rules.update_positions([ 3, 3, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); rules.set_dice(Dice { values: (1, 1) }); - assert_eq!(6, rules.get_points().0); + assert_eq!(6, rules.get_points(5).0); // ---- Sorties // Sortir toutes ses dames avant l'adversaire (simple) @@ -634,14 +637,14 @@ mod tests { 0, 0, -2, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, ]); rules.set_dice(Dice { values: (3, 1) }); - assert_eq!(4, rules.get_points().0); + assert_eq!(4, rules.get_points(5).0); // Sortir toutes ses dames avant l'adversaire (doublet) rules.update_positions([ 0, 0, -2, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, ]); rules.set_dice(Dice { values: (2, 2) }); - assert_eq!(6, rules.get_points().0); + assert_eq!(6, rules.get_points(5).0); // ---- JANS RARES // Jan de six tables @@ -649,55 +652,60 @@ mod tests { 10, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, ]); rules.set_dice(Dice { values: (2, 3) }); - assert_eq!(4, rules.get_points().0); + assert_eq!(0, rules.get_points(5).0); + rules.update_positions([ + 10, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, + ]); + rules.set_dice(Dice { values: (2, 3) }); + assert_eq!(4, rules.get_points(3).0); rules.update_positions([ 10, 1, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, ]); rules.set_dice(Dice { values: (2, 3) }); - assert_eq!(0, rules.get_points().0); + assert_eq!(0, rules.get_points(3).0); rules.update_positions([ 10, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, ]); rules.set_dice(Dice { values: (2, 3) }); - assert_eq!(0, rules.get_points().0); + assert_eq!(0, rules.get_points(3).0); // Jan de deux tables rules.update_positions([ 13, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, ]); rules.set_dice(Dice { values: (2, 2) }); - assert_eq!(6, rules.get_points().0); + assert_eq!(6, rules.get_points(5).0); rules.update_positions([ 12, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, ]); rules.set_dice(Dice { values: (2, 2) }); - assert_eq!(0, rules.get_points().0); + assert_eq!(0, rules.get_points(5).0); // Contre jan de deux tables rules.update_positions([ 13, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, ]); rules.set_dice(Dice { values: (2, 2) }); - assert_eq!((0, 6), rules.get_points()); + assert_eq!((0, 6), rules.get_points(5)); // Jan de mézéas rules.update_positions([ 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, ]); rules.set_dice(Dice { values: (1, 1) }); - assert_eq!(6, rules.get_points().0); + assert_eq!(6, rules.get_points(5).0); rules.update_positions([ 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, ]); rules.set_dice(Dice { values: (1, 2) }); - assert_eq!(4, rules.get_points().0); + assert_eq!(4, rules.get_points(5).0); // Contre jan de mézéas rules.update_positions([ 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, ]); rules.set_dice(Dice { values: (1, 1) }); - assert_eq!((0, 6), rules.get_points()); + assert_eq!((0, 6), rules.get_points(5)); // ---- JANS QUI NE PEUT // Battre à faux une dame située dans la table des petits jans @@ -706,7 +714,7 @@ mod tests { 2, 0, -2, -2, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); rules.set_dice(Dice { values: (2, 3) }); - assert_eq!((0, 4), rules.get_points()); + assert_eq!((0, 4), rules.get_points(5)); // Battre à faux une dame située dans la table des grands jans let mut rules = PointsRules::default(); @@ -714,7 +722,7 @@ mod tests { 2, 0, -2, -1, -2, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); rules.set_dice(Dice { values: (2, 4) }); - assert_eq!((0, 2), rules.get_points()); + assert_eq!((0, 2), rules.get_points(5)); // Pour chaque dé non jouable (dame impuissante) let mut rules = PointsRules::default(); @@ -722,6 +730,6 @@ mod tests { 2, 0, -2, -2, -2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); rules.set_dice(Dice { values: (2, 4) }); - assert_eq!((0, 4), rules.get_points()); + assert_eq!((0, 4), rules.get_points(5)); } } diff --git a/store/src/player.rs b/store/src/player.rs index 1e7d062..1254955 100644 --- a/store/src/player.rs +++ b/store/src/player.rs @@ -28,6 +28,9 @@ pub struct Player { pub holes: u8, pub can_bredouille: bool, pub can_big_bredouille: bool, + /// Number of dice rolls since beginning of the current setting (all 15 dames in the talon ) + /// (used to check jan de 3 coups) + pub dice_roll_count: u8, } impl Player { @@ -39,6 +42,7 @@ impl Player { holes: 0, can_bredouille: true, can_big_bredouille: true, + dice_roll_count: 0, } } @@ -113,6 +117,7 @@ mod tests { holes: 3, can_bredouille: true, can_big_bredouille: false, + dice_roll_count: 0, }; println!("{}", player.to_bits_string()); assert!(player.to_bits_string() == "1011001110");