Les Bugs en boucles

../../_images/bugWheel.png

L’objectif de ce TP est :

  • Déterminer des structures de boucles (itératives) et les implémenter.

  • Produire le comportement attendu du/des Bug(s).

Structures de contrôle de type boucles

Les structures de contrôle de type boucles permettent d’exprimer des traitements répétitifs. En Java, les boucles sont au nombre de trois: la boucle while, la boucle do…while et la boucle for.

la boucle while

La boucle while permet de répéter une séquence d’instructions tant qu’une certaine condition est vérifiée :

while (condition) {
    // bloc d'instructions
}

Lors de l’exécution du programme, si la condition est vérifiée (vraie), alors le bloc d’instructions est exécuté. Ce traitement est ensuite répété tant que la condition reste vérifiée. Si la condition n’est pas vérifiée dès le départ, le bloc d’instructions n’est pas exécuté.

La boucle while permet de traduire la structure tant que du langage algorithmique.

La boucle do…while

Comme la boucle while, la boucle do…while permet de répéter une séquence d’instructions tant qu’une certaine condition est vérifiée.

do {
    // bloc d'instructions
} while (condition);

Lors de l’exécution du programme, le bloc d’instructions est exécuté. Ensuite, si la condition est vérifiée (vraie), alors ce traitement est répété. La différence avec la boucle while est que le bloc d’expression est exécuté au moins une fois.

La boucle do…while permet de traduire la structure répéter du langage algorithmique.

La boucle for

La boucle for est un cas particulier de la boucle while. La boucle for suivante :

for (expression A ; expression B ; expression C) {
    // bloc d'instructions
}

est équivalente à la boucle while :

expression A;
while (expression B) {
    // bloc d'instructions
    expression C;
}

Ainsi, expression A correspond à l’initialisation de la boucle, expression B correspond à la condition de la boucle, et expression C correspond à une instruction exécutée après chaque itération de la boucle.

La boucle for est généralement utilisée pour faire varier un compteur entre deux bornes définies. Par exemple, pour afficher les nombres entiers entre -10 et +10, on utilisera :

for (int i = -10 ; i <= 10 ; i++) {
    System.out.println(i);
}

La boucle for permet ainsi de traduire la structure pour du langage algorithmique.

Défi n°1 : chacun sa marche

État initial

  • Sept Bugs alignés sur une grille de 7x7 cases.

  • Des murs en escalier figurant les positions à atteindre par les Bugs.

Résultat attendu

  • Chaque Bug est positionné sous sa marche d’escalier.

../../_images/init12.png ../../_images/result12.png

À faire

  1. Récupérer l'archive du programme, l’enregistrer dans le dossier du TP puis la décompresser.

    Le programme comprend les mêmes classes et fichiers que pour les TPs précédents. Se reporter à l’énoncé du TP Le monde des Bugs pour les détails et pour la référence de l’API.

  2. Positionner les variables et constantes du fichier Main.java :

    final int N_BUGS=7;
    final int Y_BUGS=0;
    final int HAUTEUR = 7;
    final int LARGEUR = 7;
    final int TAILLE_CASE = 80;
    boolean readMaze = true ;
    
  3. Récupérer la définition des murs, l’enregistrer dans le dossier du TP.

  4. Compiler le programme tel qu’il est fourni pour vérifier qu’il n’y a pas d’erreur et que l’état initial est bien celui reproduit ci-dessus.

  5. Écrire un programme (méthode enRoute()) qui produise le résultat escompté. Seule la méthode avance() (sans paramètre) sera employée pour faire progresser les Bugs.

Correction

Bug.java

Défi n°2 : chacun son biscuit

Cet exercice est très semblable au précédent.

État initial

  • Sept Bugs alignés sur une grille de 7x7 cases.

  • Des biscuits à prendre par les Bugs.

Résultat attendu

  • Chaque Bug a pris le biscuit de sa colonne.

../../_images/init23.png ../../_images/result23.png

