Snake Game
Note
🌟 Welcome to the SunFounder Facebook Community! Whether you’re into Raspberry Pi, Arduino, or ESP32, you’ll find inspiration, help ideas here.
✅ Be the first to get free learning resources.
✅ Stay updated on new products & exclusive giveaways.
✅ Share your creations and get real feedback.
👉 Need faster updates or support? Click [here] join our Facebook community
👉 Or join our WhatsApp group: Click [here]
🎁 Looking for parts?Check out our all-in-one kits below — packed with components, beginner-friendly guides, and tons of fun.
Name |
Includes Arduino board |
PURCHASE LINK |
|---|---|---|
Elite Explorer Kit |
Arduino Uno R4 WiFi |
|
3 in 1 Ultimate Starter Kit |
Arduino Uno R4 Minima |
Course Introduction
In this lesson, we’ll use a MAX7219 Dot Matrix Module, a joystick module, and an Arduino board to play a Snake game.
The snake’s movement will be controlled using the joystick.
Note
If this is your first time working with an Arduino project, we recommend downloading and reviewing the basic materials first.
Required Components
In this project, we need the following components:
SN |
COMPONENT INTRODUCTION |
QUANTITY |
PURCHASE LINK |
|---|---|---|---|
1 |
Arduino UNO R4 Minima/Arduino UNO R4 WIFI |
1 |
|
2 |
USB Type-C cable |
1 |
|
3 |
Breadboard |
1 |
|
4 |
Wires |
Several |
|
5 |
Joystick Module |
1 |
|
6 |
MAX7219 Dot Matrix Module |
1 |
Wiring
Common Connections:
MAX7219 Dot Matrix Module
CLK: Connect to 11 on the Arduino.
CS: Connect to 10 on the Arduino.
DIN: Connect to 12 on the Arduino.
GND: Connect to breadboard’s negative power bus.
VCC: Connect to breadboard’s red power bus.
Joystick Module
SW: Connect to 2 on the Arduino.
VRY: Connect to A1 on the Arduino.
VRX: Connect to A0 on the Arduino.
GND: Connect to breadboard’s negative power bus.
VCC: Connect to breadboard’s red power bus.
Writing the Code
Note
You can copy this code into Arduino IDE.
To install the library, use the Arduino Library Manager and search for LedControl and install it.
Don’t forget to select the board(Arduino UNO R4 WIFI) and the correct port before clicking the Upload button.
#include <LedControl.h>
// Initialize the LED matrix
LedControl lc = LedControl(12, 11, 10, 1); // DIN, CLK, CS, single matrix
// Joystick pin configuration
const int xPin = A0; // VRX connected to A0
const int yPin = A1; // VRY connected to A1
const int swPin = 2; // SW connected to digital pin 2
// Define grid size
#define GRID_SIZE 8
// Snake settings
int snake[64][2]; // Stores the position of each segment of the snake, max 64 segments
int snakeLength = 3; // Initial length of the snake
int direction = 0; // Snake's movement direction: 0=right, 1=down, 2=left, 3=up
bool gameOverFlag = false; // Flag to indicate if the game is over
// Dynamic speed control
int moveSpeed = 300; // Initial movement speed (in ms)
const int minSpeed = 100; // Minimum speed (in ms) to avoid uncontrollable gameplay
// Food position
int foodX, foodY;
void setup() {
lc.shutdown(0, false); // Wake up the LED matrix
lc.setIntensity(0, 5); // Set brightness level
lc.clearDisplay(0); // Clear the display
pinMode(swPin, INPUT_PULLUP); // Configure joystick button as input
Serial.begin(9600);
// Initialize the snake's position and spawn the first food
resetGame();
}
void loop() {
// Check if the game is over
if (gameOverFlag) {
if (digitalRead(swPin) == LOW) { // Restart the game if the joystick button is pressed
delay(200); // Debounce delay
resetGame();
}
return;
}
// Update direction at a higher frequency
static unsigned long lastDirectionTime = 0;
if (millis() - lastDirectionTime > 50) { // Check direction every 50ms
updateDirection();
lastDirectionTime = millis();
}
// Control the snake's movement
static unsigned long lastMoveTime = 0;
if (millis() - lastMoveTime > moveSpeed) { // Move snake based on its speed
updateSnake();
if (checkCollision()) { // Check for collisions
gameOver();
return;
}
displaySnakeAndFood(); // Refresh the display
lastMoveTime = millis();
}
}
// Reset the game state to initial conditions
void resetGame() {
lc.clearDisplay(0);
snakeLength = 3;
direction = 0;
gameOverFlag = false;
moveSpeed = 300; // Reset movement speed
// Initialize the snake's position
snake[0][0] = 4; snake[0][1] = 2; // Snake head
snake[1][0] = 4; snake[1][1] = 1; // Snake body
snake[2][0] = 4; snake[2][1] = 0; // Snake tail
// Spawn the first food
generateFood();
// Display the initial state
displaySnakeAndFood();
}
// Update the snake's position based on its direction
void updateSnake() {
// Check if the snake eats the food
if (snake[0][0] == foodX && snake[0][1] == foodY) {
snakeLength++; // Increase the snake's length
generateFood(); // Spawn new food
// Increase speed by decreasing delay, but do not go below minimum speed
moveSpeed = max(minSpeed, moveSpeed - 40);
}
// Update each segment's position from tail to head
for (int i = snakeLength - 1; i > 0; i--) {
snake[i][0] = snake[i - 1][0];
snake[i][1] = snake[i - 1][1];
}
// Update the head's position based on the direction
if (direction == 0) snake[0][1] += 1; // Move right
else if (direction == 1) snake[0][0] += 1; // Move down
else if (direction == 2) snake[0][1] -= 1; // Move left
else if (direction == 3) snake[0][0] -= 1; // Move up
}
// Display the snake and the food on the LED matrix
void displaySnakeAndFood() {
lc.clearDisplay(0); // Clear the LED matrix
// Display the snake
for (int i = 0; i < snakeLength; i++) {
// Rotate the coordinates 90 degrees to match LED matrix orientation
int rotatedRow = snake[i][1]; // New row = logical column
int rotatedCol = GRID_SIZE - 1 - snake[i][0]; // New column = GRID_SIZE - 1 - row
if (rotatedRow >= 0 && rotatedRow < GRID_SIZE &&
rotatedCol >= 0 && rotatedCol < GRID_SIZE) { // Ensure the point is within bounds
lc.setLed(0, rotatedRow, rotatedCol, true); // Light up the snake segment
}
}
// Display the food
int rotatedFoodRow = foodY; // New row = logical column
int rotatedFoodCol = GRID_SIZE - 1 - foodX; // New column = GRID_SIZE - 1 - row
lc.setLed(0, rotatedFoodRow, rotatedFoodCol, true);
}
// Update the direction based on joystick input
void updateDirection() {
int xValue = analogRead(xPin);
int yValue = analogRead(yPin);
if (xValue < 300 && direction != 0) direction = 2; // Move left
else if (xValue > 700 && direction != 2) direction = 0; // Move right
else if (yValue < 300 && direction != 1) direction = 3; // Move up
else if (yValue > 700 && direction != 3) direction = 1; // Move down
}
// Check for collisions with boundaries or self
bool checkCollision() {
// Check boundary collision
if (snake[0][0] < 0 || snake[0][0] >= GRID_SIZE ||
snake[0][1] < 0 || snake[0][1] >= GRID_SIZE) {
return true; // Collided with boundary
}
// Check self-collision
for (int i = 1; i < snakeLength; i++) {
if (snake[0][0] == snake[i][0] && snake[0][1] == snake[i][1]) {
return true; // Collided with itself
}
}
return false;
}
// Handle game over state
void gameOver() {
gameOverFlag = true;
// Display an end-game animation
for (int i = 0; i < GRID_SIZE; i++) {
for (int j = 0; j < GRID_SIZE; j++) {
lc.setLed(0, i, j, true); // Light up all LEDs
}
}
delay(2000); // Hold the animation for 2 seconds
lc.clearDisplay(0);
}
// Randomly generate a new food position
void generateFood() {
// Maintain a list of unoccupied positions
int availablePositions[GRID_SIZE * GRID_SIZE][2];
int availableCount = 0;
// Traverse the matrix and find unoccupied positions
for (int x = 0; x < GRID_SIZE; x++) {
for (int y = 0; y < GRID_SIZE; y++) {
bool isOccupied = false;
for (int i = 0; i < snakeLength; i++) {
if (snake[i][0] == x && snake[i][1] == y) {
isOccupied = true;
break;
}
}
if (!isOccupied) {
availablePositions[availableCount][0] = x;
availablePositions[availableCount][1] = y;
availableCount++;
}
}
}
// Randomly select a position from the unoccupied list
if (availableCount > 0) {
int randomIndex = random(0, availableCount);
foodX = availablePositions[randomIndex][0];
foodY = availablePositions[randomIndex][1];
}
}