#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <SPI.h>
#include <EEPROM.h>
// Pins for the display
#define TFT_CS 10
#define TFT_RST 9
#define TFT_DC 8
// Pins for the buttons
const int leftButtonPin = 2;
const int rightButtonPin = 3;
const int downButtonPin = 4;
const int rotateButtonPin = 5;
const int restartButtonPin = 6;
// Pin for the buzzer
const int buzzerPin = 7;
// Game board dimensions
const int boardWidth = 10;
const int boardHeight = 20;
// Colors
#define COLOR_BACKGROUND ILI9341_BLACK
#define COLOR_BLOCK ILI9341_WHITE
#define COLOR_TEXT ILI9341_RED
// TFT display
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
// Game board array
int gameBoard[boardWidth][boardHeight];
// Tetris pieces
const int pieces[7][4][4] = {
{ {0, 0, 0, 0},
{1, 1, 1, 1},
{0, 0, 0, 0},
{0, 0, 0, 0} },
{ {2, 2, 2, 0},
{0, 2, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0} },
{ {3, 3, 3, 0},
{3, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0} },
{ {0, 4, 4, 0},
{4, 4, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0} },
{ {5, 5, 0, 0},
{0, 5, 5, 0},
{0, 0, 0, 0},
{0, 0, 0, 0} },
{ {6, 6, 6, 0},
{0, 0, 6, 0},
{0, 0, 0, 0},
{0, 0, 0, 0} },
{ {7, 7, 0, 0},
{7, 7, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0} }
};
// Current piece position and type
int currentPiece[4][4];
int currentX = 3;
int currentY = 0;
// Game state
bool gameOver = false;
int score = 0;
int highScore = 0;
int level = 1;
int linesCleared = 0;
int gameSpeed = 500;
// EEPROM address for high score
const int highScoreAddress = 0;
void setup() {
tft.begin();
tft.setRotation(1);
tft.fillScreen(COLOR_BACKGROUND);
pinMode(leftButtonPin, INPUT_PULLUP);
pinMode(rightButtonPin, INPUT_PULLUP);
pinMode(downButtonPin, INPUT_PULLUP);
pinMode(rotateButtonPin, INPUT_PULLUP);
pinMode(restartButtonPin, INPUT_PULLUP);
pinMode(buzzerPin, OUTPUT);
// Read high score from EEPROM
EEPROM.get(highScoreAddress, highScore);
resetGame();
}
void loop() {
if (!gameOver) {
handleInput();
updateGame();
drawGameBoard();
drawScore();
delay(gameSpeed);
} else {
drawGameOverScreen();
if (digitalRead(restartButtonPin) == LOW) {
resetGame();
}
}
}
void resetGame() {
memset(gameBoard, 0, sizeof(gameBoard));
spawnPiece();
gameOver = false;
score = 0;
level = 1;
linesCleared = 0;
gameSpeed = 500;
}
void drawGameBoard() {
tft.fillScreen(COLOR_BACKGROUND);
for (int x = 0; x < boardWidth; x++) {
for (int y = 0; y < boardHeight; y++) {
if (gameBoard[x][y] != 0) {
tft.fillRect(x * 12, y * 12, 12, 12, COLOR_BLOCK);
}
}
}
drawPiece(currentPiece, currentX, currentY);
}
void drawPiece(const int piece[4][4], int x, int y) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (piece[i][j] != 0) {
tft.fillRect((x + i) * 12, (y + j) * 12, 12, 12, COLOR_BLOCK);
}
}
}
}
void drawScore() {
tft.setTextColor(COLOR_TEXT, COLOR_BACKGROUND);
tft.setTextSize(2);
tft.setCursor(10, 250);
tft.print("Score: ");
tft.print(score);
tft.setCursor(10, 270);
tft.print("Level: ");
tft.print(level);
tft.setCursor(10, 290);
tft.print("High Score: ");
tft.print(highScore);
}
void handleInput() {
if (digitalRead(leftButtonPin) == LOW) {
movePiece(-1, 0);
}
if (digitalRead(rightButtonPin) == LOW) {
movePiece(1, 0);
}
if (digitalRead(downButtonPin) == LOW) {
movePiece(0, 1);
}
if (digitalRead(rotateButtonPin) == LOW) {
rotatePiece();
}
}
void movePiece(int dx, int dy) {
if (!collision(currentPiece, currentX + dx, currentY + dy)) {
currentX += dx;
currentY += dy;
}
}
void rotatePiece() {
int rotatedPiece[4][4];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
rotatedPiece[j][3 - i] = currentPiece[i][j];
}
}
if (!collision(rotatedPiece, currentX, currentY)) {
memcpy(currentPiece, rotatedPiece, sizeof(rotatedPiece));
}
}
bool collision(const int piece[4][4], int x, int y) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (piece[i][j] != 0) {
int newX = x + i;
int newY = y + j;
if (newX < 0 || newX >= boardWidth || newY < 0 || newY >= boardHeight || gameBoard[newX][newY] != 0) {
return true;
}
}
}
}
return false;
}
void updateGame() {
if (collision(currentPiece, currentX, currentY + 1)) {
mergePiece();
clearLines();
spawnPiece();
if (collision(currentPiece, currentX, currentY)) {
gameOver = true;
tone(buzzerPin, 200, 500); // Game over sound
// Save high score if it's a new one
if (score > highScore) {
highScore = score;
EEPROM.put(highScoreAddress, highScore);
}
}
} else {
currentY++;
}
}
void mergePiece() {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (currentPiece[i][j] != 0) {
gameBoard[currentX + i][currentY + j] = currentPiece[i][j];
}
}
}
}
void clearLines() {
int lines = 0;
for (int y = 0; y < boardHeight; y++) {
bool fullLine = true;
for (int x = 0; x < boardWidth; x++) {
if (gameBoard[x][y] == 0) {
fullLine = false;
break;
}
}
if (fullLine) {
lines++;
for (int ty = y; ty > 0; ty--) {
for (int tx = 0; tx < boardWidth; tx++) {
gameBoard[tx][ty] = gameBoard[tx][ty - 1];
}
}
for (int tx = 0; tx < boardWidth; tx++) {
gameBoard[tx][0] = 0;
}
tone(buzzerPin, 1000, 100); // Line clear sound
}
}
score += lines * 10;
linesCleared += lines;
if (linesCleared >= level * 10) {
level++;
gameSpeed = max(100, gameSpeed - 50); // Increase speed but ensure a minimum delay
}
}
void spawnPiece() {
int pieceType = random(0, 7);
memcpy(currentPiece, pieces[pieceType], sizeof(currentPiece));
currentX = 3;
currentY = 0;
}
void drawGameOverScreen() {
tft.fillScreen(COLOR_BACKGROUND);
tft.setTextColor(COLOR_TEXT, COLOR_BACKGROUND);
tft.setTextSize(3);
tft.setCursor(30, 120);
tft.print("Game Over");
tft.setTextSize(2);
tft.setCursor(30, 160);
tft.print("Score: ");
tft.print(score);
tft.setCursor(30, 190);
tft.print("High Score: ");
tft.print(highScore);
tft.setCursor(30, 220);
tft.print("Press to Restart");
}