#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <SPI.h>
// Pin definitions for the TFT display and joystick
#define TFT_CS 10
#define TFT_RST 9
#define TFT_DC 8
#define JOY_X A0
#define JOY_Y A1
#define BUTTON_PIN 2 // Joystick button
// Create TFT display object
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
// Snake properties
int snakeX[100], snakeY[100]; // Snake coordinates
int snakeLength = 1; // Initial snake length
int foodX, foodY; // Food coordinates
int redFoodX, redFoodY; // Red food coordinates for Level 4
bool redFoodActive = false; // Red food status for Level 4
int score = 0; // Player score
int level = 1; // Game level
int direction = 1; // Initial direction (0: up, 1: right, 2: down, 3: left)
bool foodActive = false; // Tracks if food is currently active
// Barrier properties for Level 2
int barrierX, barrierY; // Barrier coordinates for Level 2
const int barrierDigit = 7; // Example digit for barrier (replace with actual last digit)
bool barrierActive = false;
// Timer variables for food disappearance in Level 3
unsigned long foodTimer = 0; // Timer for food
int foodCountdown = 5; // Food countdown in seconds
void setup() {
tft.begin();
tft.setRotation(3);
tft.fillScreen(ILI9341_BLACK);
// Initialize joystick button pin
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Initial snake position
snakeX[0] = tft.width() / 2;
snakeY[0] = tft.height() / 2;
spawnFood(); // Spawn initial food
}
void spawnFood() {
do {
foodX = random(0, tft.width() / 10) * 10;
foodY = random(0, tft.height() / 10) * 10;
} while (level == 2 && isFoodTooCloseToBarrier(foodX, foodY)); // Only check for Level 2
foodActive = true; // Food is active
foodCountdown = 5; // Reset countdown to 5 seconds
foodTimer = millis(); // Record the time when food is spawned
}
void spawnRedFood() {
redFoodX = random(0, tft.width() / 10) * 10;
redFoodY = random(0, tft.height() / 10) * 10;
redFoodActive = true;
}
bool isFoodTooCloseToBarrier(int foodX, int foodY) {
if (barrierActive) {
return (abs(foodX - barrierX) < 10 && abs(foodY - barrierY) < 10);
}
return false;
}
void spawnBarrier() {
barrierX = random(0, tft.width() / 10) * 10;
barrierY = random(0, tft.height() / 10) * 10;
if (snakeX[0] > tft.width() / 4 && snakeX[0] < 3 * tft.width() / 4 &&
snakeY[0] > tft.height() / 4 && snakeY[0] < 3 * tft.height() / 4) {
snakeX[0] = 10; // Move to top-left corner
snakeY[0] = 10;
}
barrierActive = true;
}
void drawSnake() {
for (int i = 0; i < snakeLength; i++) {
tft.fillRect(snakeX[i], snakeY[i], 10, 10, ILI9341_BLUE);
}
}
void clearSnake() {
for (int i = 0; i < snakeLength; i++) {
tft.fillRect(snakeX[i], snakeY[i], 10, 10, ILI9341_BLACK);
}
}
void drawFood() {
if (foodActive) {
tft.fillRect(foodX, foodY, 10, 10, ILI9341_GREEN);
}
}
void drawRedFood() {
if (redFoodActive && level >= 4) {
tft.fillRect(redFoodX, redFoodY, 10, 10, ILI9341_RED);
}
}
void drawBarrier() {
if (barrierActive && level == 2) {
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(3);
tft.setCursor(barrierX, barrierY);
tft.print(barrierDigit);
}
}
void displayScoreAndCountdown() {
tft.setCursor(0, 0);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.print("Score: ");
tft.print(score);
tft.print(" Level: ");
tft.print(level);
if (level == 3 && foodActive) {
tft.setCursor(180, 0);
tft.print("Timer: ");
tft.print(foodCountdown);
}
}
void updateSnake() {
for (int i = snakeLength; i > 0; i--) {
snakeX[i] = snakeX[i - 1];
snakeY[i] = snakeY[i - 1];
}
switch (direction) {
case 0: snakeY[0] -= 10; break; // Up
case 1: snakeX[0] += 10; break; // Right
case 2: snakeY[0] += 10; break; // Down
case 3: snakeX[0] -= 10; break; // Left
}
if (snakeX[0] >= tft.width()) snakeX[0] = 0;
if (snakeX[0] < 0) snakeX[0] = tft.width() - 10;
if (snakeY[0] >= tft.height()) snakeY[0] = 0;
if (snakeY[0] < 0) snakeY[0] = tft.height() - 10;
}
bool checkCollision() {
for (int i = 1; i < snakeLength; i++) {
if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
return true;
}
}
if (level == 2 && barrierActive) {
if (snakeX[0] >= barrierX && snakeX[0] < barrierX + 20 &&
snakeY[0] >= barrierY && snakeY[0] < barrierY + 24) {
return true;
}
}
return false;
}
void updateFoodTimer() {
if (level == 3 && foodActive) {
barrierActive = false;
if (millis() - foodTimer >= 1000) {
foodTimer = millis();
foodCountdown--;
if (foodCountdown <= 0) {
tft.fillRect(foodX, foodY, 10, 10, ILI9341_BLACK);
foodActive = false;
}
}
}
}
void loop() {
clearSnake();
updateSnake();
drawSnake();
if (snakeX[0] == foodX && snakeY[0] == foodY && foodActive) {
snakeLength++;
score++;
spawnFood();
level = score / 2 + 1;
if (level == 2 && !barrierActive) {
spawnBarrier();
}
if (level >= 4 && !redFoodActive && random(0, 5) == 0) {
spawnRedFood();
}
}
if (snakeX[0] == redFoodX && snakeY[0] == redFoodY && redFoodActive) {
snakeLength--;
score--;
redFoodActive = false;
}
if (checkCollision()) {
tft.setCursor(50, 50);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(3);
tft.print("Game Over!");
delay(3000);
while (1);
}
drawFood();
drawRedFood();
displayScoreAndCountdown();
drawBarrier();
updateFoodTimer();
delay(200 - level * 10);
}