#include <LedControl.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//OLED Score-Anzeige initialisieren
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// LedControl initialisieren (DIN, CLK, CS, # Matrizen)
LedControl lc = LedControl(51, 52, 53, 2);
const int buttonLeftPin = 5;
const int buttonRightPin = 3;
const int buttonRotatePin = 2;
const int buttonDownPin = 8;
const int buttonStartPin = 6;
const int speaker_pin = 7;
bool buttonLeftPressed = false;
bool buttonRightPressed = false;
bool buttonRotatePressed = false;
bool buttonDownPressed = false;
bool music_stop = false;
unsigned long lastMoveTime = 0;
int moveDelay = 500; // Verzögerung für automatisches Fallen
int currentPiece[4][2];
int currentX = 2, currentY = 0;
int rotation = 0;
int piece;
int level = 0;
int lines = 0;
bool started = false;
// Tetris-Steine (4x4-Matrix)
const int tetrominoes[7][4][4][2] = {
{{{2,1}, {1,1}, {2,2}, {1,2}}, {{2,1}, {1,1}, {2,2}, {1,2}}, {{2,1}, {1,1}, {2,2}, {1,2}}, {{2,1}, {1,1}, {2,2}, {1,2}}}, // O-Form
{{{1,0}, {1,1}, {1,2}, {1,3}}, {{0,2}, {1,2}, {2,2}, {3,2}}, {{1,0}, {1,1}, {1,2}, {1,3}}, {{0,2}, {1,2}, {2,2}, {3,2}}}, // I-Form
{{{1,0}, {1,1}, {1,2}, {2,2}}, {{0,1}, {1,1}, {2,1}, {2,0}}, {{0,0}, {1,0}, {1,1}, {1,2}}, {{0,1}, {0,2}, {1,1}, {2,1}}}, // J-Form
{{{1,0}, {1,1}, {1,2}, {0,2}}, {{0,1}, {1,1}, {2,1}, {2,2}}, {{1,0}, {1,1}, {1,2}, {2,0}}, {{0,1}, {1,1}, {2,1}, {0,0}}}, // L-Form
{{{0,2}, {1,1}, {1,2}, {2,1}}, {{0,0}, {0,1}, {1,1}, {1,2}}, {{0,2}, {1,1}, {1,2}, {2,1}}, {{0,0}, {0,1}, {1,1}, {1,2}}}, // Z-Form
{{{0,1}, {1,1}, {1,2}, {2,2}}, {{1,0}, {1,1}, {0,1}, {0,2}}, {{0,1}, {1,1}, {1,2}, {2,2}}, {{1,0}, {1,1}, {0,1}, {0,2}}}, // S-Form
{{{0,1}, {1,1}, {2,1}, {1,2}}, {{1,0}, {1,1}, {1,2}, {2,1}}, {{0,1}, {1,1}, {2,1}, {1,0}}, {{0,1}, {1,0}, {1,1}, {1,2}}} // T-Form
};
bool board[16][8]; // Spielfeld
bool displayBuffer[16][8] = {false};
void setup() {
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { //Alamat OLED
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
randomSeed(analogRead(0));
lc.shutdown(0, false); // Initialisiere Matrix 1
lc.shutdown(1, false); // Initialisiere Matrix 2
lc.setIntensity(0, 8);
lc.setIntensity(1, 8);
lc.clearDisplay(0);
lc.clearDisplay(1);
pinMode(buttonLeftPin, INPUT_PULLUP);
pinMode(buttonRightPin, INPUT_PULLUP);
pinMode(buttonRotatePin, INPUT_PULLUP);
pinMode(buttonDownPin, INPUT_PULLUP);
pinMode(buttonStartPin, INPUT_PULLUP);
pinMode(speaker_pin, OUTPUT);
// Array initialisieren
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 8; x++) {
board[y][x] = false;
}
}
spawnPiece();
}
void loop() {
while(!started)
{
splashScreen();
music();
if(digitalRead(buttonStartPin) == LOW || music_stop)
{
started = true;
lines = 0;
}
}
if(lines < 3)
{
level = 0; //Level 0
}
if(lines > 6)
{
level = 1; //Level 1
}
if(lines > 9)
{
level = 2; //Level 2
}
if(lines > 12)
{
level = 3; //Level 3
}
if(lines > 15)
{
level = 4; //Level 4
}
if(lines > 18)
{
level = 5; //Level 4
}
print();
moveDelay = (5-level)*50+300;
readButtons();
if (millis() - lastMoveTime > moveDelay || buttonDownPressed) {
if(buttonDownPressed){playNote('E', 10);}
if (!movePiece(0, 1)) {
fixPiece();
spawnPiece();
}
lastMoveTime = millis();
}
if (buttonLeftPressed) {
playNote('E', 10);
movePiece(1, 0);
delay(40);
}
if (buttonRightPressed) {
playNote('E', 10);
movePiece(-1, 0);
delay(40);
}
if (buttonRotatePressed) {
playNote('E', 10);
rotatePiece();
delay(40);
}
//Restart
if(digitalRead(buttonStartPin) == LOW)
{
for(int i=0;i<16;i++)
{
for(int j=0;j<8;j++)
{
board[i][j] = 0;
displayBuffer[i][j] = 0;
}
}
spawnPiece();
}
drawBoard();
}
void readButtons() {
buttonLeftPressed = !digitalRead(buttonLeftPin);
buttonRightPressed = !digitalRead(buttonRightPin);
buttonRotatePressed = !digitalRead(buttonRotatePin);
buttonDownPressed = !digitalRead(buttonDownPin);
}
void spawnPiece() {
int pieceIndex = random(0, 7);
rotation = 0;
for (int i = 0; i < 4; i++) {
currentPiece[i][0] = tetrominoes[pieceIndex][rotation][i][0];
currentPiece[i][1] = tetrominoes[pieceIndex][rotation][i][1];
}
piece = pieceIndex;
currentX = 2;
currentY = 0;
if (!canMove(0, 0)) {
// Spiel vorbei
gameOver();
for(int i=0;i<16;i++)
{
for(int j=0;j<8;j++)
{
board[i][j] = 0;
displayBuffer[i][j] = 0;
}
}
/*while (true) {
splashScreen();
if(digitalRead(buttonStartPin) == LOW)
{*/
started = false;
return;
/*}
// theoretische Endlosschleife bis Taste
}*/
}
}
bool canMove(int dx, int dy) {
for (int i = 0; i < 4; i++) {
int newX = currentX + currentPiece[i][0] + dx;
int newY = currentY + currentPiece[i][1] + dy;
if (newX < 0 || newX >= 8 || newY < 0 || newY >= 16) {
return false;
}
if (board[newY][newX]) {
return false;
}
}
return true;
}
bool movePiece(int dx, int dy) {
if (canMove(dx, dy)) {
currentX += dx;
currentY += dy;
return true;
}
return false;
}
void rotatePiece() {
int rotatedPiece[4][2];
for (int i = 0; i < 4; i++) {
//rotatedPiece[i][0] = -currentPiece[i][1];
//rotatedPiece[i][1] = currentPiece[i][0];
rotatedPiece[i][0] = tetrominoes[piece][(rotation+1)%4][i][0];
rotatedPiece[i][1] = tetrominoes[piece][(rotation+1)%4][i][1];
}
int backupX = currentX;
int backupY = currentY;
for (int i = 0; i < 4; i++) {
int newX = currentX + rotatedPiece[i][0];
int newY = currentY + rotatedPiece[i][1];
if (newX < 0) currentX++;
if (newX >= 8) currentX--;
if (newY < 0) currentY++;
if (newY >= 16) currentY--;
}
if (canMove(0, 0)) {
rotation++;
for (int i = 0; i < 4; i++) {
currentPiece[i][0] = rotatedPiece[i][0];
currentPiece[i][1] = rotatedPiece[i][1];
}
} else {
currentX = backupX;
currentY = backupY;
}
}
void fixPiece() {
for (int i = 0; i < 4; i++) {
int newX = currentX + currentPiece[i][0];
int newY = currentY + currentPiece[i][1];
board[newY][newX] = true;
}
checkLines();
}
void checkLines() {
for (int y = 15; y >= 0; y--) {
bool fullLine = true;
for (int x = 0; x < 8; x++) {
if (!board[y][x]) {
fullLine = false;
break;
}
}
if (fullLine) {
lines++;
// Lösche die Linie und verschiebe die oberen Zeilen nach unten
for(int i=0;i<8;i++)
{
if(y>7){
lc.setLed(1,y-8, i, false);
}
else{
lc.setLed(0,y, i, false);
}
delay(30);
}
for (int yy = y; yy > 0; yy--) {
for (int xx = 0; xx < 8; xx++) {
board[yy][xx] = board[yy-1][xx];
}
}
for (int xx = 0; xx < 8; xx++) {
board[0][xx] = false;
}
drawBoard();
y++; // gleiche Linie nochmal prüfen
}
}
}
void drawBoard() {
// Zuerst den Puffer leeren
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 8; x++) {
displayBuffer[y][x] = false;
}
}
// Spielfeld in den Puffer zeichnen
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 8; x++) {
if (board[y][x]) {
displayBuffer[y][x] = true;
}
}
}
// Den aktuellen Tetris-Stein in den Puffer zeichnen
for (int i = 0; i < 4; i++) {
int x = currentX + currentPiece[i][0];
int y = currentY + currentPiece[i][1];
if (y < 8) {
displayBuffer[y][x] = true;
} else {
displayBuffer[y][x] = true;
}
}
// Puffer auf die Matrizen anwenden
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
lc.setLed(0, y, x, displayBuffer[y][x]);
}
}
for (int y = 8; y < 16; y++) {
for (int x = 0; x < 8; x++) {
lc.setLed(1, y - 8, x, displayBuffer[y][x]);
}
}
}
void gameOver()
{
playNote('F', 80);
playNote('A', 60);
playNote('F', 80);
playNote('A', 60);
int up[] =
{
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111
};
int down[] =
{
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111
};
for(int rownum = 8; rownum >= 0; rownum--)
{
lc.setRow(1,rownum,up[rownum]);
delay(75);
}
for(int rownum = 8; rownum >= 0; rownum--)
{
lc.setRow(0,rownum,down[rownum]);
delay(75);
}
delay(500);
}
//================ Anzeigen auf LED-Matrix ================
void splashScreen()
{
int up[] =
{
B11100111, // o o o o o o
B00100010, // o o
B11100010, // o o o
B00100010, // o o
B11100010, // o o o o
B00000000, //
B11100111, // o o o o o o
B10100010 // o o o
};
int down[] =
{
B01100010, // o o o
B10100010, // o o o
B10100010, // o o o
B00000000, //
B11000010, // o o o o
B01000010, // o o
B10000010, // o o
B11100010 // o o o o
};
for(int rownum = 0; rownum < 8; rownum++)
{
lc.setRow(0,rownum,up[rownum]);
lc.setRow(1,rownum,down[rownum]);
}
}
void start_motion()
{
int up_3[] =
{
B00000000,
B01111110,
B01111110,
B01100000,
B01100000,
B01100000,
B01100000,
B01111110
};
int down_3[] =
{
B01111110,
B01100000,
B01100000,
B01100000,
B01100000,
B01111110,
B01111110,
B00000000
};
int up_2[] =
{
B00000000,
B00000000,
B00000000,
B00111100,
B01111110,
B01100110,
B01100000,
B01110000
};
int down_2[] =
{
B00110000,
B00111000,
B00011100,
B00001110,
B01111110,
B01111110,
B00000000,
B00000000
};
int up_1[] =
{
B00000000,
B00000000,
B00000000,
B00000000,
B00011000,
B00011100,
B00011110,
B00011000
};
int down_1[] =
{
B00011000,
B00011000,
B00011000,
B00111100,
B00111100,
B00000000,
B00000000,
B00000000
};
int clear_up[] =
{
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000
};
int clear_down[] =
{
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000
};
for(int rownum = 0; rownum < 8; rownum++)
{
lc.setRow(0,rownum,up_3[rownum]);
lc.setRow(1,rownum,down_3[rownum]);
}
delay(1000);
for(int rownum = 0; rownum < 8; rownum++)
{
lc.setRow(0,rownum,clear_up[rownum]);
lc.setRow(1,rownum,clear_down[rownum]);
}
delay(100);
for(int rownum = 0; rownum < 8; rownum++)
{
lc.setRow(0,rownum,up_2[rownum]);
lc.setRow(1,rownum,down_2[rownum]);
}
delay(1000);
for(int rownum = 0; rownum < 8; rownum++)
{
lc.setRow(0,rownum,clear_up[rownum]);
lc.setRow(1,rownum,clear_down[rownum]);
}
delay(100);
for(int rownum = 0; rownum < 8; rownum++)
{
lc.setRow(0,rownum,up_1[rownum]);
lc.setRow(1,rownum,down_1[rownum]);
}
delay(1000);
for(int rownum = 0; rownum < 8; rownum++)
{
lc.setRow(0,rownum,clear_up[rownum]);
lc.setRow(1,rownum,clear_down[rownum]);
}
delay(100);
}
//================ Score-Anzeige auf OLED ================
void print(){
display.clearDisplay();
display.setTextSize(2); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0,0); // Start at top-left corner
display.print("score: ");
display.println(lines);
display.display();
}
//================ Musik und Töne ================
void playTone(int tone, int duration) {
for (long i = 0; i < duration * 1000L; i += tone * 2) {
digitalWrite(speaker_pin, HIGH);
delayMicroseconds(tone);
digitalWrite(speaker_pin, LOW);
delayMicroseconds(tone);
}
}
void playNote(char note, int duration) {
char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' , 'D', 'E', 'F', 'G', 'J', 'A', 'B'};
int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956, 850, 760, 716, 637, 603, 568, 507 };
for (int i = 0; i < 14; i++) {
if (names[i] == note) {
playTone(tones[i], duration);
}
}
}
void music()
{
int length = 99;
char notes[] = "EbCDCbaaCEDCbbCDECaa DFAGFEECEDCbbCDECaa EbCDCbaaCEDCbbCDECaa DFAGFEECEDCbbCDECaa ECDbCab ECDbCEAJ ";
int beats[] = // Som
{
2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 2, 2, 4, 2,
2, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 2, 2, 4, 1,
2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 2, 2, 4, 2,
2, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 2, 2, 4, 1,
5, 5, 5, 5, 5, 5, 7, 2, 5, 5, 5, 5, 2, 2, 5, 5, 3
};
int tempo = 128; // Tempo
for (int i = 0; i < length; i++)
{
if(digitalRead(buttonStartPin) == LOW)
{
music_stop = true;
return;
}
if (notes[i] == ' ')
{
delay(beats[i] * tempo); //Pausa
}
else
{
if (true){
playNote(notes[i], beats[i] * tempo);
}
else
{
digitalWrite(speaker_pin,LOW);
}
}
delay(tempo / 2);
}
}Loading
ssd1306
ssd1306