#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <stdlib.h> // Include the standard library for random number generation
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define RIGHT 0
#define UP 1
#define LEFT 2
#define DOWN 3
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
int score = 0;
int level = 1;
int gamespeed = 100;
bool isGameOver = false;
int i;
int dir;
const int buzzerPin = 18;
struct FOOD {
int x;
int y;
int yes;
};
struct SNAKE {
int x[200];
int y[200];
int node;
int dir;
};
FOOD food;
SNAKE snake;
const uint8_t ele[] PROGMEM = {
0xff, //B11110000
0xff, //B10110000
0xff, //B11010000
0xff, //B11110000
0xff, //B11110000
0xff, //B10110000
0xff, //B11010000
0xff, //B11110000
};
void element(int x, int y) {
display.drawBitmap(x, y, ele, 8, 8, 1);
}
void UI() {
display.drawRect(0, 1, 128, 64, WHITE);
display.drawRect(0, 0, 128, 64, WHITE);
}
void buttonUpISR() {
dir = UP;
}
void buttonDownISR() {
dir = DOWN;
}
void buttonLeftISR() {
dir = LEFT;
}
void buttonRightISR() {
dir = RIGHT;
}
void snakeGame() {
switch (snake.dir) {
case RIGHT:
snake.x[0] += 8;
if(snake.x[0] > 120)
snake.x[0] = 0;
break;
case UP:
snake.y[0] -= 8;
if(snake.y[0] < 0)
snake.y[0] = 56;
break;
case LEFT:
snake.x[0] -= 8;
if(snake.x[0] < 0)
snake.x[0] = 120;
break;
case DOWN:
snake.y[0] += 8;
if(snake.y[0] > 56)
snake.y[0] = 0;
break;
}
// Checks for snake's collision with the food
if ((snake.x[0] == food.x) && (snake.y[0] == food.y)) {
snake.x[0] = food.x;
snake.y[0] = food.y;
snake.node++;
food.yes = 1;
score += 2;
level = score / 10 + 1;
tone(buzzerPin, 1000); // Play a tone when food is eaten
delay(100);
noTone(buzzerPin);
generateFood(); // Generate new food
}
// Checks for collision with the body
for (int i = 1; i < snake.node; i++)
if(snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i])
isGameOver = true;
// update snake body
for (i = snake.node - 1; i > 0; i--) {
snake.x[i] = snake.x[i - 1];
snake.y[i] = snake.y[i - 1];
}
}
void key() {
if (dir == DOWN && snake.dir != UP) {
snake.dir = DOWN;
}
if (dir == RIGHT && snake.dir != LEFT) {
snake.dir = RIGHT;
}
if (dir == LEFT && snake.dir != RIGHT) {
snake.dir = LEFT;
}
if (dir == UP && snake.dir != DOWN) {
snake.dir = UP;
}
}
void generateFood() {
// Generate random x and y coordinates for food within the screen bounds
do {
food.x = random(0, 15) * 8;
food.y = random(1, 7) * 8;
} while (isFoodOnSnake());
}
bool isFoodOnSnake() {
for (int i = 0; i < snake.node; i++) {
if (food.x == snake.x[i] && food.y == snake.y[i]) {
return true;
}
}
return false;
}
void displaySnake(){
for (i = 0; i < snake.node; i++)
element(snake.x[i], snake.y[i]);
}
void setup() {
pinMode(15, INPUT_PULLUP);
pinMode(2, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
pinMode(5, INPUT_PULLUP);
pinMode(buzzerPin, OUTPUT); // Set buzzer pin as output
attachInterrupt(digitalPinToInterrupt(5), buttonUpISR, FALLING);
attachInterrupt(digitalPinToInterrupt(2), buttonDownISR, FALLING);
attachInterrupt(digitalPinToInterrupt(15), buttonLeftISR, FALLING);
attachInterrupt(digitalPinToInterrupt(4), buttonRightISR, FALLING);
// Initialize random seed
randomSeed(analogRead(0));
// Initialize food
food = {40, 48, 1};
// Initialize snake
snake.x[0] = 64;
snake.y[0] = 32;
snake.x[1] = 60;
snake.y[1] = 32;
snake.dir = RIGHT;
snake.node = 2;
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
display.display();
delay(2000);
display.clearDisplay();
}
void gameOver() {
digitalWrite(buzzerPin, HIGH); // Turn on buzzer when game is over
delay(500);
digitalWrite(buzzerPin, LOW); // Turn off buzzer after 500ms
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(20, 20);
display.println("Game Over");
display.setCursor(30, 40);
display.print("Score ");
display.println(score);
display.display();
}
void loop() {
while(!isGameOver)
{
display.clearDisplay();
//UI();
displaySnake();
element(food.x, food.y);
display.display();
key();
snakeGame();
delay(gamespeed);
}
gameOver();
}