Crack The Code

Ciao!

quest'anno ho deciso di regalare un gioco DIY a mia figlia,

cercando su Instructables ho trovato questo: 


Crack the Code Game (Arduino Based Puzzle Box) di thediylife

In breve il gioco consiste nel trovare la giusta combinazione (viene generata random) nel minor numero possibile di tentativi, 8 led aiuteranno nel capire quanti numeri sono stati indovinati e se sono stati indovinati nella posizione giusta.

Ho trovato il gioco molto carino sia esteticamente che funzionalmente quindi ho deciso di replicarlo,

ho eseguito alcune modifiche però:

  • ho modificato il codice in modo che funzioni con numeri da 0 a 4 anzichè 0-9 come l'originale, questo per semplificare il gioco per renderlo più adatto ad una bambina di 6 anni (nulla vieta in futuro di riprogrammarlo)
  • ho utilizzato un clone Arduino Nano che avevo già a disposizione
  • l'ho alimentato con una batteria lipo ed un lipo-charger che potete facilmente acquistare online (evitando di dover sostituire la batteria ogni volta).
Ecco alcune foto utili della mia replica:







Ed ecco il codice modificato per avere i numeri da 0 a 4 invece che da 0 a 9:

  1. //Code Breaker
  2. //Michael Klements
  3. //The DIY Life
  4. //15 May 2020
  5.  
  6. //Encoder interrupt routine adapted from Simon Merrett's example code
  7.  
  8. #include <SPI.h>                          //Import libraries to control the OLED display
  9. #include <Wire.h>
  10. #include <Adafruit_GFX.h>
  11. #include <Adafruit_SSD1306.h>
  12. #include <Servo.h>                        //Import library to control the servo
  13.  
  14. Servo lockServo;                          //Create a servo object for the lock servo
  15.  
  16. #define SCREEN_WIDTH 128                  // OLED display width, in pixels
  17. #define SCREEN_HEIGHT 32                  // OLED display height, in pixels
  18.  
  19. #define OLED_RESET -1                     // Reset pin # (or -1 if sharing Arduino reset pin)
  20. Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);   // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
  21.  
  22. static int pinA = 2;                      //Hardware interrupt digital pin 2
  23. static int pinB = 3;                      //Hardware interrupt digital pin 3
  24. volatile byte aFlag = 0;                  //Rising edge on pinA to signal that the encoder has arrived at a detent
  25. volatile byte bFlag = 0;                  //Rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
  26. volatile byte encoderPos = 0;             //Current value of encoder position, digit being input form 0 to 9
  27. volatile byte prevEncoderPos = 0;         //To track whether the encoder has been turned and the display needs to update
  28. volatile byte reading = 0;                //Stores direct value from interrupt pin
  29.  
  30. const byte buttonPin = 4;                 //Pin number for encoder push button
  31. byte oldButtonState = HIGH;               //First button state is open because of pull-up resistor
  32. const unsigned long debounceTime = 10;    //Debounce delay time
  33. unsigned long buttonPressTime;            //Time button has been pressed for debounce
  34.  
  35. byte correctNumLEDs[4] = {9,7,12,11};      //Pin numbers for correct number LEDs (Indicate a correct digit)
  36. byte correctPlaceLEDs[4] = {8,6,13,10};    //Pin numbers for correct place LEDs (Indicate a correct digit in the correct place)
  37.  
  38. byte code[4] = {0,0,0,0};                  //Create an array to store the code digits
  39. byte codeGuess[4] = {0,0,0,0};             //Create an array to store the guessed code digits
  40. byte guessingDigit = 0;                    //Tracks the current digit being guessed
  41. byte numGuesses = 0;                       //Tracks how many guesses it takes to crack the code
  42. boolean correctGuess = true;               //Variable to check whether the code has been guessed correctly, true initially to generate a new code on startup
  43.  
  44. void setup()
  45. {
  46.   Serial.begin(9600);                                 //Starts the Serial monitor for debugging
  47.   if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))      //Connect to the OLED display
  48.   {
  49.     Serial.println(F("SSD1306 allocation failed"));   //If connection fails
  50.     for(;;);                                          //Don't proceed, loop forever
  51.   }
  52.   display.clearDisplay();                             //Clear display
  53.   lockServo.attach(5);                                //Assign the lock servo to pin 5
  54.   for(int i=0 ; i<=3 ; i++)                           //Define pin modes for the LEDs
  55.   {
  56.     pinMode(correctNumLEDs[i], OUTPUT);
  57.     pinMode(correctPlaceLEDs[i], OUTPUT);
  58.   }
  59.   pinMode(pinA, INPUT_PULLUP);                        //Set pinA as an input, pulled HIGH to the logic voltage
  60.   pinMode(pinB, INPUT_PULLUP);                        //Set pinB as an input, pulled HIGH to the logic voltage
  61.   attachInterrupt(0,PinA,RISING);                     //Set an interrupt on PinA
  62.   attachInterrupt(1,PinB,RISING);                     //Set an interrupt on PinB
  63.   pinMode (buttonPin, INPUT_PULLUP);                  //Set the encoder button as an input, pulled HIGH to the logic voltage
  64.   randomSeed(analogRead(0));                          //Randomly choose a starting point for the random function, otherwise code pattern is predictable
  65.   display.setTextColor(SSD1306_WHITE);                //Set the text colour to white
  66.   startupAni();                                       //Display the startup animation
  67. }
  68.  
  69. void loop()
  70. {
  71.   if(correctGuess)                                            //Code between games to reset if the guess is correct, initially true to open safe and then generate new code
  72.   {
  73.     lockServo.write(140);                                     //Unlock the safe
  74.     delay(300);
  75.     updateLEDs (0,4);                                         //Flashing LED sequence
  76.     delay(300);
  77.     updateLEDs (4,0);
  78.     delay(300);
  79.     updateLEDs (0,4);
  80.     delay(300);
  81.     updateLEDs (4,0);
  82.     delay(300);
  83.     updateLEDs (4,4);                                         //Turn all LEDs on
  84.     if(numGuesses >= 1)                                       //Check that its not the start of the game
  85.     {
  86.       display.clearDisplay();                                 //Clear the display
  87.       display.setTextSize(1);                                 //Set the display text size to small
  88.       display.setCursor(35,10);                               //Set the display cursor position
  89.       display.print(F("In "));                                //Set the display text
  90.       display.print(numGuesses);                              //Set the display text
  91.       display.setCursor(35,20);                               //Set the display cursor position
  92.       display.print(F("Attempts"));                           //Set the display text
  93.       display.display();                                      //Output the display text
  94.       delay(5000);
  95.     }
  96.     display.clearDisplay();                                   //Clear the display
  97.     display.setTextSize(1);                                   //Set the display text size to small
  98.     display.setCursor(35,10);                                 //Set the display cursor position
  99.     display.print(F("Push To"));                              //Set the display text
  100.     display.setCursor(35,20);                                 //Set the display cursor position
  101.     display.print(F("Lock Safe"));                            //Set the display text
  102.     display.display();                                        //Output the display text
  103.     display.setTextSize(2);                                   //Set the display text size back to large
  104.     boolean lock = false;                                     //Safe is initially not locked
  105.     boolean pressed = false;                                  //Keeps track of button press
  106.     while(!lock)                                              //While button is not pressed, wait for it to be pressed
  107.     {
  108.       byte buttonState = digitalRead (buttonPin);
  109.       if (buttonState != oldButtonState)
  110.       {
  111.         if (millis () - buttonPressTime >= debounceTime)      //Debounce button
  112.         {
  113.           buttonPressTime = millis ();                        //Time when button is pressed
  114.           oldButtonState =  buttonState;                      //Remember button state
  115.           if (buttonState == LOW)
  116.           {
  117.             pressed = true;                                   //Records button has been pressed
  118.           }
  119.           else
  120.           {
  121.             if (pressed == true)                              //Makes sure that button is pressed and then released before continuing in the code
  122.             {
  123.               lockServo.write(45);                            //Lock the safe
  124.               display.clearDisplay();                         //Clear the display
  125.               display.setCursor(30,10);                       //Set the display cursor position
  126.               display.print(F("Locked"));                     //Set the display text
  127.               display.display();                              //Output the display text
  128.               lock = true;
  129.             }
  130.           }  
  131.         }
  132.       }
  133.     }
  134.     generateNewCode();                                        //Calls function to generate a new random code
  135.     updateLEDs (0,0);
  136.     correctGuess = false;                                     //The code guess is initially set to incorrect
  137.     numGuesses = 0;                                           //Reset the number of guesses counter
  138.   }
  139.   inputCodeGuess();                                           //Calls function to allow the user to input a guess
  140.   numGuesses++;                                               //Increment the guess counter
  141.   checkCodeGuess();                                           //Calls function to check the input guess
  142.   encoderPos = 0;                                             //Reset the encoder position
  143.   guessingDigit = 0;                                          //Reset the digit being guessed
  144.   codeGuess[0] = 0;                                           //Reset the first digit of the code
  145.   updateDisplayCode();                                        //Update the displayed code
  146. }
  147.  
  148. void updateDisplayCode()                                      //Function to update the display with the input code
  149. {
  150.   String temp = "";                                           //Temporary variable to concatenate the code string
  151.   if(!correctGuess)                                           //If the guess is not correct then update the display
  152.   {
  153.     for (int i=0 ; i<guessingDigit ; i++)                     //Loops through the four digits to display them
  154.     {
  155.       temp = temp + codeGuess[i];
  156.     }
  157.     temp = temp + encoderPos;
  158.     for (int i=guessingDigit+1 ; i<=3 ; i++)
  159.     {
  160.       temp = temp + "0";
  161.     }
  162.     Serial.println(temp);                                     //Output to Serial monitor for debugging
  163.     display.setTextSize(2);                                   //Set the display text size
  164.     display.clearDisplay();                                   //Clear the display
  165.     display.setCursor(40,10);                                 //Set the display cursor position
  166.     display.println(temp);                                    //Set the display text
  167.     display.display();                                        //Update the display
  168.   }
  169. }
  170.  
  171. void generateNewCode()                                        //Function to generate a new random code
  172. {
  173.   Serial.print("Code: ");
  174.   for (int i=0 ; i<= 3 ; i++)                                 //Loops through the four digits and assigns a random number to each
  175.   {
  176.     code[i] = random(1,4);                                    //Generate a random number for each digit (was 0,9)
  177.     Serial.print(code[i]);                                    //Display the code on Serial monitor for debugging
  178.   }
  179.   Serial.println();
  180. }
  181.  
  182. void inputCodeGuess()                                         //Function to allow the user to input a guess
  183. {
  184.   for(int i=0 ; i<=3 ; i++)                                   //User must guess all four digits
  185.   {
  186.     guessingDigit = i;
  187.     boolean confirmed = false;                                //Both used to confirm button push to assign a digit to the guess code
  188.     boolean pressed = false;
  189.     encoderPos = 0;                                           //Encoder starts from 0 for each digit
  190.     while(!confirmed)                                         //While the user has not confirmed the digit input
  191.     {
  192.       byte buttonState = digitalRead (buttonPin);
  193.       if (buttonState != oldButtonState)
  194.       {
  195.         if (millis () - buttonPressTime >= debounceTime)      //Debounce button
  196.         {
  197.           buttonPressTime = millis ();                        //Time when button was pushed
  198.           oldButtonState =  buttonState;                      //Remember button state for next time
  199.           if (buttonState == LOW)
  200.           {
  201.             codeGuess[i] = encoderPos;                        //If the button is pressed, accept the current digit into the guessed code
  202.             pressed = true;
  203.           }
  204.           else
  205.           {
  206.             if (pressed == true)                              //Confirm the input once the button is released again
  207.             {
  208.               updateDisplayCode();                            //Update the code being displayed
  209.               confirmed = true;
  210.             }
  211.           }  
  212.         }
  213.       }
  214.       if(encoderPos!=prevEncoderPos)                          //Update the displayed code if the encoder position has changed
  215.       {
  216.         updateDisplayCode();
  217.         prevEncoderPos=encoderPos;
  218.       }
  219.     }
  220.   }
  221. }
  222.  
  223. void checkCodeGuess()                                         //Function to check the users guess against the generated code
  224. {
  225.   int correctNum = 0;                                         //Variable for the number of correct digits in the wrong place
  226.   int correctPlace = 0;                                       //Variable for the number of correct digits in the correct place
  227.   int usedDigits[4] = {0,0,0,0};                              //Mark off digits which have been already identified in the wrong place, avoids counting repeated digits twice
  228.   for (int i=0 ; i<= 3 ; i++)                                 //Loop through the four digits in the guessed code
  229.   {
  230.     for (int j=0 ; j<=3 ; j++)                                //Loop through the four digits in the generated code
  231.     {
  232.       if (codeGuess[i]==code[j])                              //If a number is found to match
  233.       {
  234.         if(usedDigits[j]!=1)                                  //Check that it hasn't been previously identified
  235.         {
  236.           correctNum++;                                       //Increment the correct digits in the wrong place counter
  237.           usedDigits[j] = 1;                                  //Mark off the digit as been identified
  238.           break;                                              //Stop looking once the digit is found
  239.         }
  240.       }
  241.     }
  242.   }
  243.   for (int i=0 ; i<= 3 ; i++)                                 //Compares the guess digits to the code digits for correct digits in correct place
  244.   {
  245.     if (codeGuess[i]==code[i])                                //If a correct digit in the correct place is found
  246.       correctPlace++;                                         //Increment the correct place counter
  247.   }
  248.   updateLEDs(correctNum, correctPlace);                        //Calls a function to update the LEDs to reflect the guess
  249.   if(correctPlace==4)                                          //If all 4 digits are correct then the code has been cracked
  250.   {
  251.     display.clearDisplay();                                    //Clear the display
  252.     display.setCursor(20,10);                                  //Set the display cursor position
  253.     display.print(F("Cracked"));                               //Set the display text
  254.     display.display();                                         //Output the display text
  255.     correctGuess = true;
  256.   }
  257.   else
  258.     correctGuess = false;
  259. }
  260.  
  261. void updateLEDs (int corNum, int corPla)                        //Function to update the LEDs to reflect the guess
  262. {
  263.   for(int i=0 ; i<=3 ; i++)                                     //First turn all LEDs off
  264.   {
  265.     digitalWrite(correctNumLEDs[i], LOW);
  266.     digitalWrite(correctPlaceLEDs[i], LOW);
  267.   }
  268.   for(int j=0 ; j<=corNum-1 ; j++)                              //Turn on the number of correct digits in wrong place LEDs
  269.   {
  270.     digitalWrite(correctNumLEDs[j], HIGH);
  271.   }
  272.   for(int k=0 ; k<=corPla-1 ; k++)                              //Turn on the number of correct digits in the correct place LEDs
  273.   {
  274.     digitalWrite(correctPlaceLEDs[k], HIGH);
  275.   }
  276. }
  277.  
  278. void startupAni ()
  279. {
  280.   display.setTextSize(2);                     //Set the display text size
  281.   display.setCursor(35,10);                   //Set the display cursor position
  282.   display.println(F("Crack"));                //Set the display text
  283.   display.display();                          //Output the display text
  284.   delay(500);
  285.   display.clearDisplay();                     //Clear the display
  286.   display.setCursor(45,10);
  287.   display.println(F("The"));
  288.   display.display();
  289.   delay(500);
  290.   display.clearDisplay();
  291.   display.setCursor(40,10);
  292.   display.println(F("Code"));
  293.   display.display();
  294.   delay(500);
  295.   display.clearDisplay();
  296. }
  297.  
  298. void PinA()                               //Rotary encoder interrupt service routine for one encoder pin
  299. {
  300.   cli();                                  //Stop interrupts happening before we read pin values
  301.   reading = PIND & 0xC;                   //Read all eight pin values then strip away all but pinA and pinB's values
  302.   if(reading == B00001100 && aFlag)       //Check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
  303.   {    
  304.     if(encoderPos>1)
  305.       encoderPos --;                      //Decrement the encoder's position count
  306.     else
  307.       encoderPos = 4;                     //Go back to 9 after 0
  308.     bFlag = 0;                            //Reset flags for the next turn
  309.     aFlag = 0;                            //Reset flags for the next turn
  310.   }
  311.   else if (reading == B00000100)          //Signal that we're expecting pinB to signal the transition to detent from free rotation
  312.     bFlag = 1;
  313.   sei();                                  //Restart interrupts
  314. }
  315.  
  316. void PinB()                               //Rotary encoder interrupt service routine for the other encoder pin
  317. {
  318.   cli();                                  //Stop interrupts happening before we read pin values
  319.   reading = PIND & 0xC;                   //Read all eight pin values then strip away all but pinA and pinB's values
  320.   if (reading == B00001100 && bFlag)      //Check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
  321.   {
  322.     if(encoderPos<4)
  323.       encoderPos ++;                      //Increment the encoder's position count
  324.     else
  325.       encoderPos = 1;                     //Go back to 0 after 9
  326.     bFlag = 0;                            //Reset flags for the next turn
  327.     aFlag = 0;                            //Reset flags for the next turn
  328.   }
  329.   else if (reading == B00001000)          //Signal that we're expecting pinA to signal the transition to detent from free rotation
  330.     aFlag = 1;
  331.   sei();                                  //Restart interrupts
  332. }


Ciao e buona realizzazione!

Nessun commento:

Posta un commento

Lascia un commento qui sotto, ti risponderò il prima possibile!

Altri Post