Snake Game on 8*8 leds not fully working

Hey everyone,
Im new to the arduino community and new to coding.
For my first "big" project i wanted to create a snake game on a 8*8 led square.
Ive been working on it for three days but cannot figure out how to make certain things work, i would love a little help and explanations.
Before Sharing my code this is my setup for this project :

Board : Arduino Nano
Elegoo Joystick (GND to Arduino GND,5V to Arduino 5V,VRx to A0,VRy to A1,SW to D8)
8*8 WS2812b led square with V+ to external 5V psu, 1N to D2 with 330 ohm resistor, V- to external psu and Arduino GND.

My Setup is working since i have tested with some easy animations and serial monitor, everything is working fine.

So my problem is : I want my snake game to display a Score in two digits, number of apples eaten, drawn on the leds like 01,02,03, etc when a game is over, And then be able to press the joystick button (SW) to restart a game, ever and ever until power off.

My snake game is perfectly working, my snake eats apples, grow, becomes faster, can collide with edges and himself. The problem happens when gameover, the score is not displayed properly i can only get two leds to light up, and impossible to restart using my sw button.
Im at my limits since i dont know very much, i tried asking chatgpt but nothing was successful and everything was just messing with my original code.

So if any of you can help me fix this i would gladly appreciate it, and teach me where i was making mistake, thank you and here is my code (i can only use FastLed since its the only library that works with those leds) (comments are in french for myself) :

#include <FastLED.h>

#define DATA_PIN 2        // Broche de données du carré de LED
#define NUM_LEDS 64       // Nombre de LEDs (8x8)
#define JOYSTICK_X A0     // Broche analogique pour VRx
#define JOYSTICK_Y A1     // Broche analogique pour VRy
#define JOYSTICK_SW 8     // Broche pour le bouton du joystick
#define LED_INDEX 12      // Indice de la LED de départ du Snake

CRGB leds[NUM_LEDS];
int snakeLength = 3;       // Longueur initiale du Snake
int snakeX[64];            // Coordonnées X du Snake
int snakeY[64];            // Coordonnées Y du Snake
int appleX, appleY;        // Coordonnées de la pomme
int direction = 1;         // Direction du Snake (0: haut, 1: droite, 2: bas, 3: gauche)
int speed = 350;           // Vitesse de déplacement du Snake (plus la valeur est basse, plus il est rapide)
unsigned long lastMoveTime; // Dernier moment oĂč le Snake a bougĂ©
bool gameOverFlag = false;  // Drapeau pour indiquer la fin du jeu
int applesEaten = 0;       // Nombre de pommes mangées
bool restartGame = false;  // Drapeau pour indiquer le redémarrage du jeu

void setup() {
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
  FastLED.show();

  randomSeed(analogRead(0)); // Initialisation du générateur de nombres aléatoires

  // Appel de la fonction resetGame pour initialiser le jeu
  resetGame();

  lastMoveTime = millis();

  pinMode(JOYSTICK_SW, INPUT_PULLUP); // Configuration du bouton du joystick en mode INPUT_PULLUP
}


void loop() {
  if (!gameOverFlag) {
    unsigned long currentTime = millis();
    if (currentTime - lastMoveTime >= speed) {
      moveSnake();
      lastMoveTime = currentTime;
    }

    checkCollision();
    

    // Gestion du joystick
    int xValue = analogRead(JOYSTICK_X);
    int yValue = analogRead(JOYSTICK_Y);

    if (xValue < 100) { // Joystick vers la gauche
      changeDirection(3); // Changer la direction Ă  gauche
    } else if (xValue > 900) { // Joystick vers la droite
      changeDirection(1); // Changer la direction Ă  droite
    } else if (yValue < 100) { // Joystick vers le haut
      changeDirection(0); // Changer la direction vers le haut
    } else if (yValue > 900) { // Joystick vers le bas
      changeDirection(2); // Changer la direction vers le bas
    }

    // Si restartGame est vrai, réinitialise le jeu
    if (restartGame) {
      resetGame();
    }
  } else {
    // Afficher le nombre de pommes mangées en rouge
    displayScore();
    
    // Attendre que le bouton du joystick soit enfoncé pour redémarrer le jeu
    if (digitalRead(JOYSTICK_SW) == HIGH) {
      restartGame = true; // Mettre restartGame à vrai pour redémarrer le jeu
    }
  }

  FastLED.show();
}

