## Buggle Dance Revolution (BDR)

Après toutes ces courses, il est temps de faire la fête ! Pour cela, nous allons apprendre à nos buggles à jouer à Dance Revolution, ce jeu très prisé de certains étudiants où le joueur doit bouger sur un tapis prévu à cet effet en fonction des consignes présentées à l'écran en rythme avec la musique. Mais avant cela, nous avons quelques détails à étudier.

[!java|scala|c]

### Conditionnelles sans accolades

Il y a un détail que nous avons omis à propos de la syntaxe des conditionnelles : si une branche ne contient qu'une seule instruction, les accolades sont optionnelles. Ainsi, les deux extraits suivants sont équivalents:

    if (condition) {
        quoiFaireSiLaConditionEstVraie();
    } else {
        quoiFaireSinon();
    }

    if (condition)
        quoiFaireSiLaConditionEstVraie();
    else
        quoiFaireSinon();

Vous pouvez faire de même avec les boucles dont le corps se réduit à une seule instruction. Mais attention, ceci peut être dangereux si on enchaîne les `if` comme dans l'exemple suivant.

    if (estSurBiscuit())
         if (x == 5)
              gauche();
    else
         droite();
    [!c]avancePas();[/!][!java|scala]avance();[/!]

En fait, ça ne tourne pas à droite quand il n'y a pas un baggle par terre ET que x vaut 5, mais quand la buggle a trouvé un baggle, et que x vaut une autre valeur. Autrement dit, la buggle lit le code précédent comme suit (notez que le `else` est décalé vers la droite par rapport à précédemment) :

    if (estSurBiscuit())
            if (x == 5)
                gauche();
            else
                droite();
    [!c]avancePas();[/!][!java|scala]avance();[/!]

La première leçon, c'est que l'indentation est très importante pour que les humains puissent comprendre le code, mais elle ne change pas la signification du code pour la machine. On aurait tout aussi bien pu écrire le code suivant et obtenir le même résultat. Mais attention, si on veut qu'un humain puisse relire le code, l'indentation est quasi indispensable. C'est par exemple le cas si votre code doit être relu par un professeur (pour qu'il le note ou pour lui poser une question), ou si vous comptez réutiliser votre code plus tard, ou même si vous devez debugger votre propre code après l'avoir écrit.

    if (estSurBiscuit()) if (x == 5) gauche(); else droite(); [!c]avancePas();[/!][!java|scala]avance();[/!]

La seconde leçon, c'est qu'une branche `else` se raccroche toujours au `if` le plus proche. C'est parfois un peu contre-intuitif, et il est préférable d'ajouter plus d'accolades que nécessaire pour lever toute ambiguïté.

[/!]

### Enchaînements de conditionnelles

Il arrive que l'on veuille demander à la buggle quelque chose similaire à :

    s'il pleut, prend un parapluie;
    si non, s'il fait chaud, prend une bouteille d'eau;
    si non, si nous sommes le 14 juillet, prend un drapeau français

Le piège étant que nous voudrions qu'au plus l'une de ces actions soient réalisées. C'est à dire, que s'il pleut un 14 juillet très chaud, on ne veut pas que la buggle sorte avec un parapluie, de l'eau et un drapeau, mais juste avec un parapluie. Le code suivant est donc faux.

    [!java|c|scala]if (ilPleut())
        prendreParapluie();
    if (ilFaitChaud())
        prendreDeLEau();
    if (sommes14Juillet())
        prendreDrapeau();[/!][!python]if ilPleut():
        prendreParapluie()
    if ilFaitChaud():
        prendreDeLEau()
    if sommes14Juillet():
        prendreDrapeau()[/!]

En effet, toutes les conditions sont évaluées les unes après les autres, et on risque donc d'aller au défilé un jour de pluie. À la place, il faut donc écrire quelque chose comme ce qui suit pour s'assurer que si l'on trouve une condition vraie, on n'évalue pas les suivants.

    [!java|scala|c]if (ilPleut()) {
        prendreParapluie();
    } else {
        if (ilFaitChaud()) {
            prendreDeLEau();
        } else {
            if (sommes14Juillet()) {
                prendreDrapeau();
            }
        }
    }[/!][!python]if ilPleut():
        prendreParapluie()
    else:
        if ilFaitChaud():
            prendreDeLEau()
        else:
            if sommes14Juillet():
                prendreDrapeau()[/!]

Une telle cascade de conditionnelles est un peu difficile à lire, et il est préférable [!java|scala|c] d'omettre les accolades associées aux `else` comme suit. Il y a même certains langages qui introduisent un mot-clé spécial pour ces `else if` (mais pas [!thelang]).[/!] [!python] d'introduire les sous-blocs avec le mot-clé `elif` (abréviation de «else if») pour expliciter ces branches «sinon si».[/!]

    [!c|java|scala]if (ilPleut()) {
        prendreParapluie();
    } else if (ilFaitChaud()) {
        prendreDeLEau();
    } else if (sommes14Juillet()) {
        prendreDrapeau();
    }[/!][!python]if ilPleut():
        prendreDrapeau()
    elif ilFaitChaud():
        prendreDeLEau()
    elif sommes14Juillet():
        prendreDrapeau()[/!]

