LoL Shield SNAKE Game

I wrote a game of Snake (random pixel lights up, you guide the snake up, down, left, and right to eat the pixel. Snake grows longer, don't hit sides or body of snake). I am having problems with it, and I can't exactly figure it out. Here is my code (it should be self-explanatory) and any suggestions would be appreciated.

#include <Figure.h>
#include <Bounce.h>
#include <Charliplexing.h>
#include <Font.h>
#include <WProgram.h>


char test[] = "PRESS RESET";
int testLength = sizeof(test);
int x = 0, x2 = 0;


//Shadow array to keep track of the pixels that are on/off
bool shadow[13][8] = {
  false};
//Lists for coords of the pixels of the snake
int xSnake[99];
int ySnake[99];
//Position in the lists of head and tail
int posHead = 0;
int posTail = 0;
//Coords of head
int headX = 6;
int headY = 3;
//Length variable
int snakeLength = 3;
//Coords for random pixel
int randX;
int randY;
//Directional control
int dirCurr = 3;
int dirPrev = 3;
int up = 0;
int down = 3;
int left = 1;
int right = 2;
//Buttons for controlling the snake
int buttonUp = 14; //analog pin 0 used as digital input
int buttonDown = 15; //analog pin 1
int buttonLeft = 16; // analog pin 2
int buttonRight = 17; //analog pin 3
//Debouncing of buttons
Bounce bounceUp = Bounce(buttonUp, 25);
Bounce bounceDown = Bounce(buttonDown, 25);
Bounce bounceLeft = Bounce(buttonLeft, 25);
Bounce bounceRight = Bounce(buttonRight, 25);


void setup()
{
  LedSign::Init();
  pinMode(buttonUp, INPUT);
  digitalWrite(buttonUp, LOW);
  pinMode(buttonDown, INPUT);
  digitalWrite(buttonDown, LOW);
  pinMode(buttonLeft, INPUT);
  digitalWrite(buttonLeft, LOW);
  pinMode(buttonRight, INPUT);
  digitalWrite(buttonRight, LOW);
  pinMode(18, OUTPUT);
  digitalWrite(18, HIGH);
  randomSeed(analogRead(5));
  randX = random(0,14);
  randY = random(0,9);
  LedSign::Set(randX, randY, 1);
    
}

void loop()
{
  
  LedSign::Set(headX,headY,1);
  shadow[headX][headY] = true;
  dirCurr = dirPrev;
  
  bounceUp.update();
  int valueUp = bounceUp.risingEdge();
  if (valueUp == HIGH)
  {
    dirCurr = up;
  }
  
  bounceDown.update();
  int valueDown = bounceDown.risingEdge();
  if (valueDown == HIGH)
  {
    dirCurr = down;
  }
  
  bounceLeft.update();
  int valueLeft = bounceDown.risingEdge();
  if (valueLeft == HIGH)
  {
    dirCurr = left;
  }
  
  bounceRight.update();
  int valueRight = bounceRight.risingEdge();
  if (valueRight == HIGH)
  {
    dirCurr = right;
  }
  
  if (dirCurr + dirPrev == 3)
  {
    dirCurr = dirPrev;
  }
  
  
  headX = (headX - (dirCurr == left) + (dirCurr == right));
  headY = (headY - (dirCurr == up) + (dirCurr == down));
  
  if (headX == randX && headY == randY)
  {
    LedSign::Set(randX,randY,0);
    snakeLength++;
    while(shadow[randX][randY] != true)
    {
    randX = random(0,14);
    randY = random(0,9);
    }
    LedSign::Set(randX, randY, 1);
    
  }
  
  if (shadow[headX][headY] == true)
  {
    LedSign::Init();
    for (int k = 0; k < 5; k++)
    {
      Figure::Scroll90(snakeLength-3);
      delay(250);
    }
    
      for (int j = 13; j > -(6 * testLength); j--)
    {
    
      x = j;
      LedSign::Clear();
      for (int i = 0; i < testLength; i++)
      {
      
        x2 = Font::Draw(test[i], x, 0);
        x += x2;
        if (x >= 13) break;
      }
      delay(80);
    }
  }
  
  if ((headX < 0) || (headY < 0) || (headX > 13) || (headY > 8))
  {
    LedSign::Init();
    bool shadow[13][8] = {false};
    for (int k = 0; k < 5; k++)
    {
      Figure::Scroll90(snakeLength-3);
      delay(250);
    }
    
     for (int j = 13; j > -(6 * testLength); j--)
    {
    
      x = j;
      LedSign::Clear();
      for (int i = 0; i < testLength; i++)
      {
      
        x2 = Font::Draw(test[i], x, 0);
        x += x2;
        if (x >= 13) break;
      }
      delay(80);
    }
    
  }
  
  posHead++;
  if (posHead > 99)
  {
    posHead = 0;
  }
  
  xSnake[posHead] = headX;
  ySnake[posHead] = headY;
  
  if ( abs(posHead - posTail) >= snakeLength)
  {
    LedSign::Set(xSnake[posTail], ySnake[posTail], 0);
    int xTail = xSnake[posTail];
    int yTail = ySnake[posTail];
    shadow[xTail][yTail] = false;
    posTail++;
    if (posTail > 99)
    {
      posTail = 0;
    }
  }
  delay(100);
  
  
}

I am having problems with it, and I can't exactly figure it out.

So you expect someone to read the code, and spot a problem when you don't say what that problem actually is?
You might want to add a few more details, you might get more of a response. Like the schematic you have, and what is it about the display you think is a problem as a minimum.

Sorry, I am just not exactly sure what is the problem, because the program doesn't get very far before it's done. What I can see wrong in the display is that there is a pixel left ON in the center where it starts, although the tail of the snake moves away from the center. Also, when it should be displaying "PRESS RESET" after the snake dies, it doesn't. It's very glitchy while it should be displaying it.

But on the software side, I think that the delay() used to slow the snake to a more manageable speed is keeping me from detecting button presses. I don't know how to slow the snake without reducing responsiveness to buttons.

you can use the delay if you read the button with interrupt. (arduino provide interrupt only for 2 pin, but using atmega datasheet you can use interrupt on all pins)
or don't use delay() but millis(), like in blink without delay :slight_smile:

Another method of implementing a non-blocking delay (but requires the rest of the code/framework to be modified to work appropriately) would be something like:

//delay 20 milliseconds
static int delaytime = millis() + 20;
if(millis() > delaytime){
   delaytime += 20;
   //dostuff() will get called every 20 milliseconds (50hz).
   dostuff();
}

Stuff to be aware of:
This code can be problematic if dostuff() takes longer than 20ms to execute, or if all the code outside this loop takes longer than 20ms to execute.

You can't just replace your delays with this code. The rest of your code just won't work properly if you do. You need a framework tailored to work properly. At a high lvl, it should look something like this:

void Loop(){
    ReadInput();
    static int delaytime = millis() + 200;
    if(millis() > delaytime){
       delaytime += 200;
       SnakeMove();
    }   
    SetOutput();

}

void ReadInput(){
    //Input code here
    
}

void SnakeMove(){
    //Calculate new snake position at regular intervals
    
}

void SetOutput(){
    //Set LEDs here
    
}

You should also have a state machine to handle the various game states, eg. "PRESS RESET" after the snake dies.

Thank you jraskell. I was thinking that subprograms may be needed, but I was hoping they wouldn't. I will try to rewrite the program in this style and test it as soon as I can. I wrote the original program by translating the TI BASIC version I made for my TI83+ calculator, so that is probably where the problems come from, being that Arduino code is OOP, and BASIC is not.