void moveSnake() {
  // DĂ©placer le Snake en ajoutant une nouvelle tĂȘte dans la direction actuelle
  int newHeadX = snakeX[0];
  int newHeadY = snakeY[0];

  switch (direction) {
    case 0: // Haut
      newHeadY--;
      break;
    case 1: // Droite
      newHeadX++;
      break;
    case 2: // Bas
      newHeadY++;
      break;
    case 3: // Gauche
      newHeadX--;
      break;
  }

  // DĂ©placer le corps du Snake
  for (int i = snakeLength - 1; i > 0; i--) {
    snakeX[i] = snakeX[i - 1];
    snakeY[i] = snakeY[i - 1];
  }

  // Mettre Ă  jour la position de la tĂȘte du Snake
  snakeX[0] = newHeadX;
  snakeY[0] = newHeadY;

  // Vérifier si le Snake a mangé une pomme
  if (snakeX[0] == appleX && snakeY[0] == appleY) {
    snakeLength++;
    spawnApple();

    // Accélérer légÚrement le Snake
    if (speed > 20) {
      speed -= 5;
    }
  }

  // Afficher le Snake et la pomme
  clearBoard();
  drawSnake();
  drawApple();
}

void checkCollision() {
  // Vérifier si le Snake a heurté les bords
  if (snakeX[0] < 0 || snakeX[0] >= 8 || snakeY[0] < 0 || snakeY[0] >= 8) {
    gameOver();
  }

  // Vérifier si le Snake a mangé son propre corps
  for (int i = 1; i < snakeLength; i++) {
    if (snakeX[i] == snakeX[0] && snakeY[i] == snakeY[0]) {
      gameOver();
    }
  }
}

void spawnApple() {
  appleX = random(8);
  appleY = random(8);
}

void drawSnake() {
  for (int i = 0; i < snakeLength; i++) {
    leds[snakeX[i] + snakeY[i] * 8] = CRGB(0, 255, 0); // Couleur du Snake (vert)
  }
}

void drawApple() {
  leds[appleX + appleY * 8] = CRGB(255, 0, 0); // Couleur de la pomme (rouge)
}

void clearBoard() {
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB(0, 0, 0); // Éteindre toutes les LEDs
  }
}

void displayScore() {
  // Afficher le nombre de pommes mangées en rouge
  clearBoard();

  int tens = applesEaten / 10; // Dizaines
  int units = applesEaten % 10; // Unités

  int scoreIndex = 6; // Indice de la LED Ă  partir duquel afficher le score

  // Afficher les dizaines
  if (tens > 0) {
    leds[scoreIndex] = CRGB(255, 0, 0);
    scoreIndex--;
    leds[scoreIndex] = CRGB(255, 0, 0);
    scoreIndex--;
  }

  // Afficher les unités
  leds[scoreIndex] = CRGB(255, 0, 0);
  scoreIndex--;

  // Mettre Ă  jour le score
  FastLED.show();
}


void gameOver() {
  gameOverFlag = true;
  clearBoard();
  displayScore();
  FastLED.show();
  restartGame = false; // Ne pas redémarrer automatiquement
}

void resetGame() {
  snakeLength = 3;
  direction = 1;
  speed = 150;
  gameOverFlag = false;
  applesEaten = 0;

  // Réinitialiser la position de départ du Snake
  snakeX[0] = 3;
  snakeY[0] = 3;

  // RĂ©initialiser la position de la pomme
  spawnApple();

  // Effacer l'affichage du score
  clearBoard();

  // RĂ©initialiser la variable restartGame Ă  faux
  restartGame = false;

}

void changeDirection(int newDirection) {
  if ((newDirection == 0 && direction != 2) ||   // Nouvelle direction : haut, direction actuelle : bas
      (newDirection == 1 && direction != 3) ||   // Nouvelle direction : droite, direction actuelle : gauche
      (newDirection == 2 && direction != 0) ||   // Nouvelle direction : bas, direction actuelle : haut
      (newDirection == 3 && direction != 1)) {   // Nouvelle direction : gauche, direction actuelle : droite
    direction = newDirection;
  }
}

you need additional lib

I tried with this one but i think my led are not compatible with this but maybe i can give it another shot, what should i add with this ? just add the library or implement new code lines ?
I tried only including for the moment and no changes.

#include <FastLED.h>

#include <FastLED_NeoMatrix.h>

#include <Framebuffer_GFX.h>

Thank you very much

Edit :

https://fr.aliexpress.com/item/1005002674028098.html

those are my leds, the 64 leds 8*8 panel; it doesn't have a control module so i don't think NeoMatrix will be useful

I have your code running and it has some flaws.

Not a flaw, but I had to really crank the speed down. You must be way faster than I am!

Flaw: I found that trying to eat an apple appearing at the upper left hand corner to be impossible, no matter how slowly I made the snake go.