À faire

  1. Modifier le fichier mazeData pour placer les 7 biscuits aux bons emplacements.

  2. Modifier le programme pour que chaque Bug ramasse son biscuit. Seule la méthode avance() (sans paramètre) sera employée pour faire progresser les Bugs.

Correction

Bug.java mazeData.txt

Défi n°3 : chacun chez soi

Il s’agit d’une variante de l’exercice précédent où chaque Bug doit revenir à sa position d’origine après avoir ramassé le biscuit qui lui était promis. Des solutions particulières mais non génériques s’offrent à vous pour réaliser cela. Toutefois, on vous demande d’opter pour une technique générique transposable à beaucoup d’autres situations : mémoriser le parcours.

Malgré tout, le déplacement des Bugs est encore simple et la mémorisation se résume ici à compter le nombre de pas efféctués jusque’au biscuit.

À faire

  1. Déclarer une variable nbPasFaits, locale à la méthode enRoute(). Elle servira à mémoriser, à chaque itération, le nombre de pas qu’un Bug a déja effectués en direction du biscuit.

  2. Modifier le programme pour incrémenter convenablement la variable nbPasFaits et l’utiliser pour revenir en position initiale. Aucun Bug ne devra se cogner au mur, et seule la méthode avance() (sans paramètre) sera employée pour faire progresser les Bugs.

Correction

Bug.java

Défi n°4 : la course au biscuit

La course au biscuit consiste pour chaque Bug à courir droit devant lui en ramassant les biscuits qu’il trouve. Lorsqu’il a amassé quatre biscuits, il fait demi tour et revient au plus vite à son point de départ. Si un Bug parcourt toute la piste sans parvenir à trouver quatre biscuits, il revient tout de même, bredouille, à la case de départ.

État initial

  • Huit Bugs alignés sur une grille de 8x15 cases.

  • Des biscuits à prendre par les Bugs.

Résultat attendu

  • Chaque Bug a pris au plus quatre biscuits dans sa colonne et est revenu à la case départ.

../../_images/init32.png ../../_images/result32.png

À faire

  1. Récupérer le fichier mazeData.txt contenant les positions des biscuits dans la grille.

  2. Ajuster les paramètres de la classe Main pour générer une grille de 8x15 cases.

  3. Compiler puis exécuter le programme pour vérifier l’état initial.

  4. Concevoir l’algorithme sur feuille, puis l’implémenter dans la méthode enRoute().

Correction

Bug.java

Défi n°5 : Les Bugs fêtent Divali

Chaque année, à l’occasion de la fête de Divali (la fête des lumières), on peut voir des hommes marcher sur des braises. Les Bugs Belfortains, en vacances à l’île Maurice, veulent s’y essayer, mais sans récompense point de motivation pour nos Bugs (pas comme les étudiants), alors on a placé des biscuits sur la piste de braises.

Chaque Bug doit, comme pour le défi précédent, ramasser quatre biscuits puis revenir à sa case départ. Seulement, marcher sur des braises, ça brule ! Et à force de se bruler, la force risque de quitter nos Bugs. Chaque fois qu’un Bug passe sur une case très chaude (peinte en jaune), il perd un point de vitalité. Lorsqu’il doit ramasser un biscuit placé sur une case chaude, il en perd deux car cela le ralentit. Enfin, lorsqu’un Bug a perdu tous ses points de vitalité, il grille et stoppe tout net sa progression.

État initial

  • Huit Bugs alignés sur une grille de 8x15 cases.

  • Des biscuits à prendre par les Bugs.

  • Des cases peintes en jaune figurant les zones très chaudes.

Résultat attendu

  • Chaque Bug a pris au plus quatre biscuits dans sa colonne et est revenu à la case départ.

  • Certains Bugs sont grillés.

../../_images/init41.png ../../_images/result41.png

