Code Walk Through

Let's go through the code chunk by chunk to see how it works.

chooseSkillLevel()

Download: file
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.

newGame()

Download: file
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.

indicateButton()

Download: file
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.

showSequence()

Download: file
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.

getButtonPress()

Download: file
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.

gameLost()

Download: file
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.

gameWon()

Download: file
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.

setup()

Download: file
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.

loop()

Download: file
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.

This guide was first published on Jan 23, 2017. It was last updated on Jan 23, 2017. This page (Code Walk Through) was last updated on Sep 16, 2019.