Flaw: new snake elements make a brief appearance in the same pixel - could it be the snake thinks it is eating its own tail?

Now it is not clear what you want to have displayed after the game finishes. What range of numbers do you want to display, and how is that to be done on the 8x8 matrix?

The code looks like you light up two pixels for every ten points, and one pixel for every point past those, viz:

score 3: light up 3 pixels
score 8: light up 8 pixels
score 13: light up 5 pixels

If that is what you meant, you may want to rethink it. If that is not what you meant, maybe I have read your code incorrectly, so just say what you are trying to accomplish.

The logic of getting to the score function and thence to resetting the game should be fairly simple to trace and debug.

I suggest to you to add serial printing liberally to check the values of key variables and confirm that they are properly informing the flow through your code.

It's a nice little game so far. I look forward to playing a better version. If you don't change the hardware, I can easily keep putting you newer code up to play with.

But please slow that snake down. Losing in 0.9 seconds every time is depressing. :wink:

HTH

a7

When using INPUT_PULLUP on a button pin, no button pressed is HIGH, button pressed is LOW.

Hey,
Thank you very much for this detailled answer and the time you took to help me !
I sent an "early" version of my code because after i tried to add things to make it work but it was just a mess so i revert it before sending it here and forgot to to change the speed back, i usually put it to 500 but i managed to play it at 150 so very fast.
If you want to slow it down just put an higher value here int speed = 350;
I didnt encounter the problem you're describing simply because i didnt have a game where an apple spawned there and yes i think my code spawn the snake body here and then move it to the tail quickly, i'll try to fix ot if i can manage to do it.

My display_score function here works as you think it works, but its really not what i wanted to achieve, i want the leds to "draw" the numbers like on an alarm clock for example.
I tried this with bit of code but it didn't work :

1 - declaring the variables

byte segments[10][8] = {
  {1, 1, 1, 0, 1, 1, 1, 1}, // 0
  {0, 0, 1, 0, 0, 1, 0, 0}, // 1
  {1, 0, 1, 1, 1, 0, 1, 1}, // 2
  {1, 0, 1, 1, 0, 1, 1, 1}, // 3
  {0, 1, 1, 1, 0, 1, 0, 0}, // 4
  {1, 1, 0, 1, 0, 1, 1, 1}, // 5
  {1, 1, 0, 1, 1, 1, 1, 1}, // 6
  {1, 0, 1, 0, 0, 1, 0, 0}, // 7
  {1, 1, 1, 1, 1, 1, 1, 1}, // 8
  {1, 1, 1, 1, 0, 1, 1, 0}  // 9
};

2 - Modifying displayScore function :

void displayScore() {
  clearBoard();
  int score = applesEaten;

  int digit1 = score % 10;
  int digit2 = (score / 10) % 10;

  // Afficher le chiffre des unités
  for (int i = 0; i < 8; i++) {
    if (segments[digit1][i]) {
      leds[i] = CRGB::Red;
    }
  }

  // Afficher le chiffre des dizaines
  for (int i = 0; i < 8; i++) {
    if (segments[digit2][i]) {
      leds[i + 9] = CRGB::Red; // DĂ©calage pour afficher le chiffre des dizaines Ă  droite
    }
  }
}

(comments are still in french)

This didn't work at all and made the whole game kind of buggy.

I don't plan on changing the hardware for the moment i don't have other type of leds and only wemos d1 and nano for controller so it should stay as it is.

For the reset game function i tried using some serial printing and the joystick_sw is indeed working, i don't know how to serialprint a function, it would be useful to see if restart game is indeed supposed to work.

Thank You very much :smile:

Hey !
I sent an "early" version of my code because after i tried to add things to make it work but it was just a mess so i revert it before sending it here and forgot to to change this value back to low, i changed it because in the serial monitor it sayed 0 when realeased and 1 when pressed so i figured out maybe its HIGH instead of LOW but it didn't change a thing. :face_with_diagonal_mouth:

Pas de probleme.

It should not have. If the function was separate and well-behaved, it should have worked, or not worked. But it should not have taken down any previous sucess.

I had made speed 1777. That shows how old I am. I removed the it speeds up, even though I never got far enough for it to do. Because I knew it would become too fast too soon. Or will once the game can get beyond the corner eating problem.

I did not look very closely at your code. It was fairly easy to find and make those minor adjustments, and my theory about the corner only formed when I wrote the two flaws - it occurred to me that they may have been related.

You can trace into function calls simply by printing "I'm in function nImporteQuoi() now!" as the first line of code. Obviously you can get more elaborate, but that is easy and useful.

