/* 1D Pong: FAB LAB CHAOS EDITION - BRIGHTNESS BOOST - Brillo extremo desbloqueado. - Gestión de color optimizada para máxima visibilidad. - Reset Cooperativo y Anti-retención incluidos. */ #define FASTLED_INTERNAL #include // --- Hardware --- #define NUM_LEDS 60 #define DATA_PIN 3 // Definimos el brillo máximo real #define MAX_BRIGHTNESS 255 byte playerBtnPin[] = {5, 8}; byte playerLedPin[] = {6, 9}; // --- Configuración Juego --- int endZoneSize = 6; int currentEndZone = 6; int winRounds = 10; byte playerColor[] = {0, 160}; byte difficulty = 0; // --- Estados --- CRGB leds[NUM_LEDS]; int playerScore[] = {0, 0}; int ballPos, ballDir; float ballSpeed; int gameSpeedMin; int lastHitPos = -1; unsigned long lastHitMillis = 0; unsigned long lastActivity = 0; bool isDemoMode = false; bool activeGame = false; bool lastBtnState[2] = {HIGH, HIGH}; unsigned long resetTimer = 0; int hitCounter = 0; void setup() { delay(1000); // Eliminamos TypicalLEDStrip para ganar potencia bruta de luz FastLED.addLeds(leds, NUM_LEDS); FastLED.setBrightness(MAX_BRIGHTNESS); // Limpieza inicial para asegurar brillo máximo FastLED.clear(true); for(int i=0; i<2; i++){ pinMode(playerBtnPin[i], INPUT_PULLUP); pinMode(playerLedPin[i], OUTPUT); } randomSeed(analogRead(0)); } void loop() { if (isDemoMode) runGame(true); else if (!activeGame) menuSelection(); } void menuSelection() { if (difficulty == 3) { static uint8_t h = 0; fill_rainbow(leds, NUM_LEDS, h++, 5); } else { CRGB diffColors[] = {CRGB::Green, CRGB::Yellow, CRGB::Red}; // Brillo máximo en el menú fill_solid(leds, NUM_LEDS, diffColors[difficulty]); } FastLED.show(); digitalWrite(playerLedPin[0], HIGH); digitalWrite(playerLedPin[1], HIGH); bool currentBtn0 = digitalRead(playerBtnPin[0]); if (currentBtn0 == LOW && lastBtnState[0] == HIGH) { difficulty = (difficulty + 1) % 4; lastActivity = millis(); delay(50); } lastBtnState[0] = currentBtn0; if (digitalRead(playerBtnPin[1]) == LOW) { digitalWrite(playerLedPin[0], LOW); digitalWrite(playerLedPin[1], LOW); isDemoMode = false; startGame(); } } void startGame() { activeGame = true; playerScore[0] = 0; playerScore[1] = 0; hitCounter = 0; runGame(isDemoMode); } void drawField() { // Usamos fill_solid negro en lugar de clear para asegurar refresco limpio fill_solid(leds, NUM_LEDS, CRGB::Black); if (difficulty == 3 && (hitCounter % 6 == 0) && hitCounter > 0) { static uint8_t h = 0; fill_rainbow(leds, NUM_LEDS, h++, 10); } // Franjas: Subimos el valor (V) a 150 para que se vean mejor for (int i = 0; i < currentEndZone; i++) { leds[i] = CHSV(160, 255, 150); leds[NUM_LEDS - 1 - i] = CHSV(160, 255, 150); } // Marcador: Valor al máximo (255) for (int i = 0; i < playerScore[0]; i++) leds[NUM_LEDS/2 - 1 - i] = CHSV(playerColor[0], 255, 255); for (int i = 0; i < playerScore[1]; i++) leds[NUM_LEDS/2 + i] = CHSV(playerColor[1], 255, 255); if (lastHitPos != -1 && millis() - lastHitMillis < 500) { leds[lastHitPos] = CRGB::Lime; // Usamos Lime que es más brillante que Green puro } } void runGame(bool demo) { int baseSpeeds[] = {40, 28, 15, 25}; gameSpeedMin = baseSpeeds[difficulty]; int starter = (random(100) < 50) ? 0 : 1; while (activeGame) { drawField(); FastLED.show(); ballPos = (starter == 0) ? 0 : NUM_LEDS - 1; ballDir = (starter == 0) ? 1 : -1; ballSpeed = gameSpeedMin; currentEndZone = endZoneSize; if (!demo) { digitalWrite(playerLedPin[starter], HIGH); while (digitalRead(playerBtnPin[starter]) == HIGH) { checkCoopReset(); if (!activeGame) return; } digitalWrite(playerLedPin[starter], LOW); delay(200); } while (true) { static unsigned long prevMove = 0; if (millis() - prevMove > (unsigned long)ballSpeed) { prevMove = millis(); drawField(); ballPos += ballDir; if (ballPos < 0 || ballPos >= NUM_LEDS) { if (ballPos < 0) { playerScore[1]++; starter = 1; } else { playerScore[0]++; starter = 0; } break; } // Bola siempre al máximo de blanco if (difficulty == 3 && random(100) < 20) leds[ballPos] = CHSV(millis()/2, 255, 255); else leds[ballPos] = CRGB::White; FastLED.show(); if (difficulty == 3 && random(1000) < 5 && ballPos > 10 && ballPos < 50) { delay(500); ballSpeed = ballSpeed * 0.6; } } if (demo) handleDemoAI(); else { handleInputs(); checkCoopReset(); } if (!activeGame) return; } if (playerScore[0] >= winRounds || playerScore[1] >= winRounds) { activeGame = false; victoryAnimation(playerScore[0] > playerScore[1] ? 0 : 1); } delay(500); } } void handleInputs() { for (int p = 0; p < 2; p++) { bool currentBtn = digitalRead(playerBtnPin[p]); if (currentBtn == LOW && lastBtnState[p] == HIGH) { lastHitPos = ballPos; lastHitMillis = millis(); bool isBallInMyZone = (p == 0) ? (ballPos < currentEndZone && ballDir == -1) : (ballPos >= NUM_LEDS - currentEndZone && ballDir == 1); if (isBallInMyZone) { hitCounter++; if (difficulty == 3) currentEndZone = random(1, 7); triggerHit(p); } } lastBtnState[p] = currentBtn; } } void triggerHit(int p) { int dist = (p == 0) ? ballPos : (NUM_LEDS - 1 - ballPos); ballDir *= -1; float multiplier = 1.0; if (dist == 0) multiplier = random(20, 50) / 100.0; else if (dist <= 4) multiplier = 1.0 - ((5 - dist) * 0.10); ballSpeed = max(ballSpeed * multiplier, 4.0f); lastActivity = millis(); } void checkCoopReset() { if (digitalRead(playerBtnPin[0]) == LOW && digitalRead(playerBtnPin[1]) == LOW) { if (resetTimer == 0) resetTimer = millis(); if (millis() - resetTimer > 3000) { activeGame = false; isDemoMode = false; resetTimer = 0; fill_solid(leds, NUM_LEDS, CRGB::White); FastLED.show(); delay(500); FastLED.clear(true); } } else { resetTimer = 0; } } void handleDemoAI() { int p = (ballDir == -1) ? 0 : 1; if ((p == 0 && ballPos == 0) || (p == 1 && ballPos == NUM_LEDS-1)) { if (random(100) > 15) { lastHitPos = ballPos; lastHitMillis = millis(); triggerHit(p); } } } void victoryAnimation(int winner) { for(int i=0; i<5; i++) { fill_solid(leds, NUM_LEDS, (winner == 0) ? CRGB::Red : CRGB::Blue); FastLED.show(); delay(300); fill_solid(leds, NUM_LEDS, CRGB::Black); FastLED.show(); delay(300); } } void exitDemo() { isDemoMode = false; activeGame = false; lastActivity = millis(); delay(500); }