#include <U8g2lib.h>
#include <Wire.h>
#include <TM1637Display.h>
#define OLED_RESET -1
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, OLED_RESET, U8X8_PIN_NONE, U8X8_PIN_NONE);
#define JOYSTICK_X A0
#define JOYSTICK_Y A1
#define JOYSTICK_BUTTON 2
#define BUZZER_PIN 3
#define TM1637_CLK 4
#define TM1637_DIO 5
TM1637Display display(TM1637_CLK, TM1637_DIO); // Create TM1637 display object
int snakeX[50], snakeY[50]; // Snake position array
int snakeLength = 3; // Initial length of the snake
int foodX, foodY; // Food position
int direction = 0; // 0: up, 1: right, 2: down, 3: left
int score = 0; // Player's score
int screenWidth = 128;
int screenHeight = 64;
int snakeSize = 4; // Each block of snake is 4x4 pixels
unsigned long lastMoveTime = 0;
unsigned long moveInterval = 200; // Snake movement speed (milliseconds)
void setup() {
// Initialize OLED display
u8g2.begin();
// Initialize joystick
pinMode(JOYSTICK_BUTTON, INPUT_PULLUP);
// Initialize buzzer
pinMode(BUZZER_PIN, OUTPUT);
// Initialize TM1637 7-segment display
display.setBrightness(0x0f); // Set maximum brightness
display.showNumberDec(score); // Display the initial score (0)
// Set initial snake position
snakeX[0] = screenWidth / 2;
snakeY[0] = screenHeight / 2;
// Generate initial food position
generateFood();
}
void loop() {
u8g2.clearBuffer(); // Clear the display buffer
// Read joystick for direction changes
int xVal = analogRead(JOYSTICK_X);
int yVal = analogRead(JOYSTICK_Y);
// Fixing the direction inversion
if (yVal > 600) direction = 0; // Up
if (xVal < 400) direction = 1; // Right
if (yVal < 400) direction = 2; // Down
if (xVal > 600) direction = 3; // Left
// Move the snake at regular intervals
if (millis() - lastMoveTime > moveInterval) {
moveSnake();
lastMoveTime = millis();
}
// Draw snake
for (int i = 0; i < snakeLength; i++) {
u8g2.drawBox(snakeX[i], snakeY[i], snakeSize, snakeSize);
}
// Draw food
u8g2.drawBox(foodX, foodY, snakeSize, snakeSize);
// Check if snake eats the food
if (snakeX[0] == foodX && snakeY[0] == foodY) {
snakeLength++;
score++; // Increment score when food is eaten
display.showNumberDec(score); // Update score on TM1637 display
generateFood();
// Play sound when eating food
playEatSound();
}
// Check for collisions (with self or walls)
if (checkCollision()) {
gameOver();
}
u8g2.sendBuffer(); // Send buffer to OLED
}
void moveSnake() {
// Move snake body
for (int i = snakeLength - 1; i > 0; i--) {
snakeX[i] = snakeX[i - 1];
snakeY[i] = snakeY[i - 1];
}
// Move snake head
switch (direction) {
case 0: snakeY[0] -= snakeSize; break; // Up
case 1: snakeX[0] += snakeSize; break; // Right
case 2: snakeY[0] += snakeSize; break; // Down
case 3: snakeX[0] -= snakeSize; break; // Left
}
}
void generateFood() {
foodX = (random(screenWidth / snakeSize)) * snakeSize;
foodY = (random(screenHeight / snakeSize)) * snakeSize;
}
bool checkCollision() {
// Check wall collision
if (snakeX[0] < 0 || snakeX[0] >= screenWidth || snakeY[0] < 0 || snakeY[0] >= screenHeight) {
return true;
}
// Check self-collision
for (int i = 1; i < snakeLength; i++) {
if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
return true;
}
}
return false;
}
void gameOver() {
// Play game over sound
playGameOverSound();
// Display game over score (hold the score on the display)
display.showNumberDec(score);
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr); // Choose a font
u8g2.drawStr(35, 30, "Game Over!");
u8g2.sendBuffer();
delay(2000); // Pause for 2 seconds
// Reset the game
snakeLength = 3;
snakeX[0] = screenWidth / 2;
snakeY[0] = screenHeight / 2;
score = 0; // Reset the score
display.showNumberDec(score); // Update the 7-segment display
generateFood();
}
// Function to play sound when snake eats food
void playEatSound() {
tone(BUZZER_PIN, 1000, 100); // Play a 1kHz tone for 100ms
delay(100);
noTone(BUZZER_PIN);
}
// Function to play game over sound
void playGameOverSound() {
tone(BUZZER_PIN, 400, 300); // Play a 400Hz tone for 300ms
delay(300);
tone(BUZZER_PIN, 200, 300); // Play a 200Hz tone for 300ms
delay(300);
noTone(BUZZER_PIN);
}