gemini refact

This commit is contained in:
Henri Bourcereau 2025-07-27 10:13:19 +02:00
parent 3e1775428d
commit b5dd94fd3a

View file

@ -361,35 +361,175 @@ impl MoveRules {
with_excedents: bool,
ignored_rules: Vec<TricTracRule>,
) -> Vec<(CheckerMove, CheckerMove)> {
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,
ignored_rules.clone(),
);
// if we got valid sequences with 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(
dice_min,
dice_max,
with_excedents,
ignore_empty,
ignored_rules,
);
moves_seqs.append(&mut moves_seqs_order2);
let empty_removed = moves_seqs
// Etape 1: Générer tous les mouvements potentiels sans appliquer les règles complexes récursives.
let mut potential_moves = self.generate_all_potential_moves(with_excedents);
// Etape 2: Appliquer les filtres pour les règles complexes de manière itérative.
// Règle: MustFillQuarter
if !ignored_rules.contains(&TricTracRule::MustFillQuarter) {
let filling_moves: Vec<_> = potential_moves
.iter()
.filter(|moves| {
let mut board = self.board.clone();
if board.move_checker(&Color::White, moves.0).is_ok()
&& board.move_checker(&Color::White, moves.1).is_ok()
{
return board.any_quarter_filled(Color::White);
}
false
})
.cloned()
.collect();
if !filling_moves.is_empty() {
potential_moves = filling_moves;
}
}
// Règle: Règles de sortie (Exit)
if !ignored_rules.contains(&TricTracRule::Exit) {
// Filtrer les mouvements qui ne respectent pas les règles de base de sortie.
potential_moves.retain(|moves| self.check_exit_rules(moves).is_ok());
// Gérer ExitByEffectPossible: si un mouvement sans excédent est possible, les mouvements avec excédent sont interdits.
let non_excess_moves = self.generate_all_potential_moves(false);
if !non_excess_moves.is_empty() {
// Vérifier s'il existe des mouvements valides sans excédent
let has_valid_non_excess_move = non_excess_moves.iter().any(|moves| {
self.check_exit_rules(moves).is_ok()
&& self.check_corner_rules(moves).is_ok()
&& self.check_opponent_can_fill_quarter_rule(moves).is_ok()
});
if has_valid_non_excess_move {
potential_moves.retain(|moves| {
!self.is_exit_by_excess(moves, &non_excess_moves)
});
}
}
}
// Règle: MustPlayAllDice
let has_two_dice_move = potential_moves
.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);
.any(|(m1, m2)| *m1 != EMPTY_MOVE && *m2 != EMPTY_MOVE);
if has_two_dice_move {
potential_moves.retain(|(m1, m2)| *m1 != EMPTY_MOVE && *m2 != EMPTY_MOVE);
}
// Règle: MustPlayStrongerDie
if !has_two_dice_move && !potential_moves.is_empty() {
let (dice1, dice2) = self.dice.values;
if dice1 != dice2 {
let stronger_die = cmp::max(dice1, dice2);
let uses_stronger_die = potential_moves.iter().any(|(m1, _)| {
if m1.get_from() == 0 {
return false;
}
let dist = (m1.get_to() as i8 - m1.get_from() as i8).unsigned_abs();
dist == stronger_die
});
if uses_stronger_die {
potential_moves.retain(|(m1, _)| {
if m1.get_from() == 0 {
return false;
}
let dist = (m1.get_to() as i8 - m1.get_from() as i8).unsigned_abs();
dist == stronger_die
});
}
}
}
if potential_moves.is_empty() && with_excedents {
return vec![(EMPTY_MOVE, EMPTY_MOVE)];
}
potential_moves
}
/// Helper pour `get_possible_moves_sequences`.
/// Détermine si un mouvement est une sortie "par effet" (avec excédent)
/// en vérifiant s'il n'existe pas dans la liste des mouvements sans excédent.
fn is_exit_by_excess(
&self,
moves: &(CheckerMove, CheckerMove),
non_excess_moves: &[(CheckerMove, CheckerMove)],
) -> bool {
if !moves.0.is_exit() && !moves.1.is_exit() {
return false;
}
!non_excess_moves.contains(moves)
}
/// Nouvelle fonction itérative pour générer les mouvements bruts.
fn generate_all_potential_moves(
&self,
with_excedents: bool,
) -> Vec<(CheckerMove, CheckerMove)> {
let mut moves_seqs = Vec::new();
let color = &Color::White;
let forbid_exits = self.has_checkers_outside_last_quarter();
let (dice1, dice2) = self.dice.values;
let dice_orders = if dice1 == dice2 {
vec![(dice1, dice2)]
} else {
vec![(dice1, dice2), (dice2, dice1)]
};
for (d1, d2) in dice_orders {
for first_move in
self.board
.get_possible_moves(*color, d1, with_excedents, false, forbid_exits)
{
let mut board2 = self.board.clone();
if board2.move_checker(color, first_move).is_err() {
continue;
}
let mut has_second_dice_move = false;
for second_move in
board2.get_possible_moves(*color, d2, with_excedents, true, forbid_exits)
{
let current_moves = (first_move, second_move);
// Valider la séquence en la simulant sur un plateau vierge
let mut scratch_board = self.board.clone();
if scratch_board.move_checker(color, current_moves.0).is_ok()
&& scratch_board.move_checker(color, current_moves.1).is_ok()
{
// Si l'exécution est valide, appliquer les autres règles
if self.check_corner_rules(&current_moves).is_ok()
&& self
.check_opponent_can_fill_quarter_rule(&current_moves)
.is_ok()
&& !(self.is_move_by_puissance(&current_moves)
&& self.can_take_corner_by_effect())
{
if !moves_seqs.contains(&current_moves) {
moves_seqs.push(current_moves);
}
has_second_dice_move = true;
}
}
}
if !has_second_dice_move {
let current_moves = (first_move, EMPTY_MOVE);
if self.check_corner_rules(&current_moves).is_ok()
&& self
.check_opponent_can_fill_quarter_rule(&current_moves)
.is_ok()
&& !(self.is_move_by_puissance(&current_moves)
&& self.can_take_corner_by_effect())
{
if !moves_seqs.contains(&current_moves) {
moves_seqs.push(current_moves);
}
}
}
}
}
moves_seqs
}
@ -447,14 +587,16 @@ impl MoveRules {
pub fn get_quarter_filling_moves_sequences(&self) -> Vec<(CheckerMove, CheckerMove)> {
let mut moves_seqs = Vec::new();
let color = &Color::White;
let ignored_rules = vec![TricTracRule::Exit, TricTracRule::MustFillQuarter];
for moves in self.get_possible_moves_sequences(true, ignored_rules) {
// Utilise la nouvelle méthode non-récursive
let potential_moves = self.generate_all_potential_moves(true);
for moves in potential_moves {
let mut board = self.board.clone();
board.move_checker(color, moves.0).unwrap();
board.move_checker(color, moves.1).unwrap();
// println!("get_quarter_filling_moves_sequences board : {:?}", board);
if board.any_quarter_filled(*color) && !moves_seqs.contains(&moves) {
moves_seqs.push(moves);
// On ne peut pas juste unwrap, il faut gérer l'erreur
if board.move_checker(color, moves.0).is_ok() && board.move_checker(color, moves.1).is_ok() {
if board.any_quarter_filled(*color) && !moves_seqs.contains(&moves) {
moves_seqs.push(moves);
}
}
}
moves_seqs
@ -468,64 +610,10 @@ impl MoveRules {
ignore_empty: bool,
ignored_rules: Vec<TricTracRule>,
) -> Vec<(CheckerMove, CheckerMove)> {
let mut moves_seqs = Vec::new();
let color = &Color::White;
let forbid_exits = self.has_checkers_outside_last_quarter();
for first_move in
self.board
.get_possible_moves(*color, dice1, with_excedents, false, forbid_exits)
{
let mut board2 = self.board.clone();
if board2.move_checker(color, first_move).is_err() {
println!("err move");
continue;
}
// XXX : the goal here is to replicate moves_allowed() checks without using get_possible_moves_sequences to
// avoid an infinite loop...
let mut has_second_dice_move = false;
for second_move in
board2.get_possible_moves(*color, dice2, with_excedents, true, forbid_exits)
{
if self.check_corner_rules(&(first_move, second_move)).is_ok()
&& self
.check_opponent_can_fill_quarter_rule(&(first_move, second_move))
.is_ok()
&& !(self.is_move_by_puissance(&(first_move, second_move))
&& self.can_take_corner_by_effect())
&& (ignored_rules.contains(&TricTracRule::Exit)
|| self.check_exit_rules(&(first_move, second_move)).is_ok())
&& (ignored_rules.contains(&TricTracRule::MustFillQuarter)
|| self
.check_must_fill_quarter_rule(&(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()
&& self
.check_opponent_can_fill_quarter_rule(&(first_move, EMPTY_MOVE))
.is_ok()
&& !(self.is_move_by_puissance(&(first_move, EMPTY_MOVE))
&& self.can_take_corner_by_effect())
&& (ignored_rules.contains(&TricTracRule::Exit)
|| self.check_exit_rules(&(first_move, EMPTY_MOVE)).is_ok())
&& (ignored_rules.contains(&TricTracRule::MustFillQuarter)
|| self
.check_must_fill_quarter_rule(&(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
// NOTE: Cette fonction est maintenant obsolète et remplacée par la logique dans get_possible_moves_sequences.
// On la garde pour la compatibilité mais elle devrait être enlevée à terme.
// Pour l'instant, on délègue à la nouvelle implémentation.
self.get_possible_moves_sequences(with_excedents, ignored_rules)
}
fn _get_direct_exit_moves(&self, state: &GameState) -> Vec<CheckerMove> {