Now that I see where you want to head with the score, I can take a look. This day, however, may not find me with the time. If you put up complete (compiles) but yet non-working, and say how they disappoint, I may have time to at least read the code at the beach.

This is often a problem. But you may have been tired and flailing, trying this and that and not conducting careful sttep-by-step debugging and experimentation. So give it a rest, and it is also good for a few more pairs of eyes to take a peek.

a7

1 Like

Ill try that and take all the time you need i am not at all in arush and here to learn, thank you for your time i will try to apply your advices and keep you updated !
:smile:

The button state for "pressed" is LOW.

  1. You were calling resetGame() too early in setup(). The SW pin was not being set to INPUT_PULLUP.
  2. I move resetGame() to the bottom of setup()
  3. I changed button press from HIGH to LOW and your game works.
  4. You were calling changing the state of restartGame() when you should have been calling resetGame()
//https://forum.arduino.cc/t/snake-game-on-8-8-leds-not-fully-working/1176943

// #include <FastLED_NeoMatrix.h> // for displaying numbers and letters
#include <FastLED.h>

#define DATA_PIN 2        // Broche de données du carré de LED
#define NUM_LEDS 64       // Nombre de LEDs (8x8)
#define JOYSTICK_X A0     // Broche analogique pour VRx
#define JOYSTICK_Y A1     // Broche analogique pour VRy
#define JOYSTICK_SW 8     // Broche pour le bouton du joystick
#define LED_INDEX 12      // Indice de la LED de départ du Snake

CRGB leds[NUM_LEDS];
int snakeLength = 3;       // Longueur initiale du Snake
int snakeX[64];            // Coordonnées X du Snake
int snakeY[64];            // Coordonnées Y du Snake
int appleX, appleY;        // Coordonnées de la pomme
int direction = 1;         // Direction du Snake (0: haut, 1: droite, 2: bas, 3: gauche)
int speed = 350;           // Vitesse de déplacement du Snake (plus la valeur est basse, plus il est rapide)
unsigned long lastMoveTime; // Dernier moment oĂč le Snake a bougĂ©
bool gameOverFlag = false;  // Drapeau pour indiquer la fin du jeu
int applesEaten = 0;       // Nombre de pommes mangées
bool restartGame = false;  // Drapeau pour indiquer le redémarrage du jeu

byte segments[10][8] = {
  {1, 1, 1, 0, 1, 1, 1, 1}, // 0
  {0, 0, 1, 0, 0, 1, 0, 0}, // 1
  {1, 0, 1, 1, 1, 0, 1, 1}, // 2
  {1, 0, 1, 1, 0, 1, 1, 1}, // 3
  {0, 1, 1, 1, 0, 1, 0, 0}, // 4
  {1, 1, 0, 1, 0, 1, 1, 1}, // 5
  {1, 1, 0, 1, 1, 1, 1, 1}, // 6
  {1, 0, 1, 0, 0, 1, 0, 0}, // 7
  {1, 1, 1, 1, 1, 1, 1, 1}, // 8
  {1, 1, 1, 1, 0, 1, 1, 0}  // 9
};

void setup() {
  // Serial.begin(115200);
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
  FastLED.show();
  randomSeed(analogRead(0)); // Initialisation du générateur de nombres aléatoires
  lastMoveTime = millis();
  pinMode(JOYSTICK_SW, INPUT_PULLUP); // Configuration du bouton du joystick en mode INPUT_PULLUP

  // Appel de la fonction resetGame pour initialiser le jeu
  resetGame();
}


void loop() {
  if (!gameOverFlag) {
    unsigned long currentTime = millis();
    if (currentTime - lastMoveTime >= speed) {
      moveSnake();
      lastMoveTime = currentTime;
    }

    checkCollision();
    

    // Gestion du joystick
    int xValue = analogRead(JOYSTICK_X);
    int yValue = analogRead(JOYSTICK_Y);

    if (xValue < 100) { // Joystick vers la gauche
      changeDirection(3); // Changer la direction Ă  gauche
    } else if (xValue > 900) { // Joystick vers la droite
      changeDirection(1); // Changer la direction Ă  droite
    } else if (yValue < 100) { // Joystick vers le haut
      changeDirection(0); // Changer la direction vers le haut
    } else if (yValue > 900) { // Joystick vers le bas
      changeDirection(2); // Changer la direction vers le bas
    }

    // Si restartGame est vrai, réinitialise le jeu
    if (restartGame) {
      resetGame();
    }
  } else {
    // Afficher le nombre de pommes mangées en rouge
    displayScore();
    
    // Attendre que le bouton du joystick soit enfoncé pour redémarrer le jeu
    // if (digitalRead(JOYSTICK_SW) == HIGH) {
    if (digitalRead(JOYSTICK_SW) == LOW) { // BUTTON pressed = LOW
      // Serial.println("BUTTON");
      resetGame(); // need to RESET game
      // restartGame = true; // Mettre restartGame à vrai pour redémarrer le jeu
    }
  }
  FastLED.show();
}