À faire

  1. Récupérer le fichier mazeData.txt contenant les positions des biscuits dans la grille.

  2. Ajuster les paramètres de la classe Main pour générer une grille de 8x15 cases.

  3. Déclarer le tableau des cases chaudes au début de la méthode Main() comme ceci (le copier/coller est vivement recommandé) :

    final int[][] casesColorees = {
            {14,0},{14,1},{14,2},{14,3},{14,4},{14,5},{14,6},{14,7},
            {13,0},{13,1},{13,6},{13,7},
            {12,1},{12,6},
            {11,1},{11,2},{11,5},{11,6},
            {10,0},{10,1},{10,2},{10,3},{10,4},{10,5},{10,6},{10,7},
            {9,1},{9,6},
            {8,0},{8,1},{8,2},{8,3},{8,4},{8,5},{8,6},{8,7},
            {5,2},{5,6},
            {4,1},{4,6},
            {3,1},{3,3},{3,5},{3,7},
            {2,4},
            {1,6}
    };
    
  1. Compiler puis exécuter le programme pour vérifier l’état initial.

  2. Concevoir l’algorithme sur feuille, puis l’implémenter dans la méthode enRoute(). On considérera ici que sur le chemin du retour, lorsque le Bug revient à sa case de départ, les braises sont refroidies et qu’il ne se brule donc plus. Les illustrations ci-dessus correspodent à cette situation.

  3. Modifer le programme pour considérer cette fois que les braises sont toujours chaudes lors du retour.

Indication

  • Dans l’exemple, chaque Bug s’est vu attribué 9 points de vie au départ de la course.

Note

  • Pour comparer deux objets Java, on utilise la méthode equals(), implémentée pour la plupart des classes. Ainsi, pour comparer la couleur du sol avec la couleur de référence (ici le jaune), on écrira un test comme celui-ci :

    if( getCouleurSol().equals(StdDraw.YELLOW) ){ ... }
    

Correction en version braises refroidies au retour

Bug.java

Défi n°6 : Un Bug au JO

L’un de vos Bugs s’est découvert une passion pour la course à pieds. Il espère parvenir à se qualifier pour les prochains jeux bugolympiques et s’en donne les moyens : Il s’entraine chaque jour sur la piste d’athlétisme et suit rigoureusement le programme que lui a établi son coach. Chaque matin, le Bug doit faire exactement dix tours de la piste carrée reproduite ci-dessous, dans le sens conventionnel c’est à dire anti-horaire. Mais au fil des tours, ses pointes usent la piste et cette dernière se dégrade rapidement. On constate la dégradation par son changement de couleur tour après tour.

État initial

  • Un Bug sur la piste.

Résultat attendu

  • Le Bug a fait ses dix tours de piste.

  • La piste est noircie.

../../_images/init5.png ../../_images/result5.png

