Snake Game on 8*8 leds not fully working

Seeing we are all online if you want to discord i can do that to it might be easier !

No thanks. Here I will stay, as there is some slight chance we aren't just talking amongst ourselves, and what we arrive at will be here forever for those who come looking for answers in the future.

a7

oh yeah i see it now ill definitely give it a shot like that !

Find out why reset does not signal a restart. Using reset versus restart probably did not reset many variables (or reset too many). I used reset in the simulator (and missed the problem).

1 Like

thats true but i would have posted the corrected code with infos, but yeah no problem was just a suggestion :wink:

So for the displayscore i tried those 3 versions the 3 gave the same outcome : crashed the whole thing at the end of a game

1st Version :

using this array :

const byte digitWidth = 3;
const byte digitHeight = 5;

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

Then

void displayDigit(int x, int y, int digit) {
  byte digitPatterns[10][5] = {
    { B11111, B10001, B10001, B10001, B11111 }, // 0
    { B11100, B10000, B10000, B10000, B11100 }, // 1
    { B11111, B10001, B11111, B10000, B11111 }, // 2
    { B11111, B10001, B11111, B10001, B11111 }, // 3
    { B10001, B10001, B11111, B10001, B10001 }, // 4
    { B11111, B10000, B11111, B10001, B11111 }, // 5
    { B11111, B10000, B11111, B10001, B11111 }, // 6
    { B11111, B10001, B10000, B10000, B10000 }, // 7
    { B11111, B10001, B11111, B10001, B11111 }, // 8
    { B11111, B10001, B11111, B10000, B11111 }  // 9
  };

  int ledNum;
  for (int col = 0; col < 3; col++) {
    int tempX = x + col;
    for (int row = 0; row < 5; row++) {
      ledNum = 8 * (y + row) + tempX;
      if (bitRead(digitPatterns[digit][row], col) == 1) {
        leds[ledNum] = CRGB(255, 0, 0); // Red
      } else {
        leds[ledNum] = CRGB::Black; // Off
      }
    }
  }
}

2nd Version :

void displayScore() {
  int tens = applesEaten / 10; // Dizaines
  int units = applesEaten % 10; // Unités

  int scoreIndex = 6;

  for (int digitRow = 0; digitRow < digitHeight; digitRow++) {
    int scoreValue = 0;

    if (tens > 0) {
      scoreValue = digits[tens][digitRow];
      tens = 0;
    } else if (units > 0) {
      scoreValue = digits[units][digitRow];
    }

    for (int col = 0; col < digitWidth; col++) {
      leds[scoreIndex] = (scoreValue & 0x01) ? CRGB(255, 0, 0) : CRGB(0, 0, 0);
      scoreValue >>= 1;
      scoreIndex--;
    }
  }
}

3rd Version :

void displayScore() {
  int dizaines = applesEaten / 10; 
  int unites = applesEaten % 10; 

  byte motifsChiffres[10][8] = {
    {B11100, B10100, B10100, B10100, B11100, B00000, B11100, B00000}, // 0
    {B11000, B11000, B11000, B11000, B11100, B00000, B11000, B00000}, // 1
    {B11100, B10000, B10000, B11000, B11100, B00000, B11100, B00000}, // 2
    {B11100, B10000, B10000, B11100, B11100, B00000, B11100, B00000}, // 3
    {B10100, B10100, B11100, B10000, B10000, B00000, B10000, B00000}, // 4
    {B11100, B10000, B10000, B11100, B11100, B00000, B10000, B00000}, // 5
    {B11100, B10000, B10000, B11100, B11100, B00000, B11100, B00000}, // 6
    {B11100, B10000, B10000, B10000, B10000, B00000, B10000, B00000}, // 7
    {B11100, B10000, B10000, B11100, B11100, B00000, B11100, B00000}, // 8
    {B11100, B10100, B10100, B11100, B10000, B00000, B10000, B00000}  // 9
  };

  int indiceScore = 6; 

  for (int ligne = 0; ligne < 8; ligne++) {
    for (int colonne = 0; colonne < 3; colonne++) {
      int bitChiffre = (motifsChiffres[dizaines][ligne] >> colonne) & 0x01;
      leds[indiceScore] = (bitChiffre == 1) ? CRGB(255, 0, 0) : CRGB(0, 0, 0);
      indiceScore--;
    }
  }

  indiceScore = 3;

  for (int ligne = 0; ligne < 8; ligne++) {
    for (int colonne = 0; colonne < 3; colonne++) {
      int bitChiffre = (motifsChiffres[unites][ligne] >> colonne) & 0x01;
      leds[indiceScore] = (bitChiffre == 1) ? CRGB(255, 0, 0) : CRGB(0, 0, 0);
      indiceScore--;
    }
  }
}