void moveSnake() {
  // DĂ©placer le Snake en ajoutant une nouvelle tĂȘte dans la direction actuelle
  int newHeadX = snakeX[0];
  int newHeadY = snakeY[0];

  switch (direction) {
    case 0: // Haut
      newHeadY--;
      break;
    case 1: // Droite
      newHeadX++;
      break;
    case 2: // Bas
      newHeadY++;
      break;
    case 3: // Gauche
      newHeadX--;
      break;
  }

  // DĂ©placer le corps du Snake
  for (int i = snakeLength - 1; i > 0; i--) {
    snakeX[i] = snakeX[i - 1];
    snakeY[i] = snakeY[i - 1];
  }

  // Mettre Ă  jour la position de la tĂȘte du Snake
  snakeX[0] = newHeadX;
  snakeY[0] = newHeadY;

  // Vérifier si le Snake a mangé une pomme
  if (snakeX[0] == appleX && snakeY[0] == appleY) {
    snakeLength++;
    spawnApple();

    // Accélérer légÚrement le Snake
    if (speed > 20) {
      speed -= 5;
    }
  }

  // Afficher le Snake et la pomme
  clearBoard();
  drawSnake();
  drawApple();
}

void checkCollision() {
  // Vérifier si le Snake a heurté les bords
  if (snakeX[0] < 0 || snakeX[0] >= 8 || snakeY[0] < 0 || snakeY[0] >= 8) {
    gameOver();
  }

  // Vérifier si le Snake a mangé son propre corps
  for (int i = 1; i < snakeLength; i++) {
    if (snakeX[i] == snakeX[0] && snakeY[i] == snakeY[0]) {
      gameOver();
    }
  }
}

void spawnApple() {
  appleX = random(8);
  appleY = random(8);
}

void drawSnake() {
  for (int i = 0; i < snakeLength; i++) {
    leds[snakeX[i] + snakeY[i] * 8] = CRGB(0, 255, 0); // Couleur du Snake (vert)
  }
}

void drawApple() {
  leds[appleX + appleY * 8] = CRGB(255, 0, 0); // Couleur de la pomme (rouge)
}

void clearBoard() {
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB(0, 0, 0); // Éteindre toutes les LEDs
  }
}

void displayScore() {
  // clearBoard();
  int score = applesEaten;

  int digit1 = score % 10;
  int digit2 = (score / 10) % 10;

  // Afficher le chiffre des unités
  for (int i = 0; i < 8; i++) {
    if (segments[digit1][i]) {
      leds[i] = CRGB::Red;
    }
  }

  // Afficher le chiffre des dizaines
  for (int i = 0; i < 8; i++) {
    if (segments[digit2][i]) {
      leds[i + 9] = CRGB::Red; // DĂ©calage pour afficher le chiffre des dizaines Ă  droite
    }
  }
}

void ORIGINALdisplayScore() {
  // Afficher le nombre de pommes mangées en rouge
  clearBoard();

  int tens = applesEaten / 10; // Dizaines
  int units = applesEaten % 10; // Unités

  int scoreIndex = 6; // Indice de la LED Ă  partir duquel afficher le score

  // Afficher les dizaines
  if (tens > 0) {
    leds[scoreIndex] = CRGB(255, 0, 0);
    scoreIndex--;
    leds[scoreIndex] = CRGB(255, 0, 0);
    scoreIndex--;
  }

  // Afficher les unités
  leds[scoreIndex] = CRGB(255, 0, 0);
  scoreIndex--;

  // Mettre Ă  jour le score
  FastLED.show();
}


void gameOver() {
  gameOverFlag = true;
  clearBoard();
  displayScore();
  FastLED.show();
  restartGame = false; // Ne pas redémarrer automatiquement
}

void resetGame() {
  snakeLength = 3;
  direction = 1;
  speed = 150;
  gameOverFlag = false;
  applesEaten = 0;

  // Réinitialiser la position de départ du Snake
  snakeX[0] = 3;
  snakeY[0] = 3;

  // RĂ©initialiser la position de la pomme
  spawnApple();

  // Effacer l'affichage du score
  clearBoard();

  // RĂ©initialiser la variable restartGame Ă  faux
  restartGame = false;

}