À faire

  1. Récupérer le fichier mazeData.txt définissant la piste.

  2. Ajuster les paramètres de la classe Main pour générer une grille de 11x11 cases ainsi qu’un seul Bug.

  3. Concevoir l’algorithme de progression du Bug en n’employant que les méthodes

    • avance() sans paramètre

    • gauche()

    Penser à définir des constantes pour ne pas devoir employer de valeurs littérales dans les boucles.

  1. Implémenter l’algorithme et en vérifier la bonne marche. Le Bug pourra envoyer un message à chaque passage sur la ligne.

  2. Compléter votre conception en ajoutant la gestion de la couleur de la piste. Lors du premier tour, elle doit être blanche (code RVB = [255,255,255]) pour se griser tour après tour et devenir gris foncé lors du dernier tour (code RVB = [128,128,128]). Toutes les couleurs adoptées seront définies par un seul paramètre coefCouleur appliqué aux trois canaux (code RVB [coefCouleur, coefCouleur, coefCouleur]).

    La définition de la couleur en Java peut alors se faire ainsi

    import java.awt.*; // à placer tout au début du fichier endehors de la classe
    Color couleurPiste = new Color(coefCouleur,coefCouleur,coefCouleur) ;
    

    Indication

    On peut se donner une loi de variation affine commune aux trois canaux de couleur et prenant la valeur 255 pour tour=0 et la valeur 128 pour tour=10. L’expression correspondante est donc une équation de droite que vous devez déterminer après l’avoir esquissée au brouillon pour \(tour \in [0 ; 10[\).

Correction

Bug.java

Défi n°7 : Un Bug au JO (suite)

Courir sur la piste, c’est lassant. Alors votre Bug a décidé de vaier un peu et d’aller en forêt. Seulement avec les obstacles et les etangs, ce n’est pas facile non plus. Il a tout de même réussi à déterminer un tour composé de quatre cotés identiques et reproduit ci-dessous (résultat attendu) .

Comme l’effort est plus intense, il décide de limiter son entrainement à sept tours de ce parcours. Malgré tout, l’herbe s’abime elle aussi et noircit un peu plus à chaque passage.

État initial

  • Un Bug dans la forêt.

  • L’herbe est verte, les étangs sont bleus.

Résultat attendu

  • Le Bug a fait ses sept tours de parcours.

  • L’herbe est noircie sur le parcours.

../../_images/init6.png ../../_images/result6.png

À faire

  1. Ajuster les paramètres de la classe Main pour générer une grille de 15x15 cases ainsi qu’un seul Bug.

  2. Concevoir l’algorithme de progression du Bug en n’employant que les méthodes

    • avance() sans paramètre

    • gauche()

    On pourra cette fois tolérer des valeurs littérales pour les nombres de pas de chaque segment.

  3. Implémenter l’algorithme et en vérifier la bonne marche. Le Bug pourra envoyer un message à chaque passage sur la ligne. La liste des cases étangs peintes en bleu se définit ainsi

      final int[][] casesColorees = {
            {14,0},{14,1},{14,7},{14,13},{14,14},
            {13,0},{13,6},{13,7},{13,8},{13,14},
            {12,3},{12,7},{12,11},
            {11,2},{11,3},{11,11},{11,12},
            {10,2},{10,7},{10,12},
            {9,6},{9,7},{9,8},
            {8,1},{8,5},{8,6},{8,8},{8,9},{8,13},
            {7,0},{7,1},{7,2},{7,4},{7,5},{7,9},{7,10},{7,12},{7,13},{7,14},
            {6,1},{6,5},{6,6},{6,8},{6,9},{6,13},
            {5,6},{5,7},{5,8},
            {4,2},{4,7},{4,12},
            {3,2},{3,3},{3,11},{3,12},
            {2,3},{2,7},{2,11},
            {1,0},{1,6},{1,7},{1,8},{1,14},
            {0,0},{0,1},{0,7},{0,13},{0,14}
    };
    

Pour fixer la couleur de fond (vert) vous positionnez la constante COULEUR_FOND convenablement dans la classe Main.

  1. Compléter votre conception en ajoutant la gestion de la couleur de l’herbe. Lors du premier tour, elle doit être verte (code RVB = [0,255,0]) pour se noircir tour après tour et devenir vert foncé lors du dernier tour (code RVB = [0,128,0]).

Correction

Bug.java

Défi n°8 : Les Bugs perdus

Un Bug est perdu dans un étrange labyrinthe et il a besoin de vous pour trouver la sortie (représentée par les cases oranges). Vous ne pouvez pas donner le chemin tout simplement avec quelque chose comme droite();avance();avance(); parce qu’il faut pouvoir secourir tout Bug quelle que soit sa position de départ. La bonne nouvelle est que le chemin vers la sortie est en quelque sorte écrit au sol : En effet, ce labyrinthe est composé de plusieurs corridors, avec des biscuits par terre. À chaque embranchement, il faut prendre

  • à gauche si le corridor qu’on vient de parcourir contient 3 biscuits ou plus,

  • à droite s’il contient 2 biscuits ou moins.

Vous devez compter exactement 5 cases par couloir. Les cases aux intersections doivent être comptées comme les dernières de leur couloir, pas comme les premières après avoir tourné. La forme générale de votre code doit donc être quelque chose comme

TANT QUE la sortie n'est pas ateinte
FAIRE
  prendre le prochain couloir pour décider
  s'il faut tourner à gauche ou à droite au prochain embranchement.
FIN_TANTQUE

Vous pouvez déterminer si vous avez rejoint la sortie (indiquée en orange) avec la méthode isSortieTrouvee() fournie. Pour prendre un couloir, il suffit de courir d’une intersection à l’autre tout en comptant les biscuits en chemin. Ce qui complique un peu, c’est qu’au début du couloir, vous vous trouvez bien entendu à une intersection, mais vous souhaitez avancer quand même. Le plus simple pour cela est alors d’utiliser une boucle do / while à la place d’une boucle while pour se déplacer d’une intersection à l’autre. Une autre petite difficulté est la gestion des couloirs qui débouchent sur un bord ou dans un coin. Dans ces cas, il est parfois impossible de pouvoir avancer dans la direction déterminée par le nombre de biscuits trouvés. La stratégie à adopter sera alors la suivante :

SI direction D interdite ALORS
  choisir la direction "tout droit"
  SI direction interdite ALORS
    choisir la direction opposée à D
  FIN_SI
FIN_SI

État initial

  • Un Bug dans un dédale.

  • Des biscuits, une sortie (cases oranges).

Résultat attendu

  • Le Bug a trouvé la sortie en suivant la régle enoncée.

../../_images/init7.png ../../_images/result7.png

À faire

  1. Ajuster les valeurs des constantes dans la classe Main comme ci-dessous

    final int N_BUGS=1;
    final int Y_BUGS=1;
    final int HAUTEUR = 18;
    final int LARGEUR = 23;
    final int TAILLE_CASE = 30;
    final Color COULEUR_PP = StdDraw.ORANGE ;
    
  2. Définir les deux cases à colorier pour la sortie avec

    final int[][] casesColorees = {
          {16,22},{16,21}
    };
    
  1. Copier le code de la méthode isSortieTrouvee() dans la classe Bug

    /**
    * indique si on est sur la sortie,
    * cad si la case est de couleur ORANGE
    * @return vrai si on est sur la sortie
    */
    public boolean isSortieTrouvee(){
       return getCouleurSol().equals(StdDraw.ORANGE);
    }
    
  2. Récupérer le fichier mazeData.txt définissant la piste et les positions des biscuits.

  3. Compiler puis vérifier que l’état initial correspond bien à ce qui est attendu.

  4. Concevoir au brouillon l’algorithme de résolution de ce labyrinthe aux régles particulières; puis l’implémenter en java dans la méthode enRoute(). Il peut-être intéressant pour la mise au point de permettre au Bug de donner des informations sur sa progression, comme par exemple

Bug 0 dit : 3 biscuits ==> à gauche
Bug 0 dit : 5 biscuits ==> à gauche
Bug 0 dit : 2 biscuits ==> à droite
Bug 0 dit : 1 biscuits ==> à droite
Bug 0 dit : 2 biscuits ==> à droite
Bug 0 dit : 5 biscuits ==> à gauche
Bug 0 dit : 3 biscuits ==> à gauche
Bug 0 dit : 1 biscuits ==> à droite
Bug 0 dit : 2 biscuits ==> à droite
Bug 0 dit : 1 biscuits ==> à droite
Bug 0 dit : 4 biscuits ==> à gauche
Bug 0 dit : 2 biscuits ==> à droite
Bug 0 dit : 3 biscuits ==> à gauche
Bug 0 dit : 1 biscuits ==> à droite
Bug 0 dit : 3 biscuits ==> à gauche
Bug 0 dit : 3 biscuits ==> à gauche
Bug 0 dit : 3 biscuits ==> à gauche
Bug 0 dit : 1 biscuits ==> à droite
Bug 0 dit : 3 biscuits ==> à gauche
Bug 0 dit : 2 biscuits ==> à droite
Bug 0 dit : 2 biscuits ==> à droite
Bug 0 dit : 2 biscuits ==> à droite
Bug 0 dit : 4 biscuits ==> à gauche
Bug 0 dit : 3 biscuits ==> à gauche
Bug 0 dit : 2 biscuits ==> à droite
Bug 0 dit : Youpi !

Correction

Bug.java