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