Trying to make a game.... Feels close. Button input situation?

I'm trying to make this simple game similar to a Simon game but different. The code feels like it's close but I'm getting a wrong answer time every time I try to play it.

I want the Arduino to light up 4 or 5 leds and the player pushes buttons that correspond to those leds in the same order to advance to the next level. As they get them correct a neopixel illuminates another pixel. It's just a simple kids game but I don't get to dabble with the IDE much so I'm always struggling a bit.

This code starts when the start button is pushed, runs through the leds then when I hit a button it always says error even though it's not.

I've ran some different code on the setup and it all works from a hardware side. Anyone see anything super obvious wrong in this code? THANKS!

#include <Arduino.h>
#include <Adafruit_NeoPixel.h>

const int NEOPIXEL_PIN = 19;
const int NEOPIXEL_COUNT = 24;
Adafruit_NeoPixel neopixels = Adafruit_NeoPixel(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);


const int buttonStart = 2;
const int button1 = 3;
const int button2 = 4;
const int button3 = 5;
const int button4 = 6;
const int button5 = 7;
const int button6 = 8;
const int button7 = 9;

const int led1 = 10;
const int led2 = 11;
const int led3 = 12;
const int led4 = 13;
const int led5 = 14;
const int led6 = 15;
const int led7 = 16;
const int ledError = 18;

int sequence[20];
int level = 1;
int currentStep = 0;
bool gameStarted = false;



void setup() {
  pinMode(buttonStart, INPUT_PULLUP);
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(button3, INPUT_PULLUP);
  pinMode(button4, INPUT_PULLUP);
  pinMode(button5, INPUT_PULLUP);
  pinMode(button6, INPUT_PULLUP);
  pinMode(button7, INPUT_PULLUP);
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);
  pinMode(led4, OUTPUT);
  pinMode(led5, OUTPUT);
  pinMode(led6, OUTPUT);
  pinMode(led7, OUTPUT);

  pinMode(ledError, OUTPUT);
  randomSeed(analogRead(0));
  neopixels.begin();
}

void loop() {
  if (!gameStarted) {
    // Wait for game to start
    if (digitalRead(buttonStart) == LOW) {
      delay(1000); //was 5000
      newGame();
    }
    return;
  }

  if (level > 20) {
    // Game won!
    digitalWrite(led1, LOW);
    digitalWrite(led2, LOW);
    digitalWrite(led3, LOW);
    digitalWrite(led4, LOW);
    digitalWrite(led5, LOW);
    digitalWrite(led6, LOW);
    digitalWrite(led7, LOW);
    level = 1;
    currentStep = 0;
    gameStarted = false;
    return;
  }

  if (currentStep == level) {
    // Next level
    level++;
    currentStep = 0;
    showSequence();
    return;
  }

  if (digitalRead(button1) == LOW) {
    checkStep(led1);
  }
  if (digitalRead(button2) == LOW) {
    checkStep(led2);
  }
  if (digitalRead(button3) == LOW) {
    checkStep(led3);
  }
  if (digitalRead(button4) == LOW) {
    checkStep(led4);
  }
  if (digitalRead(button5) == LOW) {
    checkStep(led5);
  }
  if (digitalRead(button6) == LOW) {
    checkStep(led6);
  }
   if (digitalRead(button7) == LOW) {
    checkStep(led7);
  }
}

void newGame() {
  gameStarted = true;
  for (int i = 0; i < 20; i++) {
    sequence[i] = random(7) + 1;
  }
  level = 1;
  currentStep = 0;
  digitalWrite(led1, LOW);
  digitalWrite(led2, LOW);
  digitalWrite(led3, LOW);
  digitalWrite(led4, LOW);
  digitalWrite(led5, LOW);
  digitalWrite(led6, LOW);
  digitalWrite(led7, LOW);
  showSequence();
}