void changeDirection(int newDirection) {
  if ((newDirection == 0 && direction != 2) ||   // Nouvelle direction : haut, direction actuelle : bas
      (newDirection == 1 && direction != 3) ||   // Nouvelle direction : droite, direction actuelle : gauche
      (newDirection == 2 && direction != 0) ||   // Nouvelle direction : bas, direction actuelle : haut
      (newDirection == 3 && direction != 1)) {   // Nouvelle direction : gauche, direction actuelle : droite
    direction = newDirection;
  }
}

diagram.json for wokwi.com

{
  "version": 1,
  "author": "Anonymous maker",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-arduino-nano", "id": "nano", "top": 14.4, "left": -48.5, "attrs": {} },
    {
      "type": "wokwi-analog-joystick",
      "id": "joystick1",
      "top": -205.8,
      "left": 246.8,
      "rotate": 180,
      "attrs": {}
    },
    {
      "type": "wokwi-neopixel-matrix",
      "id": "ring1",
      "top": -210.24,
      "left": -38.3,
      "attrs": { "pixleate": "1", "rows": "8", "cols": "8" }
    },
    { "type": "wokwi-vcc", "id": "vcc1", "top": -66.44, "left": -67.2, "attrs": {} }
  ],
  "connections": [
    [ "nano:5V", "joystick1:VCC", "red", [ "v48", "h124.8", "v-345.6", "h115.2" ] ],
    [ "joystick1:GND", "nano:GND.1", "black", [ "v-9.6", "h-57.6", "v307.2", "h-105.6" ] ],
    [ "joystick1:GND", "ring1:GND", "black", [ "v-9.6", "h-57.6", "v201.6", "h-163.2" ] ],
    [ "nano:2", "ring1:DIN", "green", [ "v-19.2", "h-9.6" ] ],
    [ "joystick1:VERT", "nano:A1", "green", [ "v-48", "h-134.4", "v355.2", "h-163.2" ] ],
    [ "joystick1:HORZ", "nano:A0", "green", [ "v-38.4", "h-115.2", "v355.2", "h-182.4" ] ],
    [ "joystick1:SEL", "nano:8", "green", [ "v-28.8", "h-96", "v240", "h-182.4" ] ],
    [ "vcc1:VCC", "ring1:VCC", "red", [ "v38.4", "h115.2" ] ]
  ],
  "dependencies": {}
}
1 Like

@xfpd :

LOL. I had to hope that rotating the analog joystick 180 degrees would fix the left right up down problem. ICU did the same


Saved me looking closer at the code.

a7

Yes... in the simulation at first - up was down, left was right - this was causing me to crash into myself. Changing the device seemed better (to me) than changing the code/wiring.

[EDIT]
I added the two code blocks from Post #6. The game still works, is broken but starts and restarts, and the "score" is not being displayed right. (probably transposed x,y coordinates). due to "7-segment" array being displayed on 8x8 matrix.

//https://forum.arduino.cc/t/snake-game-on-8-8-leds-not-fully-working/1176943

// #include <FastLED_NeoMatrix.h> // for displaying numbers and letters
#include <FastLED.h>

#define DATA_PIN 2        // Broche de données du carré de LED
#define NUM_LEDS 64       // Nombre de LEDs (8x8)
#define JOYSTICK_X A0     // Broche analogique pour VRx
#define JOYSTICK_Y A1     // Broche analogique pour VRy
#define JOYSTICK_SW 8     // Broche pour le bouton du joystick
#define LED_INDEX 12      // Indice de la LED de départ du Snake

CRGB leds[NUM_LEDS];
int snakeLength = 3;       // Longueur initiale du Snake
int snakeX[64];            // Coordonnées X du Snake
int snakeY[64];            // Coordonnées Y du Snake
int appleX, appleY;        // Coordonnées de la pomme
int direction = 1;         // Direction du Snake (0: haut, 1: droite, 2: bas, 3: gauche)
int speed = 350;           // Vitesse de déplacement du Snake (plus la valeur est basse, plus il est rapide)
unsigned long lastMoveTime; // Dernier moment oĂč le Snake a bougĂ©
bool gameOverFlag = false;  // Drapeau pour indiquer la fin du jeu
int applesEaten = 0;       // Nombre de pommes mangées
bool restartGame = false;  // Drapeau pour indiquer le redémarrage du jeu