For the Reset/restartGame problem it seems to be random and cause by the buggy display score i first proposed

All that looks plausible if yet flawed.

Rather than trying to get the score displayed in the context of the game, I suggest to you to write a simpler complete sketch that only does one thing:

  draw a digit N starting at X and Y in the matrix.

Test it by drawing every digit 0..9 at, say, 2,3 on the grid, with a simple delay(1000) between digits.

You will end up, I recommend, with a function that look like

void drawDigit(int theDigit, int X, int Y)
{
// details
}

Then place this back in the context where you wish to use it and maybe

     // clear the display and then
    drawDigit(tens, 1, 3);
    drawDigit(ones, 5, 3);

F\from you displayScore() function.

This is basic divide and conquer. Clear the decks and get the little pieces working, then you can use them confident and never need to look inside them again.

a7

I had chatGPT scour the 50000 lines of my Arduino code I fed it as a training base and added @xpfd's test sketch. Tthe Umbrella Academy finished (corrected a few defects) the sketch it came up with.

In an unrelated industrial accident I lost my own UNO-based hardware version, so I grabbed @xpfd's.


Wokwi_badge Old school digit draw


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

# include <FastLED.h>

# define DATA_PIN 2        // Broche de données du carré de LED
# define NUM_LEDS 64       // Nombre de LEDs (8x8)

CRGB leds[NUM_LEDS];

void setup() {
  Serial.begin(115200);
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
  leds[1] = CRGB(255, 0, 0);
  leds[2] = CRGB(0, 255, 0);
  leds[3] = CRGB(0, 0, 255);
  FastLED.show();
  delay(777);
}

void loop()
{ 
  int number = random(10);
  int x = random(6);
  int y = random(4);

  fill_solid(leds, NUM_LEDS, CRGB(200, 255, 240));
  drawN(number, x, y);
  FastLED.show();

  delay(777);
}

unsigned char font[10][3] = {
/*0*/ {0b11111, 0b10001, 0b11111,},
/*1*/ {0b00000, 0b11111, 0b00000,},
/*2*/ {0b11101, 0b10101, 0b10111,},
/*3*/ {0b10101, 0b10101, 0b11111,},
/*4*/ {0b00111, 0b00100, 0b11111,},
/*5*/ {0b10111, 0b10101, 0b01001,},
/*6*/ {0b11111, 0b10101, 0b11101,},
/*7*/ {0b00001, 0b00001, 0b11111,},
/*8*/ {0b11111, 0b10101, 0b11111,},
/*9*/ {0b10111, 0b10101, 0b11111,},
};

void drawN(int N, int X, int Y)
{
  Y <<= 3;

  for (unsigned char colm = 0; colm < 3; colm++, X++) {
    unsigned char yT = Y;
    unsigned char theBits = font[N][colm];
    unsigned char mask = 0x1;

    for (unsigned char row = 0; row < 5; row++) {
      leds[yT + X] = (mask & theBits) ?   CRGB(170, 42, 42) :CRGB(200, 255, 240);
      mask <<= 1;
      yT += 8; 
    }
  }
}

HTH

a7

1 Like

I tried to play a bit with your code to learn, i managed to recreate one on a simple program that change numbers when i move the joystick but i really can't seem to implement it correctly on my snake game code here is the best i could do :

#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() {
  // 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();
}

unsigned char font[10][3] = {
/*0*/ {0b11111, 0b10001, 0b11111,},
/*1*/ {0b00000, 0b11111, 0b00000,},
/*2*/ {0b11101, 0b10101, 0b10111,},
/*3*/ {0b10101, 0b10101, 0b11111,},
/*4*/ {0b00111, 0b00100, 0b11111,},
/*5*/ {0b10111, 0b10101, 0b01001,},
/*6*/ {0b11111, 0b10101, 0b11101,},
/*7*/ {0b00001, 0b00001, 0b11111,},
/*8*/ {0b11111, 0b10101, 0b11111,},
/*9*/ {0b10111, 0b10101, 0b11111,},
};

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;
  }
}

