Serial.read() is falling behind with inputs

The title pretty much says it all haha, but a little bit of background info first.

I'm working on a simple game played in the Arduino's serial monitor where a random array is displayed that is an arrow pointing in one of the 4 compass directions (N,S,E,W). The player then inputs the direction the arrow was pointing using the WASD keys on the key board. Then the function checkInput() checks to see if the players input matches the random array displayed. If they do match then the score is incremented, if not the score stays the same. The game then does a final comparison to see if the score has stayed the same or changed. If the score has changed the game continues, if not it tells the player it has lost the game and restarts over again.

My problem right now is that after the array is displayed and I input a direction, it seems that the input is not used right away and instead gets used the next time the loop is run. This puts the game into a game over state and the player will never win haha.

Any body have any ideas how to fix this? The game code and an output of a round are below:

Game Code

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW 8    
#define COL 8

int score = 0;  //Score that will be incremented
int prevScore = 0;  //Comparison Score

char input; 

int left[ROW][COL] = {{0,0,0,1,1,0,0,0},    //Left Arrow
                      {0,0,1,1,0,0,0,0},
                      {0,1,1,0,0,0,0,0},
                      {1,1,0,0,0,0,0,0},
                      {1,1,0,0,0,0,0,0},
                      {0,1,1,0,0,0,0,0},
                      {0,0,1,1,0,0,0,0},
                      {0,0,0,1,1,0,0,0}};
                      
int right[ROW][COL] = {{0,0,0,1,1,0,0,0},  //Right Arrow
                       {0,0,0,0,1,1,0,0},
                       {0,0,0,0,0,1,1,0},
                       {0,0,0,0,0,0,1,1},
                       {0,0,0,0,0,0,1,1},
                       {0,0,0,0,0,1,1,0},
                       {0,0,0,0,1,1,0,0},
                       {0,0,0,1,1,0,0,0}};
                      
int up[ROW][COL] = {  {0,0,0,1,1,0,0,0},  //Up Arrow
                      {0,0,1,1,1,1,0,0},
                      {0,1,1,0,0,1,1,0},
                      {1,1,0,0,0,0,1,1},
                      {1,0,0,0,0,0,0,1},
                      {0,0,0,0,0,0,0,0},
                      {0,0,0,0,0,0,0,0},
                      {0,0,0,0,0,0,0,0}};
                      
int down[ROW][COL] = { {0,0,0,0,0,0,0,0},  //Down Arrow
                       {0,0,0,0,0,0,0,0},
                       {0,0,0,0,0,0,0,0},
                       {1,0,0,0,0,0,0,1},
                       {1,1,0,0,0,0,1,1},
                       {0,1,1,0,0,1,1,0},
                       {0,0,1,1,1,1,0,0},
                       {0,0,0,1,1,0,0,0}};
                       
int win[ROW][COL] = {  {0,0,0,0,0,0,0,0},  //Array displayed if the player wins, Not implemented yet
                       {0,1,1,0,0,1,1,0},
                       {0,1,1,0,0,1,1,0},
                       {0,1,1,0,0,1,1,0},
                       {0,1,1,0,0,1,1,0},
                       {0,1,1,1,1,1,1,0},
                       {0,1,1,0,0,1,1,0},
                       {0,1,0,0,0,1,1,0}};
                       
int lose[ROW][COL] = { {0,1,1,0,0,0,0,0}, //L displayed if the player loses
                       {0,1,1,0,0,0,0,0},
                       {0,1,1,0,0,0,0,0},
                       {0,1,1,0,0,0,0,0},
                       {0,1,1,0,0,0,0,0},
                       {0,1,1,0,0,0,0,0},
                       {0,1,1,1,1,1,1,0},
                       {0,1,1,1,1,1,1,0}};

//displayArray takes in an array 8x8 in size and prints it to
//the Serial Monitor
void displayArray(int array[8][8]) {  
   for (int i = 0; i < 8; i++) {
       for (int j = 0; j < 8; j++) {
           Serial.print(array[i][j]);
       }
       Serial.print("\n");
   }
};


//clearScreen prints 20 new lines to clear the Serial Montor Scree
void clearScreen(){
  for(int i = 0; i<20;i++){
    Serial.println();
  }
};


