Simon des Fous

De Wiki LOGre
Aller à : navigation, rechercher
Langue : Français  • English

Présentation

Vous vous souvenez sans doute tous du jeu électronique Simon, comportant 4 boutons de couleur (rouge bleu vert et jaune), dans lequel "l'ordinateur" joue une séquence que vous devez répeter. À chaque fois que vous parvenez à reproduire la séquence, le jeu ajoute une nouvelle touche à la séquence, jusqu'à ce que le joueur perde.

C'est trop facile.

Et si ... Et si on définissait un jeu de permutations  ? Dire que quand le jeu vous dit de faire un Rouge, il faut en fait faire un Bleu, et inversement, et idem pour Jaune et Vert ?

Après quelques parties, les permutations seraient instinctives ... À nouveau trop facile.

Et en créant un mécanisme permettant de définir ses propres permutations à chaque partie ?

Voilà !

L'idée est donc de faire une sorte de Simon, mais sur lequel, avec 8 connecteurs (Origine Rouge, bleu, vert, jaune ; Permutation Rouge, bleu, vert, jaune) et 4 cables permettant de créer les permutations qu'on veut et les changer à la volée (même pendant la partie, pour les plus acharnés). Ce jeu ne sera donc plus uniquement un jeu de mémoire/répétition car il aura une forte composante de réflexion rapide (appliquer les permutations de tête sans se planter).

Bref, un jeu à rendre fou...

État actuel

  • Projet abouti Prototype breadboard : fonctionnel !
  • Projet abouti Schéma et board façon shield Arduino : Fait ! Sous eagle, désolé pour Marc
  • Projet en cours Fabrication des PCB : En cours (seeed)
  • Projet en cours Design du boitier : En cours
  • Projet suspendu Impression du boitier : à faire
  • Projet suspendu Montage du boitier V1 : à faire

Composants

Prototype sur breadboard

  • Arduino UNO
  • 4 boutons poussoir
  • 4 Leds
  • 9 résistances 10k
  • 4 résistances 330 ohms
  • 1 porte logique 4 OU (interrupt sur les boutons) MC14072
  • 1 buzzer
  • 4 capa 100nF pour débouncer les boutons
  • Un tas de fil et une breadboard (duh !)

Version 1 sur PCB (en cours)

  • Arduino UNO
  • 4 boutons poussoir à LED (velleman série R2000 par exemple)
  • 9 résistances 10k
  • 4 résistances 330 ohms
  • 1 porte logique 4 OU MC14072
  • 1 buzzer
  • 4 capa 100nF pour débouncer les boutons
  • Le shield Simon Des Fous
  • 9 connecteurs Molex 2 pins 0.254mm de pas (male à souder sur shield, femelle à souder sur composants)
  • 2 connecteurs molex 4 pins 0.254mm de pas (idem, utilisé pour les permutations)
  • 2 headers male double/femelle 8 pins OU male simple
  • 2 headers male double/femelle 6 pins OU male simple

Photos

Sources

Git

Les différentes sources du projet sont disponibles sur le Git du LOG, sur le repo suivant : git@logre.eu:simon-des-fous

PCB

Pas à proprement parler les sources, mais si vous n'avez pas accès au git du LOG, ca peut dépanner :

Code arduino

const int BTN = 2; // interrupt on any button hit
 
//Definition of pins connected to LEDs
const int LEDR = 3;
const int LEDV = 4;
const int LEDB = 5;
const int LEDJ = 6;
 
//Buzzer output
const int BUZZER = 9;
 
//Input buttons
const int BTNR = 10;
const int BTNV = 11;
const int BTNB = 12;
const int BTNJ = 13;
 
//Input references for permutations
const int REFRED = A1;
const int REFBLU = A2;
const int REFGRE = A3;
const int REFYEL = A4;
 
//Analog values for each color
const int ANALOGRED = 1023;
const int ANALOGBLUE = 768;
const int ANALOGGREEN = 512;
const int ANALOGYELLOW = 256;
 
//Codename for colors (used in arrays indexes and such)
const int ROUGE = 1;
const int VERT = 2;
const int BLEU = 3;
const int JAUNE = 4;
 
typedef struct {
  int bouton;
  int led;
  boolean state;
  int tone;
} couleur;
 