### Les graffitis dans le monde des buggles

Les buggles peuvent écrire des choses par terre dans leur monde. Pour ce faire, elles utilisent les quatre méthodes suivantes:

- `[!c]int[/!][!java]boolean[/!] estSurMessage()[!scala]:Boolean[/!]`: renvoie `[!c]1[/!][!java|scala]true[/!][!python]True[/!]` si et seulement s'il y a un message écrit par terre.
- `[!c]char*[/!][!java]String [/!]litMessage()[!scala]: String[/!]` : renvoie le message qu'il y a écrit par terre (s'il y a rien, on obtient une chaîne vide).
- `[!java|c]void[/!] ecritMessage([!c]char*[/!][!java]String [/!]msg[!scala]: String[/!])` : écrit le message spécifié en argument par terre. S'il y a déjà quelque chose écrit par terre, on ajoute le nouveau message à la fin du précédent.
- `[!java|c]void [/!]clearMessage()` : efface ce qui est écrit par terre.

### Objectif de cet exercice

L'objectif est donc d'organiser une partie de BDR entre les buggles en leur apprenant à bouger en fonction des indications écrites par terre. Ces indications sont des messages au sol, avec le code suivant:

Indication | Quoi faire | Mnémotechnique
[!java|c]'R'[/!][!scala|python]"R"[/!] | Tourner à droite et avancer d'une case | Right (signifie «droite» en anglais)
[!java|c]'L'[/!][!scala|python]"L"[/!] | Tourner à gauche et avancer d'une case | Left (signifie «gauche» en anglais)
[!java|c]'I'[/!][!scala|python]"I"[/!] | Se retourner (demi-tour) et avancer d'une case | Inverse
[!java|c]'A'[/!][!scala|python]"A"[/!] | Avancer d'une case | Première lettre de l'alphabet
[!java|c]'B'[/!][!scala|python]"B"[/!] | Avancer de deux cases | Deuxième lettre de l'alphabet
[!java|c]'C'[/!][!scala|python]"C"[/!] | Avancer de trois cases | Troisième lettre de l'alphabet
[!java|c]'Z'[/!][!scala|python]"Z"[/!] | Reculer d'une case | A une lettre de la fin de l'alphabet
[!java|c]'Y'[/!][!scala|python]"Y"[/!] | Reculer de deux cases | A deux lettres de la fin de l'alphabet
[!java|c]'X'[/!][!scala|python]"X"[/!] | Reculer de trois cases | A trois lettres de la fin de l'alphabet
(n'importe quoi d'autre) | Arrêter de danser. |

### Indications

Cet exercice peut sembler un peu compliqué, mais il s'agit principalement de traduire le contenu du tableau ci-dessus dans un enchaînement de conditionnelles.

Vous devez continuer à danser tant qu'il reste des pas de danse à faire. c-à-d tant qu'on est sur une case décrite dans le tableau. Le plus simple pour cela est d'utiliser une variable booléenne (`fini`) en condition d'arrêt d'une boucle while. Cette variable est initialisée à la valeur faux (`[!c]0[/!][!java|scala]false[/!][!python]False[/!]`). Si on trouve une case ne répondant à aucune ligne du tableau, on change la valeur de cette variable à vrai (`[!c]1[/!][!java|scala]true[/!][!python]True[/!]`). Cela arrête la boucle, et le programme.

[!java|c]

Une autre subtilité est qu'il est un peu pénible en Java de déterminer si deux chaînes de caractères sont identiques. Nous utiliserons donc la méthode `char getIndication[!c]Bdr[/!]()` à la place de `[!java]String[/!][!c]char*[/!] litMessage()`. Cette méthode, qui n'est connue que des buggles des exercices BDR, renvoie le premier caractère du message au sol (ou ' ' --une espace-- s'il n'y a rien d'écrit au sol). Cela nous permet de travailler avec des caractères (`char`), qui sont moins pénibles que les chaînes en [!thelang].

[/!]

### Trucs et astuces

Si vous ne parvenez plus à comprendre pourquoi votre buggle n'exécute pas les pas de danse demandés, essayez d'ajouter `baisseBrosse()` dans votre méthode. Cela demandera à la buggle de poser une brosse par terre, laissant une trace au sol quand elle avance. Cela devrait vous aider à suivre sa trajectoire, mais pensez à retirer cette appel lorsque vous voulez tester si votre solution marche : on vous demande de faire danser les buggles, pas de dégrader le dance floor.

Quand votre programme fonctionne enfin, passez à l'exercice suivant.