byte segments[10][8] = {
  {1, 1, 1, 0, 1, 1, 1, 1}, // 0
  {0, 0, 1, 0, 0, 1, 0, 0}, // 1
  {1, 0, 1, 1, 1, 0, 1, 1}, // 2
  {1, 0, 1, 1, 0, 1, 1, 1}, // 3
  {0, 1, 1, 1, 0, 1, 0, 0}, // 4
  {1, 1, 0, 1, 0, 1, 1, 1}, // 5
  {1, 1, 0, 1, 1, 1, 1, 1}, // 6
  {1, 0, 1, 0, 0, 1, 0, 0}, // 7
  {1, 1, 1, 1, 1, 1, 1, 1}, // 8
  {1, 1, 1, 1, 0, 1, 1, 0}  // 9
};

void setup() {
  // Serial.begin(115200);
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
  FastLED.show();
  randomSeed(analogRead(0)); // Initialisation du générateur de nombres aléatoires
  lastMoveTime = millis();
  pinMode(JOYSTICK_SW, INPUT_PULLUP); // Configuration du bouton du joystick en mode INPUT_PULLUP

  // Appel de la fonction resetGame pour initialiser le jeu
  resetGame();
}


void loop() {
  if (!gameOverFlag) {
    unsigned long currentTime = millis();
    if (currentTime - lastMoveTime >= speed) {
      moveSnake();
      lastMoveTime = currentTime;
    }

    checkCollision();
    

    // Gestion du joystick
    int xValue = analogRead(JOYSTICK_X);
    int yValue = analogRead(JOYSTICK_Y);

    if (xValue < 100) { // Joystick vers la gauche
      changeDirection(3); // Changer la direction Ă  gauche
    } else if (xValue > 900) { // Joystick vers la droite
      changeDirection(1); // Changer la direction Ă  droite
    } else if (yValue < 100) { // Joystick vers le haut
      changeDirection(0); // Changer la direction vers le haut
    } else if (yValue > 900) { // Joystick vers le bas
      changeDirection(2); // Changer la direction vers le bas
    }

    // Si restartGame est vrai, réinitialise le jeu
    if (restartGame) {
      resetGame();
    }
  } else {
    // Afficher le nombre de pommes mangées en rouge
    displayScore();
    
    // Attendre que le bouton du joystick soit enfoncé pour redémarrer le jeu
    // if (digitalRead(JOYSTICK_SW) == HIGH) {
    if (digitalRead(JOYSTICK_SW) == LOW) { // BUTTON pressed = LOW
      // Serial.println("BUTTON");
      resetGame(); // need to RESET game
      // restartGame = true; // Mettre restartGame à vrai pour redémarrer le jeu
    }
  }
  FastLED.show();
}

void moveSnake() {
  // DĂ©placer le Snake en ajoutant une nouvelle tĂȘte dans la direction actuelle
  int newHeadX = snakeX[0];
  int newHeadY = snakeY[0];

  switch (direction) {
    case 0: // Haut
      newHeadY--;
      break;
    case 1: // Droite
      newHeadX++;
      break;
    case 2: // Bas
      newHeadY++;
      break;
    case 3: // Gauche
      newHeadX--;
      break;
  }

  // DĂ©placer le corps du Snake
  for (int i = snakeLength - 1; i > 0; i--) {
    snakeX[i] = snakeX[i - 1];
    snakeY[i] = snakeY[i - 1];
  }

  // Mettre Ă  jour la position de la tĂȘte du Snake
  snakeX[0] = newHeadX;
  snakeY[0] = newHeadY;

  // Vérifier si le Snake a mangé une pomme
  if (snakeX[0] == appleX && snakeY[0] == appleY) {
    snakeLength++;
    spawnApple();

    // Accélérer légÚrement le Snake
    if (speed > 20) {
      speed -= 5;
    }
  }

  // Afficher le Snake et la pomme
  clearBoard();
  drawSnake();
  drawApple();
}

void checkCollision() {
  // Vérifier si le Snake a heurté les bords
  if (snakeX[0] < 0 || snakeX[0] >= 8 || snakeY[0] < 0 || snakeY[0] >= 8) {
    gameOver();
  }

  // Vérifier si le Snake a mangé son propre corps
  for (int i = 1; i < snakeLength; i++) {
    if (snakeX[i] == snakeX[0] && snakeY[i] == snakeY[0]) {
      gameOver();
    }
  }
}

void spawnApple() {
  appleX = random(8);
  appleY = random(8);
}

void drawSnake() {
  for (int i = 0; i < snakeLength; i++) {
    leds[snakeX[i] + snakeY[i] * 8] = CRGB(0, 255, 0); // Couleur du Snake (vert)
  }
}

