moves allowed : result with error #2
This commit is contained in:
parent
86220f6408
commit
e43a742c1e
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue