Crazy Simon

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

Presentation

Remember in the 80s, the Simon electronic game, composed of 4 colored buttons (red, blue, green and yellow), in which an "AI" plays a sequence of colors you have to reapeat. Each time you succeed in reproducing the exact sequence, the game adds another color, until you can't remember all of them and make a mistake in replaying them.

Too easy.

What if ... What if we define a permutation system ? For exemple, when Simon says Red, you have to reply Blue and vice versa ? And as well for green and yellow ?

After only a few games, the permutations will become instinctive... Still too easy.

What if we add a system to define our own permutations for each game if necessary ?

Voilà !

The idea is to create a kind of Simon game, but with 8 connectors allowing to define the permutations before starting the game (4 connectors for the Question part, 4 for the Answer, and using cables between them) thus allowing creating any wanted permutation and change them on the fly. This game is no longer a pure memory game, having a strong wits component (applying the permutations correctly)

All in all, a game that will make you crazy...

Current state

  • Projet abouti breadboard prototype : working!
  • Projet abouti Schema and board, Arduino shield like : done ! with Eagle
  • Projet en cours PCB manufacturing : In progress (seeed)
  • Projet en cours box design : In progress
  • Projet suspendu Box impression : to do
  • Projet suspendu Assembly of v1 box : to do

Components

Breadboard Prototype

  • Arduino UNO
  • 4 push buttons
  • 4 Leds
  • 9 resistor 10k
  • 4 resistor 330 ohms
  • 1 logical gate 4 OR (interrupt on buttons) MC14072
  • 1 buzzer
  • 4 capacitor 100nF to debounce buttons
  • A LOT of wires and a breadboard (duh !)

Version 1 on PCB (in progress)

  • Arduino UNO
  • 4 push buttons with LED (velleman series R2000 )
  • 9 resistor 10k
  • 4 resistor 330 ohms
  • 1 logical gate 4 OR (interrupt on buttons) MC14072
  • 1 buzzer
  • 4 capacitor 100nF to debounce buttons
  • The CrazySimon arduino shield PCB
  • 9 Molex connectors 2 pins 0.254mm step (male to solder on shield, female to solder on components)
  • 2 Molex connectors 4 pins 0.254mm step
  • 2 headers male double/female 8 pins OU male only
  • 2 headers male double/female 6 pins OU male only

Photos

Sources

Git

Sources are available on LOG's git following repository : git@logre.eu:simon-des-fous

PCB

Not sources stricly speaking, but if you don't have access to git, this may help :

Arduino code

Note that some terms are in french (namely some color references) but I tried to comment in english only

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);
 }
 
}