void showSequence() {
  digitalWrite(led1, LOW);
  digitalWrite(led2, LOW);
  digitalWrite(led3, LOW);
  digitalWrite(led4, LOW);
  digitalWrite(led5, LOW);
  digitalWrite(led6, LOW);
  digitalWrite(led7, LOW);

  int ledsToShow[4];
  for (int i = 0; i < 4; i++) {
    int ledIndex = random(7) + 1;
    while (ledIndex == ledsToShow[0] || ledIndex == ledsToShow[1] || ledIndex == ledsToShow[2] || ledIndex == ledsToShow[3]) {
      ledIndex = random(7) + 1;
    }
    ledsToShow[i] = ledIndex;
  }

  for (int i = 0; i < 4; i++) {
    delay(800); //WAS 700
    if (ledsToShow[i] == 1) {
      digitalWrite(led1, HIGH);
    } else if (ledsToShow[i] == 2) {
      digitalWrite(led2, HIGH);
    } else if (ledsToShow[i] == 3) {
      digitalWrite(led3, HIGH);
    } else if (ledsToShow[i] == 4) {
      digitalWrite(led4, HIGH);
    } else if (ledsToShow[i] == 5) {
      digitalWrite(led5, HIGH);
    } else if (ledsToShow[i] == 6) {
      digitalWrite(led6, HIGH);
    } else {
      digitalWrite(led7, HIGH);
    }
    delay(400); // WAS 300
    digitalWrite(led1, LOW);
    digitalWrite(led2, LOW);
    digitalWrite(led3, LOW);
    digitalWrite(led4, LOW);
    digitalWrite(led5, LOW);
    digitalWrite(led6, LOW);
    digitalWrite(led7, LOW);
  }
}


void checkStep(int ledPin) {
  if (sequence[currentStep] == (ledPin - 9)) {
    // Correct step...
    digitalWrite(ledPin, HIGH);
    delay(500); //WAS 300
    digitalWrite(ledPin, LOW);
    neopixels.setPixelColor(currentStep + 15, 0, 255, 0); // set the current pixel to green with an offset of 15
    neopixels.show(); // show the updated pixels
    currentStep++;
    if (currentStep == level) {
      delay(1000);
    }
  


  } else {
    // Wrong step...
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
    neopixels.setPixelColor(i, neopixels.Color(200, 50, 20));
  }
    neopixels.show();
    digitalWrite(ledError, HIGH); // Turn on error LED
    delay(2000); // Wait for 1 second
    neopixels.fill(0, 0, 0); // Turn off neopixels
    neopixels.show();
    digitalWrite(ledError, LOW); // Turn off error LED

   
    //level = 1;
   // currentStep = 0;
    gameStarted = false;
  }
}







Which button?

What does the error say?

It's any of the 7 buttons. Say it lights up the pattern of LED1, LED4, LED2 and LED5. I push the button1 associated with LED1 to get started and it immediately puts the ledError pin HIGH and says it was a wrong answer.

Hi, @tatticus

Is your code expecting buttons 1, 4, 2, and 5 to be ALL pressed at the same time?

Tom... :smiley: :+1: :coffee: :australia:
PS. You may need 5 SWITCHES and a load button. You select your pattern with the switches, then press a LOAD button to check if the combination is correct?

are you debouncing the buttons? could there be a 2nd button press

consider using arrays

// check multiple buttons and toggle LEDs

enum { Off = HIGH, On = LOW };

byte pinsLed [] = { 10, 11, 12 };
byte pinsBut [] = { A1, A2, A3 };
#define N_BUT   sizeof(pinsBut)

byte butState [N_BUT];

// -----------------------------------------------------------------------------
int
chkButtons ()
{
    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        byte but = digitalRead (pinsBut [n]);

        if (butState [n] != but)  {
            butState [n] = but;

            delay (10);     // debounce

            if (On == but)
                return n;
        }
    }
    return -1;
}

// -----------------------------------------------------------------------------
void
loop ()
{
    switch (chkButtons ())  {
    case 2:
        digitalWrite (pinsLed [2], ! digitalRead (pinsLed [2]));
        break;

    case 1:
        digitalWrite (pinsLed [1], ! digitalRead (pinsLed [1]));
        break;

    case 0:
        digitalWrite (pinsLed [0], ! digitalRead (pinsLed [0]));
        break;
    }
}

// -----------------------------------------------------------------------------
void
setup ()
{
    Serial.begin (9600);

    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        pinMode (pinsBut [n], INPUT_PULLUP);
        butState [n] = digitalRead (pinsBut [n]);
    }

    for (unsigned n = 0; n < sizeof(pinsLed); n++)  {
        digitalWrite (pinsLed [n], Off);
        pinMode      (pinsLed [n], OUTPUT);
    }
}

Did you test what the numbers are in your sequence array?

Hello tatticus

Keep it simple and stupid and start with two buttons and two LED to get the practice. Later on you can scale it up to n-buttons and n-LEDs.
This is the best way to get common with array´s and data structures.

Have a nice day and enjoy coding in C++.

1 Like

Hi @tatticus,

the best way (unfortunately) would be to restructure the whole sketch using debounced buttons, arrays for buttons and LEDs and optimize the code to reduce complexity.

I guess that's not what you want to do but would highly support debugging ...

If you want to debug the existing code, you may add some Serial.print()s and analyze if your sketch follows the way you want it to go:

Something like this (but not limited to this function):