//checkInput takes in a character and an integer.
//Using a switch statement, the character is compared to a few cases.
//Within each case the the supplied integer is checked against another
//integer and if they match the score variable is incremented.
void checkInput(char in, int y) {
    switch (in) {
        case 'w':
            if (y == 1) { score++; }
            break;
        case 'a':
            if (y == 4) { score++; }
            break;
        case 's':
            if (y == 2) { score++; }
            break;
        case 'd':
            if (y == 3) { score++; }
            break;
        default:
            break;
    }    
};

//randomWork takes in an integer and displays an array based
//based on the integer supplied. 
void randomWork(int x){
  switch (x) {
            case 1:
                displayArray(up);
                break;
            case 2:
                displayArray(down);
                break;
            case 3:
                displayArray(right);
                break;
            case 4:
                displayArray(left);
                break;
            default:
                Serial.print("You Should Not See This");
                break;
        }
}
  
//countDown displays a countdown to the start of the game.
void countDown(){
  Serial.println("The Game will begin in: ");
  for(int i=1; i<4;i++){
    Serial.println(i);
    delay(1000);
  }
  Serial.println("GO!");
};

void setup() {
  Serial.begin(9600);  //Start serial communication
  randomSeed(analogRead(0)); //create a random seed for random()
  countDown(); //Start the countdown to the game starting
};

void loop(){
  int array_disp = random(1,4); //Generate a random number between 1-4
  
  randomWork(array_disp);  //display the array based on the random integer
  
  Serial.println(input); //Used for debugging purposes
  
  while(Serial.available()){
    input = Serial.read();    //Wait for the player to input a character
  }  
  
  delay(3000);
  
  Serial.println(input); //Used for debugging purposes
  
  checkInput(input,array_disp);   //Use checkInput to see if the score gets incremented
  
  delay(1000);
  
  Serial.println(score);  //debugging
  Serial.println(prevScore);  //debugging
  Serial.println(input);  //debugging
  
  delay(5000);
  
  //Checks if the two scores are the same, if they are it means the 
  //player input an incorrect score and they have lost the game.
  if (prevScore == score) { 
    displayArray(lose);
    Serial.println("Game Over, good try!!!");
    Serial.print("Score: ");
    Serial.print(score);
    score = 0;
    prevScore = 0;
    delay(10000);
  }
  
  //Checks if the two scores are different, if they are the player has
  //entered the correct input.
  if (prevScore != score) {
    prevScore = score;
  }
  
  clearScreen();  //Clear the screen
  
  
}

Output:

The Game will begin in: 
1
2
3
GO!
00011000
00001100
00000110
00000011
00000011
00000110
00001100
00011000
[]     //The input supplied by the player is supposed to be here, used for debugging purposes. Supposed to be 'd' in this case 
0     //The score of the game
0     //The previous score (used for comparison purposes)

01100000
01100000
01100000
01100000
01100000
01100000
01111110
01111110
Game Over, good try!!!
Score: 0


00000000
00000000
00000000
10000001
11000011
01100110
00111100
00011000

d    //The input that was supposed to be previously shown
0
0
d
01100000
01100000
01100000
01100000
01100000
01100000
01111110
01111110
Game Over, good try!!!
Score: 0

Any help appreciated, thanks for your time!!

Go faster:
Serial.begin(9600); //Start serial communication

use 115200, 230400

  int array_disp = random(1,4); //Generate a random number between 1-4

No, it doesn't.

I'm not exactly sure how your game works, but what about putting all the game code after you receive the serial event? That way you only process the players input after you actually receive it.

Something like this perchance:

void loop(){
  int array_disp = random(1,4); //Generate a random number between 1-4

    randomWork(array_disp);  //display the array based on the random integer

  Serial.println(input); //Used for debugging purposes
  if (Serial.available() > 0) {
    input = Serial.read();    //read the character from the serial port
    delay(3000);

    Serial.println(input); //Used for debugging purposes

    checkInput(input,array_disp);   //Use checkInput to see if the score gets incremented

    delay(1000);

    Serial.println(score);  //debugging
    Serial.println(prevScore);  //debugging
    Serial.println(input);  //debugging

    delay(5000);

    //Checks if the two scores are the same, if they are it means the 
    //player input an incorrect score and they have lost the game.
    if (prevScore == score) { 
      displayArray(lose);
      Serial.println("Game Over, good try!!!");
      Serial.print("Score: ");
      Serial.print(score);
      score = 0;
      prevScore = 0;
      delay(10000);
    }
    //Checks if the two scores are different, if they are the player has
    //entered the correct input.
    if (prevScore != score) {
      prevScore = score;
    }
    clearScreen();  //Clear the screen
  }
}

