diff --git a/doc/backlog.md b/doc/backlog.md index 6da0c82..4cdd180 100644 --- a/doc/backlog.md +++ b/doc/backlog.md @@ -4,13 +4,16 @@ position dans tutoriel : ## DONE +### Epic : jeu simple + +Store +- gestion des évènements + - déplacement de dames + - jet des dés +- déplacements physiques possibles + ## TODO -### Doc - -Cheatsheet : arbre des situations et priorité des règles - - ### Epic : jeu simple - déplacements autorisés par les règles (pourront être validés physiquement si jeu avec écoles) diff --git a/store/src/board.rs b/store/src/board.rs index 51ea842..ab1ed7d 100644 --- a/store/src/board.rs +++ b/store/src/board.rs @@ -28,12 +28,6 @@ fn transpose(matrix: Vec>) -> Vec> { out } -impl Default for CheckerMove { - fn default() -> Self { - EMPTY_MOVE - } -} - impl CheckerMove { pub fn new(from: Field, to: Field) -> Result { // println!("from {} to {}", from, to); @@ -52,13 +46,6 @@ impl CheckerMove { Ok(Self { from, to }) } - /// Get the mirrord CheckerMove (ie change colors) - pub fn mirror(&self) -> Self { - let from = if self.from == 0 { 0 } else { 25 - self.from }; - let to = if self.to == 0 { 0 } else { 25 - self.to }; - Self { from, to } - } - // Construct the move resulting of two successive moves pub fn chain(self, cmove: Self) -> Result { if self.to != cmove.from { @@ -114,13 +101,6 @@ impl Board { Board::default() } - /// Get the mirrord board (ie change colors) - pub fn mirror(&self) -> Self { - let mut positions = self.positions.map(|c| 0 - c); - positions.reverse(); - Board { positions } - } - /// Globally set pieces on board ( for tests ) pub fn set_positions(&mut self, positions: [i8; 24]) { self.positions = positions; @@ -310,8 +290,10 @@ impl Board { } // the square is blocked on the opponent rest corner or if there are opponent's men on the square - let opp_corner_field = if color == &Color::White { 13 } else { 12 }; - Ok(field == opp_corner_field || self.positions[field - 1] < 0) + match color { + Color::White => Ok(field == 13 || self.positions[field - 1] < 0), + Color::Black => Ok(field == 12 || self.positions[23 - field] > 1), + } } pub fn get_field_checkers(&self, field: Field) -> Result<(u8, Option<&Color>), Error> { diff --git a/store/src/game.rs b/store/src/game.rs index 060c20c..0c7c704 100644 --- a/store/src/game.rs +++ b/store/src/game.rs @@ -69,6 +69,15 @@ impl PointsRules for GameState { } } +impl MoveRules for GameState { + fn board(&self) -> &Board { + &self.board + } + fn dice(&self) -> &Dice { + &self.dice + } +} + impl Default for GameState { fn default() -> Self { Self { @@ -266,8 +275,18 @@ impl GameState { } let color = &self.players[player_id].color; - let rules = MoveRules::new(color, &self.board, self.dice, moves); - if !rules.moves_follow_rules() { + // Check moves possibles on the board + if !self.moves_possible(color, moves) { + return false; + } + + // Check moves conforms to the dice + if !self.moves_follows_dices(color, moves) { + return false; + } + + // Check move is allowed by the rules (to desactivate when playing with schools) + if self.moves_allowed(color, moves).is_err() { return false; } } diff --git a/store/src/game_rules_moves.rs b/store/src/game_rules_moves.rs index b485f47..1092b97 100644 --- a/store/src/game_rules_moves.rs +++ b/store/src/game_rules_moves.rs @@ -30,100 +30,29 @@ pub enum MoveError { MustPlayStrongerDie, } -/// MoveRules always consider that the current player is White -/// You must use 'mirror' functions on board & CheckerMoves if player is Black -#[derive(Default)] -pub struct MoveRules { - pub board: Board, - pub dice: Dice, - pub moves: (CheckerMove, CheckerMove), -} +pub trait MoveRules { + fn board(&self) -> &Board; + fn dice(&self) -> &Dice; -impl MoveRules { - /// Revert board if color is black - pub fn new( - color: &Color, - board: &Board, - dice: Dice, - moves: &(CheckerMove, CheckerMove), - ) -> Self { - let (board, moves) = if *color == Color::Black { - (board.mirror(), (moves.0.mirror(), moves.1.mirror())) - } else { - (board.clone(), *moves) - }; - Self { board, dice, moves } - } - - pub fn moves_follow_rules(&self) -> bool { - // Check moves possibles on the board - if !self.moves_possible() { - return false; - } - - // Check moves conforms to the dice - if !self.moves_follows_dices() { - return false; - } - - // Check move is allowed by the rules (to desactivate when playing with schools) - if self.moves_allowed().is_err() { - return false; - } - true - } - - /// ---- moves_possibles : First of three checks for moves - fn moves_possible(&self) -> bool { - let color = &Color::White; + fn moves_possible(&self, color: &Color, moves: &(CheckerMove, CheckerMove)) -> bool { // Check move is physically possible - if !self.board.move_possible(color, &self.moves.0) { + if !self.board().move_possible(color, &moves.0) { return false; } // Chained_move : "Tout d'une" - if let Ok(chained_move) = self.moves.0.chain(self.moves.1) { - if !self.board.move_possible(color, &chained_move) { + if let Ok(chained_move) = moves.0.chain(moves.1) { + if !self.board().move_possible(color, &chained_move) { return false; } - } else if !self.board.move_possible(color, &self.moves.1) { + } else if !self.board().move_possible(color, &moves.1) { return false; } true } - /// ----- moves_follows_dices : Second of three checks for moves - fn moves_follows_dices(&self) -> bool { - // Prise de coin par puissance - if self.is_move_by_puissance() { - return true; - } - - let (dice1, dice2) = self.dice.values; - let (move1, move2): &(CheckerMove, CheckerMove) = &self.moves; - - let move1_dices = self.get_move_compatible_dices(move1); - if move1_dices.is_empty() { - return false; - } - let move2_dices = self.get_move_compatible_dices(move2); - if move2_dices.is_empty() { - return false; - } - if move1_dices.len() == 1 - && move2_dices.len() == 1 - && move1_dices[0] == move2_dices[0] - && dice1 != dice2 - { - return false; - } - - // no rule was broken - true - } - - fn get_move_compatible_dices(&self, cmove: &CheckerMove) -> Vec { - let (dice1, dice2) = self.dice.values; + fn get_move_compatible_dices(&self, color: &Color, cmove: &CheckerMove) -> Vec { + let (dice1, dice2) = self.dice().values; let mut move_dices = Vec::new(); if cmove.get_to() == 0 { @@ -135,7 +64,10 @@ impl MoveRules { } // Exits - let min_dist = 25 - cmove.get_from(); + let min_dist = match color { + Color::White => 25 - cmove.get_from(), + Color::Black => cmove.get_from(), + }; if dice1 as usize >= min_dist { move_dices.push(dice1); } @@ -154,13 +86,44 @@ impl MoveRules { move_dices } - /// ---- moves_allowed : Third of three checks for moves - fn moves_allowed(&self) -> Result<(), MoveError> { - self.check_corner_rules(&self.moves)?; + fn moves_follows_dices(&self, color: &Color, moves: &(CheckerMove, CheckerMove)) -> bool { + // Prise de coin par puissance + if self.is_move_by_puissance(color, moves) { + return true; + } - let color = &Color::White; - if self.is_move_by_puissance() { - if self.can_take_corner_by_effect() { + let (dice1, dice2) = self.dice().values; + let (move1, move2): &(CheckerMove, CheckerMove) = moves; + + let move1_dices = self.get_move_compatible_dices(color, move1); + if move1_dices.is_empty() { + return false; + } + let move2_dices = self.get_move_compatible_dices(color, move2); + if move2_dices.is_empty() { + return false; + } + if move1_dices.len() == 1 + && move2_dices.len() == 1 + && move1_dices[0] == move2_dices[0] + && dice1 != dice2 + { + return false; + } + + // no rule was broken + true + } + + fn moves_allowed( + &self, + color: &Color, + moves: &(CheckerMove, CheckerMove), + ) -> Result<(), MoveError> { + self.check_corner_rules(color, moves)?; + + if self.is_move_by_puissance(color, moves) { + if self.can_take_corner_by_effect(color) { return Err(MoveError::CornerByEffectPossible); } else { // subsequent rules cannot be broken whith a move by puissance @@ -168,10 +131,10 @@ impl MoveRules { } } // Si possible, les deux dés doivent être joués - let possible_moves_sequences = self.get_possible_moves_sequences(true); + let possible_moves_sequences = self.get_possible_moves_sequences(color, true); // TODO : exclure de ces possibilités celles qui devraient provoquer des CornerNeedsTwoCheckers & ExitNeedsAllCheckersOnLastQuarter... - if !possible_moves_sequences.contains(&self.moves) && !possible_moves_sequences.is_empty() { - println!(">>{:?}<<", self.moves); + if !possible_moves_sequences.contains(moves) && !possible_moves_sequences.is_empty() { + println!(">>{:?}<<", moves); println!("{:?}", possible_moves_sequences); let empty_removed = possible_moves_sequences .iter() @@ -183,13 +146,16 @@ impl MoveRules { } // check exit rules - if self.moves.0.is_exit() || self.moves.1.is_exit() { + if moves.0.is_exit() || moves.1.is_exit() { // toutes les dames doivent être dans le jan de retour let has_outsiders = !self - .board + .board() .get_color_fields(*color) .iter() - .filter(|(field, _count)| *field < 19) + .filter(|(field, _count)| { + (*color == Color::White && *field < 19) + || (*color == Color::Black && *field > 6) + }) .collect::>() .is_empty(); if has_outsiders { @@ -197,8 +163,8 @@ impl MoveRules { } // toutes les sorties directes sont autorisées, ainsi que les nombres défaillants - let possible_moves_sequences = self.get_possible_moves_sequences(false); - if !possible_moves_sequences.contains(&self.moves) { + let possible_moves_sequences = self.get_possible_moves_sequences(color, false); + if !possible_moves_sequences.contains(moves) { // À ce stade au moins un des déplacements concerne un nombre en excédant // - si d'autres séquences de mouvements sans nombre en excédant étaient possibles, on // refuse cette séquence @@ -207,10 +173,16 @@ impl MoveRules { } // - la dame choisie doit être la plus éloignée de la sortie - let mut checkers = self.board.get_color_fields(*color); - checkers.sort_by(|a, b| b.0.cmp(&a.0)); - let mut farthest = 24; - let mut next_farthest = 24; + let mut checkers = self.board().get_color_fields(*color); + checkers.sort_by(|a, b| { + if *color == Color::White { + b.0.cmp(&a.0) + } else { + a.0.cmp(&b.0) + } + }); + let mut farthest = if *color == Color::White { 24 } else { 1 }; + let mut next_farthest = if *color == Color::White { 24 } else { 1 }; let mut has_two_checkers = false; if let Some((field, count)) = checkers.first() { farthest = *field; @@ -225,19 +197,21 @@ impl MoveRules { // s'il reste au moins deux dames, on vérifie que les plus éloignées soint choisies if has_two_checkers { - if self.moves.0.get_to() == 0 && self.moves.1.get_to() == 0 { + if moves.0.get_to() == 0 && moves.1.get_to() == 0 { // Deux coups sortants en excédant - if cmp::max(self.moves.0.get_from(), self.moves.1.get_from()) - > next_farthest - { + if *color == Color::White { + if cmp::max(moves.0.get_from(), moves.1.get_from()) > next_farthest { + return Err(MoveError::ExitNotFasthest); + } + } else if cmp::min(moves.0.get_from(), moves.1.get_from()) < next_farthest { return Err(MoveError::ExitNotFasthest); } } else { // Un seul coup sortant en excédant le coup sortant doit concerner la plus éloignée du bord - let exit_move_field = if self.moves.0.get_to() == 0 { - self.moves.0.get_from() + let exit_move_field = if moves.0.get_to() == 0 { + moves.0.get_from() } else { - self.moves.1.get_from() + moves.1.get_from() }; if exit_move_field != farthest { return Err(MoveError::ExitNotFasthest); @@ -248,24 +222,42 @@ impl MoveRules { } // --- interdit de jouer dans cadran que l'adversaire peut encore remplir ---- - let farthest = cmp::max(self.moves.0.get_to(), self.moves.1.get_to()); - let in_opponent_side = farthest > 12; - if in_opponent_side && self.board.is_quarter_fillable(Color::Black, farthest) { + let farthest = if *color == Color::White { + cmp::max(moves.0.get_to(), moves.1.get_to()) + } else { + cmp::min(moves.0.get_to(), moves.1.get_to()) + }; + let in_opponent_side = if *color == Color::White { + farthest > 12 + } else { + farthest < 13 + }; + + if in_opponent_side + && self + .board() + .is_quarter_fillable(color.opponent_color(), farthest) + { return Err(MoveError::OpponentCanFillQuarter); } // --- remplir cadran si possible & conserver cadran rempli si possible ---- - let filling_moves_sequences = self.get_quarter_filling_moves_sequences(); - if !filling_moves_sequences.contains(&self.moves) && !filling_moves_sequences.is_empty() { + let filling_moves_sequences = self.get_quarter_filling_moves_sequences(color); + if !filling_moves_sequences.contains(moves) && !filling_moves_sequences.is_empty() { return Err(MoveError::MustFillQuarter); } // no rule was broken Ok(()) } - fn check_corner_rules(&self, moves: &(CheckerMove, CheckerMove)) -> Result<(), MoveError> { - let corner_field: Field = self.board.get_color_corner(&Color::White); - let (corner_count, _color) = self.board.get_field_checkers(corner_field).unwrap(); + fn check_corner_rules( + &self, + color: &Color, + moves: &(CheckerMove, CheckerMove), + ) -> Result<(), MoveError> { + // ------- corner rules ---------- + let corner_field: Field = self.board().get_color_corner(color); + let (corner_count, _color) = self.board().get_field_checkers(corner_field).unwrap(); let (from0, to0, from1, to1) = ( moves.0.get_from(), moves.0.get_to(), @@ -287,20 +279,27 @@ impl MoveRules { fn get_possible_moves_sequences( &self, + color: &Color, with_excedents: bool, ) -> Vec<(CheckerMove, CheckerMove)> { - let (dice1, dice2) = self.dice.values; + let (dice1, dice2) = self.dice().values; let (dice_max, dice_min) = if dice1 > dice2 { (dice1, dice2) } else { (dice2, dice1) }; - let mut moves_seqs = - self.get_possible_moves_sequences_by_dices(dice_max, dice_min, with_excedents, false); + let mut moves_seqs = self.get_possible_moves_sequences_by_dices( + color, + dice_max, + dice_min, + with_excedents, + false, + ); // if we got valid sequences whith the highest die, we don't accept sequences using only the // lowest die let ignore_empty = !moves_seqs.is_empty(); let mut moves_seqs_order2 = self.get_possible_moves_sequences_by_dices( + color, dice_min, dice_max, with_excedents, @@ -316,11 +315,13 @@ impl MoveRules { moves_seqs } - fn get_quarter_filling_moves_sequences(&self) -> Vec<(CheckerMove, CheckerMove)> { + fn get_quarter_filling_moves_sequences( + &self, + color: &Color, + ) -> Vec<(CheckerMove, CheckerMove)> { let mut moves_seqs = Vec::new(); - let color = &Color::White; - for moves in self.get_possible_moves_sequences(true) { - let mut board = self.board.clone(); + for moves in self.get_possible_moves_sequences(color, true) { + let mut board = self.board().clone(); board.move_checker(color, moves.0).unwrap(); board.move_checker(color, moves.1).unwrap(); if board.any_quarter_filled(*color) { @@ -332,18 +333,18 @@ impl MoveRules { fn get_possible_moves_sequences_by_dices( &self, + color: &Color, dice1: u8, dice2: u8, with_excedents: bool, ignore_empty: bool, ) -> Vec<(CheckerMove, CheckerMove)> { let mut moves_seqs = Vec::new(); - let color = &Color::White; for first_move in self - .board + .board() .get_possible_moves(*color, dice1, with_excedents, false) { - let mut board2 = self.board.clone(); + let mut board2 = self.board().clone(); if board2.move_checker(color, first_move).is_err() { println!("err move"); continue; @@ -351,30 +352,38 @@ impl MoveRules { let mut has_second_dice_move = false; for second_move in board2.get_possible_moves(*color, dice2, with_excedents, true) { - if self.check_corner_rules(&(first_move, second_move)).is_ok() { + if self + .check_corner_rules(color, &(first_move, second_move)) + .is_ok() + { moves_seqs.push((first_move, second_move)); has_second_dice_move = true; } } - if !has_second_dice_move - && with_excedents - && !ignore_empty - && self.check_corner_rules(&(first_move, EMPTY_MOVE)).is_ok() - { - // empty move - moves_seqs.push((first_move, EMPTY_MOVE)); + if !has_second_dice_move && with_excedents && !ignore_empty { + if self + .check_corner_rules(color, &(first_move, EMPTY_MOVE)) + .is_ok() + { + // empty move + moves_seqs.push((first_move, EMPTY_MOVE)); + } } //if board2.get_color_fields(*color).is_empty() { } moves_seqs } - fn get_direct_exit_moves(&self, state: &GameState) -> Vec { + fn get_direct_exit_moves(&self, state: &GameState, color: &Color) -> Vec { let mut moves = Vec::new(); let (dice1, dice2) = state.dice.values; // sorties directes simples - let (field1_candidate, field2_candidate) = (25 - dice1 as usize, 25 - dice2 as usize); + let (field1_candidate, field2_candidate) = if color == &Color::White { + (25 - dice1 as usize, 25 - dice2 as usize) + } else { + (dice1 as usize, dice2 as usize) + }; let (count1, col1) = state.board.get_field_checkers(field1_candidate).unwrap(); let (count2, col2) = state.board.get_field_checkers(field2_candidate).unwrap(); if count1 > 0 { @@ -390,9 +399,12 @@ impl MoveRules { } // sortie directe tout d'une - let fieldall_candidate = (25 - dice1 - dice2) as usize; + let fieldall_candidate = if color == &Color::White { + 25 - dice1 - dice2 + } else { + dice1 + dice2 + } as usize; let (countall, _col) = state.board.get_field_checkers(fieldall_candidate).unwrap(); - let color = &Color::White; if countall > 0 { if col1.is_none() || col1 == Some(color) { moves.push(CheckerMove::new(fieldall_candidate, field1_candidate).unwrap()); @@ -406,39 +418,44 @@ impl MoveRules { moves } - fn is_move_by_puissance(&self) -> bool { - let (dice1, dice2) = self.dice.values; - let (move1, move2): &(CheckerMove, CheckerMove) = &self.moves; + fn is_move_by_puissance(&self, color: &Color, moves: &(CheckerMove, CheckerMove)) -> bool { + let (dice1, dice2) = self.dice().values; + let (move1, move2): &(CheckerMove, CheckerMove) = moves; let dist1 = (move1.get_to() as i8 - move1.get_from() as i8).unsigned_abs(); let dist2 = (move2.get_to() as i8 - move2.get_from() as i8).unsigned_abs(); // Both corners must be empty - let (count1, _color) = self.board.get_field_checkers(12).unwrap(); - let (count2, _color2) = self.board.get_field_checkers(13).unwrap(); + let (count1, _color) = self.board().get_field_checkers(12).unwrap(); + let (count2, _color2) = self.board().get_field_checkers(13).unwrap(); if count1 > 0 || count2 > 0 { return false; } - let color = &Color::White; move1.get_to() == move2.get_to() - && move1.get_to() == self.board.get_color_corner(color) - && (cmp::min(dist1, dist2) == cmp::min(dice1, dice2) - 1 + && move1.get_to() == self.board().get_color_corner(color) + && ((*color == Color::White + && cmp::min(dist1, dist2) == cmp::min(dice1, dice2) - 1 && cmp::max(dist1, dist2) == cmp::max(dice1, dice2) - 1) + || (*color == Color::Black + && cmp::min(dist1, dist2) == cmp::min(dice1, dice2) + 1 + && cmp::max(dist1, dist2) == cmp::max(dice1, dice2) + 1)) } - fn can_take_corner_by_effect(&self) -> bool { + fn can_take_corner_by_effect(&self, color: &Color) -> bool { // return false if corner already taken - let color = &Color::White; - let corner_field: Field = self.board.get_color_corner(color); - let (count, _col) = self.board.get_field_checkers(corner_field).unwrap(); + let corner_field: Field = self.board().get_color_corner(color); + let (count, _col) = self.board().get_field_checkers(corner_field).unwrap(); if count > 0 { return false; } - let (dice1, dice2) = self.dice.values; - let (field1, field2) = (corner_field - dice1 as usize, corner_field - dice2 as usize); - let res1 = self.board.get_field_checkers(field1); - let res2 = self.board.get_field_checkers(field2); + let (dice1, dice2) = self.dice().values; + let (field1, field2) = match color { + Color::White => (12 - dice1, 12 - dice2), + Color::Black => (13 + dice1, 13 + dice2), + }; + let res1 = self.board().get_field_checkers(field1.into()); + let res2 = self.board().get_field_checkers(field2.into()); if res1.is_err() || res2.is_err() { return false; } @@ -454,45 +471,51 @@ mod tests { #[test] fn can_take_corner_by_effect() { - let mut rules = MoveRules::default(); - rules.board.set_positions([ + let mut state = GameState::default(); + state.board.set_positions([ 10, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15, ]); - rules.dice.values = (4, 4); - assert!(rules.can_take_corner_by_effect()); + state.dice.values = (4, 4); + assert!(state.can_take_corner_by_effect(&Color::White)); - rules.dice.values = (5, 5); - assert!(!rules.can_take_corner_by_effect()); + state.dice.values = (5, 5); + assert!(!state.can_take_corner_by_effect(&Color::White)); - rules.board.set_positions([ + state.board.set_positions([ 10, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15, ]); - rules.dice.values = (4, 4); - assert!(!rules.can_take_corner_by_effect()); + state.dice.values = (4, 4); + assert!(!state.can_take_corner_by_effect(&Color::White)); + + state.board.set_positions([ + 10, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -13, + ]); + state.dice.values = (1, 1); + assert!(state.can_take_corner_by_effect(&Color::Black)); } #[test] fn prise_en_puissance() { - let mut state = MoveRules::default(); + let mut state = GameState::default(); // prise par puissance ok state.board.set_positions([ 10, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15, ]); state.dice.values = (5, 5); - state.moves = ( + let moves = ( CheckerMove::new(8, 12).unwrap(), CheckerMove::new(8, 12).unwrap(), ); - assert!(state.is_move_by_puissance()); - assert!(state.moves_follows_dices()); - assert!(state.moves_allowed().is_ok()); + assert!(state.is_move_by_puissance(&Color::White, &moves)); + assert!(state.moves_follows_dices(&Color::White, &moves)); + assert!(state.moves_allowed(&Color::White, &moves).is_ok()); // opponent corner must be empty state.board.set_positions([ 10, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -13, ]); - assert!(!state.is_move_by_puissance()); - assert!(!state.moves_follows_dices()); + assert!(!state.is_move_by_puissance(&Color::White, &moves)); + assert!(!state.moves_follows_dices(&Color::White, &moves)); // Si on a la possibilité de prendre son coin à la fois par effet, c'est à dire naturellement, et aussi par puissance, on doit le prendre par effet state.board.set_positions([ @@ -500,44 +523,44 @@ mod tests { ]); assert_eq!( Err(MoveError::CornerByEffectPossible), - state.moves_allowed() + state.moves_allowed(&Color::White, &moves) ); // on a déjà pris son coin : on ne peux plus y deplacer des dames par puissance state.board.set_positions([ 8, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15, ]); - assert!(!state.is_move_by_puissance()); - assert!(!state.moves_follows_dices()); + assert!(!state.is_move_by_puissance(&Color::White, &moves)); + assert!(!state.moves_follows_dices(&Color::White, &moves)); } #[test] fn exit() { - let mut state = MoveRules::default(); + let mut state = GameState::default(); // exit ok state.board.set_positions([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, ]); state.dice.values = (5, 5); - state.moves = ( + let moves = ( CheckerMove::new(20, 0).unwrap(), CheckerMove::new(20, 0).unwrap(), ); - assert!(state.moves_follows_dices()); - assert!(state.moves_allowed().is_ok()); + assert!(state.moves_follows_dices(&Color::White, &moves)); + assert!(state.moves_allowed(&Color::White, &moves).is_ok()); // toutes les dames doivent être dans le jan de retour state.board.set_positions([ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, ]); state.dice.values = (5, 5); - state.moves = ( + let moves = ( CheckerMove::new(20, 0).unwrap(), CheckerMove::new(20, 0).unwrap(), ); assert_eq!( Err(MoveError::ExitNeedsAllCheckersOnLastQuarter), - state.moves_allowed() + state.moves_allowed(&Color::White, &moves) ); // on ne peut pas sortir une dame avec un nombre excédant si on peut en jouer une avec un nombre défaillant @@ -545,247 +568,263 @@ mod tests { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 2, 0, ]); state.dice.values = (5, 5); - state.moves = ( + let moves = ( CheckerMove::new(20, 0).unwrap(), CheckerMove::new(23, 0).unwrap(), ); - assert_eq!(Err(MoveError::ExitByEffectPossible), state.moves_allowed()); + assert_eq!( + Err(MoveError::ExitByEffectPossible), + state.moves_allowed(&Color::White, &moves) + ); // on doit jouer le nombre excédant le plus éloigné state.board.set_positions([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, ]); state.dice.values = (5, 5); - state.moves = ( + let moves = ( CheckerMove::new(20, 0).unwrap(), CheckerMove::new(23, 0).unwrap(), ); - assert_eq!(Err(MoveError::ExitNotFasthest), state.moves_allowed()); - state.moves = ( + assert_eq!( + Err(MoveError::ExitNotFasthest), + state.moves_allowed(&Color::White, &moves) + ); + let moves = ( CheckerMove::new(20, 0).unwrap(), CheckerMove::new(21, 0).unwrap(), ); - assert!(state.moves_allowed().is_ok()); + assert!(state.moves_allowed(&Color::White, &moves).is_ok()); // Cas de la dernière dame state.board.set_positions([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, ]); state.dice.values = (5, 5); - state.moves = ( + let moves = ( CheckerMove::new(23, 0).unwrap(), CheckerMove::new(0, 0).unwrap(), ); - assert!(state.moves_follows_dices()); - assert!(state.moves_allowed().is_ok()); + assert!(state.moves_follows_dices(&Color::White, &moves)); + assert!(state.moves_allowed(&Color::White, &moves).is_ok()); } #[test] fn move_check_opponent_fillable_quarter() { - let mut state = MoveRules::default(); + let mut state = GameState::default(); state.board.set_positions([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, ]); state.dice.values = (5, 5); - state.moves = ( + let moves = ( CheckerMove::new(11, 16).unwrap(), CheckerMove::new(11, 16).unwrap(), ); - assert!(state.moves_allowed().is_ok()); + assert!(state.moves_allowed(&Color::White, &moves).is_ok()); state.board.set_positions([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, -12, 0, 0, 0, 0, 1, 0, ]); state.dice.values = (5, 5); - state.moves = ( + let moves = ( CheckerMove::new(11, 16).unwrap(), CheckerMove::new(11, 16).unwrap(), ); assert_eq!( Err(MoveError::OpponentCanFillQuarter), - state.moves_allowed() + state.moves_allowed(&Color::White, &moves) ); } #[test] fn move_check_fillable_quarter() { - let mut state = MoveRules::default(); + let mut state = GameState::default(); state.board.set_positions([ 3, 3, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, ]); state.dice.values = (5, 4); - state.moves = ( + let moves = ( CheckerMove::new(1, 6).unwrap(), CheckerMove::new(2, 6).unwrap(), ); - assert!(state.moves_allowed().is_ok()); - state.moves = ( + assert!(state.moves_allowed(&Color::White, &moves).is_ok()); + let moves = ( CheckerMove::new(1, 5).unwrap(), CheckerMove::new(2, 7).unwrap(), ); - assert_eq!(Err(MoveError::MustFillQuarter), state.moves_allowed()); + assert_eq!( + Err(MoveError::MustFillQuarter), + state.moves_allowed(&Color::White, &moves) + ); state.board.set_positions([ 2, 3, 2, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); state.dice.values = (2, 3); - state.moves = ( + let moves = ( CheckerMove::new(6, 8).unwrap(), CheckerMove::new(6, 9).unwrap(), ); - assert_eq!(Err(MoveError::MustFillQuarter), state.moves_allowed()); - state.moves = ( + assert_eq!( + Err(MoveError::MustFillQuarter), + state.moves_allowed(&Color::White, &moves) + ); + let moves = ( CheckerMove::new(2, 4).unwrap(), CheckerMove::new(5, 8).unwrap(), ); - assert!(state.moves_allowed().is_ok()); + assert!(state.moves_allowed(&Color::White, &moves).is_ok()); } #[test] fn move_play_all_dice() { - let mut state = MoveRules::default(); + let mut state = GameState::default(); state.board.set_positions([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, ]); state.dice.values = (1, 3); - state.moves = ( + let moves = ( CheckerMove::new(22, 0).unwrap(), CheckerMove::new(0, 0).unwrap(), ); - assert_eq!(Err(MoveError::MustPlayAllDice), state.moves_allowed()); - state.moves = ( + assert_eq!( + Err(MoveError::MustPlayAllDice), + state.moves_allowed(&Color::White, &moves) + ); + let moves = ( CheckerMove::new(22, 23).unwrap(), CheckerMove::new(23, 0).unwrap(), ); - assert!(state.moves_allowed().is_ok()); + assert!(state.moves_allowed(&Color::White, &moves).is_ok()); } #[test] fn move_rest_corner_enter() { // direct - let mut state = MoveRules::default(); + let mut state = GameState::default(); state.board.set_positions([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); state.dice.values = (2, 1); - state.moves = ( + let moves = ( CheckerMove::new(10, 12).unwrap(), CheckerMove::new(11, 12).unwrap(), ); - assert!(state.moves_follows_dices()); - assert!(state.moves_allowed().is_ok()); + assert!(state.moves_follows_dices(&Color::White, &moves)); + assert!(state.moves_allowed(&Color::White, &moves).is_ok()); // par puissance state.dice.values = (3, 2); - state.moves = ( + let moves = ( CheckerMove::new(10, 12).unwrap(), CheckerMove::new(11, 12).unwrap(), ); - assert!(state.moves_follows_dices()); - assert!(state.moves_allowed().is_ok()); + assert!(state.moves_follows_dices(&Color::White, &moves)); + assert!(state.moves_allowed(&Color::White, &moves).is_ok()); } #[test] fn move_rest_corner_blocked() { - let mut state = MoveRules::default(); + let mut state = GameState::default(); state.board.set_positions([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); state.dice.values = (2, 1); - state.moves = ( + let moves = ( CheckerMove::new(0, 0).unwrap(), CheckerMove::new(0, 0).unwrap(), ); - assert!(state.moves_follows_dices()); - assert!(state.moves_allowed().is_ok()); + assert!(state.moves_follows_dices(&Color::White, &moves)); + assert!(state.moves_allowed(&Color::White, &moves).is_ok()); state.board.set_positions([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, ]); state.dice.values = (2, 1); - state.moves = ( + let moves = ( CheckerMove::new(23, 24).unwrap(), CheckerMove::new(0, 0).unwrap(), ); - assert!(state.moves_follows_dices()); - let res = state.moves_allowed(); + assert!(state.moves_follows_dices(&Color::White, &moves)); + let res = state.moves_allowed(&Color::White, &moves); println!("{:?}", res); - assert!(state.moves_allowed().is_ok()); + assert!(state.moves_allowed(&Color::White, &moves).is_ok()); - state.moves = ( + let moves = ( CheckerMove::new(0, 0).unwrap(), CheckerMove::new(0, 0).unwrap(), ); - assert_eq!(Err(MoveError::MustPlayAllDice), state.moves_allowed()); + assert_eq!( + Err(MoveError::MustPlayAllDice), + state.moves_allowed(&Color::White, &moves) + ); } #[test] fn move_rest_corner_exit() { - let mut state = MoveRules::default(); + let mut state = GameState::default(); state.board.set_positions([ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, ]); state.dice.values = (2, 3); - state.moves = ( + let moves = ( CheckerMove::new(12, 14).unwrap(), CheckerMove::new(1, 4).unwrap(), ); assert_eq!( Err(MoveError::CornerNeedsTwoCheckers), - state.moves_allowed() + state.moves_allowed(&Color::White, &moves) ); } #[test] fn move_play_stronger_dice() { - let mut state = MoveRules::default(); + let mut state = GameState::default(); state.board.set_positions([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, -1, -1, -1, 0, 0, 0, 0, 0, 0, ]); state.dice.values = (2, 3); - state.moves = ( + let moves = ( CheckerMove::new(12, 14).unwrap(), CheckerMove::new(0, 0).unwrap(), ); // let poss = state.get_possible_moves_sequences(&Color::White, true); // println!("{:?}", poss); - assert_eq!(Err(MoveError::MustPlayStrongerDie), state.moves_allowed()); - state.moves = ( + assert_eq!( + Err(MoveError::MustPlayStrongerDie), + state.moves_allowed(&Color::White, &moves) + ); + let moves = ( CheckerMove::new(12, 15).unwrap(), CheckerMove::new(0, 0).unwrap(), ); - assert!(state.moves_allowed().is_ok()); + assert!(state.moves_allowed(&Color::White, &moves).is_ok()); } #[test] fn moves_possible() { - let mut state = MoveRules::default(); + let state = GameState::default(); // Chained moves - state.moves = ( + let moves = ( CheckerMove::new(1, 5).unwrap(), CheckerMove::new(5, 9).unwrap(), ); - assert!(state.moves_possible()); + assert!(state.moves_possible(&Color::White, &moves)); // not chained moves - state.moves = ( + let moves = ( CheckerMove::new(1, 5).unwrap(), CheckerMove::new(6, 9).unwrap(), ); - assert!(!state.moves_possible()); + assert!(!state.moves_possible(&Color::White, &moves)); // black moves - let state = MoveRules::new( - &Color::Black, - &Board::default(), - Dice::default(), - &( - CheckerMove::new(24, 20).unwrap(), - CheckerMove::new(20, 19).unwrap(), - ), + let moves = ( + CheckerMove::new(24, 20).unwrap(), + CheckerMove::new(20, 19).unwrap(), ); - assert!(state.moves_possible()); + assert!(state.moves_possible(&Color::Black, &moves)); } }