wip fix moves rules

This commit is contained in:
Henri Bourcereau 2024-05-21 21:22:04 +02:00
parent aff489f134
commit 07ec82ae7c
3 changed files with 140 additions and 20 deletions

View file

@ -64,6 +64,10 @@ impl CheckerMove {
pub fn get_to(&self) -> Field { pub fn get_to(&self) -> Field {
self.to self.to
} }
pub fn is_exit(&self) -> bool {
self.to == 0 && self != &EMPTY_MOVE
}
} }
/// Represents the Tric Trac board /// Represents the Tric Trac board
@ -344,6 +348,7 @@ impl Board {
color: Color, color: Color,
dice: u8, dice: u8,
with_excedants: bool, with_excedants: bool,
check_rest_corner_exit: bool,
) -> Vec<CheckerMove> { ) -> Vec<CheckerMove> {
let mut moves = Vec::new(); let mut moves = Vec::new();
@ -359,7 +364,11 @@ impl Board {
} }
}; };
for (field, _count) in self.get_color_fields(color) { for (field, count) in self.get_color_fields(color) {
// check rest corner exit
if field == self.get_color_corner(&color) && count == 2 && check_rest_corner_exit {
continue;
}
let mut dest = get_dest(field as i32); let mut dest = get_dest(field as i32);
if !(0..25).contains(&dest) { if !(0..25).contains(&dest) {
if with_excedants { if with_excedants {
@ -457,6 +466,9 @@ impl Board {
} }
pub fn remove_checker(&mut self, color: &Color, field: Field) -> Result<(), Error> { pub fn remove_checker(&mut self, color: &Color, field: Field) -> Result<(), Error> {
if field == 0 {
return Ok(());
}
let checker_color = self.get_checkers_color(field)?; let checker_color = self.get_checkers_color(field)?;
if Some(color) != checker_color { if Some(color) != checker_color {
return Err(Error::FieldInvalid); return Err(Error::FieldInvalid);

View file

@ -24,6 +24,10 @@ pub enum MoveError {
OpponentCanFillQuarter, OpponentCanFillQuarter,
// remplir cadran si possible & conserver cadran rempli si possible ---- // remplir cadran si possible & conserver cadran rempli si possible ----
MustFillQuarter, MustFillQuarter,
// On n'a pas le droit de jouer d'une manière qui empêche de jouer les deux dés si on a la possibilité de les jouer.
MustPlayAllDice,
// Si on ne peut jouer qu'un seul dé, on doit jouer le plus fort si possible.
MustPlayStrongerDie,
} }
pub trait MoveRules { pub trait MoveRules {
@ -136,12 +140,24 @@ pub trait MoveRules {
return Err(MoveError::CornerNeedsTwoCheckers); 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) {
return Err(MoveError::CornerByEffectPossible); if self.can_take_corner_by_effect(color) {
return Err(MoveError::CornerByEffectPossible);
} else {
// subsequent rules cannot be broken whith a move by puissance
return Ok(());
}
}
// Si possible, les deux dés doivent être joués
let possible_moves_sequences = self.get_possible_moves_sequences(color, true);
if !possible_moves_sequences.contains(moves) && !possible_moves_sequences.is_empty() {
println!(">>{:?}<<", moves);
println!("{:?}", possible_moves_sequences);
return Err(MoveError::MustPlayAllDice);
} }
// check exit rules // check exit rules
if moves.0.get_to() == 0 || moves.1.get_to() == 0 { if moves.0.is_exit() || moves.1.is_exit() {
// toutes les dames doivent être dans le jan de retour // toutes les dames doivent être dans le jan de retour
let has_outsiders = !self let has_outsiders = !self
.board() .board()
@ -157,8 +173,8 @@ pub trait MoveRules {
return Err(MoveError::ExitNeedsAllCheckersOnLastQuarter); 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 nombres défaillants
let possible_moves_sequences = self.get_possible_moves_sequences(color); let possible_moves_sequences = self.get_possible_moves_sequences(color, false);
if !possible_moves_sequences.contains(moves) { if !possible_moves_sequences.contains(moves) {
// À ce stade au moins un des déplacements concerne un nombre en excédant // À 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 // - si d'autres séquences de mouvements sans nombre en excédant étaient possibles, on
@ -245,11 +261,23 @@ pub trait MoveRules {
Ok(()) Ok(())
} }
fn get_possible_moves_sequences(&self, color: &Color) -> Vec<(CheckerMove, CheckerMove)> { 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 mut moves_seqs = self.get_possible_moves_sequences_by_dices(color, dice1, dice2); let mut moves_seqs =
let mut moves_seqs_order2 = self.get_possible_moves_sequences_by_dices(color, dice1, dice2); self.get_possible_moves_sequences_by_dices(color, dice1, dice2, with_excedents);
let mut moves_seqs_order2 =
self.get_possible_moves_sequences_by_dices(color, dice2, dice1, with_excedents);
moves_seqs.append(&mut moves_seqs_order2); moves_seqs.append(&mut moves_seqs_order2);
let empty_removed = moves_seqs
.iter()
.filter(|(c1, c2)| *c1 != EMPTY_MOVE && *c2 != EMPTY_MOVE);
if empty_removed.count() > 0 {
moves_seqs.retain(|(c1, c2)| *c1 != EMPTY_MOVE && *c2 != EMPTY_MOVE);
}
moves_seqs moves_seqs
} }
@ -258,7 +286,7 @@ pub trait MoveRules {
color: &Color, color: &Color,
) -> Vec<(CheckerMove, CheckerMove)> { ) -> Vec<(CheckerMove, CheckerMove)> {
let mut moves_seqs = Vec::new(); let mut moves_seqs = Vec::new();
for moves in self.get_possible_moves_sequences(color) { for moves in self.get_possible_moves_sequences(color, true) {
let mut board = self.board().clone(); let mut board = self.board().clone();
board.move_checker(color, moves.0).unwrap(); board.move_checker(color, moves.0).unwrap();
board.move_checker(color, moves.1).unwrap(); board.move_checker(color, moves.1).unwrap();
@ -274,23 +302,29 @@ pub trait MoveRules {
color: &Color, color: &Color,
dice1: u8, dice1: u8,
dice2: u8, dice2: u8,
with_excedents: bool,
) -> Vec<(CheckerMove, CheckerMove)> { ) -> Vec<(CheckerMove, CheckerMove)> {
let mut moves_seqs = Vec::new(); let mut moves_seqs = Vec::new();
for first_move in self.board().get_possible_moves(*color, dice1, false) { for first_move in self
.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() { if board2.move_checker(color, first_move).is_err() {
println!("err move"); println!("err move");
continue; continue;
} }
if board2.get_color_fields(*color).is_empty() {
// no checkers left : empty move let mut has_second_dice_move = false;
println!("empty move"); for second_move in board2.get_possible_moves(*color, dice2, with_excedents, true) {
moves_seqs.push((first_move, EMPTY_MOVE)); moves_seqs.push((first_move, second_move));
} else { has_second_dice_move = true;
for second_move in board2.get_possible_moves(*color, dice2, false) {
moves_seqs.push((first_move, second_move));
}
} }
if !has_second_dice_move && with_excedents {
// empty move
moves_seqs.push((first_move, EMPTY_MOVE));
}
//if board2.get_color_fields(*color).is_empty() {
} }
moves_seqs moves_seqs
} }
@ -513,7 +547,7 @@ mod tests {
); );
let moves = ( let moves = (
CheckerMove::new(20, 0).unwrap(), CheckerMove::new(20, 0).unwrap(),
CheckerMove::new(20, 0).unwrap(), CheckerMove::new(21, 0).unwrap(),
); );
assert!(state.moves_allowed(&Color::White, &moves).is_ok()); assert!(state.moves_allowed(&Color::White, &moves).is_ok());
@ -597,6 +631,71 @@ mod tests {
assert!(state.moves_allowed(&Color::White, &moves).is_ok()); assert!(state.moves_allowed(&Color::White, &moves).is_ok());
} }
#[test]
fn move_play_all_dice() {
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);
let moves = (
CheckerMove::new(22, 0).unwrap(),
CheckerMove::new(0, 0).unwrap(),
);
assert_eq!(
Err(MoveError::MustPlayAllDice),
state.moves_allowed(&Color::White, &moves)
);
let moves = (
CheckerMove::new(22, 23).unwrap(),
CheckerMove::new(23, 0).unwrap(),
);
let res = state.moves_allowed(&Color::White, &moves);
assert!(state.moves_allowed(&Color::White, &moves).is_ok());
}
#[test]
fn move_rest_corner_exit() {
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);
let moves = (
CheckerMove::new(12, 14).unwrap(),
CheckerMove::new(1, 4).unwrap(),
);
assert_eq!(
Err(MoveError::CornerNeedsTwoCheckers),
state.moves_allowed(&Color::White, &moves)
);
}
#[test]
fn move_play_stronger_dice() {
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);
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(&Color::White, &moves)
);
let moves = (
CheckerMove::new(12, 15).unwrap(),
CheckerMove::new(0, 0).unwrap(),
);
assert!(state.moves_allowed(&Color::White, &moves).is_ok());
}
#[test] #[test]
fn moves_possible() { fn moves_possible() {
let state = GameState::default(); let state = GameState::default();

View file

@ -4,6 +4,15 @@ use crate::dice::Dice;
#[derive(std::cmp::PartialEq, Debug)] #[derive(std::cmp::PartialEq, Debug)]
pub enum PointsRule { pub enum PointsRule {
FilledQuarter, FilledQuarter,
// jans de récompense :
// - battre une dame seule (par autant de façons de le faire, y compris
// utilisant une dame du coin de repos)
// - battre le coin adverse : si deux dames (hormis les deux dernière de son propre coin de
// repos) peuvent battre le coin vide adverse
// jans qui ne peut (pts pour l'adversaire) :
// - battre à faux : si on passe par une case pleine pour atteindre la
// case que l'on peut battre
// - si on ne peut pas jouer ses deux dés
} }
pub trait PointsRules { pub trait PointsRules {