couleur red = {BTNR,LEDR,false,209};
couleur blue = {BTNB,LEDB,false,252};
couleur green = {BTNV,LEDV,false,310};
couleur yellow = {BTNJ,LEDJ,false,415};
 
 
volatile boolean answer = false; // has player begun to answer ?
volatile boolean r = false; // is Red button down ?
volatile boolean g = false; // is Green button down ?
volatile boolean b = false; // is Blue button down ?
volatile boolean y = false; // is Yellow button down ?
volatile int buttonValue = 0; // Int value of currently down button (see Codenames above) 0 is no buttons pressed
 
boolean newGame = true; // Should we begin a new game ?
 
int seq[32] ; // array in which we store the sequence to replay
int permut[5] = {0,ROUGE,VERT,BLEU,JAUNE}; // switch table
 
void setup(){
 pinMode(BTN,INPUT);
 pinMode(LEDR,OUTPUT);
 pinMode(LEDV,OUTPUT);
 pinMode(LEDB,OUTPUT);
 pinMode(LEDJ,OUTPUT);
 pinMode(BTNR,INPUT);
 pinMode(BTNB,INPUT);
 pinMode(BTNJ,INPUT);
 pinMode(BTNV,INPUT);
 pinMode(REFRED,INPUT);
 pinMode(REFBLU,INPUT);
 pinMode(REFGRE,INPUT);
 pinMode(REFYEL,INPUT);
 pinMode(BUZZER,OUTPUT);
 
 Serial.begin(9600); 
 attachInterrupt(0,activeAnswer,RISING);
 
 randomSeed(analogRead(A0)); // initialize random number generator
}
 
void emptySeq(){ // Empties the expected sequence (called before a new game)
 for (int i = 0; i <32 ; i++){
  seq[i]=0; 
 } 
}
 
/* 
* Return the integer value of an analog reference. Used to read switching table.
* @param int analogValue : The analog value read from a pin
* @return int : The codename value of the associated color (See Analog values for each color)
*/
int analogToColor(int analogValue){ 
   if(analogValue > 0.9*ANALOGRED && analogValue < 1.1*ANALOGRED){
     return ROUGE;
   }
   if(analogValue > 0.9*ANALOGBLUE && analogValue < 1.1*ANALOGBLUE){
     return BLEU;
   }
   if(analogValue > 0.9*ANALOGGREEN && analogValue < 1.1*ANALOGGREEN){
     return VERT;
   }
   if(analogValue > 0.9*ANALOGYELLOW && analogValue < 1.1*ANALOGYELLOW){
     return JAUNE;
   }
   return ROUGE; // By default, let s return red, for the sake of returning something, which should not happen
}
 
/**
* Reads the analog values to determine switching table
*/
void readRefs(){
 
 int refrouge = analogRead(REFRED);
 int refbleu = analogRead(REFBLU);
 int refvert = analogRead(REFGRE);
 int refjaune = analogRead(REFYEL);
 permut[ROUGE] = analogToColor(refrouge);
 permut[BLEU] = analogToColor(refbleu);
 permut[VERT] = analogToColor(refvert);
 permut[JAUNE] = analogToColor(refjaune);
 Serial.print("rouge = ");
 Serial.println(refrouge);
 Serial.print("bleu = ");
 Serial.println(refbleu);
 Serial.print("vert = ");
 Serial.println(refvert);
 Serial.print("jaune = ");
 Serial.println(refjaune);
 
}
 
/*
* Reads all four buttons to determine which one was pressed. Used in interrupt and while reading answer
*/
void readButtons(){
  r = digitalRead(red.bouton);
  g = digitalRead(green.bouton);
  b = digitalRead(blue.bouton);
  y = digitalRead(yellow.bouton);
  buttonValue = 0; // default : no button pressed;
  if(r) buttonValue = ROUGE;
  if(b) buttonValue = BLEU;
  if(g) buttonValue = VERT;
  if(y) buttonValue = JAUNE;
 
}
 
/*
* Called on the interrupt : switch to answer mode and read buttons
*/
void activeAnswer(){
 
 if(answer ==false){ //only enter once, further readings will be made in answer section
  answer = true; 
  readButtons();
 }
}
 
/*
* Turns off all leds (duh !)
*/
void shutLeds(){
 
  digitalWrite(red.led,LOW);
 digitalWrite(blue.led,LOW);
 digitalWrite(yellow.led,LOW);
 digitalWrite(green.led,LOW);
 noTone(BUZZER);
 
}
 
