/*
Wokwi | projects
T-EterPlay
mplik — 12:42 AM 9/8/26
When you say "I'll just turn on the computer for a moment" and end up
building your own Tetris on Arduino in the middle of the night...
https://wokwi.com/projects/474401524601901057
Rafał Emme
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define MARGIN_LEFT 12
#define BOARD_WIDTH 10
#define BOARD_HEIGHT 20
#define BLOCK_SIZE 4
const int BUZZ_PIN = 13;
const int LED_PIN = 12;
const int BTN_P_PIN = 11;
const int BTN_L_PIN = 10;
const int BTN_D_PIN = 9;
const int BTN_R_PIN = 8;
const int BTN_ROT_PIN = 7;
const byte BLOCKS[7][4][4] = {
{{1, 1, 1, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, // I
{{1, 1, 1, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, // L
{{1, 1, 1, 0}, {0, 0, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, // J
{{1, 1, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, // O
{{0, 1, 1, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, // S
{{1, 1, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, // T
{{1, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}} // Z
};
// globals
bool pauseState = false;
bool oldPauseState = HIGH;
bool rotationReleased = true;
bool board[BOARD_WIDTH][BOARD_HEIGHT] = {false};
int currBlock, currX, currY, rotation;
int nextBlock;
int points = 0;
int highScore = 0;
int lines = 0;
int level = 1;
unsigned long lastDrop = 0;
unsigned long lastMove = 0;
unsigned long interval = 500;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// "downloadBlock"
bool pobierzKlocek(int k, int r, int x, int y) {
int nx = x, ny = y;
if (r == 1) {
nx = y;
ny = 3 - x;
}
else if (r == 2) {
nx = 3 - x;
ny = 3 - y;
}
else if (r == 3) {
nx = 3 - y;
ny = x;
}
return BLOCKS[k][ny][nx];
}
void newBlock() {
currBlock = nextBlock;
nextBlock = random(0, 7);
currX = BOARD_WIDTH / 2 - 2;
currY = 0;
rotation = 0;
if (collision(currX, currY, rotation)) {
if (points > highScore) {
highScore = points;
EEPROM.write(0, highScore);
}
points = 0;
lines = 0; // reset linii
level = 1; // reset poziomu na 1
interval = 500; // reset prędkości
memset(board, 0, sizeof(board));
}
}
bool collision(int nx, int ny, int nr) {
for (int x = 0; x < 4; x++) {
for (int y = 0; y < 4; y++) {
if (pobierzKlocek(currBlock, nr, x, y)) {
int px = nx + x;
int py = ny + y;
if (px < 0 || px >= BOARD_WIDTH || py >= BOARD_HEIGHT) return true;
if (py >= 0 && board[px][py]) return true;
}
}
}
return false;
}
void freezeBlock() {
for (int x = 0; x < 4; x++) {
for (int y = 0; y < 4; y++) {
if (pobierzKlocek(currBlock, rotation, x, y)) {
if (currY + y >= 0) {
board[currX + x][currY + y] = true;
}
}
}
}
checkLines();
newBlock();
}
void checkLines() {
for (int y = BOARD_HEIGHT - 1; y >= 0; y--) {
bool full = true;
for (int x = 0; x < BOARD_WIDTH; x++) {
if (!board[x][y]) {
full = false;
break;
}
}
if (full) {
points += 10;
lines++;
if (lines >= level * 10) {
level++;
if (interval > 100) interval -= 40;
}
tone(BUZZ_PIN, 1500, 200);
digitalWrite(LED_PIN, HIGH);
delay(150);
digitalWrite(LED_PIN, LOW);
for (int ty = y; ty > 0; ty--) {
for (int tx = 0; tx < BOARD_WIDTH; tx++) {
board[tx][ty] = board[tx][ty - 1];
}
}
for (int tx = 0; tx < BOARD_WIDTH; tx++) board[tx][0] = false;
y++;
}
}
}
void setup() {
randomSeed(analogRead(A3));
pinMode(BTN_L_PIN, INPUT_PULLUP);
pinMode(BTN_R_PIN, INPUT_PULLUP);
pinMode(BTN_D_PIN, INPUT_PULLUP);
pinMode(BTN_ROT_PIN, INPUT_PULLUP);
pinMode(BTN_P_PIN, INPUT_PULLUP);
pinMode(BUZZ_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
//digitalWrite(LED_PIN, LOW);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
while (1);
}
display.setRotation(3);
highScore = EEPROM.read(0);
if (highScore == 255) {
highScore = 0;
}
display.clearDisplay();
currBlock = random(0, 7);
nextBlock = random(0, 7);
currX = BOARD_WIDTH / 2 - 2;
currY = 0;
}
void loop() {
bool currPauseState = digitalRead(BTN_P_PIN);
if (currPauseState == LOW && oldPauseState == HIGH) {
pauseState = !pauseState;
delay(200);
}
oldPauseState = currPauseState;
if (!pauseState) {
// =====================================
bool stanLeft = digitalRead(BTN_L_PIN);
bool stanRight = digitalRead(BTN_R_PIN);
bool stanDown = digitalRead(BTN_D_PIN);
bool stanRotate = digitalRead(BTN_ROT_PIN);
// Left / Right (slowed down)
if (millis() - lastMove > 120) {
if (stanLeft == LOW) {
if (!collision(currX - 1, currY, rotation)) currX--;
tone(BUZZ_PIN, 1000, 50);
lastMove = millis();
}
else if (stanRight == LOW) {
if (!collision(currX + 1, currY, rotation)) currX++;
tone(BUZZ_PIN, 1000, 50);
lastMove = millis();
}
}
// accelerating downwards
unsigned long currInterval = interval;
if (stanDown == LOW) {
currInterval = 50;
}
// block rotation (slowed down)
if (stanRotate == LOW) {
if (rotationReleased) {
int nextRotation = (rotation + 1) % 4;
if (!collision(currX, currY, nextRotation)) {
rotation = nextRotation;
}
rotationReleased = false;
}
} else {
rotationReleased = true;
}
// block descent
if (millis() - lastDrop > currInterval) {
if (!collision(currX, currY + 1, rotation)) {
currY++;
} else {
freezeBlock();
}
lastDrop = millis();
}
}
// render graphics
display.clearDisplay();
display.drawRect(MARGIN_LEFT - 1, 0,
BOARD_WIDTH * BLOCK_SIZE + 2,
BOARD_HEIGHT * BLOCK_SIZE + 2,
SSD1306_WHITE);
for (int x = 0; x < BOARD_WIDTH; x++) {
for (int y = 0; y < BOARD_HEIGHT; y++) {
if (board[x][y]) {
display.fillRect(MARGIN_LEFT + x * BLOCK_SIZE,
y * BLOCK_SIZE + 1,
BLOCK_SIZE - 1,
BLOCK_SIZE - 1,
SSD1306_WHITE);
}
}
}
for (int x = 0; x < 4; x++) {
for (int y = 0; y < 4; y++) {
if (pobierzKlocek(currBlock, rotation, x, y)) {
display.fillRect(MARGIN_LEFT + (currX + x) * BLOCK_SIZE,
(currY + y) * BLOCK_SIZE + 1,
BLOCK_SIZE - 1,
BLOCK_SIZE - 1,
SSD1306_WHITE);
}
}
}
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
// display points
display.setCursor(0, 90);
display.print("PTS:");
display.setCursor(30, 90);
display.print(points);
// display high scrore
display.setCursor(0, 100);
display.print("HI:");
display.setCursor(30, 100);
display.print(highScore);
// show next block
display.setCursor(0, 110);
display.print("NEXT:");
for (int x = 0; x < 4; x++) {
for (int y = 0; y < 4; y++) {
if (BLOCKS[nextBlock][y][x]) {
display.fillRect(30 + x * 2, 110 + y * 2, 2, 2, SSD1306_WHITE);
}
}
}
// display level
display.setCursor(0, 120);
display.print("L:");
display.print(level);
if (pauseState) {
display.setCursor(18, 28);
display.print("PAUSE");
}
display.display();
delay(20);
}Rotate
Left <> Right
Drop
Pause