use crate::probleme::case::Case; use crate::probleme::squelette::Interstice; use crate::probleme::Probleme; use crate::NB_AUXILIAIRES; /// Définition des coups et de leurs issues. pub mod coups; pub use coups::*; /// Représente le plateau avec des robots mobiles. // Invariant : la case pos_robot et les cases pos_auxiliaires sont toutes deux à deux différentes #[derive(Clone)] pub struct Calculateur<'a> { probleme: &'a Probleme<'a>, pos_robot: Case, pos_auxiliaires: [Case; NB_AUXILIAIRES], } impl<'a> Calculateur<'a> { // L'invariant est assuré par l'invariant de probleme : les socles ne se superposent pas. pub fn initialiser(probleme: &'a Probleme<'a>) -> Self { Self { probleme, pos_robot: *probleme.socle_robot(), pos_auxiliaires: *probleme.socles_auxiliaires(), } } /// Initialise un calculateur en plaçant les robots autre part que sur leur socle. /// Attentention : cette méthode ne vérifier pas que les cases entrées sont toutes différentes. /// Si tel n'est pas le cas, le comportement est pour l'instant indéfini. pub fn initialiser_hors_socle( probleme: &'a Probleme<'a>, pos_robot: Case, pos_auxiliaires: [Case; NB_AUXILIAIRES], ) -> Self { Self { probleme, pos_robot, pos_auxiliaires, } } /// Replace les robots sur leur socle. pub fn reinitialiser(&mut self) { self.pos_robot = *self.probleme.socle_robot(); self.pos_auxiliaires = *self.probleme.socles_auxiliaires(); } pub fn probleme(&self) -> &Probleme { self.probleme } /// Renvoie la position d'un des pions. pub fn pos_pion(&self, pion: &TypePion) -> &Case { match *pion { TypePion::Robot => &self.pos_robot, TypePion::Auxiliaire(i) => &self.pos_auxiliaires[i], } } /// Renvoie la position de tous les pions. pub fn config(&self) -> (&Case, &[Case; NB_AUXILIAIRES]) { (&self.pos_robot, &self.pos_auxiliaires) } pub fn est_case_vide(&self, case: &Case) -> bool { !(*case == self.pos_robot || self.pos_auxiliaires.contains(case)) } pub fn calculer_coup(&self, coup: &Coup) -> Case { let mut case_arrivee = *self.pos_pion(&coup.pion); // Invariant : case_arrivee n'est pas la case d'un autre pion loop { let case_suivante = match coup.direction { Direction::Haut => case_arrivee.case_haut(), Direction::Bas => case_arrivee.case_bas(), Direction::Gauche => case_arrivee.case_gauche(), Direction::Droite => case_arrivee.case_droite(), }; if let Some(case_suivante) = case_suivante { // Conservation de l'invariant : on ne modifie case_arrivee que si case_suivante est libre if self .probleme .squelette .est_libre(&Interstice::entre(case_arrivee, case_suivante)) && self.est_case_vide(&case_suivante) { case_arrivee = case_suivante; continue; } } break; } case_arrivee } pub fn jouer_coup(&mut self, coup: &Coup) -> ResultatCoup { let case_arrivee = self.calculer_coup(coup); if case_arrivee == *self.probleme.cible() && coup.pion == TypePion::Robot { self.pos_robot = case_arrivee; ResultatCoup::CibleAtteinte } else if case_arrivee != *self.pos_pion(&coup.pion) { match coup.pion { TypePion::Robot => self.pos_robot = case_arrivee, TypePion::Auxiliaire(i) => self.pos_auxiliaires[i] = case_arrivee, } ResultatCoup::Mouvement } else { ResultatCoup::Blocage } } } /// Un calculteur avec une mémoire des coups joués, ce qui permet de les annuler. pub struct CalculateurAvecMemoire<'a> { calculateur: Calculateur<'a>, memoire: Vec<(TypePion, Case)>, } impl<'a> CalculateurAvecMemoire<'a> { pub fn initialiser(probleme: &'a Probleme<'a>) -> Self { Self { calculateur: Calculateur::initialiser(probleme), memoire: vec![], } } /// Replace les robots sur leur socle. pub fn reinitialiser(&mut self) { self.calculateur.reinitialiser(); self.memoire = vec![]; } pub fn probleme(&self) -> &Probleme { self.calculateur.probleme } pub fn pos_pion(&self, pion: &TypePion) -> &Case { self.calculateur.pos_pion(pion) } fn calculer_coup(&self, coup: &Coup) -> Case { self.calculateur.calculer_coup(coup) } fn deplacer_pion(&mut self, pion: &TypePion, case: &Case) { match *pion { TypePion::Robot => self.calculateur.pos_robot = *case, TypePion::Auxiliaire(i) => self.calculateur.pos_auxiliaires[i] = *case, } } pub fn jouer_coup(&mut self, coup: &Coup) -> ResultatCoup { let case_arrivee = self.calculer_coup(coup); if case_arrivee != *self.pos_pion(&coup.pion) { self.memoire.push((coup.pion, *self.pos_pion(&coup.pion))); self.deplacer_pion(&coup.pion, &case_arrivee); if case_arrivee == *self.calculateur.probleme.cible() && coup.pion == TypePion::Robot { ResultatCoup::CibleAtteinte } else { ResultatCoup::Mouvement } } else { ResultatCoup::Blocage } } pub fn nombre_coups(&self) -> usize { self.memoire.len() } pub fn annuler_coup(&mut self) -> Result<(), ()> { match self.memoire.pop() { Some((pion, case)) => { self.deplacer_pion(&pion, &case); Ok(()) } None => Err(()), } } }