Snake Game For UNO R4 WiFi Matrix

I was messing around and wanted to do something with the matrix display on my UNO R4 WiFi board. This one will home in on the food and when it runs into itself it will flash a skull on the screen and start over. This is my first attempt and something on R4. Here is the code:

#include "Arduino_LED_Matrix.h"

ArduinoLEDMatrix matrix;

#define WIDTH 12
#define HEIGHT 8
#define MAX_LENGTH 96

struct Point {
  int x;
  int y;
};

Point snake[MAX_LENGTH];
int snakeLength = 3;

Point food;
bool gameOver = false;

unsigned long lastMove = 0;
unsigned long moveInterval = 300;

// 💀 Skull icon pattern (8x8 centered on screen)
const uint8_t skull[8][12] = {
  {0,0,1,1,0,0,0,0,1,1,0,0},
  {0,1,0,1,1,0,0,1,1,0,1,0},
  {1,1,1,1,1,1,1,1,1,1,1,1},
  {1,1,1,1,1,1,1,1,1,1,1,1},
  {1,1,0,1,1,1,1,1,1,0,1,1},
  {0,1,1,1,0,0,0,0,1,1,1,0},
  {0,0,1,1,1,1,1,1,1,1,0,0},
  {0,0,0,1,1,0,0,1,1,0,0,0}
};

void generateFood() {
  while (true) {
    food.x = random(0, WIDTH);
    food.y = random(0, HEIGHT);
    bool onSnake = false;
    for (int i = 0; i < snakeLength; i++) {
      if (snake[i].x == food.x && snake[i].y == food.y) {
        onSnake = true;
        break;
      }
    }
    if (!onSnake) break;
  }
}

void resetGame() {
  snakeLength = 3;
  snake[0] = {5, 4};
  snake[1] = {4, 4};
  snake[2] = {3, 4};
  generateFood();
  gameOver = false;
}

void showSkull() {
  uint8_t buffer[HEIGHT * WIDTH];
  for (int y = 0; y < HEIGHT; y++) {
    for (int x = 0; x < WIDTH; x++) {
      buffer[y * WIDTH + x] = skull[y][x];
    }
  }

  for (int i = 0; i < 3; i++) {
    matrix.loadPixels(buffer, HEIGHT * WIDTH);
    delay(400);
    matrix.clear();
    delay(200);
  }
}

bool isOccupied(Point p) {
  for (int i = 0; i < snakeLength; i++) {
    if (snake[i].x == p.x && snake[i].y == p.y) return true;
  }
  return false;
}

void setup() {
  matrix.begin();
  Serial.begin(9600);
  randomSeed(analogRead(A0));
  resetGame();
  Serial.println("SNAKE GAME STARTED");
}

void loop() {
  if (gameOver) {
    Serial.print("Game Over! Final score: ");
    Serial.println(snakeLength - 3);
    showSkull();
    resetGame();
    delay(500);
  }

  if (millis() - lastMove > moveInterval) {
    lastMove = millis();

    Point head = snake[0];
    Point next = head;

    // 🧭 Greedy direction
    if (food.x < head.x) next.x--;
    else if (food.x > head.x) next.x++;
    else if (food.y < head.y) next.y--;
    else if (food.y > head.y) next.y++;

    // Wrap around
    if (next.x < 0) next.x = WIDTH - 1;
    if (next.x >= WIDTH) next.x = 0;
    if (next.y < 0) next.y = HEIGHT - 1;
    if (next.y >= HEIGHT) next.y = 0;

    // 💥 Check for self collision
    if (isOccupied(next)) {
      gameOver = true;
      return;
    }

    // 🐍 Move snake
    for (int i = snakeLength; i > 0; i--) {
      snake[i] = snake[i - 1];
    }
    snake[0] = next;

    if (next.x == food.x && next.y == food.y) {
      snakeLength++;
      if (snakeLength >= MAX_LENGTH) snakeLength = MAX_LENGTH;
      generateFood();

      Serial.print("Score: ");
      Serial.println(snakeLength - 3);
    }

    draw();
  }
}

void draw() {
  uint8_t frame[HEIGHT][WIDTH] = {};
  for (int i = 0; i < snakeLength; i++) {
    frame[snake[i].y][snake[i].x] = 1;
  }
  frame[food.y][food.x] = 1;
  matrix.renderBitmap(frame, HEIGHT, WIDTH);
}

2 Likes