moves allowed : result with error #2

This commit is contained in:
Henri Bourcereau 2024-05-20 14:27:17 +02:00
parent 86220f6408
commit e43a742c1e

View file

@ -12,6 +12,27 @@ use std::{fmt, str};
use base64::{engine::general_purpose, Engine as _}; use base64::{engine::general_purpose, Engine as _};
#[derive(std::cmp::PartialEq, Debug)]
pub enum MoveError {
// 2 checkers must go at the same time on an empty corner
// & the last 2 checkers of a corner must leave at the same time
CornerNeedsTwoCheckers,
// Prise de coin de repos par puissance alors qu'il est possible
// de le prendre directement (par "effet")
CornerByEffectPossible,
// toutes les dames doivent être dans le jan de retour
ExitNeedsAllCheckersOnLastQuarter,
// mouvement avec nombre en exédant alors qu'une séquence de mouvements
// sans nombre en excédant est possible
ExitByEffectPossible,
// Sortie avec nombre en excédant d'une dame qui n'est pas la plus éloignée
ExitNotFasthest,
// Jeu dans un cadran que l'adversaire peut encore remplir
OpponentCanFillQuarter,
// remplir cadran si possible & conserver cadran rempli si possible ----
MustFillQuarter,
}
/// The different stages a game can be in. (not to be confused with the entire "GameState") /// The different stages a game can be in. (not to be confused with the entire "GameState")
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Stage { pub enum Stage {
@ -264,7 +285,7 @@ impl GameState {
} }
// Check move is allowed by the rules (to desactivate when playing with schools) // Check move is allowed by the rules (to desactivate when playing with schools)
if !self.moves_allowed(color, moves) { if self.moves_allowed(color, moves).is_err() {
return false; return false;
} }
} }
@ -355,7 +376,11 @@ impl GameState {
true true
} }
fn moves_allowed(&self, color: &Color, moves: &(CheckerMove, CheckerMove)) -> bool { fn moves_allowed(
&self,
color: &Color,
moves: &(CheckerMove, CheckerMove),
) -> Result<(), MoveError> {
// ------- corner rules ---------- // ------- corner rules ----------
let corner_field: Field = self.board.get_color_corner(color); let corner_field: Field = self.board.get_color_corner(color);
let (corner_count, _color) = self.board.get_field_checkers(corner_field).unwrap(); let (corner_count, _color) = self.board.get_field_checkers(corner_field).unwrap();
@ -367,17 +392,17 @@ impl GameState {
); );
// 2 checkers must go at the same time on an empty corner // 2 checkers must go at the same time on an empty corner
if (to0 == corner_field || to1 == corner_field) && (to0 != to1) && corner_count == 0 { if (to0 == corner_field || to1 == corner_field) && (to0 != to1) && corner_count == 0 {
return false; return Err(MoveError::CornerNeedsTwoCheckers);
} }
// the last 2 checkers of a corner must leave at the same time // the last 2 checkers of a corner must leave at the same time
if (from0 == corner_field || from1 == corner_field) && (from0 != from1) && corner_count == 2 if (from0 == corner_field || from1 == corner_field) && (from0 != from1) && corner_count == 2
{ {
return false; return Err(MoveError::CornerNeedsTwoCheckers);
} }
if self.is_move_by_puissance(color, moves) && self.can_take_corner_by_effect(color) { if self.is_move_by_puissance(color, moves) && self.can_take_corner_by_effect(color) {
return false; return Err(MoveError::CornerByEffectPossible);
} }
// check exit rules // check exit rules
@ -394,7 +419,7 @@ impl GameState {
.collect::<Vec<&(usize, i8)>>() .collect::<Vec<&(usize, i8)>>()
.is_empty(); .is_empty();
if has_outsiders { if has_outsiders {
return false; return Err(MoveError::ExitNeedsAllCheckersOnLastQuarter);
} }
// toutes les sorties directes sont autorisées, ainsi que les nombre défaillants // toutes les sorties directes sont autorisées, ainsi que les nombre défaillants
@ -404,7 +429,7 @@ impl GameState {
// - si d'autres séquences de mouvements sans nombre en excédant étaient possibles, on // - si d'autres séquences de mouvements sans nombre en excédant étaient possibles, on
// refuse cette séquence // refuse cette séquence
if !possible_moves_sequences.is_empty() { if !possible_moves_sequences.is_empty() {
return false; return Err(MoveError::ExitByEffectPossible);
} }
// - la dame choisie doit être la plus éloignée de la sortie // - la dame choisie doit être la plus éloignée de la sortie
@ -436,10 +461,10 @@ impl GameState {
// Deux coups sortants en excédant // Deux coups sortants en excédant
if *color == Color::White { if *color == Color::White {
if cmp::max(moves.0.get_from(), moves.1.get_from()) > next_farthest { if cmp::max(moves.0.get_from(), moves.1.get_from()) > next_farthest {
return false; return Err(MoveError::ExitNotFasthest);
} }
} else if cmp::min(moves.0.get_from(), moves.1.get_from()) < next_farthest { } else if cmp::min(moves.0.get_from(), moves.1.get_from()) < next_farthest {
return false; return Err(MoveError::ExitNotFasthest);
} }
} else { } else {
// Un seul coup sortant en excédant le coup sortant doit concerner la plus éloignée du bord // Un seul coup sortant en excédant le coup sortant doit concerner la plus éloignée du bord
@ -449,7 +474,7 @@ impl GameState {
moves.1.get_from() moves.1.get_from()
}; };
if exit_move_field != farthest { if exit_move_field != farthest {
return false; return Err(MoveError::ExitNotFasthest);
} }
} }
} }
@ -473,16 +498,16 @@ impl GameState {
.board .board
.is_quarter_fillable(color.opponent_color(), farthest) .is_quarter_fillable(color.opponent_color(), farthest)
{ {
return false; return Err(MoveError::OpponentCanFillQuarter);
} }
// --- remplir cadran si possible & conserver cadran rempli si possible ---- // --- remplir cadran si possible & conserver cadran rempli si possible ----
let filling_moves_sequences = self.get_quarter_filling_moves_sequences(color); let filling_moves_sequences = self.get_quarter_filling_moves_sequences(color);
if !filling_moves_sequences.contains(moves) && !filling_moves_sequences.is_empty() { if !filling_moves_sequences.contains(moves) && !filling_moves_sequences.is_empty() {
return false; return Err(MoveError::MustFillQuarter);
} }
// no rule was broken // no rule was broken
true Ok(())
} }
fn get_possible_moves_sequences(&self, color: &Color) -> Vec<(CheckerMove, CheckerMove)> { fn get_possible_moves_sequences(&self, color: &Color) -> Vec<(CheckerMove, CheckerMove)> {
@ -911,7 +936,7 @@ mod tests {
); );
assert!(state.is_move_by_puissance(&Color::White, &moves)); assert!(state.is_move_by_puissance(&Color::White, &moves));
assert!(state.moves_follows_dices(&Color::White, &moves)); assert!(state.moves_follows_dices(&Color::White, &moves));
assert!(state.moves_allowed(&Color::White, &moves)); assert!(state.moves_allowed(&Color::White, &moves).is_ok());
// opponent corner must be empty // opponent corner must be empty
state.board.set_positions([ state.board.set_positions([
@ -924,7 +949,10 @@ mod tests {
state.board.set_positions([ state.board.set_positions([
5, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15, 5, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15,
]); ]);
assert!(!state.moves_allowed(&Color::White, &moves)); assert_eq!(
Err(MoveError::CornerByEffectPossible),
state.moves_allowed(&Color::White, &moves)
);
// on a déjà pris son coin : on ne peux plus y deplacer des dames par puissance // on a déjà pris son coin : on ne peux plus y deplacer des dames par puissance
state.board.set_positions([ state.board.set_positions([
@ -957,7 +985,7 @@ mod tests {
); );
assert!(state.moves_possible(&Color::White, &moves)); assert!(state.moves_possible(&Color::White, &moves));
assert!(state.moves_follows_dices(&Color::White, &moves)); assert!(state.moves_follows_dices(&Color::White, &moves));
assert!(state.moves_allowed(&Color::White, &moves)); assert!(state.moves_allowed(&Color::White, &moves).is_ok());
// toutes les dames doivent être dans le jan de retour // toutes les dames doivent être dans le jan de retour
state.board.set_positions([ state.board.set_positions([
@ -968,7 +996,10 @@ mod tests {
CheckerMove::new(20, 0).unwrap(), CheckerMove::new(20, 0).unwrap(),
CheckerMove::new(20, 0).unwrap(), CheckerMove::new(20, 0).unwrap(),
); );
assert!(!state.moves_allowed(&Color::White, &moves)); assert_eq!(
Err(MoveError::ExitNeedsAllCheckersOnLastQuarter),
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 // on ne peut pas sortir une dame avec un nombre excédant si on peut en jouer une avec un nombre défaillant
state.board.set_positions([ state.board.set_positions([
@ -979,23 +1010,29 @@ mod tests {
CheckerMove::new(20, 0).unwrap(), CheckerMove::new(20, 0).unwrap(),
CheckerMove::new(23, 0).unwrap(), CheckerMove::new(23, 0).unwrap(),
); );
assert!(!state.moves_allowed(&Color::White, &moves)); assert_eq!(
Err(MoveError::ExitByEffectPossible),
state.moves_allowed(&Color::White, &moves)
);
// on doit jouer le nombre excédant le plus éloigné // on doit jouer le nombre excédant le plus éloigné
state.board.set_positions([ state.board.set_positions([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 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.dice.values = (5, 5);
let moves = ( let moves = (
CheckerMove::new(20, 0).unwrap(), CheckerMove::new(20, 0).unwrap(),
CheckerMove::new(23, 0).unwrap(), CheckerMove::new(23, 0).unwrap(),
); );
assert!(!state.moves_allowed(&Color::White, &moves)); assert_eq!(
Err(MoveError::ExitNotFasthest),
state.moves_allowed(&Color::White, &moves)
);
let moves = ( let moves = (
CheckerMove::new(20, 0).unwrap(), CheckerMove::new(20, 0).unwrap(),
CheckerMove::new(20, 0).unwrap(), CheckerMove::new(20, 0).unwrap(),
); );
assert!(state.moves_allowed(&Color::White, &moves)); assert!(state.moves_allowed(&Color::White, &moves).is_ok());
// Cas de la dernière dame // Cas de la dernière dame
state.board.set_positions([ state.board.set_positions([
@ -1008,11 +1045,11 @@ mod tests {
); );
assert!(state.moves_possible(&Color::White, &moves)); assert!(state.moves_possible(&Color::White, &moves));
assert!(state.moves_follows_dices(&Color::White, &moves)); assert!(state.moves_follows_dices(&Color::White, &moves));
assert!(state.moves_allowed(&Color::White, &moves)); assert!(state.moves_allowed(&Color::White, &moves).is_ok());
} }
#[test] #[test]
fn move_check_oponnent_fillable_quarter() { fn move_check_opponent_fillable_quarter() {
let mut state = GameState::default(); let mut state = GameState::default();
state.board.set_positions([ 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0,
@ -1022,7 +1059,7 @@ mod tests {
CheckerMove::new(11, 16).unwrap(), CheckerMove::new(11, 16).unwrap(),
CheckerMove::new(11, 16).unwrap(), CheckerMove::new(11, 16).unwrap(),
); );
assert!(state.moves_allowed(&Color::White, &moves)); assert!(state.moves_allowed(&Color::White, &moves).is_ok());
state.board.set_positions([ 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, -12, 0, 0, 0, 0, 1, 0,
@ -1032,7 +1069,10 @@ mod tests {
CheckerMove::new(11, 16).unwrap(), CheckerMove::new(11, 16).unwrap(),
CheckerMove::new(11, 16).unwrap(), CheckerMove::new(11, 16).unwrap(),
); );
assert!(!state.moves_allowed(&Color::White, &moves)); assert_eq!(
Err(MoveError::OpponentCanFillQuarter),
state.moves_allowed(&Color::White, &moves)
);
} }
#[test] #[test]
@ -1046,12 +1086,15 @@ mod tests {
CheckerMove::new(1, 6).unwrap(), CheckerMove::new(1, 6).unwrap(),
CheckerMove::new(2, 6).unwrap(), CheckerMove::new(2, 6).unwrap(),
); );
assert!(state.moves_allowed(&Color::White, &moves)); assert!(state.moves_allowed(&Color::White, &moves).is_ok());
let moves = ( let moves = (
CheckerMove::new(1, 5).unwrap(), CheckerMove::new(1, 5).unwrap(),
CheckerMove::new(2, 7).unwrap(), CheckerMove::new(2, 7).unwrap(),
); );
assert!(!state.moves_allowed(&Color::White, &moves)); assert_eq!(
Err(MoveError::MustFillQuarter),
state.moves_allowed(&Color::White, &moves)
);
state.board.set_positions([ 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, 2, 3, 2, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -1061,11 +1104,14 @@ mod tests {
CheckerMove::new(6, 8).unwrap(), CheckerMove::new(6, 8).unwrap(),
CheckerMove::new(6, 9).unwrap(), CheckerMove::new(6, 9).unwrap(),
); );
assert!(!state.moves_allowed(&Color::White, &moves)); assert_eq!(
Err(MoveError::MustFillQuarter),
state.moves_allowed(&Color::White, &moves)
);
let moves = ( let moves = (
CheckerMove::new(2, 4).unwrap(), CheckerMove::new(2, 4).unwrap(),
CheckerMove::new(5, 8).unwrap(), CheckerMove::new(5, 8).unwrap(),
); );
assert!(state.moves_allowed(&Color::White, &moves)); assert!(state.moves_allowed(&Color::White, &moves).is_ok());
} }
} }