wip check jans

This commit is contained in:
Henri Bourcereau 2024-05-25 19:56:38 +02:00
parent b528fa3ac6
commit 0df394c0b1
6 changed files with 246 additions and 139 deletions

View file

@ -36,60 +36,56 @@ pub enum MoveError {
pub struct MoveRules {
pub board: Board,
pub dice: Dice,
pub moves: (CheckerMove, CheckerMove),
}
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()))
pub fn new(color: &Color, board: &Board, dice: Dice) -> Self {
let board = if *color == Color::Black {
board.mirror()
} else {
(board.clone(), *moves)
board.clone()
};
Self { board, dice, moves }
Self { board, dice }
}
pub fn moves_follow_rules(&self) -> bool {
pub fn moves_follow_rules(&self, moves: &(CheckerMove, CheckerMove)) -> bool {
// Check moves possibles on the board
// Check moves conforms to the dice
// Check move is allowed by the rules (to desactivate when playing with schools)
self.moves_possible() && self.moves_follows_dices() && self.moves_allowed().is_ok()
self.moves_possible(moves)
&& self.moves_follows_dices(moves)
&& self.moves_allowed(moves).is_ok()
}
/// ---- moves_possibles : First of three checks for moves
fn moves_possible(&self) -> bool {
fn moves_possible(&self, moves: &(CheckerMove, CheckerMove)) -> bool {
let color = &Color::White;
// 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 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 {
fn moves_follows_dices(&self, moves: &(CheckerMove, CheckerMove)) -> bool {
// Prise de coin par puissance
if self.is_move_by_puissance() {
if self.is_move_by_puissance(moves) {
return true;
}
let (dice1, dice2) = self.dice.values;
let (move1, move2): &(CheckerMove, CheckerMove) = &self.moves;
let (move1, move2): &(CheckerMove, CheckerMove) = &moves;
let move1_dices = self.get_move_compatible_dices(move1);
if move1_dices.is_empty() {
@ -144,10 +140,10 @@ impl MoveRules {
}
/// ---- moves_allowed : Third of three checks for moves
fn moves_allowed(&self) -> Result<(), MoveError> {
self.check_corner_rules(&self.moves)?;
fn moves_allowed(&self, moves: &(CheckerMove, CheckerMove)) -> Result<(), MoveError> {
self.check_corner_rules(&moves)?;
if self.is_move_by_puissance() {
if self.is_move_by_puissance(moves) {
if self.can_take_corner_by_effect() {
return Err(MoveError::CornerByEffectPossible);
} else {
@ -157,17 +153,13 @@ impl MoveRules {
}
// Si possible, les deux dés doivent être joués
let (m1, m2) = self.moves;
if m1.get_from() == 0 || m2.get_from() == 0 {
if moves.0.get_from() == 0 || moves.1.get_from() == 0 {
let mut possible_moves_sequences = self.get_possible_moves_sequences(true);
println!("{:?}", possible_moves_sequences);
possible_moves_sequences.retain(|moves| self.check_exit_rules(moves).is_ok());
// possible_moves_sequences.retain(|moves| self.check_corner_rules(moves).is_ok());
// 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()
{
if self.moves == (EMPTY_MOVE, EMPTY_MOVE) {
if !possible_moves_sequences.contains(&moves) && !possible_moves_sequences.is_empty() {
if *moves == (EMPTY_MOVE, EMPTY_MOVE) {
return Err(MoveError::MustPlayAllDice);
}
let empty_removed = possible_moves_sequences
@ -181,10 +173,10 @@ impl MoveRules {
}
// check exit rules
self.check_exit_rules(&self.moves)?;
self.check_exit_rules(moves)?;
// --- 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 farthest = cmp::max(moves.0.get_to(), moves.1.get_to());
let in_opponent_side = farthest > 12;
if in_opponent_side && self.board.is_quarter_fillable(Color::Black, farthest) {
return Err(MoveError::OpponentCanFillQuarter);
@ -192,7 +184,7 @@ impl MoveRules {
// --- 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() {
if !filling_moves_sequences.contains(moves) && !filling_moves_sequences.is_empty() {
return Err(MoveError::MustFillQuarter);
}
// no rule was broken
@ -290,7 +282,7 @@ impl MoveRules {
Ok(())
}
fn get_possible_moves_sequences(
pub fn get_possible_moves_sequences(
&self,
with_excedents: bool,
) -> Vec<(CheckerMove, CheckerMove)> {
@ -321,7 +313,7 @@ impl MoveRules {
moves_seqs
}
fn get_quarter_filling_moves_sequences(&self) -> Vec<(CheckerMove, CheckerMove)> {
pub fn get_quarter_filling_moves_sequences(&self) -> Vec<(CheckerMove, CheckerMove)> {
let mut moves_seqs = Vec::new();
let color = &Color::White;
for moves in self.get_possible_moves_sequences(true) {
@ -414,11 +406,10 @@ impl MoveRules {
moves
}
fn is_move_by_puissance(&self) -> bool {
fn is_move_by_puissance(&self, moves: &(CheckerMove, CheckerMove)) -> bool {
let (dice1, dice2) = self.dice.values;
let (move1, move2): &(CheckerMove, CheckerMove) = &self.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();
let dist1 = (moves.0.get_to() as i8 - moves.0.get_from() as i8).unsigned_abs();
let dist2 = (moves.1.get_to() as i8 - moves.1.get_from() as i8).unsigned_abs();
// Both corners must be empty
let (count1, _color) = self.board.get_field_checkers(12).unwrap();
@ -428,8 +419,8 @@ impl MoveRules {
}
let color = &Color::White;
move1.get_to() == move2.get_to()
&& move1.get_to() == self.board.get_color_corner(color)
moves.0.get_to() == moves.1.get_to()
&& moves.0.get_to() == self.board.get_color_corner(color)
&& (cmp::min(dist1, dist2) == cmp::min(dice1, dice2) - 1
&& cmp::max(dist1, dist2) == cmp::max(dice1, dice2) - 1)
}
@ -487,20 +478,20 @@ mod tests {
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(&moves));
assert!(state.moves_follows_dices(&moves));
assert!(state.moves_allowed(&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(&moves));
assert!(!state.moves_follows_dices(&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([
@ -508,15 +499,15 @@ mod tests {
]);
assert_eq!(
Err(MoveError::CornerByEffectPossible),
state.moves_allowed()
state.moves_allowed(&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(&moves));
assert!(!state.moves_follows_dices(&moves));
}
#[test]
@ -527,25 +518,25 @@ mod tests {
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(&moves));
assert!(state.moves_allowed(&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(&moves)
);
// on ne peut pas sortir une dame avec un nombre excédant si on peut en jouer une avec un nombre défaillant
@ -553,39 +544,42 @@ 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(&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(&moves));
let moves = (
CheckerMove::new(20, 0).unwrap(),
CheckerMove::new(21, 0).unwrap(),
);
assert!(state.moves_allowed().is_ok());
assert!(state.moves_allowed(&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(&moves));
assert!(state.moves_allowed(&moves).is_ok());
}
#[test]
@ -595,23 +589,23 @@ mod tests {
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(&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(&moves)
);
}
@ -622,31 +616,31 @@ mod tests {
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(&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(&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(&moves));
let moves = (
CheckerMove::new(2, 4).unwrap(),
CheckerMove::new(5, 8).unwrap(),
);
assert!(state.moves_allowed().is_ok());
assert!(state.moves_allowed(&moves).is_ok());
}
#[test]
@ -656,17 +650,17 @@ mod tests {
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(&moves));
let moves = (
CheckerMove::new(22, 23).unwrap(),
CheckerMove::new(23, 0).unwrap(),
);
assert!(state.moves_allowed().is_ok());
assert!(state.moves_allowed(&moves).is_ok());
}
#[test]
@ -677,21 +671,21 @@ mod tests {
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(&moves));
assert!(state.moves_allowed(&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(&moves));
assert!(state.moves_allowed(&moves).is_ok());
}
#[test]
@ -701,31 +695,31 @@ mod tests {
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(&moves));
assert!(state.moves_allowed(&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(&moves));
// let res = state.moves_allowed(&moves);
// println!("{:?}", res);
assert!(state.moves_allowed().is_ok());
assert!(state.moves_allowed(&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(&moves));
}
#[test]
@ -735,13 +729,13 @@ mod tests {
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(&moves)
);
}
@ -752,18 +746,21 @@ mod tests {
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(&moves)
);
let moves = (
CheckerMove::new(12, 15).unwrap(),
CheckerMove::new(0, 0).unwrap(),
);
assert!(state.moves_allowed().is_ok());
assert!(state.moves_allowed(&moves).is_ok());
}
#[test]
@ -771,29 +768,25 @@ mod tests {
let mut state = MoveRules::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(&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(&moves));
// black moves
let state = MoveRules::new(
&Color::Black,
&Board::default(),
Dice::default(),
&(
CheckerMove::new(24, 20).unwrap(),
CheckerMove::new(20, 19).unwrap(),
),
let state = MoveRules::new(&Color::Black, &Board::default(), Dice::default());
let moves = (
CheckerMove::new(24, 20).unwrap().mirror(),
CheckerMove::new(20, 19).unwrap().mirror(),
);
assert!(state.moves_possible());
assert!(state.moves_possible(&moves));
}
}