void drawApple() {
  leds[appleX + appleY * 8] = CRGB(255, 0, 0); // Couleur de la pomme (rouge)
}

void clearBoard() {
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB(0, 0, 0); // Éteindre toutes les LEDs
  }
}

void displayScore() {
  clearBoard();
  int score = applesEaten;

  int digit1 = score % 10;
  int digit2 = (score / 10) % 10;

  // Afficher le chiffre des unités
  for (int i = 0; i < 8; i++) {
    if (segments[digit1][i]) {
      leds[i] = CRGB::Red;
    }
  }

  // Afficher le chiffre des dizaines
  for (int i = 0; i < 8; i++) {
    if (segments[digit2][i]) {
      leds[i + 9] = CRGB::Red; // DĂ©calage pour afficher le chiffre des dizaines Ă  droite
    }
  }
}

void ORIGINALdisplayScore() {
  // Afficher le nombre de pommes mangées en rouge
  clearBoard();

  int tens = applesEaten / 10; // Dizaines
  int units = applesEaten % 10; // Unités

  int scoreIndex = 6; // Indice de la LED Ă  partir duquel afficher le score

  // Afficher les dizaines
  if (tens > 0) {
    leds[scoreIndex] = CRGB(255, 0, 0);
    scoreIndex--;
    leds[scoreIndex] = CRGB(255, 0, 0);
    scoreIndex--;
  }

  // Afficher les unités
  leds[scoreIndex] = CRGB(255, 0, 0);
  scoreIndex--;

  // Mettre Ă  jour le score
  FastLED.show();
}


void gameOver() {
  gameOverFlag = true;
  clearBoard();
  displayScore();
  FastLED.show();
  restartGame = false; // Ne pas redémarrer automatiquement
}

void resetGame() {
  snakeLength = 3;
  direction = 1;
  speed = 150;
  gameOverFlag = false;
  applesEaten = 0;

  // Réinitialiser la position de départ du Snake
  snakeX[0] = 3;
  snakeY[0] = 3;

  // RĂ©initialiser la position de la pomme
  spawnApple();

  // Effacer l'affichage du score
  clearBoard();

  // RĂ©initialiser la variable restartGame Ă  faux
  restartGame = false;

}

void changeDirection(int newDirection) {
  if ((newDirection == 0 && direction != 2) ||   // Nouvelle direction : haut, direction actuelle : bas
      (newDirection == 1 && direction != 3) ||   // Nouvelle direction : droite, direction actuelle : gauche
      (newDirection == 2 && direction != 0) ||   // Nouvelle direction : bas, direction actuelle : haut
      (newDirection == 3 && direction != 1)) {   // Nouvelle direction : gauche, direction actuelle : droite
    direction = newDirection;
  }
}

This is for a seven segment LED, not an LED matrix.

Comment-out clearBoard(); and the game works again... but the 7-seg versus 8x8 display matrix needs to be addressed. I would not use the 7-seg array and use the FastLEDmatrix library to display scores.

I'd go old school and just bit copy the numbers into place row by row.

There's room for two digits, I'd have to see it first to know if it would be pleasing visually.

A quick test just turning on specific pixels might give you a decision before working on the code.

There's some room also to play with the definition of the bits used for each of the digits.

Conceptually the code is simple, a double loop and management of some pixel numbers.

HTH

a7

1 Like

Thank you very much to both of you for the time you took to help me !
I dont have anything much to say except for the last proposition of @alto777 , its exactly what i want to do but i tried to make it work and can't make it work but i'll give another shot tonight !
:pray: :pray:

Practice making your scoreDisplay() function on this simulation. You can see some examples I have been experimenting with:

1 Like

after testing @xfpd new code it seems that the resetgame works only two times then all my leds lights up in multiple colors :

and i cant restart the game anymore.

Problem 2 i still have to fix the little green dot that spawns in the upper left corner

Ill do that thank you !

So...

(pseudocode)

  set to make digit at X, Y
  for every colm 0 to 2
      grab a temporary TX = X + colm

      for every row 0 to 4
           make the pixel at TX, Y + row on or off according to this digit's map (definition 3x5 matrix)

You can convert between an X, Y concept of the matrix to a pixel number with some simple maths, viz:

    pixel number = 8 * Y + X

and like

    Y = pixel number / 8
    X = pixel number % 8

which I know you can hack (and maybe correct, sorry very hot right now, brain on the beach thing) because you played the same trick separating the tens digit from the units digit... it's the same thing here only in base 8.

I can't test this, so it may be upside down backwards and otherwise flawed, but the idea is to go row by row though each column copying the stored digit representation into the display buffer.

HTH

a7

1 Like