Let's go through the code chunk by chunk to see how it works.
void chooseSkillLevel() { while (!CircuitPlayground.rightButton()) { if (CircuitPlayground.leftButton()) { skillLevel = skillLevel + 1; if (skillLevel > 4) skillLevel = 1; CircuitPlayground.clearPixels(); for (int p=0; p<skillLevel; p++) { CircuitPlayground.setPixelColor(p, 0xFFFFFF); } delay(DEBOUNCE); } } }
The outer while()
loop looks for the right button press, which starts the new game. Inside the loop is an if
statement that looks for the left button press and increments the skill level for each press. The value is checked and reset back to 1 if it exceeds the max level, which is 4. The current level is shown using the first 4 NeoPixels.
void newGame() { // Set game sequence length based on skill level switch (skillLevel) { case 1: sequenceLength = 8; break; case 2: sequenceLength = 14; break; case 3: sequenceLength = 20; break; case 4: sequenceLength = 31; break; } // Populate the game sequence for (int i=0; i<sequenceLength; i++) { simonSequence[i] = random(4); } // We start with the first step in the sequence currentStep = 1; }
The entire game sequence is created before the game is started. The game sequence length is set based on the skill level in the switch
block. The game sequence is then filled with random numbers between 0-3 to represent one of the four game pads. The currentStep is set to the starting position of 1.
void indicateButton(uint8_t b, uint16_t duration) { CircuitPlayground.clearPixels(); for (int p=0; p<3; p++) { CircuitPlayground.setPixelColor(simonButton[b].pixel[p], simonButton[b].color); } CircuitPlayground.playTone(simonButton[b].freq, duration); CircuitPlayground.clearPixels(); }
This function displays the NeoPixels associated with the "button" passed in by b
, plays the tone associated with this "button" for the length of time passed in by duration
. After the tone is played, the lights are turned off.
void showSequence() { // Set tone playback duration based on current sequence length uint16_t toneDuration; if (currentStep<=5) { toneDuration = 420; } else if (currentStep<=13) { toneDuration = 320; } else { toneDuration = 220; } // Play back sequence up to current step for (int i=0; i<currentStep; i++) { delay(50); indicateButton(simonSequence[i], toneDuration); } }
The game sequence is played back up to the current game play location. The duration of the tone is set in the if
statement based on the current location. This sets the play back speed of the sequence. Then, the sequence is played back up to its current location.
uint8_t getButtonPress() { for (int b=0; b<4; b++) { for (int p=0; p<2; p++) { if (CircuitPlayground.readCap(simonButton[b].capPad[p]) > CAP_THRESHOLD) { indicateButton(b, DEBOUNCE); return b; } } } return NO_BUTTON; }
The 2 touch pads associated with each "button" are scanned to see if either are currently being touched. If so, it is indicated and its index is returned, otherwise NO_BUTTON
is returned.
void gameLost(int b) { // Show button that should have been pressed for (int p=0; p<3; p++) { CircuitPlayground.setPixelColor(simonButton[b].pixel[p], simonButton[b].color); } // Play sad sound :( CircuitPlayground.playTone(FAILURE_TONE, 1500); // And just sit here until reset while (true) {} }
The "button" that is passed in via b
is shown, the sad tone is played, and the game ends at an infinite loop.
void gameWon() { // Play 'razz' special victory signal for (int i=0; i<3; i++) { indicateButton(3, 100); // RED indicateButton(1, 100); // YELLOW indicateButton(2, 100); // BLUE indicateButton(0, 100); // GREEN } indicateButton(3, 100); // RED indicateButton(1, 100); // YELLOW // Change tones to failure tone for (int b=0; b<4; b++) simonButton[b].freq = FAILURE_TONE; // Continue for another 0.8 seconds for (int i=0; i<2; i++) { indicateButton(2, 100); // BLUE indicateButton(0, 100); // GREEN indicateButton(3, 100); // RED indicateButton(1, 100); // YELLOW } // Change tones to silence for (int b=0; b<4; b++) simonButton[b].freq = 0; // Loop lights forever while (true) { indicateButton(2, 100); // BLUE indicateButton(0, 100); // GREEN indicateButton(3, 100); // RED indicateButton(1, 100); // YELLOW } }
Plays the super special victory 'razz' and ends with an infinite loop of blinking lights.
void setup() { // Initialize the Circuit Playground CircuitPlayground.begin(); // Set play level skillLevel = 1; CircuitPlayground.clearPixels(); CircuitPlayground.setPixelColor(0, 0xFFFFFF); chooseSkillLevel(); // Sowing the seeds of random randomSeed(millis()); // Create game newGame(); }
Per the rules of Arduino, this is the first thing called. It initializes the Circuit Playground, gets the skill level, seeds the pseudo random number generator, and sets up a new game.
void loop() { // Show sequence up to current step showSequence(); // Read player button presses for (int s=0; s<currentStep; s++) { startGuessTime = millis(); guess = NO_BUTTON; while ((millis() - startGuessTime < GUESS_TIMEOUT) && (guess==NO_BUTTON)) { guess = getButtonPress(); } if (guess != simonSequence[s]) { gameLost(simonSequence[s]); } } currentStep++; if (currentStep > sequenceLength) { delay(SEQUENCE_DELAY); gameWon(); } delay(SEQUENCE_DELAY); }
Per the rules of Arduino, this is called over and over and over and over. First, the current sequence is played back. Then the player interaction is dealt with. For each step in the sequence, a button is read. If it's the wrong button, or the player took too long to press the button, the value is set to NO_BUTTON
. The button value is then compared to the current sequence value. If it's correct, the game continues. If it's wrong, the game is over. Once the step values exceeds the game sequence length, the game is won.
Page last edited January 19, 2017
Text editor powered by tinymce.