/*Arduino TETRIS on 8x8 Matrix WS2812b
by mircemk, April 2025
*/
#include <FastLED.h>
// LED Matrix configuration
#define LED_PIN 6
#define NUM_LEDS 64
#define BRIGHTNESS 50
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#define MATRIX_WIDTH 8
#define MATRIX_HEIGHT 8
#define BUZZER_PIN 2
// Button pins
#define LEFT_BUTTON_PIN 9
#define RIGHT_BUTTON_PIN 10
#define ROTATE_BUTTON_PIN 8
// Game parameters
#define INITIAL_GAME_SPEED 500 // Milliseconds
#define SPEED_INCREASE 10 // ms to decrease after each piece
#define MIN_GAME_SPEED 150 // Fastest game speed in milliseconds
#define NOTE_B0 31
#define NOTE_C1 33
#define NOTE_CS1 35
#define NOTE_D1 37
#define NOTE_DS1 39
#define NOTE_E1 41
#define NOTE_F1 44
#define NOTE_FS1 46
#define NOTE_G1 49
#define NOTE_GS1 52
#define NOTE_A1 55
#define NOTE_AS1 58
#define NOTE_B1 62
#define NOTE_C2 65
#define NOTE_CS2 69
#define NOTE_D2 73
#define NOTE_DS2 78
#define NOTE_E2 82
#define NOTE_F2 87
#define NOTE_FS2 93
#define NOTE_G2 98
#define NOTE_GS2 104
#define NOTE_A2 110
#define NOTE_AS2 117
#define NOTE_B2 123
#define NOTE_C3 131
#define NOTE_CS3 139
#define NOTE_D3 147
#define NOTE_DS3 156
#define NOTE_E3 165
#define NOTE_F3 175
#define NOTE_FS3 185
#define NOTE_G3 196
#define NOTE_GS3 208
#define NOTE_A3 220
#define NOTE_AS3 233
#define NOTE_B3 247
#define NOTE_C4 262
#define MODE_NORMAL 0
#define MODE_KIDS 1
byte gameMode = MODE_NORMAL;
bool gameOverScreenShown = false;
// Colors
CRGB leds[NUM_LEDS];
#define BLACK CRGB(0, 0, 0)
#define RED CRGB(255, 0, 0)
#define GREEN CRGB(0, 255, 0)
#define BLUE CRGB(0, 0, 255)
#define YELLOW CRGB(255, 255, 0)
#define CYAN CRGB(0, 255, 255)
#define MAGENTA CRGB(255, 0, 255)
#define ORANGE CRGB(255, 165, 0)
// Tetromino shapes
// Each tetromino is defined as 4 cells, each cell having x and y coordinates
typedef struct {
byte shapes[4][4][2]; // [rotation][cell][x,y]
CRGB color;
} Tetromino;
// Tetromino types (I, O, T, S, Z, J, L)
Tetromino tetrominos[7] = {
// I-piece
{
{{{0,0}, {1,0}, {2,0}, {3,0}},
{{0,0}, {0,1}, {0,2}, {0,3}},
{{0,0}, {1,0}, {2,0}, {3,0}},
{{0,0}, {0,1}, {0,2}, {0,3}}},
CYAN
},
// O-piece
{
{{{0,0}, {1,0}, {0,1}, {1,1}},
{{0,0}, {1,0}, {0,1}, {1,1}},
{{0,0}, {1,0}, {0,1}, {1,1}},
{{0,0}, {1,0}, {0,1}, {1,1}}},
YELLOW
},
// T-piece
{
{{{0,0}, {1,0}, {2,0}, {1,1}},
{{1,0}, {0,1}, {1,1}, {1,2}},
{{1,0}, {0,1}, {1,1}, {2,1}},
{{0,0}, {0,1}, {0,2}, {1,1}}},
MAGENTA
},
// S-piece
{
{{{1,0}, {2,0}, {0,1}, {1,1}},
{{0,0}, {0,1}, {1,1}, {1,2}},
{{1,0}, {2,0}, {0,1}, {1,1}},
{{0,0}, {0,1}, {1,1}, {1,2}}},
GREEN
},
// Z-piece
{
{{{0,0}, {1,0}, {1,1}, {2,1}},
{{1,0}, {0,1}, {1,1}, {0,2}},
{{0,0}, {1,0}, {1,1}, {2,1}},
{{1,0}, {0,1}, {1,1}, {0,2}}},
RED
},
// J-piece
{
{{{0,0}, {0,1}, {1,1}, {2,1}},
{{1,0}, {2,0}, {1,1}, {1,2}},
{{0,0}, {1,0}, {2,0}, {2,1}},
{{0,0}, {0,1}, {0,2}, {1,0}}},
BLUE
},
// L-piece
{
{{{2,0}, {0,1}, {1,1}, {2,1}},
{{0,0}, {1,0}, {1,1}, {1,2}},
{{0,0}, {1,0}, {2,0}, {0,1}},
{{0,0}, {0,1}, {0,2}, {1,2}}},
ORANGE
}
};
// simple tetrominos
Tetromino kidstetrominos[7] = {
// Single pixel (red)
{
{{{0,0}, {0,0}, {0,0}, {0,0}},
{{0,0}, {0,0}, {0,0}, {0,0}},
{{0,0}, {0,0}, {0,0}, {0,0}},
{{0,0}, {0,0}, {0,0}, {0,0}}},
RED
},
// Two horizontal pixels (yellow)
{
{{{0,0}, {1,0}, {0,0}, {0,0}},
{{0,0}, {1,0}, {0,0}, {0,0}},
{{0,0}, {1,0}, {0,0}, {0,0}},
{{0,0}, {1,0}, {0,0}, {0,0}}},
YELLOW
},
// Two vertical pixels (blue)
{
{{{0,0}, {0,1}, {0,0}, {0,0}},
{{0,0}, {0,1}, {0,0}, {0,0}},
{{0,0}, {0,1}, {0,0}, {0,0}},
{{0,0}, {0,1}, {0,0}, {0,0}}},
BLUE
},
// Small L shape (green)
{
{{{0,0}, {0,1}, {1,1}, {0,0}},
{{0,0}, {0,1}, {1,1}, {0,0}},
{{0,0}, {0,1}, {1,1}, {0,0}},
{{0,0}, {0,1}, {1,1}, {0,0}}},
GREEN
},
// Small square (magenta)
{
{{{0,0}, {1,0}, {0,1}, {1,1}},
{{0,0}, {1,0}, {0,1}, {1,1}},
{{0,0}, {1,0}, {0,1}, {1,1}},
{{0,0}, {1,0}, {0,1}, {1,1}}},
MAGENTA
},
// Three horizontal pixels (cyan)
{
{{{0,0}, {1,0}, {2,0}, {0,0}},
{{0,0}, {1,0}, {2,0}, {0,0}},
{{0,0}, {1,0}, {2,0}, {0,0}},
{{0,0}, {1,0}, {2,0}, {0,0}}},
CYAN
},
// Diagonal two pixels (orange)
{
{{{0,0}, {1,1}, {0,0}, {0,0}},
{{0,0}, {1,1}, {0,0}, {0,0}},
{{0,0}, {1,1}, {0,0}, {0,0}},
{{0,0}, {1,1}, {0,0}, {0,0}}},
ORANGE
}
};
const byte letters[][8] = {
// M
{B11011,
B11011,
B10101,
B10001,
B10001,
B10001,
B10001,
B00000},
// I
{B11111,
B00100,
B00100,
B00100,
B00100,
B00100,
B11111,
B00000},
// N
{B10001,
B11001,
B11101,
B10111,
B10011,
B10001,
B10001,
B00000},
// T
{B11111,
B00100,
B00100,
B00100,
B00100,
B00100,
B00100,
B00000},
// E
{B11111,
B10000,
B10000,
B11110,
B10000,
B10000,
B11111,
B00000},
// R
{B11110,
B10001,
B10001,
B11110,
B10100,
B10010,
B10001,
B00000},
// S
{B01111,
B10000,
B10000,
B01110,
B00001,
B00001,
B11110,
B00000}
};
const byte digits[10][8] = {
// 0
{B00000000,
B00111000,
B01000100,
B01000100,
B01000100,
B01000100,
B00111000,
B00000000},
// 1
{B00000000,
B00010000,
B00110000,
B00010000,
B00010000,
B00010000,
B00111000,
B00000000},
// 2
{B00000000,
B00111000,
B01000100,
B00001000,
B00010000,
B00100000,
B01111100,
B00000000},
// 3
{B00000000,
B00111000,
B01000100,
B00001000,
B00001100,
B01000100,
B00111000,
B00000000},
// 4
{B00000000,
B00001000,
B00011000,
B00101000,
B01001000,
B01111100,
B00001000,
B00000000},
// 5
{B00000000,
B01111100,
B01000000,
B01111000,
B00000100,
B01000100,
B00111000,
B00000000},
// 6
{B00000000,
B00111000,
B01000000,
B01111000,
B01000100,
B01000100,
B00111000,
B00000000},
// 7
{B00000000,
B01111100,
B00000100,
B00001000,
B00010000,
B00100000,
B00100000,
B00000000},
// 8
{B00000000,
B00111000,
B01000100,
B00111000,
B01000100,
B01000100,
B00111000,
B00000000},
// 9
{B00000000,
B00111000,
B01000100,
B01000100,
B00111100,
B00000100,
B00111000,
B00000000}
};
const byte SMILEY[8] = {
B00111100,
B01000010,
B10100101,
B10000001,
B10100101,
B10011001,
B01000010,
B00111100
};
const Tetromino* currentTetrominoSet;
void displayEndAnimation() {
// Display static smiley once
clearDisplay();
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
if (SMILEY[row] & (1 << (7 - col))) {
leds[getPixelIndex(col, row)] = CRGB::Yellow;
}
}
}
FastLED.show();
// Just wait for button press without redrawing
while (true) {
if (digitalRead(LEFT_BUTTON_PIN) == LOW || digitalRead(RIGHT_BUTTON_PIN) == LOW) {
break;
}
delay(100); // Small delay to check buttons
}
}
void displayScrollingScore(long score) {
// Convert score to string
char scoreStr[7];
sprintf(scoreStr, "%ld", score);
int scoreLen = strlen(scoreStr);
// Display each digit scrolling from right to left
for (int pos = 8; pos >= -scoreLen * 6; pos--) {
clearDisplay();
// Display each digit in its current position
for (int i = 0; i < scoreLen; i++) {
int digitPos = pos + (i * 6); // 6 pixels spacing between digits
if (digitPos < 8 && digitPos > -6) { // Only display if digit is visible
int digit = scoreStr[i] - '0';
displayLetter(digits[digit], digitPos, CRGB(255, 255, 0)); // Orange color
}
}
FastLED.show();
delay(100); // Scroll speed
}
// Pause at the end
delay(500);
}
void playMoveSound() {
// Quick, high-pitched blip (1200 Hz)
tone(BUZZER_PIN, 1200, 30); // Short duration for quick response
}
void playRotateSound() {
// Two-tone ascending sound
tone(BUZZER_PIN, 1000, 25);
delay(25);
tone(BUZZER_PIN, 1500, 25); // Higher pitch for rotation
}
void playLandSound() {
// Descending "bounce" effect
tone(BUZZER_PIN, 800, 100);
delay(50);
tone(BUZZER_PIN, 1200, 80);
delay(30);
tone(BUZZER_PIN, 1500, 100);
}
void playClearLineSound() {
// Cheerful ascending arpeggio
tone(BUZZER_PIN, 800, 50);
delay(50);
tone(BUZZER_PIN, 1000, 50);
delay(50);
tone(BUZZER_PIN, 1200, 50);
delay(50);
tone(BUZZER_PIN, 1500, 100);
}
void playClearLineSound(int linesCleared) {
switch(linesCleared) {
case 1:
// Simple two-tone
tone(BUZZER_PIN, 1000, 50);
delay(50);
tone(BUZZER_PIN, 1500, 100);
break;
case 2:
// Triple ascending
tone(BUZZER_PIN, 1000, 50);
delay(50);
tone(BUZZER_PIN, 1200, 50);
delay(50);
tone(BUZZER_PIN, 1500, 100);
break;
case 3:
// Four-note ascending
tone(BUZZER_PIN, 1000, 50);
delay(50);
tone(BUZZER_PIN, 1200, 50);
delay(50);
tone(BUZZER_PIN, 1500, 50);
delay(50);
tone(BUZZER_PIN, 1800, 100);
break;
case 4:
// Special Tetris fanfare
tone(BUZZER_PIN, 1500, 80);
delay(80);
tone(BUZZER_PIN, 1800, 80);
delay(80);
tone(BUZZER_PIN, 2000, 80);
delay(80);
tone(BUZZER_PIN, 2500, 300); // Final triumphant note
break;
}
}
void playGameOverSound() {
// Playful "game over" tune
tone(BUZZER_PIN, 1500, 100);
delay(100);
tone(BUZZER_PIN, 1200, 100);
delay(100);
tone(BUZZER_PIN, 1000, 100);
delay(100);
tone(BUZZER_PIN, 800, 300);
}
void playStartSound() {
// Cheerful startup fanfare
tone(BUZZER_PIN, 1000, 80);
delay(80);
tone(BUZZER_PIN, 1200, 80);
delay(80);
tone(BUZZER_PIN, 1500, 80);
// delay(80);
// tone(BUZZER_PIN, 2000, 200); // Final triumphant note
}
void playModeSelectorSound() {
// Quick two-tone acknowledgment
tone(BUZZER_PIN, 1200, 50);
delay(50);
tone(BUZZER_PIN, 1500, 100);
}
// Add this function to select game mode
void selectGameMode() {
playStartSound();
bool modeSelected = false;
while (!modeSelected) {
// Split screen in two colors
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
if (x < 4) {
// Left half - Normal mode
leds[getPixelIndex(x, y)] = CRGB(0, 150, 255); // Sky blue
} else {
// Right half - Kids mode
leds[getPixelIndex(x, y)] = CRGB(255, 0, 255); // Magenta
}
}
}
FastLED.show();
// Check buttons
if (digitalRead(LEFT_BUTTON_PIN) == LOW) {
playModeSelectorSound();
gameMode = MODE_NORMAL;
modeSelected = true;
currentTetrominoSet = tetrominos; // Set normal tetrominos
// Clear screen first
clearDisplay();
FastLED.show();
delay(300);
// Smaller 5x6 "N" letter centered on the display
const byte letterN[8] = {
B00000000,
B01001000,
B01101000,
B01011000,
B01001000,
B01001000,
B00000000,
B00000000
};
// Display N in the middle (starting at x=1)
for (int i = 0; i < 3; i++) {
clearDisplay();
displayLetter(letterN, 1, CRGB(0, 150, 255)); // Sky blue
FastLED.show();
delay(200);
clearDisplay();
FastLED.show();
delay(200);
}
}
else if (digitalRead(RIGHT_BUTTON_PIN) == LOW) {
playModeSelectorSound();
gameMode = MODE_KIDS;
modeSelected = true;
currentTetrominoSet = kidstetrominos; // Set kids tetrominos
// Clear screen first
clearDisplay();
FastLED.show();
delay(300);
// Smaller 5x6 "K" letter centered on the display
const byte letterK[8] = {
B00000000,
B01001000,
B01010000,
B01100000,
B01010000,
B01001000,
B00000000,
B00000000
};
// Display K in the middle (starting at x=1)
for (int i = 0; i < 3; i++) {
clearDisplay();
displayLetter(letterK, 1, CRGB(255, 0, 255)); // Magenta
FastLED.show();
delay(200);
clearDisplay();
FastLED.show();
delay(200);
}
}
}
// Clear screen and add delay before starting game
clearDisplay();
FastLED.show();
delay(500);
}
void displayLetter(const byte* letter, int xOffset, CRGB color) {
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
if (xOffset + x >= 0 && xOffset + x < 8) { // Only draw if within display bounds
if (letter[y] & (1 << (7-x))) {
leds[getPixelIndex(xOffset + x, y)] = color;
}
}
}
}
}
// Game state
bool gameBoard[MATRIX_WIDTH][MATRIX_HEIGHT] = {0}; // True if a cell is occupied
CRGB boardColors[MATRIX_WIDTH][MATRIX_HEIGHT]; // Color of each cell
// Current tetromino state
byte currentPiece = 0; // Index of current tetromino
byte currentRotation = 0; // Current rotation (0-3)
int currentX = 3; // X position of top-left corner
int currentY = 0; // Y position of top-left corner
unsigned long lastFallTime = 0;
unsigned long gameSpeed = INITIAL_GAME_SPEED;
boolean gameOver = false;
unsigned int score = 0;
// Button state variables
bool leftPressed = false;
bool rightPressed = false;
bool rotatePressed = false;
unsigned long lastButtonCheckTime = 0;
#define DEBOUNCE_TIME 200 // Debounce time in milliseconds
void setup() {
randomSeed(analogRead(A0) * analogRead(A1)); // Using multiple readings for better randomness
pinMode(BUZZER_PIN, OUTPUT);
// Initialize LED strip
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
FastLED.setBrightness(BRIGHTNESS);
clearDisplay();
// Initialize button pins
pinMode(LEFT_BUTTON_PIN, INPUT_PULLUP);
pinMode(RIGHT_BUTTON_PIN, INPUT_PULLUP);
pinMode(ROTATE_BUTTON_PIN, INPUT_PULLUP);
Serial.begin(9600);
Serial.println("Tetris initialized!");
displaySplashScreen();
selectGameMode(); // Add this line after splash screen
spawnNewPiece();
}
void loop() {
if (gameOver) {
if (!gameOverScreenShown) {
displayGameOver();
gameOverScreenShown = true;
} else if (checkAnyButtonPressed()) {
// Wait for button release to prevent immediate restart
delay(200);
resetGame();
gameOverScreenShown = false;
}
return;
}
checkButtons();
// Move the piece down at regular intervals
if (millis() - lastFallTime > gameSpeed) {
if (!movePieceDown()) {
// Piece has landed
placePiece();
clearLines();
if (!spawnNewPiece()) {
gameOver = true;
}
// Increase game speed
if (gameSpeed > MIN_GAME_SPEED) {
gameSpeed -= SPEED_INCREASE;
}
}
lastFallTime = millis();
}
updateDisplay();
}
void checkButtons() {
// Check buttons with debounce
if (millis() - lastButtonCheckTime > DEBOUNCE_TIME) {
// Check left button
if (digitalRead(LEFT_BUTTON_PIN) == LOW && !leftPressed) {
leftPressed = true;
movePieceLeft();
lastButtonCheckTime = millis();
} else if (digitalRead(LEFT_BUTTON_PIN) == HIGH) {
leftPressed = false;
}
// Check right button
if (digitalRead(RIGHT_BUTTON_PIN) == LOW && !rightPressed) {
rightPressed = true;
movePieceRight();
lastButtonCheckTime = millis();
} else if (digitalRead(RIGHT_BUTTON_PIN) == HIGH) {
rightPressed = false;
}
// Check rotate button
if (digitalRead(ROTATE_BUTTON_PIN) == LOW && !rotatePressed) {
rotatePressed = true;
rotatePiece();
lastButtonCheckTime = millis();
} else if (digitalRead(ROTATE_BUTTON_PIN) == HIGH) {
rotatePressed = false;
}
}
}
bool checkAnyButtonPressed() {
return (digitalRead(LEFT_BUTTON_PIN) == LOW ||
digitalRead(RIGHT_BUTTON_PIN) == LOW ||
digitalRead(ROTATE_BUTTON_PIN) == LOW);
}
// Helper functions for the LED matrix Type
int getPixelIndex(int x, int y) {
// Simple row-major pattern (no zigzag):
return y * MATRIX_WIDTH + x;
// Simple column-major pattern (no zigzag):
// return x * MATRIX_HEIGHT + y;
// Column-major zigzag pattern:
// if (x % 2 == 0) {
// Even columns go top to bottom
// return x * MATRIX_HEIGHT + y;
// } else {
// Odd columns go bottom to top
// return x * MATRIX_HEIGHT + (MATRIX_HEIGHT - 1 - y);
// }
// Flipped row-major zigzag pattern:
// if (y % 2 == 0) {
// Even rows go right to left
// return y * MATRIX_WIDTH + (MATRIX_WIDTH - 1 - x);
// } else {
// Odd rows go left to right
// return y * MATRIX_WIDTH + x;
// }
}
void clearDisplay() {
fill_solid(leds, NUM_LEDS, BLACK);
FastLED.show();
}
void updateDisplay() {
fill_solid(leds, NUM_LEDS, BLACK);
// Draw the fixed blocks
for (int x = 0; x < MATRIX_WIDTH; x++) {
for (int y = 0; y < MATRIX_HEIGHT; y++) {
if (gameBoard[x][y]) {
leds[getPixelIndex(x, y)] = boardColors[x][y];
}
}
}
// Draw the current piece
for (int i = 0; i < 4; i++) {
int x = currentX + currentTetrominoSet[currentPiece].shapes[currentRotation][i][0];
int y = currentY + currentTetrominoSet[currentPiece].shapes[currentRotation][i][1];
if (x >= 0 && x < MATRIX_WIDTH && y >= 0 && y < MATRIX_HEIGHT) {
leds[getPixelIndex(x, y)] = currentTetrominoSet[currentPiece].color;
}
}
FastLED.show();
}
// Game mechanics
bool isValidPosition(int pieceIndex, int rotation, int posX, int posY) {
for (int i = 0; i < 4; i++) {
int x = posX + currentTetrominoSet[pieceIndex].shapes[rotation][i][0];
int y = posY + currentTetrominoSet[pieceIndex].shapes[rotation][i][1];
if (x < 0 || x >= MATRIX_WIDTH || y < 0 || y >= MATRIX_HEIGHT) {
return false;
}
if (y >= 0 && gameBoard[x][y]) {
return false;
}
}
return true;
}
bool movePieceLeft() {
if (isValidPosition(currentPiece, currentRotation, currentX - 1, currentY)) {
currentX--;
playMoveSound();
return true;
}
return false;
}
bool movePieceRight() {
if (isValidPosition(currentPiece, currentRotation, currentX + 1, currentY)) {
currentX++;
playMoveSound();
return true;
}
return false;
}
bool movePieceDown() {
if (isValidPosition(currentPiece, currentRotation, currentX, currentY + 1)) {
currentY++;
return true;
}
return false;
}
bool rotatePiece() {
byte nextRotation = (currentRotation + 1) % 4;
if (isValidPosition(currentPiece, nextRotation, currentX, currentY)) {
currentRotation = nextRotation;
playRotateSound();
return true;
}
// Try wall kick (adjust the position if rotation is blocked by a wall)
// Try moving left
if (isValidPosition(currentPiece, nextRotation, currentX - 1, currentY)) {
currentX--;
currentRotation = nextRotation;
playRotateSound();
return true;
}
// Try moving right
if (isValidPosition(currentPiece, nextRotation, currentX + 1, currentY)) {
currentX++;
currentRotation = nextRotation;
playRotateSound();
return true;
}
return false;
}
void placePiece() {
for (int i = 0; i < 4; i++) {
int x = currentX + currentTetrominoSet[currentPiece].shapes[currentRotation][i][0];
int y = currentY + currentTetrominoSet[currentPiece].shapes[currentRotation][i][1];
if (x >= 0 && x < MATRIX_WIDTH && y >= 0 && y < MATRIX_HEIGHT) {
gameBoard[x][y] = true;
boardColors[x][y] = currentTetrominoSet[currentPiece].color;
playLandSound();
}
}
}
bool spawnNewPiece() {
static byte lastPiece = random(0, 7);
byte newPiece;
do {
newPiece = random(0, 7);
} while (newPiece == lastPiece && random(0, 100) < 70);
lastPiece = newPiece;
currentPiece = newPiece;
// Use different rotation options based on game mode
if (gameMode == MODE_KIDS) {
currentRotation = 0; // Kids mode pieces don't need rotation
} else {
currentRotation = random(0, 4);
}
currentX = (MATRIX_WIDTH / 2) - 1;
currentY = 0;
if (!isValidPosition(currentPiece, currentRotation, currentX, currentY)) {
return false;
}
return true;
}
void clearLines() {
int linesCleared = 0;
for (int y = MATRIX_HEIGHT - 1; y >= 0; y--) {
bool lineIsFull = true;
// Check if the line is full
for (int x = 0; x < MATRIX_WIDTH; x++) {
if (!gameBoard[x][y]) {
lineIsFull = false;
break;
}
}
if (lineIsFull) {
linesCleared++;
// Flash the line
for (int i = 0; i < 3; i++) {
// Flash white
for (int x = 0; x < MATRIX_WIDTH; x++) {
leds[getPixelIndex(x, y)] = CRGB::White;
}
FastLED.show();
delay(50);
// Flash black
for (int x = 0; x < MATRIX_WIDTH; x++) {
leds[getPixelIndex(x, y)] = CRGB::Black;
}
FastLED.show();
delay(50);
}
// Move all lines above this one down
for (int moveY = y; moveY > 0; moveY--) {
for (int x = 0; x < MATRIX_WIDTH; x++) {
gameBoard[x][moveY] = gameBoard[x][moveY - 1];
boardColors[x][moveY] = boardColors[x][moveY - 1];
}
}
// Clear the top line
for (int x = 0; x < MATRIX_WIDTH; x++) {
gameBoard[x][0] = false;
}
// Since the lines have moved down, we need to check this row again
y++;
}
}
// Update score
if (linesCleared > 0) {
playClearLineSound();
// More points for clearing multiple lines at once
score += (linesCleared * linesCleared) * 100;
}
}
void resetGame() {
playStartSound();
// Set the appropriate tetromino set based on game mode
currentTetrominoSet = (gameMode == MODE_KIDS) ? kidstetrominos : tetrominos;
// Clear the display first
clearDisplay();
...
This file has been truncated, please download it to see its full contents.
Comments
Please log in or sign up to comment.