void checkStep(int ledPin) {
  Serial.print(currentStep);
  Serial.print("\t");
  Serial.print(sequence[currentStep]);
  Serial.print("\t");
  Serial.println(ledPin);
  Serial.println("-------------------");
  if (sequence[currentStep] == (ledPin - 9)) {
    // Correct step...
   // ... and so on ....

Do not forget to add Serial.begin() to setup():

void setup(){
   Serial.begin(115200);
  //  ... and so on ...

Good luck!
ec2021

Hi @tatticus !

Look for this:

#include <Arduino.h>
#include <Bounce2.h>
#include <Adafruit_NeoPixel.h>

#define DEBUG
//#define GAME_DEBUG
// #define BUTTON_DEBUG
// #define BLINK_DEBUG

#define INITIAL_STATE LOW
#define NUM_LEDS 7
#define NEOPIXEL_PIN 23
#define NUM_NEOPIXELS 16
#define MAX_LEVEL 3

Adafruit_NeoPixel ring = Adafruit_NeoPixel(NUM_NEOPIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);

const byte startButtonPin = A8;
const byte repeatLedPin = 9;

struct INFOS
{
  const byte buttonPin;
  const byte ledPin;
};
INFOS info[NUM_LEDS] =
{
  {A0, 2},
  {A1, 3},
  {A2, 4},
  {A3, 5},
  {A4, 6},
  {A5, 7},
  {A6, 8}
};

Bounce2::Button button[NUM_LEDS] = {Bounce2::Button()};
Bounce2::Button startButton = Bounce2::Button();

uint8_t sequence[NUM_LEDS] = {0, 1, 2, 3, 4, 5, 6};
byte sequenceIndex = 0;

byte level = 1;
byte numShowSequence = 1;    // Number of times to show sequence for the player
byte numLedsPerSequence = 4; // Amount of LEDs that will blink in sequence
bool gameStarted = false;

bool startTimer = false;
unsigned long levelMillis = 0;
unsigned long ledMillis = 0;
byte countLed = 0;
byte max_level_time = 8; // Time in seconds

void checkInput(byte i);
void showSequence();
void newGame();
void swapEntries(byte a, byte b);
void rainbowCycle(int wait);
int32_t Wheel(byte WheelPos);

#ifdef DEBUG
  #define LOG(...) Serial.print(__VA_ARGS__)
  #define LOGLN(...) Serial.println(__VA_ARGS__)
#else
  #define LOG(...)
  #define LOGLN(...)
#endif

void printDebug(byte i)
{
  LOG("Button ");
  LOG(i);
  LOG(" pressed!");
}

void setup()
{
  Serial.begin(9600);

  startButton.attach(startButtonPin, INPUT_PULLUP);
  startButton.setPressedState(LOW);
  startButton.interval(5);

  for (byte i = 0; i < NUM_LEDS; i++)
  {
    button[i].attach(info[i].buttonPin, INPUT_PULLUP);
    button[i].setPressedState(LOW);
    button[i].interval(5);

    pinMode(info[i].ledPin, OUTPUT);
    digitalWrite(info[i].ledPin, INITIAL_STATE);
  }

  pinMode(repeatLedPin, OUTPUT);
  digitalWrite(repeatLedPin, INITIAL_STATE);

  ring.begin();
  ring.show();

  LOGLN("Press start for new game.");
}

void loop()
{
  if(gameStarted == false)
  {
    startButton.update();

    if(startButton.pressed())
    {
      newGame();
    }
  }
  else
  {
    for (byte i = 0; i < NUM_LEDS; i++)
    {
      button[i].update();

      if (button[i].pressed())
      {
        digitalWrite(info[i].ledPin, HIGH);
        checkInput(i);

        #ifdef BUTTON_DEBUG
          printDebug(i);
        #endif

        break;
      }
    }
  }

  if((startTimer == true) && (gameStarted == true))
  {
    if((millis() - levelMillis) < (max_level_time * 1000UL))
    {
      if((millis() - ledMillis) >= ((max_level_time * 1000UL) / NUM_NEOPIXELS ))
      {
        ring.setPixelColor(countLed, ring.Color(255, 128, 0));
        ring.show();
        countLed++;
        ledMillis = millis();
      }
    }
    else
    {
      gameStarted = false;
      startTimer = false;
      countLed = 0;

      LOGLN("\nYOU LOSE!\nPress start for new game.");

      digitalWrite(repeatLedPin, LOW);

      for (byte i = 0; i < NUM_LEDS; i++)
      {
        digitalWrite(info[i].ledPin, LOW);
      }

      for (byte i = 0; i < ring.numPixels(); i++)
      {
        ring.setPixelColor(i, ring.Color(255, 0, 0));
        ring.show();
        delay(100);
      }

      delay(2000);

      ring.clear();
      ring.show();
    }
  }
}

void newGame()
{
  LOGLN("Starting new game...");

  delay(2000);

  gameStarted = true;
  level = 1;
  sequenceIndex = 0;
  countLed = 0;
  startTimer = false;

  showSequence();
}

void showSequence()
{
  LOG("Current level: ");
  LOGLN(level);

  for (int i = 0; i < 10; i++)                             // Perform 10 swaps
  {
    randomSeed(micros());
    swapEntries(random(0, NUM_LEDS), random(0, NUM_LEDS)); // perform 10 swaps
  }

  #ifdef GAME_DEBUG
    LOG("Seq. = [");

    for(byte i = 0; i < numLedsPerSequence; i++)
    {
      LOG(sequence[i]);

      if(i < (numLedsPerSequence - 1))
      {
        LOG(", ");
      }
    }
    LOGLN("]");
  #endif

  for (int j = 0; j < numShowSequence; j++)
  {
    #ifdef BLINK_DEBUG
      LOG("Blink sequence = [");
    #endif

    for (byte i = 0; i < numLedsPerSequence; i++)
    {
      #ifdef BLINK_DEBUG
        LOG(sequence[i]);
        if(i < (numLedsPerSequence - 1))
        {
          LOG(", ");
        }
        else
        {
          LOGLN("]");
        }
      #endif
      digitalWrite(info[sequence[i]].ledPin, HIGH);
      delay(600);
      digitalWrite(info[sequence[i]].ledPin, LOW);
      delay(600);
    }

    digitalWrite(repeatLedPin, HIGH);
    delay(600);
    digitalWrite(repeatLedPin, LOW);

    startTimer = true;
    levelMillis = millis();
  }
  #ifdef GAME_DEBUG
    LOG("Inp. = [");
  #endif
}

void checkInput(byte button)
{
  bool victory = false;

  if (sequence[sequenceIndex] == button)
  {
    #ifdef GAME_DEBUG
      LOG(button);

      if(sequenceIndex < (numLedsPerSequence - 1))
      {
        LOG(", ");
      }
      else
      {
        LOGLN("]");
      }
    #endif

    sequenceIndex++;

    if(sequenceIndex == numLedsPerSequence)
    {
      level++;
      sequenceIndex = 0;
      victory = true;

      LOGLN("Level up!");

      digitalWrite(repeatLedPin, LOW);

      for (byte i = 0; i < NUM_LEDS; i++)
      {
        digitalWrite(info[i].ledPin, LOW);
      }

      if (level <= MAX_LEVEL)
      {
        for (byte i = 0; i < ring.numPixels(); i++)
        {
          ring.setPixelColor(i, ring.Color(0, 255, 0));
          ring.show();
          delay(100);
        }

        delay(2000);
      }

      ring.clear();
      ring.show();
    }
  }
  else
  {
    gameStarted = false;

    LOGLN("\nYOU LOSE!\nPress start for new game.");

    digitalWrite(repeatLedPin, LOW);

    for (byte i = 0; i < NUM_LEDS; i++)
    {
      digitalWrite(info[i].ledPin, LOW);
    }

    for (byte i = 0; i < ring.numPixels(); i++)
    {
      ring.setPixelColor(i, ring.Color(255, 0, 0));
      ring.show();
      delay(100);
    }

    delay(2000);

    ring.clear();
    ring.show();
  }

  if(victory == true)
  {
    if (level > MAX_LEVEL)
    {
      gameStarted = false;
      LOGLN("YOU WON!");
      rainbowCycle(10);
    }
    else
    {
      showSequence();
    }
  }
}

void swapEntries(byte a, byte b)
{
  byte temp = sequence[a];
  sequence[a] = sequence[b];
  sequence[b] = temp;
}

void rainbowCycle(int wait)
{
  uint16_t i, j;

  for (j = 0; j < 256 * 5; j++)
  {
    for (i = 0; i < ring.numPixels(); i++)
    {
      ring.setPixelColor(i, Wheel(((i * 256 / ring.numPixels()) + j) & 255));
    }
    ring.show();
    delay(wait);
  }

  ring.clear();
  ring.show();
  LOGLN("Press start for new game.");
}

int32_t Wheel(byte WheelPos)
{
  WheelPos = 255 - WheelPos;
  if (WheelPos < 85)
  {
    return ring.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if (WheelPos < 170)
  {
    WheelPos -= 85;
    return ring.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return ring.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

It works better in VSCode extension but you can try here too:

Best regards.

The credit for swap function goes to @J-M-L.

The credit for rainbow function goes to a Wokwi user called Jason.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.