Create a stand-alone sketch (without the game) that only makes numbers, then add to that a function to changes numbers.

Here is the program that changes the number when i press the joystick :

#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 pour l'axe X du joystick
#define JOYSTICK_Y A1     // Broche pour l'axe Y du joystick
#define BUTTON_PIN 8      // Broche du bouton

CRGB leds[NUM_LEDS];
int currentNumber = 0;

void setup() {
  Serial.begin(115200);
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
  FastLED.show();
  pinMode(BUTTON_PIN, INPUT_PULLUP);
}

void loop() {
  // Lire les valeurs analogiques des axes X et Y du joystick
  int xValue = analogRead(JOYSTICK_X);
  int yValue = analogRead(JOYSTICK_Y);

  // Lire l'Ă©tat du bouton
  int buttonState = digitalRead(BUTTON_PIN);

  // Si le bouton est enfoncé, changez le chiffre affiché
  if (buttonState == LOW) {
    currentNumber = random(10);
  }

  fill_solid(leds, NUM_LEDS, CRGB::Black); // Éteindre toutes les LEDs

  // Afficher le chiffre actuel Ă  la position (0, 0)
  drawN(currentNumber, 0, 0);

  FastLED.show();
  delay(100);
}

unsigned char font[10][3] = {
  /*0*/ {0b11111, 0b10001, 0b11111,},
  /*1*/ {0b00000, 0b11111, 0b00000,},
  /*2*/ {0b11101, 0b10101, 0b10111,},
  /*3*/ {0b10101, 0b10101, 0b11111,},
  /*4*/ {0b00111, 0b00100, 0b11111,},
  /*5*/ {0b10111, 0b10101, 0b01001,},
  /*6*/ {0b11111, 0b10101, 0b11101,},
  /*7*/ {0b00001, 0b00001, 0b11111,},
  /*8*/ {0b11111, 0b10101, 0b11111,},
  /*9*/ {0b10111, 0b10101, 0b11111,},
};

void drawN(int N, int X, int Y) {
  Y <<= 3;

  for (unsigned char colm = 0; colm < 3; colm++, X++) {
    unsigned char yT = Y;
    unsigned char theBits = font[N][colm];
    unsigned char mask = 0x1;

    for (unsigned char row = 0; row < 5; row++) {
      leds[yT + X] = (mask & theBits) ? CRGB(170, 42, 42) : CRGB(200, 255, 240);
      mask <<= 1;
      yT += 8;
    }
  }
}

i used @alto777 code as a base.

edit : forgot to add this one that does the same but with units and tens :

#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 pour l'axe X du joystick
#define JOYSTICK_Y A1     // Broche pour l'axe Y du joystick
#define BUTTON_PIN 8      // Broche du bouton

CRGB leds[NUM_LEDS];
int tensDigit = 0;      // Chiffre des dizaines
int onesDigit = 0;      // Chiffre des unités

void setup() {
  Serial.begin(115200);
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
  FastLED.show();
  pinMode(BUTTON_PIN, INPUT_PULLUP);
}

void loop() {
  // Lire les valeurs analogiques des axes X et Y du joystick
  int xValue = analogRead(JOYSTICK_X);
  int yValue = analogRead(JOYSTICK_Y);

  // Lire l'Ă©tat du bouton
  int buttonState = digitalRead(BUTTON_PIN);

  // Si le bouton est enfoncé, changez les chiffres des dizaines et des unités
  if (buttonState == LOW) {
    tensDigit = random(10);
    onesDigit = random(10);
  }

  fill_solid(leds, NUM_LEDS, CRGB::Black); // Éteindre toutes les LEDs

  // Afficher les chiffres des dizaines et des unités
  drawN(tensDigit, 1, 0);   // Chiffre des dizaines Ă  la position (1, 0)
  drawN(onesDigit, 5, 0);   // Chiffre des unités à la position (5, 0)

  FastLED.show();
  delay(100);
}

