Le code (désolé j'ai mis les commentaires en anglais, déformation...)
Pour simplifier la gestion du bouton, j'ai utilisé la librairie de @bricoleau qu'il faudra donc installer.
// ----------------------------------------------------------------------
// SIMPLE LED GAME - © J-M-L for the Arduino Forum
// cf https://forum.arduino.cc/index.php?topic=644261.msg4358456#msg4358456
// Need to install this french library: http://forum.arduino.cc/index.php?topic=375232.0 - give him Karma!
// (thanks @Bricoleau! https://forum.arduino.cc/index.php?action=profile;u=193346)
// LEDs will blink in sequence and when you press the button the current LED state will be remembered
// (turning it off if it was on and turning it on if it was off)
//
// Goal is to turn on the LEDs matching a special challenge pattern
//
// version 1.0: November 3rd, 2019
//
// new features ideas:
// - add sound or music
// - generate random patterns, may be starting with 1 LEd, then 2, then 3... to increase complexity (use SIMON like (memory game) appraoch where you only show the next LED to add to the group)
// - replace button with rotary encoder and use rotary for setting difficulty / game speed
// - display scores using LEDs smartly
// - add an optional LCD to provide user feedback
// - make it multi-user
//
// BSD license, all text above must be included in any redistribution
// ----------------------------------------------------------------------
#include <simpleBouton.h> // http://forum.arduino.cc/index.php?topic=375232.0
// our button is wired on PIN A0
const uint8_t buttonPin = A0;
simpleBouton actionButton(buttonPin); // wired : buttonPin --- Button --- GND
// These are constant you can play with to match your preferences
const uint32_t resetDuration = 10000UL; // press the button 10 seconds to reset the game
const unsigned long updatePeriodMin = 20; // start pace of the game. the smaller the more difficult
const unsigned int updatePeriodIncrement = 20; // after each lap slow down the game by this amount
const unsigned long blinkPeriod = 250; // used to show animations
const uint8_t ledPins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; // order of the pins for each LED
typedef uint16_t lightPattern_t; // could be uint8_t if you have less than 8 LEDs or uint32_t if you have more than 16 and less than 32
// define in this array the sequence of challenges you want to run
// each 1 means a LED to turn ON, each 0 means this LEDs needs to be OFF
const lightPattern_t challenge[] { // max 255 challenges as they will be indexed with a uint8_t
0b000000000111, // first group of 3 LEDs
0b000000111000, // second group of 3 LEDs
0b000111000000, // third group of 3 LEDs
0b111000000000, // fouth group of 3 LEDs
0b111000000111, // first and fouth group of 3 LEDs
0b111000111000, // ... you get the idea
0b010101010101,
0b101010101010,
0b111111111111, // all LEDs
};
// Program Variable, no need to mess around with the below
lightPattern_t ledsOn; // variable to memorize what the user has activated. Each bit set at 1 if a LED ON
const uint8_t nbLeds = sizeof(ledPins) / sizeof(ledPins[0]);
const uint8_t nbChallenges = sizeof(challenge) / sizeof(challenge[0]);
uint8_t currentChallenge = 0xFF; // initialize at max so first one becomes 0
unsigned long updatePeriod = updatePeriodMin;
uint8_t activeLedIndex;
unsigned long nbCyclesToWin;
unsigned long nbClicksToWin;
unsigned long timeToWin;
unsigned long lastChrono;
// ------------ Utilities ------------
void turnLedsOff(uint32_t pause = 0)
{
for (const auto& aPin : ledPins) {
digitalWrite(aPin, LOW);
if (pause) delay(pause);
}
}
void turnLedsOn(uint32_t pause = 0)
{
for (const auto& aPin : ledPins) {
digitalWrite(aPin, HIGH);
if (pause) delay(pause);
}
}
void showCurrentChallenge(uint8_t nbBlink = 6)
{
for (uint8_t count = 0; count < nbBlink; count++) {
delay(blinkPeriod);
for (uint8_t ledIndex = 0; ledIndex < nbLeds; ledIndex++) {
if (bitRead(challenge[currentChallenge], ledIndex)) digitalWrite(ledPins[ledIndex], HIGH);
}
delay(blinkPeriod);
turnLedsOff();
}
}
void gameSetup()
{
currentChallenge++; // next challenge
if (currentChallenge >= nbChallenges) currentChallenge = 0 ; // rollover
ledsOn = 0; // all leds off
turnLedsOff();
updatePeriod = updatePeriodMin;
showCurrentChallenge();
activeLedIndex = 0;
nbCyclesToWin = 0;
nbClicksToWin = 0;
digitalWrite(ledPins[activeLedIndex], HIGH);
timeToWin = lastChrono = millis();
}
void resetGame()
{
currentChallenge = 0xFF; // this way during the setup we go to next one which will be 0, ie the first challenge in the array
gameSetup();
}
void showWinLedPettern(uint8_t nbBlink = 6) // by default blink 6 times
{
turnLedsOff();
for (uint8_t count = 0; count < nbBlink; count++) {
turnLedsOn(blinkPeriod / nbLeds);
turnLedsOff(blinkPeriod / nbLeds);
}
}
inline bool testWin()
{
return (ledsOn == challenge[currentChallenge]); // bit comparison, would work up to 32 bits with lightPattern_t being a uint32_t
}
// ------------ State Machine ------------
void testTick()
{
if (millis() - lastChrono >= updatePeriod) {
if (bitRead(ledsOn, activeLedIndex) == 0) digitalWrite(ledPins[activeLedIndex], LOW); // turn off previous LED if needed
activeLedIndex++; // get to next LED (regardless if it's on or not)
if (activeLedIndex >= nbLeds) {
activeLedIndex = 0; // rollover - don't use modulo as slower than a test
nbCyclesToWin++; // one more cycle
updatePeriod += updatePeriodIncrement; // slows down the game
}
digitalWrite(ledPins[activeLedIndex], HIGH); // turn on the next LED
lastChrono = millis();
}
}
void testClick()
{
uint32_t downDuration = actionButton.dureeEnfonce();
actionButton.actualiser();
if (actionButton.vientDEtreEnfonce()) { // if we just pressed the button
nbClicksToWin++;
if (bitRead(ledsOn, activeLedIndex) == 0) bitSet(ledsOn, activeLedIndex);
else bitClear(ledsOn, activeLedIndex);
if (testWin()) {
Serial.print(F("You won in "));
Serial.print((millis() - timeToWin) / 1000.0);
Serial.print(F(" seconds and took "));
Serial.print(nbCyclesToWin);
Serial.print(F(" cycle")); if (nbCyclesToWin > 1) Serial.print(F("s"));
Serial.print(F(" and required "));
Serial.print(nbClicksToWin);
Serial.print(F(" click"));
if (nbClicksToWin > 1) Serial.print(F("s"));
Serial.println(F("."));
showWinLedPettern(); // little animation
gameSetup(); // get ready for next game
}
}
// if we press the button a long time, we reset the game
if (downDuration >= resetDuration) resetGame();
}
void setup()
{
Serial.begin(115200);
for (const auto& aPin : ledPins) pinMode(aPin, OUTPUT);
resetGame();
}
void loop()
{
testTick();
testClick();
}
On peut ensuite être un peu créatif, rajouter un piezo pour faire un peu de bruit (histoire d'ennuyer les parents), un écran LCD pour afficher les scores, générer des défis aléatoirement (là j'en ai codé que quelques uns à la main dans le tableau challenge[]
.
Au début je voulais mettre un rotary encoder au lieu du bouton mais c'était trop gros pour mon PCB. On aurait pu utiliser la rotation pour régler la vitesse de défilement par exemple...
bref - c'est une base extensible, n'hésitez pas à poster les jeux que vous pourriez écrire sur cette même base matérielle
amusez vous bien !