Bonjour à tous,
je réalise actuellement une version électronique des Tours de Hanoi sur base d'un arduino UNO et de composants électroniques simples. La tour est, dans ma réalisation, dite inversée car la base de la tour se trouve en haut.
Je partage ici mon code commenté pour ceux que ça intéresse.
Le principe technique est le suivant : quinze LED (3x cinq couleurs différentes) sont disposées et branchés en sortie et trois boutons sont disposés et branchés en entrée comme ceci :
5 5 5
4 4 4
3 3 3
2 2 2
1 1 1
b b b
1 = rouge, 2 = orange, 3 = jaune, 4 = vert, 5 = bleu, b = bouton
Le principe logique est le suivant : lorsque l'on appuie sur un bouton, on sélectionne la pièce disponible (allumée) la plus éloignée de la base de la tour correspondante sauf si celle-ci est vide. Ensuite, on sélectionne la tour de destination avec le bouton correspondant et on pose la pièce sélectionnée dessus. On ne peut pas poser une pièce sur une colonne où une autre pièce, plus éloignée de la base, se trouve déjà.
Voici quelques exemples de déplacements :
5 x x 5 x x 5 x x 5 x x 5 x x 5 x x 5 x x 5 x x
4 x x 4 x x 4 x x 4 x x 4 x x 4 x x 4 x x 4 x x
3 x x 3 x x 3 x x 3 x x x 3 x x 3 x x 3 x x 3 x
2 x x 2 x x x x 2 x x 2 x x 2 x x 2 x 2 x x 2 x
1 x x x 1 x x 1 x x x 1 x x 1 1 x x 1 x x x 1 x
sens de lecture = de gauche à droite
1 = rouge, 2 = orange, 3 = jaune, 4 = vert, 5 = bleu, x = led éteinte
Et voici le code final :
/*
* 5
* TOUR DE HANOI 4
* 3
* 2
* 1
*
* copyleft anonyme
* anarchie vaincra
*
*/
#define PIECES 5 // nombre de couleurs
#define DEPART 1 // tour de départ
const int pinButton[3] = {14,15,16}; // entrées boutons sur A0, A1, A2
const int pinLed[PIECES][3] = {0,1,2,3,4,5,6,7,8,9,10,11,17,18,19}; // sorties LED
const int pinTone = 13; // sortie son
unsigned long lastMillis = 0; // chrono du clignotement
int thisRow, thisColor, selectedRow, selectedColor; // mémoires cycliques et de sélection
int maxColor[3]; // couleur max des tours
boolean buttonState[3], lastButtonState[3]; // états des boutons pour détecteur de bord
boolean selectMode = false; // mode : sélection ou mouvement
boolean Blink; // permutateur pour le clignotement
boolean ledState[PIECES][3]; // états des LED
void setup() { // configuration et initialisation
pinMode(pinTone, OUTPUT); // sortie son
for (thisRow = 0 ; thisRow < 3 ; thisRow++) { // pour chaque bouton
pinMode(pinButton[thisRow], INPUT_PULLUP); // active les broches des boutons
if (thisRow != DEPART) maxColor[thisRow] = PIECES+1 ; // initialise le max de départ
else maxColor[thisRow] = 0 ;
for (thisColor = 0 ; thisColor < PIECES ; thisColor++) { // pour chaque couleur
pinMode(pinLed[thisColor][thisRow], OUTPUT); // active les broches des LED
if (thisRow != DEPART) ledState[thisColor][thisRow] = LOW; // pour la tour de départ
else {
ledState[thisColor][thisRow] = HIGH; // enregistre les états
digitalWrite(pinLed[thisColor][thisRow], HIGH); // allume la tour
}
}
}
}
void loop() { // boucle principale
delay(10); // stabilisation
if (selectMode == true) doBlink(selectedColor, selectedRow); // clignote si demandé
for (thisRow = 0 ; thisRow < 3 ; thisRow++) { // pour chaque bouton
buttonState[thisRow] = digitalRead(pinButton[thisRow]); // lit l'entrée
if (buttonState[thisRow] != lastButtonState[thisRow]){ // décèle un changement
lastButtonState[thisRow] = buttonState[thisRow]; // réinitialise l'état
if (buttonState[thisRow] == LOW){ // lorsque le bouton est enfoncé
if (selectMode == false){ // si aucune sélection n'est en cours
if (maxColor[thisRow] == PIECES+1) tone(pinTone, 220, 80); // tour vide
else {
selectedRow = thisRow; // enregistre la colonne
selectedColor = maxColor[thisRow]; // enregistre la couleur
selectMode =! selectMode; // permute le mode
}
}
else { // si une sélection est faite
if (maxColor[selectedRow] > maxColor[thisRow]) tone(pinTone, 220, 80); // mouvement invalide
else {
ledState[selectedColor][selectedRow] = LOW; // retire du registre
ledState[selectedColor][thisRow] = HIGH; // ajoute au registre
maxColor[thisRow] = maxColor[selectedRow]; // transfère le max
maxColor[selectedRow] = refreshMax(selectedRow); // actualise le max originel
digitalWrite(pinLed[selectedColor][selectedRow], LOW); // éteind
digitalWrite(pinLed[selectedColor][thisRow], HIGH); // allume
selectMode =! selectMode; // permute le mode
}
}
}
}
}
}
void doBlink(int color, int row) { // fonction clignotement
if(millis() - lastMillis > 80) { // si 0,08s sont écoulées
Blink =! Blink; // permute l'état
digitalWrite(pinLed[color][row], Blink); // écrit l'état
lastMillis = millis(); // enregistre le temps
}
}
int refreshMax(int row) { // fonction rafraichissement du max
for (int color = 0 ; color < PIECES ; color++) { // pour chaque couleur
if (ledState[color][row] == HIGH) return color; // teste à partir de 0 et renvoit si allumé
}
return PIECES+1; // si rien n'est allumé, renvoit le code "tour vide"
}
En espérant que cela permette à d'autres de saisir la remarquable opportunité qu'offre l'arduino à ceux qui souhaiteraient approfondir leurs connaissances en électronique et en programmation sur des bases saines.
Il est aussi à noter que ce jeu est utilisé dans le domaine de la psychologie cognitive et est très agréable pour les enfants (que nous avons ou que nous sommes).