unsigned char font[10][3] = {
  /*0*/ {0b11111, 0b10001, 0b11111,},
  /*1*/ {0b00000, 0b11111, 0b00000,},
  /*2*/ {0b11101, 0b10101, 0b10111,},
  /*3*/ {0b10101, 0b10101, 0b11111,},
  /*4*/ {0b00111, 0b00100, 0b11111,},
  /*5*/ {0b10111, 0b10101, 0b01001,},
  /*6*/ {0b11111, 0b10101, 0b11101,},
  /*7*/ {0b00001, 0b00001, 0b11111,},
  /*8*/ {0b11111, 0b10101, 0b11111,},
  /*9*/ {0b10111, 0b10101, 0b11111,},
};

void drawN(int N, int X, int Y) {
  Y <<= 3;

  for (unsigned char colm = 0; colm < 3; colm++, X++) {
    unsigned char yT = Y;
    unsigned char theBits = font[N][colm];
    unsigned char mask = 0x1;

    for (unsigned char row = 0; row < 5; row++) {
      leds[yT + X] = (mask & theBits) ? CRGB(170, 42, 42) : CRGB(200, 255, 240);
      mask <<= 1;
      yT += 8;
    }
  }
}

Or


Make this function just serial print the score, like "Your score is 42" for the moment.

Work on the rest of the game. When you ready, re-write disolayScore() to draw the digits.

Each digit means a call to drawN to put it on the screen somewhere. Use the same logic as before to calculate the tens and the units digits.

I think you got this


a7

1 Like

Ill try this approach tomorrow, and keep you updated !

thanks !

There you go.

a7

I'm over budget on this most entertaining game. You can see what a small life I lead when I say that this is what passes for fun.

But now she who must not be kept waiting has texted, my ride will arrive soon and the beach awaits us on this a perfect perfect day, so maybe I won't even read code under the umbrella.

This version changes the snake to use a ring buffer, which seems oddly appropriate and relieves the code of having to shuffle the snake segments.

Either way of holding the snake could mean that drawing does not have to erase the screen - most of the snake pixels remain snake coloured - it is only necessary to paint the new head and erase the former tail... an exercise for the reader as they say. :expressionless:

I redid the birth of the 3 unit snake, as I did not understand how the snake comes into being. Now there is no time when snake parts mysteriously appear briefly at 0,0.

I made spawnApple leave the top and left-hand side alone, this may fix the born in the corner error.

Click for the cdode
// https://wokwi.com/projects/378469495159511041
// https://forum.arduino.cc/t/snake-game-on-8-8-leds-not-fully-working/1176943

# 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

// ring buffer for sanke pixels
int snakeX[64];            // Coordonnées X du Snake
int snakeY[64];            // Coordonnées Y du Snake

//...
int headIdx;                // maintained as head of snake
int tailIdx;                // temporary is just H + L -1, so may not even need it
const int iMask = 0x3f;      // mask for snake number math 0..63

int appleX, appleY;        // Coordonnées de la pomme
int direction = 1;         // Direction du Snake (0: haut, 1: droite, 2: bas, 3: gauche)

const int startSpeed = 879;   // old man speed.
int speed = startSpeed;       // 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() {
  Serial.begin(115200);
  Serial.println("no flying blind!\n");

  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
  FastLED.show();
// let's have the same gane every time for awhile  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

  resetGame();
}

void loop() {
  if (gameOverFlag) {
    // 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) {
    while (digitalRead(JOYSTICK_SW) == HIGH)
      ; // hang out until

    Serial.println("BUTTON");
    resetGame(); // need to RESET game
    // restartGame = true; // Mettre restartGame à vrai pour redémarrer le jeu
    return;
  }

  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();
  }

  FastLED.show();
}

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

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