I don't like to see long stretches of code within loop(). I find it much easier to follow and debug the logic if the code is put into different functions (which are called from loop()) so that each function just does one little piece of the project.

Have you tried testing the code without bothering with random numbers - perhaps pick numbers from an array so they are predictable and repeatable. Only switch to random numbers whene everything else works.

You have a number of looooonnnng delay()s that probably don't help.

How about just using the characters <>^v to show the user the directions?

...R

Why have the delays in loop - they make no sense, and this is missing something

  if (prevScore != score) {
    prevScore = score;
  }

hint when do you change the score?

Mark

Thank you very much all of you for your fast replies! I used a lot of your suggestions and finally got the code working. I'll post the code below for viewing enjoyment.

Thanks again!

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW 8    
#define COL 8

int score = 0;  //Score that will be incremented
int prevScore = 0;  //Comparison Score

int input; 

int array_disp = 0;
int num = 0;

int left[ROW][COL] = {{0,0,0,1,1,0,0,0},    //Left Arrow
                      {0,0,1,1,0,0,0,0},
                      {0,1,1,0,0,0,0,0},
                      {1,1,0,0,0,0,0,0},
                      {1,1,0,0,0,0,0,0},
                      {0,1,1,0,0,0,0,0},
                      {0,0,1,1,0,0,0,0},
                      {0,0,0,1,1,0,0,0}};
                      
int right[ROW][COL] = {{0,0,0,1,1,0,0,0},  //Right Arrow
                       {0,0,0,0,1,1,0,0},
                       {0,0,0,0,0,1,1,0},
                       {0,0,0,0,0,0,1,1},
                       {0,0,0,0,0,0,1,1},
                       {0,0,0,0,0,1,1,0},
                       {0,0,0,0,1,1,0,0},
                       {0,0,0,1,1,0,0,0}};
                      
int up[ROW][COL] = {  {0,0,0,1,1,0,0,0},  //Up Arrow
                      {0,0,1,1,1,1,0,0},
                      {0,1,1,0,0,1,1,0},
                      {1,1,0,0,0,0,1,1},
                      {1,0,0,0,0,0,0,1},
                      {0,0,0,0,0,0,0,0},
                      {0,0,0,0,0,0,0,0},
                      {0,0,0,0,0,0,0,0}};
                      
int down[ROW][COL] = { {0,0,0,0,0,0,0,0},  //Down Arrow
                       {0,0,0,0,0,0,0,0},
                       {0,0,0,0,0,0,0,0},
                       {1,0,0,0,0,0,0,1},
                       {1,1,0,0,0,0,1,1},
                       {0,1,1,0,0,1,1,0},
                       {0,0,1,1,1,1,0,0},
                       {0,0,0,1,1,0,0,0}};
                       
int win[ROW][COL] = {  {0,0,0,0,0,0,0,0},  //Array displayed if the player wins, Not implemented yet
                       {0,1,1,0,0,1,1,0},
                       {0,1,1,0,0,1,1,0},
                       {0,1,1,0,0,1,1,0},
                       {0,1,1,0,0,1,1,0},
                       {0,1,1,1,1,1,1,0},
                       {0,1,1,0,0,1,1,0},
                       {0,1,0,0,0,1,1,0}};
                       
int lose[ROW][COL] = { {0,1,1,0,0,0,0,0}, //L displayed if the player loses
                       {0,1,1,0,0,0,0,0},
                       {0,1,1,0,0,0,0,0},
                       {0,1,1,0,0,0,0,0},
                       {0,1,1,0,0,0,0,0},
                       {0,1,1,0,0,0,0,0},
                       {0,1,1,1,1,1,1,0},
                       {0,1,1,1,1,1,1,0}};

