/*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();
  
  // Reset game board
  for (int x = 0; x < MATRIX_WIDTH; x++) {
    for (int y = 0; y < MATRIX_HEIGHT; y++) {
      gameBoard[x][y] = false;
    }
  }
  
  // Reset game parameters
  gameSpeed = INITIAL_GAME_SPEED;
  gameOver = false;
  score = 0;
  gameOverScreenShown = false;
  
  // Show a quick start animation
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB::Green;
    FastLED.show();
    delay(20);
  }
  clearDisplay();
  delay(500);
  
  // Spawn a new piece
  spawnNewPiece();
}

void displaySplashScreen() {
  playStartSound();
  const char text[] = "MINI TETRIS";
  const int textLength = strlen(text);
  const int totalWidth = textLength * 8;  // Each letter is 8 pixels wide
  const CRGB colors[] = {CRGB::Red, CRGB::Green, CRGB::Blue, CRGB::Yellow, 
                        CRGB::Cyan, CRGB::Magenta, CRGB::Orange};
  const int numColors = sizeof(colors) / sizeof(colors[0]);
  
  // Scroll the entire text from right to left
  for (int scroll = 8; scroll >= -totalWidth; scroll--) {
    clearDisplay();
    
    int letterPos = 0;
    for (int i = 0; i < textLength; i++) {
      char c = text[i];
      int xPos = scroll + (i * 8);
      
      // Skip spaces
      if (c == ' ') {
        continue;
      }
      
      // Map characters to array indices
      int letterIndex;
      switch (c) {
        case 'M': letterIndex = 0; break;
        case 'I': letterIndex = 1; break;
        case 'N': letterIndex = 2; break;
        case 'T': letterIndex = 3; break;
        case 'E': letterIndex = 4; break;
        case 'R': letterIndex = 5; break;
        case 'S': letterIndex = 6; break;
        default: continue;
      }
      
      // Display letter with color cycling
      displayLetter(letters[letterIndex], xPos, colors[letterPos % numColors]);
      letterPos++;
    }
    
    FastLED.show();
    delay(60);  // Adjust speed of scrolling
  }
  
  // Final flash effect
  for (int i = 0; i < 3; i++) {
    fill_solid(leds, NUM_LEDS, CRGB::White);
    FastLED.show();
    delay(100);
    clearDisplay();
    delay(100);
  }
}



void displayGameOver() {
  playGameOverSound();
  
  // Flash "Game Over" effect
  for (int i = 0; i < 3; i++) {
    fill_solid(leds, NUM_LEDS, CRGB::Red);
    FastLED.show();
    delay(500);
    clearDisplay();
    FastLED.show();
    delay(500);
  }
  
  // Display final score
  delay(500);
  displayScrollingScore(score);
  
  // Show stable smiley and wait for restart
  displayEndAnimation();
  
  gameOverScreenShown = true;
}