/*
* Waits for given duration in jumps of 10ms, so as to enter in answer mode as soon as needed
* @param int msec : duration to wait in miliseconds
*/
void myDelay(int msec){
  int i =0;
  while (i<msec){
    if(answer) {return;} // If any button was pressed (interrupt), cancel the wait to enter in answer mode asap
    delay(10);
    i += 10;
  }
}
 
/*
* Main part, reading the answers and comparing them to the expected sequence
*/
void readAnswer(){
 
 shutLeds();
 int i =0;
 
 Serial.println("debut seq reponse ");
 
 int start = millis();
 int last = millis();
 
 boolean added = false; //tracks if we incremented i to go to next expected value
 int correct = 1000; // any kind of initialization, as correct answer will be read 2 lines after
 while(last-start< 5000 && correct !=0){ //5 sec between each press maximum
 
   correct = permut[seq[i]];
   if(buttonValue != 0){  
 
 
     Serial.print("Hit nb");
     Serial.print(i);
     Serial.print(", expected ");
     Serial.println(correct);
     //Serial.println(output);
     if(correct == buttonValue){ // If player hit the correct button
       allumerCouleur(buttonValue,false); // light the associated led
       Serial.println("correct");
       start = millis(); //reset between-buttons timer
       added = false; 
     }else{ // player got wrong answer
       start = millis() -10000; // force exit 
     }
 
 
   }else{ // no button pressed
      shutLeds(); // turn off leds
 
      if(!added && last-start > 50){ // if we just released a button, increase the counter to keep track of which answer is expected now
      // Note : the last-start bit is a kind of software debouncing
      // If you put capacitors in parallel of your buttons, you can comment out/delete the IF statement (but not its contents)
        Serial.println("next hit ");
        i ++;
        added = true;
      }
   }
 
  readButtons();
  last = millis();
 
 
 }
 
 // end of answer sequence : either the user completed if correctly, or made a mistake
 
 if(correct == 0) {//correctly responded
   Serial.println("next level");
   int nextButton = getNextLevel(); // add another color the the current sequence
   seq[i] = nextButton;
   delay(500); // wait a bit before repeating
 }else{
   displayError(correct); // display what was expected
   newGame=true; // start a new game
 }
 answer = false; // We re no longer in answer mode
 Serial.println("Fin seq reponse"); 
}
 
/*
* Flashes the correct expected LED when playser made a mistake
* @param int correct : integer code of correct color
*/
void displayError(int correct){
  allumerCouleur(correct,true);
  Serial.println("faux");
  delay(2000); // wait for 2 sec before restarting
  shutLeds();
}
 
/*
* Gets a random new color to add to the sequence
* @return int code of any of the 4 colors (1 to 4)
*/ 
int getNextLevel(){
  return (int)random(ROUGE,JAUNE+1);
}
 
/*
* Lights the led of given color code
* @param int color : color code of led to light
*/
void allumerCouleur(int color, boolean error){
 
  switch (color){
   case ROUGE : digitalWrite(red.led,HIGH); tone(BUZZER,red.tone); break;
   case VERT : digitalWrite(green.led,HIGH); tone(BUZZER,green.tone); break;
   case JAUNE : digitalWrite(yellow.led,HIGH); tone(BUZZER,yellow.tone); break;
   case BLEU : digitalWrite(blue.led,HIGH); tone(BUZZER,blue.tone); break; 
  }
  if(error){
    tone(BUZZER,42); // If it was an error, sound the disgraceful horn instead
  }
 
}
 
/*
* Flashes a led for a small time, used to flash the current sequence
* Usual light/wait/shut
*/
void clignotter(int color){
  allumerCouleur(color,false);
  myDelay(500);
  shutLeds();
 
}
 
/*
* Displays the current sequence of colors (readily interrupted but a button press)
*/
void afficherSequence(){
  //int taille = sizeof(seq)/sizeof(seq[0]);
  int i = 0;
  while(seq[i] != 0){
    if(answer) {return;}
    int color = seq[i];
    clignotter(color); // blinks current color
    myDelay(100); // wait a bit before each blink
    i++;
  }
}
 
void loop(){
 
 if(newGame){ // new game initialization
   emptySeq(); // empties the sequence
   seq[0] = getNextLevel(); //get a random first color
   newGame=false; // disable new game
   readRefs(); // read switching table
   delay(1000); // wait 1 sec before restarting
 }
 
 if (answer){ // if any button was pressed (interrupt) : enter in answer mode
   readAnswer();
 }else{ // else, display current sequence in loop
   afficherSequence();
   myDelay(1000);
 }
 
}