/* now the snake element are in a ring buffer, so no need to shuffle
    // 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
  headIdx--; headIdx &= iMask; // snake grows to the left (-), and extends right (+)
  snakeX[headIdx] = newHeadX;
  snakeY[headIdx] = newHeadY;

  // Vérifier si le Snake a mangé une pomme
  if (snakeX[headIdx] == appleX && snakeY[headIdx] == 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[headIdx] < 0 || snakeX[headIdx] >= 8 || snakeY[headIdx] < 0 || snakeY[headIdx] >= 8) {
    gameOver();
  }

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

void spawnApple() {
  appleX = random(1, 8);
  appleY = random(1, 8);

  applesEaten += 2 + random(4); // seems only fair
}

void drawSnake() {

  Serial.print("gonna draw  ");

  Serial.print(headIdx); Serial.print(" -> ");

  for (int i = 0; i < snakeLength; i++) {
    int theIdx = (headIdx + i) & iMask;

    Serial.print(snakeX[theIdx]); Serial.print(",");
    Serial.print(snakeY[theIdx]); Serial.print("   ");

    int thePixel = snakeY[theIdx] * 8 + snakeX[theIdx];
    leds[thePixel] = CRGB(0, 255, 100); // Couleur du Snake (vert)
  }
  Serial.println();
}

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() {
  fill_solid(leds, NUM_LEDS, CRGB(10, 20, 30));
  Serial.print("your score = "); Serial.println(applesEaten);
  //  delay(1000);  // sue me.

  if (applesEaten >= 100) applesEaten = 99;

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

  drawN(tens, 1, 2);
  drawN(units, 5, 2);

  FastLED.show();

  //  delay(1300);    // get off my back
}

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

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

  headIdx = 0;

  // Réinitialiser la position de départ du Snake

  // I did not see where the snake gets its initial 3 part coordinates, so:
  snakeX[headIdx] = 5;
  snakeY[headIdx] = 3;

  /* this broke something. still a mystery how the snake gets born */
  /* OK, snake has to be grown so it dosen't immediately step on itself. Last issue? */

    int theIdx = (headIdx + 1) & iMask;
    snakeX[theIdx] = 4;
    snakeY[theIdx] = 3;

    theIdx = (headIdx + 2) & iMask;
    snakeX[theIdx] = 3;
    snakeY[theIdx] = 3;

Serial.println("draw snake right now WTF"); drawSnake();

  // 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 on speed dial! do a printing state trick one day if
    //    Serial.print("change direction to "); Serial.println(direction);
  }
}

unsigned char font[10][3] = {
  /*0*/ {0b11111, 0b10001, 0b11111,},
  /*1*/ {0b00000, 0b11111, 0b00000,},
  /*2*/ {0b11101, 0b10101, 0b10111,},
  /*3*/ {0b10101, 0b10101, 0b11111,},
  /*4*/ {0b00111, 0b00100, 0b11111,},
  /*5*/ {0b10111, 0b10101, 0b11101,},
  /*6*/ {0b11111, 0b10101, 0b11101,},
  /*7*/ {0b00001, 0b00001, 0b11111,},
  /*8*/ {0b11111, 0b10101, 0b11111,},
  /*9*/ {0b10111, 0b10101, 0b11111,},
};

void drawN(int N, int X, int Y)
{
  Y <<= 3;

  for (unsigned char colm = 0; colm < 3; colm++, X++) {
    unsigned char yT = Y;
    unsigned char theBits = font[N][colm];
    unsigned char mask = 0x1;

    for (unsigned char row = 0; row < 5; row++) {
      leds[yT + X] = (mask & theBits) ?   CRGB(255, 242, 242) : CRGB(10, 20, 30);
      mask <<= 1;
      yT += 8;
    }
  }
}

The game logic is still 99 percent @exo1's, it is functioning well enough. I changed the scoring so I could get a bigger score to test drawing the score two digits at the fin du jeu.

Unless you get an apple dropped on your head?

Play with it here:

Wokwi_badge Snake Game WIP


HTH

a7

1 Like

Thank you very much ! have a wonderful time a t the beach :wink:

Always and I hope again this day. Less perfectly perfect, but still perfect.

I removed the magic numbers from your sketch and made the playing area 16 x 16. I changed it to not need to erase the screen between frames. I let the snake leave a trail - perhaps this could enter into a score, like how many of the pixels got visited.

The project linked in #35 above will always be the latest and may not always be in working order.

a7

has been further hacked. I replaced the two arrays snakeX and snakeY with one array, and lost a bunch of maths used to manipulate X and Y as separate variables.

Now "snake coords" fit in one byte and each of X and Y have four bits in that byte YYYYXXXX.

I modified a few of the helping functions. The game still uses the skeleton logic of @exo1, but the low-level logic is now somewhat clever and obscure and kinder on memory of which we were in no danger of running out. I am not saying these are all good things.

The field is 16 by 16, and I ungracefully put in the ability for the snake to eat an apple that happens to land on the snake body.

a7

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