//displayArray takes in an array 8x8 in size and prints it to
//the Serial Monitor
void displayArray(int array[8][8]) {  
   for (int i = 0; i < 8; i++) {
       for (int j = 0; j < 8; j++) {
           Serial.print(array[i][j]);
           Serial.print(" ");
       }
       Serial.print("\n");
   }
};


//clearScreen prints 20 new lines to clear the Serial Montor Scree
void clearScreen(){
  for(int i = 0; i<20;i++){
    Serial.println();
  }
};


//checkInput takes in a character and an integer.
//Using a switch statement, the character is compared to a few cases.
//Within each case the the supplied integer is checked against another
//integer and if they match the score variable is incremented.
void checkInput(int in, int y) {
    switch (in) {
        //w
        case 119:
            if (y == 1) { score++; }
            break;
        //a
        case 97:
            if (y == 4) { score++; }
            break;
        //s
        case 115:
            if (y == 2) { score++; }
            break;
        //d
        case 100:
            if (y == 3) { score++; }
            break;
        default:
            break;
    }    
};

void generateDirection(){
  int array_disp = num =  random(1,4);
  displayRandom(array_disp);
};

//randomWork takes in an integer and displays an array based
//based on the integer supplied. 
void displayRandom(int x){
  switch (x) {
            case 1:
                displayArray(up);
                break;
            case 2:
                displayArray(down);
                break;
            case 3:
                displayArray(right);
                break;
            case 4:
                displayArray(left);
                break;
            default:
                Serial.print("You Should Not See This");
                break;
        }
};
  
//countDown displays a countdown to the start of the game.
void countDown(){
  Serial.println("The Game will begin in: ");
  for(int i=1; i<4;i++){
    Serial.println(i);
    delay(1000);
  }
  Serial.println("GO!");
};

//Checks to see if the score has changed, that determines whether or 
//not the player continues or loses.
void scoreCheck(){
      if (prevScore == score) {
        clearScreen(); 
        displayArray(lose);
        Serial.println("Game Over, good try!!!");
        Serial.print("Score: ");
        Serial.print(score);
        score = 0;
        prevScore = 0;
        delay(1000);
      }
  
    else {
      prevScore = score;
    }
};

void setup() {
  Serial.begin(115200);  //Start serial communication
  randomSeed(analogRead(0)); //create a random seed for random()
  countDown(); //Start the countdown to the game starting
  generateDirection();  //generate the first direction to be displayed
};

void loop(){
  if (Serial.available()>0){ 
    input = Serial.read();
  
    checkInput(input,num);   //Use checkInput to see if the score gets incremented
  
    delay(500);
    
    scoreCheck();    //Check to see if the scores have changed
    clearScreen();  //Clear the screen
    generateDirection();
    }
}

I don't like to see long stretches of code within loop(). I find it much easier to follow and debug the logic if the code is put into different functions (which are called from loop()) so that each function just does one little piece of the project.

I am just the opposite - I hate having to jump all over the place trying to find some function to see what is going on. I also think all the jumping back & forth hurts performance, as every function call requires putting stuff on the stack & heap, doing, whatever the function does, putting everything back, and then restarting the code.

CrossRoads:
I am just the opposite - I hate having to jump all over the place trying to find some function to see what is going on.

I find when the functions are sensibly named the whole thing is obvious from a quick read of loop() - for example

void loop()  {
    readButtons();
    flashLeds();
    moveServo();
}

Then if I am having trouble I just need to look at the function that is misbehaving.

Undoubtedly there is some overhead in switching between functions but I haven't yet had a project where that "wastage" mattered. In any case the compiler can sometimes be clever enough to generate inline code without calls. I know that because it did it when I didn't want it to.

...R

Writing blink without delay style code also doesn't lend itself well to jumping out to functions.

CrossRoads:
Writing blink without delay style code also doesn't lend itself well to jumping out to functions.

Why not? It is generally necessary to do something, record when that happened, an possibly change the interval to the next event. That can all be done in a function.

CrossRoads:
Writing blink without delay style code also doesn't lend itself well to jumping out to functions.

The example I wrote here does exactly that.

...R