#include "pitches.h"
#include <EEPROM.h>
#include <TM1637Display.h>
// --- PIN-CONFIG ---
const int ledButtonArray[] = {A4, A5, 9};
const int ledStarsArray[] = {3, 5, 6, A3, 10, 11};
const int ledEightArray[] = {3, 5, 6, 7, 8, A3, 10, 11};
const int ledAllArray[] = {3, 5, 6, 7, 8, A3, 10, 11, A4, A5, 9};
const int buttonPins[] = {4, 0, 13}; // RED=0, BLUE=1, WHITE=2
const int piezoPin = A0;
byte lightSensor = A1;
byte potPin = A2;
byte switchPin = 2;
#define CLK 1
#define DIO 12
TM1637Display display = TM1637Display(CLK, DIO);
// --- GLOBAL VARIABLES & SETTINGS---
int currentStreak = 0;
int activeLED = -1;
int previousActiveLED = -1;
unsigned long startTime = 0;
bool gameInProgress = false;
int lives = 3;
unsigned long lastTick = 0;
int lightSensorValue = 0;
unsigned long whiteButtonPressStart = 0;
bool isPressingWhite = false;
unsigned long blueButtonPressStart = 0;
bool isPressingBlue = false;
unsigned long redButtonPressStart = 0;
bool isPressingRed = false;
int moveHistory[5] = {-1, -1, -1, -1, -1};
int historyIndex = 0;
// DIFFICULTIES
int difficulty = 0;
unsigned long currentTimeLimit;
int highScores[] = {0, 0, 0};
int tempo = 240;
int wholenote = (60000 * 4) / tempo;
// --- SEGMENT-TEXTS ---
const uint8_t allON[] = {0xff, 0xff, 0xff, 0xff};
const uint8_t godJulText[] = { 0x00, 0x00, 0x00, 0x00, 0x3d, 0x5c, 0x5e, 0x00, 0x1e, 0x1c, 0x38, 0x00, 0x00, 0x38, 0x5c, 0x1c, 0x04, 0x6d, 0x00, 0x00, 0x5c, 0x58, 0x74, 0x00, 0x00, 0x77, 0x50, 0x1c, 0x04, 0x5e, 0x00, 0x00, 0x00, 0x00 };
const uint8_t getReadyText[] = { 0x00, 0x00, 0x00, 0x00, 0x3d, 0x79, 0x07, 0x00, 0x50, 0x79, 0x77, 0x5e, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00 };
const uint8_t gameOverText[] = { 0x00, 0x00, 0x00, 0x00, 0x3d, 0x77, 0x37, 0x79, 0x00, 0x3f, 0x1c, 0x79, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00 };
// --- MUSIC ARRAYS ---
int jingleMelody[] = { NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_G5, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_F5, NOTE_F5, NOTE_F5, NOTE_F5, NOTE_F5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_D5, NOTE_D5, NOTE_E5, NOTE_D5, NOTE_G5 };
int jingleDurations[] = { 8, 8, 4, 8, 8, 4, 8, 8, 8, 8, 2, 8, 8, 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 4, 4 };
int jingleNotes = sizeof(jingleMelody) / sizeof(jingleMelody[0]);
const uint16_t interstellarMelody[] = { NOTE_C5, NOTE_D5, NOTE_E5, NOTE_F5, NOTE_E5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_F5, NOTE_G5, NOTE_D5, NOTE_E5, NOTE_F5, NOTE_G5, NOTE_A5, NOTE_G5, NOTE_F5, NOTE_E5, NOTE_F5, NOTE_G5, NOTE_A5, NOTE_B5, NOTE_C6, NOTE_B5, NOTE_A5, NOTE_B5, NOTE_G5, NOTE_A5, NOTE_B5, NOTE_E5, NOTE_B5, NOTE_G5, NOTE_A5, NOTE_G5, NOTE_A5, NOTE_B5, NOTE_D6, NOTE_E5, NOTE_G5, NOTE_A5, NOTE_B5, NOTE_G5, NOTE_A5, NOTE_B5, NOTE_D6, NOTE_E5, NOTE_C6, NOTE_B5, NOTE_C6, NOTE_A5, NOTE_B5, NOTE_C6, NOTE_D6, NOTE_A5, NOTE_C6, NOTE_B5, NOTE_C6, NOTE_A5, NOTE_B5, NOTE_C6, NOTE_D6, NOTE_A5, NOTE_C6, NOTE_B5, NOTE_C6, NOTE_A5, NOTE_E5, NOTE_C6, NOTE_B5, NOTE_C6, NOTE_A5, NOTE_B5, NOTE_C6, NOTE_D6, NOTE_C6, NOTE_B5, NOTE_C6, NOTE_D6, NOTE_E6, NOTE_B5, NOTE_C6, NOTE_D6, NOTE_E6, NOTE_B5, NOTE_C6, NOTE_D6, NOTE_E6, NOTE_B5, NOTE_C6, NOTE_D6, NOTE_E6, NOTE_D6, NOTE_C6, NOTE_B5, NOTE_C6, NOTE_D6, NOTE_E6, NOTE_F6, NOTE_E6, NOTE_D6, NOTE_E6, NOTE_C6, NOTE_D6, NOTE_E6, NOTE_F6, NOTE_G6, NOTE_D6, NOTE_E6, NOTE_F6, NOTE_G6, NOTE_A6, NOTE_G6, NOTE_F6, NOTE_E6, NOTE_F6, NOTE_G6, NOTE_A6, NOTE_B6, NOTE_C7, NOTE_B6, NOTE_A6, NOTE_B6, NOTE_G6, NOTE_A6, NOTE_B6, NOTE_E6, NOTE_B6, NOTE_G6, NOTE_A6, NOTE_G6, NOTE_A6, NOTE_B6, NOTE_D7, NOTE_E6, NOTE_G6, NOTE_A6, NOTE_B6, NOTE_G6, NOTE_A6, NOTE_B6, NOTE_D7, NOTE_E6, NOTE_C7, NOTE_B6, NOTE_C7, NOTE_A6, NOTE_B6, NOTE_C7, NOTE_D7, NOTE_A6, NOTE_C7, NOTE_B6, NOTE_C7, NOTE_A6, NOTE_B6, NOTE_C7, NOTE_D7, NOTE_A6, NOTE_C7, NOTE_B6, NOTE_C7, NOTE_A6, NOTE_E6, NOTE_C7, NOTE_B6, NOTE_C7, NOTE_A6, NOTE_B6, NOTE_C7, NOTE_D7, NOTE_C7, NOTE_B6, NOTE_C7, NOTE_D7, NOTE_E7, NOTE_B6, NOTE_C7, NOTE_D7, NOTE_E7, NOTE_B6, NOTE_C7, NOTE_D7, NOTE_E7, NOTE_B6, NOTE_C7, NOTE_D7, NOTE_E7, NOTE_B6, NOTE_C7, NOTE_D7, NOTE_E7, NOTE_E6, NOTE_E6, NOTE_E6, NOTE_E6, NOTE_E6 };
int interstellarTotalNotes = 198;
int marioMelody[] = { NOTE_E5,8, NOTE_E5,8, REST,8, NOTE_E5,8, REST,8, NOTE_C5,8, NOTE_E5,8, NOTE_G5,4, REST,4, NOTE_G4,8, REST,4, NOTE_C5,-4, NOTE_G4,8, REST,4, NOTE_E4,-4, NOTE_A4,4, NOTE_B4,4, NOTE_AS4,8, NOTE_A4,4, NOTE_G4,-8, NOTE_E5,-8, NOTE_G5,-8, NOTE_A5,4, NOTE_F5,8, NOTE_G5,8, REST,8, NOTE_E5,4,NOTE_C5,8, NOTE_D5,8, NOTE_B4,-4 };
int gameOverMelody[] = { NOTE_C5,-4, NOTE_G4,-4, NOTE_E4,4, NOTE_A4,-8, NOTE_B4,-8, NOTE_A4,-8, NOTE_GS4,-8, NOTE_AS4,-8, NOTE_GS4,-8, NOTE_G4,-12, NOTE_F4, -12, NOTE_G4,1 };
int victoriousMelody[] = { NOTE_A4, -2, NOTE_A4, 4, NOTE_C5,4, NOTE_A4, 4, NOTE_G4, 4, NOTE_F4, -2, NOTE_E4, -2, NOTE_A3, -2, NOTE_A3,4, NOTE_C4,4, NOTE_A3,4, NOTE_G3, 4, NOTE_F3, 4, NOTE_G3, 4, NOTE_F3,4, NOTE_E3,-2 };
int marioNotes = sizeof(marioMelody) / sizeof(marioMelody[0]) / 2;
int gameOverNotes = sizeof(gameOverMelody) / sizeof(gameOverMelody[0]) / 2;
int victoriousMelodyNotes = sizeof(victoriousMelody) / sizeof(victoriousMelody[0])/2;
uint8_t encodeChar(char c) {
switch (toupper(c)) {
case 'A': return 0x77; case 'B': return 0x7C; case 'C': return 0x39;
case 'D': return 0x5E; case 'E': return 0x79; case 'F': return 0x71;
case 'G': return 0x3D; case 'H': return 0x76; case 'I': return 0x06;
case 'L': return 0x38; case 'M': return 0x37; case 'N': return 0x54;
case 'O': return 0x3F; case 'P': return 0x73; case 'Q': return 0x67;
case 'R': return 0x50; case 'S': return 0x6D; case 'T': return 0x78;
case 'U': return 0x3E; case 'V': return 0x1C; case 'W': return 0x1D;
case 'X': return 0x76; case 'Y': return 0x6E; case ' ': return 0x00;
default: return 0x00;
}
}
// --- HELP FUNCTIONS ---
void turnOffAllLEDs() { for (int i = 0; i < 11; i++) digitalWrite(ledAllArray[i], LOW); }
void playBeep(int freq) {
tone(piezoPin, freq, 200);
delay(250);
noTone(piezoPin);
}
void scrollText(const char* text) {
display.clear();
if (strcmp(text, "NOOB") == 0) {
uint8_t t[] = {0x54, 0x5c, 0x5c, 0x7c}; // nOOb
display.setSegments(t);
} else if (strcmp(text, "HARD") == 0) {
uint8_t t[] = {0x76, 0x77, 0x50, 0x5e}; // HArd
display.setSegments(t);
} else if (strcmp(text, "HACKER") == 0) {
uint8_t t[] = {0x76, 0x77, 0x39, 0x79}; // HACE
display.setSegments(t);
}
delay(1000);
}
// --- MELODI-FUNKTIONER (Flyttade HÖGST UPP) ---
void playBonusWinFanfare() {
int notes[] = {NOTE_C5, NOTE_E5, NOTE_G5, NOTE_C6, NOTE_G5, NOTE_C6};
int durations[] = {100, 100, 100, 300, 100, 500};
for (int i = 0; i < 6; i++) {
// TURN ON LIGHTS AT THE LAST TONE
if (i == 6) {
for(int j = 0; j < 11; j++) digitalWrite(ledAllArray[j], HIGH);
}
tone(piezoPin, notes[i], durations[i]);
delay(durations[i] + 20);
noTone(piezoPin);
}
delay(200);
turnOffAllLEDs();
}
void playPapageno() {
int notes[] = {NOTE_G5, NOTE_A5, NOTE_B5, NOTE_C6, NOTE_D6, NOTE_FS5, NOTE_G5};
int durs[] = {80, 80, 80, 80, 80, 300, 400};
for(int repeat = 0; repeat < 2; repeat++){
if (repeat == 1){
delay(200);
}
for (int i = 0; i < 7; i++) {
if (i == 5) {
delay(300);
}
tone(piezoPin, notes[i], durs[i]);
delay(durs[i] * 1.3);
noTone(piezoPin);
}
}
}
void playIndianaJones() {
int notes1[] = {NOTE_E4, NOTE_F4, NOTE_G4, NOTE_C5, NOTE_D4, NOTE_E4, NOTE_F4};
int durs1[] = {8, 16, 8, 2, 8, 16, 2};
for (int i = 0; i < 7; i++) {
int noteDuration = 1500 / durs1[i];
tone(piezoPin, notes1[i], noteDuration);
delay(noteDuration * 1.3);
noTone(piezoPin);
}
delay(300);
int notes2[] = {NOTE_G4, NOTE_A4, NOTE_B4, NOTE_F5, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5};
int durs2[] = {8, 16, 8, 2, 8, 16, 4, 4, 4 };
for (int i = 0; i < 9; i++) {
int noteDuration = 1500 / durs2[i];
tone(piezoPin, notes2[i], noteDuration);
delay(noteDuration * 1.3);
noTone(piezoPin);
}
}
void playDarthVader() {
int notes[] = {NOTE_A2, NOTE_A2, NOTE_A2, NOTE_F2, NOTE_C3, NOTE_A2, NOTE_F2, NOTE_C3, NOTE_A2};
int durs[] = {2, 2, 2, 8, 8, 2, 8, 8, 2};
for (int i = 0; i < 9; i++) {
if (i == 4 || i == 7 ) {
delay(200);
}
int noteDuration = 1000 / durs[i];
tone(piezoPin, notes[i], noteDuration);
delay(noteDuration * 1.3);
noTone(piezoPin);
}
}
// --- BONUS SPEL ---
void playMorseMemory() {
int pattern[] = {1,1, 0, 0, 1,1, 0,0,0, 1,0,1, 0,1,1,0,1,1}; // 1=LONG, 0=SHORT
for(int i=0; i<18; i++) {
int duration = (pattern[i] == 1) ? 300 : 100;
tone(piezoPin, 600, duration);
delay(duration + 50);
}
}
void triggerMemoryBonus() {
if (moveHistory[0] == -1) return;
turnOffAllLEDs();
display.clear();
// 1. START WITH MORSE-CODE
playMorseMemory();
uint8_t memText[] = {0x37, 0x79, 0x54, 0x5c}; // "nEno"
display.setSegments(memText);
delay(500);
// 2. SHOW SEGMENT
for (int i = 0; i < 5; i++) {
digitalWrite(ledButtonArray[moveHistory[i]], HIGH);
tone(piezoPin, 440 + (moveHistory[i] * 100), 200);
delay(400);
digitalWrite(ledButtonArray[moveHistory[i]], LOW);
delay(100);
}
// 3. WAIT FOR ANSWER, PLAY jAWS-THEME
unsigned long bonusStart = millis();
int correctAnswers = 0;
display.showNumberDec(20);
while (millis() - bonusStart < 10000) {
unsigned long elapsed = millis() - bonusStart;
// JAWS-LOGIC
int jawsSpeed = map(elapsed, 0, 10000, 800, 200); // FASTER AND FASTER
if (elapsed % jawsSpeed < 50) {
tone(piezoPin, (elapsed % (jawsSpeed*2) < jawsSpeed) ? 165 : 175, 100); // INTERCHANGING E AND F
}
for (int i = 0; i < 3; i++) {
if (digitalRead(buttonPins[i]) == LOW) {
if (i == moveHistory[correctAnswers]) {
tone(piezoPin, 880, 100);
digitalWrite(ledButtonArray[i], HIGH);
delay(200);
digitalWrite(ledButtonArray[i], LOW);
correctAnswers++;
if (correctAnswers == 5) {
playBonusWinFanfare();
currentStreak += 20;
display.showNumberDec(currentStreak);
delay(1000);
startTime = millis();
return;
}
while(digitalRead(buttonPins[i]) == LOW);
} else {
// FEL SVAR - MEN FÖRLORAR INTE LIV
tone(piezoPin, 150, 500);
delay(500);
return;
}
}
}
}
// TIMES UP!
tone(piezoPin, 150, 500);
delay(500);
}
// --- Huvudspel-funktioner ---
void playSuperMarioMelody() {
int noteIndex = 0;
int textStep = 0;
int totalNotes = marioNotes * 2;
int totalFrames = sizeof(getReadyText) - 3;
while (noteIndex < totalNotes) {
int dur = 150;
// PLAY NEXT NOTE
if (noteIndex < totalNotes) {
int div = marioMelody[noteIndex + 1];
dur = (div > 0) ? (wholenote / div) : (wholenote / abs(div) * 1.5);
tone(piezoPin, marioMelody[noteIndex], dur * 0.9);
noteIndex += 2;
}
// Show text
display.setSegments(getReadyText + (textStep % totalFrames));
textStep++;
delay(dur);
noTone(piezoPin);
}
display.clear();
}
void playVictoriousMelody(){
for (int i = 0; i < victoriousMelodyNotes * 2; i += 2) {
int div = victoriousMelody[i + 1];
int dur = (div > 0) ? (wholenote / div) : (wholenote / abs(div) * 1.5);
tone(piezoPin, victoriousMelody[i], dur * 0.9);
digitalWrite(ledAllArray[random(11)], HIGH); delay(dur); turnOffAllLEDs();
}
}
void playOneUpSound() {
int n[] = {NOTE_E6, NOTE_G6, NOTE_E7, NOTE_C7, NOTE_D7, NOTE_G7};
for(int i=0; i<6; i++) { tone(piezoPin, n[i], 125); delay(130); }
noTone(piezoPin);
}
void playCoinSound() {
tone(piezoPin, NOTE_B5, 100); delay(100);
tone(piezoPin, NOTE_E6, 850); delay(800);
noTone(piezoPin);
}
void flashGameLost() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) digitalWrite(ledButtonArray[j], HIGH); delay(100); turnOffAllLEDs(); delay(100); } }
void pickNextLED() {
int randomIndex;
do { randomIndex = random(3); } while (randomIndex == previousActiveLED);
turnOffAllLEDs();
activeLED = randomIndex;
// SAVES NEW CHOICE
for(int i = 0; i < 4; i++) moveHistory[i] = moveHistory[i+1];
moveHistory[4] = activeLED;
digitalWrite(ledButtonArray[activeLED], HIGH);
previousActiveLED = activeLED;
startTime = millis();
lastTick = millis();
}
void startNewGame() {
gameInProgress = true;
currentStreak = 0;
lives = 3;
display.showNumberDec(0);
for(int i=0; i<8; i++) digitalWrite(ledEightArray[i], HIGH);
playSuperMarioMelody();
for(int i=0; i<8; i++) digitalWrite(ledEightArray[i], LOW);
pickNextLED();
}
void triggerCoinBonanza() {
int iterations = random(6, 11);
int bonanzaButton = buttonPins[activeLED];
int bonanzaLED = ledButtonArray[activeLED];
unsigned long bonanzaTimeLimit = 1200; // 1.2 seconds per coin
// 1. START-HINT
display.setSegments(allON);
tone(piezoPin, NOTE_B5, 100); delay(100);
tone(piezoPin, NOTE_E6, 500);
delay(500);
noTone(piezoPin);
for (int i = 0; i < iterations; i++) {
digitalWrite(bonanzaLED, HIGH);
unsigned long bonanzaStart = millis();
bool hit = false;
// 2. WAIT FOR HIT OR TIME LIMIT
while(millis() - bonanzaStart < bonanzaTimeLimit) {
if(digitalRead(bonanzaButton) == LOW) {
hit = true;
break;
}
}
if (hit) {
tone(piezoPin, NOTE_B5, 100); delay(100);
tone(piezoPin, NOTE_E6, 500);
currentStreak++;
display.showNumberDec(currentStreak);
digitalWrite(bonanzaLED, LOW);
while(digitalRead(bonanzaButton) == LOW) delay(5);
delay(150);
noTone(piezoPin);
}
else {
digitalWrite(bonanzaLED, LOW);
playBeep(200);
delay(300);
break;
}
}
display.clear();
display.showNumberDec(currentStreak);
delay(300);
startTime = millis();
}
void triggerTurboReactor() {
turnOffAllLEDs();
display.clear();
uint8_t turboText[] = {0x78, 0x1c, 0x50, 0x5c}; // "tUrb"
display.setSegments(turboText);
// SIREN AND WARNING LIGHTS
for(int i=0; i<6; i++) {
for(int j=0; j<6; j++) {
digitalWrite(ledStarsArray[j], HIGH);
tone(piezoPin, 600 + (j*100), 50);
delay(50);
digitalWrite(ledStarsArray[j], LOW);
}
}
int reactorHits = 0;
float speedFactor = 2000; // START TIME IN MS
while(reactorHits < 8) {
int r = random(3);
digitalWrite(ledButtonArray[r], HIGH);
tone(piezoPin, 800, 50);
unsigned long startHit = millis();
bool hit = false;
while(millis() - startHit < speedFactor) {
if(digitalRead(buttonPins[r]) == LOW) {
hit = true;
break;
}
}
if(hit) {
reactorHits++;
tone(piezoPin, 1000 + (reactorHits * 100), 100);
digitalWrite(ledButtonArray[r], LOW);
speedFactor *= 0.85;
delay(150);
} else {
tone(piezoPin, 150, 500);
delay(500);
return;
}
}
// VICTORY!
currentStreak += 15;
display.showNumberDec(currentStreak);
for(int i=0; i<5; i++) {
for(int j=0; j<11; j++) digitalWrite(ledAllArray[j], HIGH);
tone(piezoPin, 1000, 100); delay(100);
turnOffAllLEDs(); delay(100);
}
playBonusWinFanfare();
startTime = millis();
}
void handleCorrectHit() {
tone(piezoPin, NOTE_C6, 40);
currentStreak++;
display.showNumberDec(currentStreak);
// ECERY 30TH POINT
if (currentStreak > 0 && currentStreak % 30 == 0) {
if(random(0, 2) == 0) triggerMemoryBonus();
else triggerTurboReactor();
}
//7 % CHANCE FOR COIN BONANZA
else if (random(0, 100) < 7) {
triggerCoinBonanza();
}
pickNextLED();
}
void gameOver() {
turnOffAllLEDs();
if (currentStreak > highScores[difficulty]) {
highScores[difficulty] = currentStreak;
EEPROM.put(difficulty * 4, currentStreak);
playVictoriousMelody();
}
display.clear();
int textStep = 0, noteIndex = 0;
int totalSteps = sizeof(gameOverText) - 3;
while (textStep < totalSteps) {
int dur = 180;
if (noteIndex < gameOverNotes * 2) {
int div = gameOverMelody[noteIndex + 1];
dur = (div > 0) ? (wholenote / div) : (wholenote / abs(div) * 1.5);
tone(piezoPin, gameOverMelody[noteIndex], dur * 0.8);
noteIndex += 2;
}
display.setSegments(&gameOverText[textStep], 4, 0);
textStep++; delay(dur); noTone(piezoPin);
}
flashGameLost();
display.showNumberDec(highScores[difficulty]);
gameInProgress = false;
}
void handleMiss() {
lives--;
turnOffAllLEDs();
if (lives > 0) {
int lostLifeMelody[] = { NOTE_B4, NOTE_F4, NOTE_F4, NOTE_E4, NOTE_D4, NOTE_C4 };
for (int i = 0; i < 6; i++) { tone(piezoPin, lostLifeMelody[i], 150); delay(200); noTone(piezoPin); }
uint8_t livesText[] = { 0x38, 0x00, 0x00, display.encodeDigit(lives) };
display.setSegments(livesText);
delay(1500);
display.showNumberDec(currentStreak);
pickNextLED();
} else { gameOver(); }
}
void runChristmasMode() {
display.clear();
while(digitalRead(buttonPins[2]) == LOW) delay(10);
int currentNote = 0, textStep = 0;
unsigned long lastNoteTime = 0; int currentNoteDuration = 0;
while(true) {
if (digitalRead(buttonPins[2]) == LOW) { noTone(piezoPin); delay(500); break; }
if (millis() - lastNoteTime >= (unsigned long)currentNoteDuration) {
noTone(piezoPin);
int noteDur = 1200 / jingleDurations[currentNote];
currentNoteDuration = noteDur * 1.3;
tone(piezoPin, jingleMelody[currentNote], noteDur);
lastNoteTime = millis();
currentNote = (currentNote + 1) % jingleNotes;
display.setSegments(godJulText + textStep);
textStep = (textStep + 1) % (sizeof(godJulText) - 3);
}
}
}
void runHarryPotterMode() {
Serial.end(); // Frigör Pin 1
display.clear();
turnOffAllLEDs();
// Definitioner av texter
const uint8_t hp1[] = {0x76, 0x77, 0x50, 0x50, 0x6e, 0x00, 0x73, 0x5c, 0x07, 0x07, 0x79, 0x50, 0x00, 0x04, 0x6d, 0x00, 0x76, 0x79, 0x50, 0x79, 0x00, 0x00, 0x00, 0x00};
const uint8_t hp2[] = {0x50, 0x5c, 0x54, 0x00, 0x38, 0x04, 0x70, 0x79, 0x6d, 0x00, 0x73, 0x04, 0x79, 0x00, 0x00, 0x00, 0x00};
const uint8_t hp3[] = {0x6d, 0x79, 0x79, 0x00, 0x07, 0x76, 0x79, 0x00, 0x3d, 0x5c, 0x38, 0x5e, 0x00, 0x6d, 0x54, 0x04, 0x07, 0x39, 0x76, 0x00, 0x00, 0x00, 0x00};
const uint8_t hp4[] = {0x5e, 0x5c, 0x00, 0x54, 0x5c, 0x07, 0x00, 0x3d, 0x5c, 0x00, 0x07, 0x5c, 0x00, 0x6d, 0x38, 0x79, 0x79, 0x73, 0x00, 0x00, 0x00, 0x00};
const uint8_t hp5[] = {0x73, 0x1c, 0x50, 0x79, 0x00, 0x54, 0x77, 0x3d, 0x04, 0x39, 0x00, 0x00, 0x00, 0x00};
const uint8_t hp6[] = {0x07, 0x76, 0x79, 0x00, 0x5e, 0x77, 0x50, 0x70, 0x00, 0x38, 0x5c, 0x50, 0x5e, 0x00, 0x00, 0x00, 0x00};
const uint8_t hp7[] = {0x38, 0x5c, 0x1c, 0x04, 0x6d, 0x00, 0x04, 0x6d, 0x00, 0x77, 0x00, 0x76, 0x79, 0x50, 0x5c, 0x00, 0x00, 0x00, 0x00};
const uint8_t hp8[] = {0x77, 0x50, 0x1c, 0x04, 0x5e, 0x00, 0x04, 0x6d, 0x00, 0x77, 0x00, 0x76, 0x79, 0x50, 0x5c, 0x00, 0x00, 0x00, 0x00};
const uint8_t hp9[] = {0x76, 0x5c, 0x3d, 0x1c, 0x77, 0x50, 0x07, 0x6d, 0x00, 0x04, 0x6d, 0x00, 0x7c, 0x79, 0x6d, 0x07, 0x00, 0x00, 0x00, 0x00};
int choice = random(0, 9);
const uint8_t* currentText;
int textLen;
switch(choice) {
case 0: currentText = hp1; textLen = sizeof(hp1); break;
case 1: currentText = hp2; textLen = sizeof(hp2); break;
case 2: currentText = hp3; textLen = sizeof(hp3); break;
case 3: currentText = hp4; textLen = sizeof(hp4); break;
case 4: currentText = hp5; textLen = sizeof(hp5); break;
case 5: currentText = hp6; textLen = sizeof(hp6); break;
case 6: currentText = hp7; textLen = sizeof(hp7); break;
case 7: currentText = hp8; textLen = sizeof(hp8); break;
case 8: currentText = hp9; textLen = sizeof(hp9); break;
}
#ifndef REST
#define REST 0
#endif
int melody[] = { REST, NOTE_D4, NOTE_G4, NOTE_AS4, NOTE_A4, NOTE_G4, NOTE_D5, NOTE_C5, NOTE_A4, NOTE_G4, NOTE_AS4, NOTE_A4, NOTE_F4, NOTE_GS4, NOTE_D4, NOTE_D4, NOTE_G4, NOTE_AS4, NOTE_A4, NOTE_G4, NOTE_D5, NOTE_F5, NOTE_E5, NOTE_DS5, NOTE_B4, NOTE_DS5, NOTE_D5, NOTE_CS5, NOTE_CS4, NOTE_B4, NOTE_G4, NOTE_AS4, NOTE_D5, NOTE_AS4, NOTE_D5, NOTE_AS4, NOTE_DS5, NOTE_D5, NOTE_CS5, NOTE_A4, NOTE_AS4, NOTE_D5, NOTE_CS5, NOTE_CS4, NOTE_D4, NOTE_D5, REST, NOTE_AS4, NOTE_D5, NOTE_AS4, NOTE_D5, NOTE_AS4, NOTE_F5, NOTE_E5, NOTE_DS5, NOTE_B4, NOTE_DS5, NOTE_D5, NOTE_CS5, NOTE_CS4, NOTE_AS4, NOTE_G4 };
int durations[] = { 2, 4, 4, 8, 4, 2, 4, 2, 2, 4, 8, 4, 2, 4, 1, 4, 4, 8, 4, 2, 4, 2, 4, 2, 4, 4, 8, 4, 2, 4, 1, 4, 2, 4, 2, 4, 2, 4, 2, 4, 4, 8, 4, 2, 4, 1, 4, 4, 2, 4, 2, 4, 2, 4, 2, 4, 4, 8, 4, 2, 4, 1 };
int numNotes = sizeof(durations) / sizeof(int);
int textStep = 0;
for (int note = 0; note < numNotes; note++) {
int duration = 1000 / durations[note];
uint8_t segments[4];
for(int i=0; i<4; i++) segments[i] = currentText[(textStep + i) % textLen];
display.setSegments(segments);
textStep++;
turnOffAllLEDs();
digitalWrite(ledButtonArray[0], HIGH);
if (note % 4 == 0) digitalWrite(ledStarsArray[random(6)], HIGH);
// Starta tonen (men blockera inte koden än)
if (melody[note] > 0) tone(piezoPin, melody[note], duration);
else noTone(piezoPin);
// SMART VÄNTELOOP:
// Istället för delay() kollar vi knappen hela tiden medan vi väntar
unsigned long noteStart = millis();
unsigned long waitTime = duration * 1.10; // Total tid att vänta
while(millis() - noteStart < waitTime) {
// Om man trycker på knappen (men vi ignorerar de första 5 noterna så man hinner släppa vid start)
if (note > 5 && digitalRead(buttonPins[0]) == LOW) {
noTone(piezoPin);
turnOffAllLEDs();
display.clear();
display.showNumberDec(highScores[difficulty]);
return; // AVSLUTA DIREKT
}
// Stäng av tonen när dess tid gått ut (men fortsätt vänta för pausen)
if (millis() - noteStart > duration) {
noTone(piezoPin);
}
}
noTone(piezoPin);
turnOffAllLEDs();
}
display.clear();
display.showNumberDec(highScores[difficulty]);
}
void runInterstellarMode() {
display.clear();
const uint8_t stayText[] = {0x6d, 0x07, 0x77, 0x6e};
display.setSegments(stayText);
for (int thisNote = 0; thisNote < interstellarTotalNotes; thisNote++) {
// START-SPÄRR
if (thisNote > 10) {
if (digitalRead(buttonPins[1]) == LOW) break;
}
int noteDuration = (thisNote < 192) ? 150 : 600;
turnOffAllLEDs();
if (thisNote < 192) digitalWrite(ledButtonArray[1], HIGH);
else { for (int i = 0; i < 11; i++) digitalWrite(ledAllArray[i], HIGH); }
tone(piezoPin, interstellarMelody[thisNote], noteDuration * 0.8);
delay(noteDuration * 0.8);
turnOffAllLEDs();
delay(noteDuration * 0.2);
noTone(piezoPin);
}
}
void selectDifficulty() {
// SÄKERHETS-SPÄRR: Vänta tills man släpper startknappen
while(digitalRead(buttonPins[2]) == LOW) {
delay(10);
}
bool selecting = true;
display.clear();
uint8_t selText[] = {0x6d, 0x79, 0x38, 0x00};
display.setSegments(selText);
delay(500);
while(selecting) {
bool blinkState = (millis() / 200) % 2;
for(int i=0; i<3; i++) digitalWrite(ledButtonArray[i], blinkState ? HIGH : LOW);
// VIT KNAPP (Pin 2) -> NOOB
if (digitalRead(buttonPins[2]) == LOW) {
difficulty = 0; currentTimeLimit = 8000; scrollText("NOOB");
playPapageno();
selecting = false;
}
// RÖD KNAPP (Pin 0) -> HARD
else if (digitalRead(buttonPins[0]) == LOW) {
difficulty = 1; currentTimeLimit = 6000; scrollText("HARD");
playIndianaJones();
selecting = false;
}
// BLÅ KNAPP (Pin 1) -> HACKER
else if (digitalRead(buttonPins[1]) == LOW) {
difficulty = 2; currentTimeLimit = 3000; scrollText("HACKER");
playDarthVader();
selecting = false;
}
}
turnOffAllLEDs();
display.showNumberDec(highScores[difficulty]);
while(digitalRead(buttonPins[0]) == LOW || digitalRead(buttonPins[1]) == LOW || digitalRead(buttonPins[2]) == LOW) {
delay(10);
}
isPressingWhite = false;
whiteButtonPressStart = 0;
delay(200);
}
void pulsatingStarsEffect(int p1, int p2, int p3, int p4, int p5, int p6, int d, int m) {
unsigned long s = millis();
while (millis() - s < d) {
int el = millis() - s;
analogWrite(p1, (sin(el * 0.002) * 127) + 128); analogWrite(p2, (sin(el * 0.002 + 1) * 127) + 128);
analogWrite(p3, (sin(el * 0.002 + 2) * 127) + 128); analogWrite(p4, (sin(el * 0.002 + 3) * 127) + 128);
analogWrite(p5, (sin(el * 0.002 + 4) * 127) + 128); analogWrite(p6, (sin(el * 0.002 + 5) * 127) + 128);
for(int i=0; i<3; i++) if(digitalRead(buttonPins[i]) == LOW) return;
}
turnOffAllLEDs();
}
void potentiometerMusic(){
int p = analogRead(potPin);
int m = map(p, 0, 1023, 1, 11);
int f = map(p, 0, 1023, 500, 1000);
for (int i = 0; i < 11; i++) digitalWrite(ledAllArray[i], i < m ? HIGH : LOW);
tone(piezoPin, f, 100);
}
// --- SETUP ---
void setup() {
for (int i = 0; i < 3; i++) {
pinMode(ledButtonArray[i], OUTPUT);
pinMode(buttonPins[i], INPUT_PULLUP);
}
for (int i = 0; i < 11; i++) {
pinMode(ledAllArray[i], OUTPUT);
digitalWrite(ledAllArray[i], LOW);
}
pinMode(lightSensor, INPUT);
pinMode(potPin, INPUT);
pinMode(switchPin, INPUT_PULLUP);
randomSeed(analogRead(0));
for(int i=0; i<3; i++) {
int stored;
EEPROM.get(i * 4, stored);
highScores[i] = (stored == -1 || stored > 1000) ? 0 : stored;
}
display.setBrightness(5);
display.showNumberDec(highScores[difficulty]);
delay(500);
isPressingWhite = false;
whiteButtonPressStart = 0;
display.showNumberDec(highScores[difficulty]);
// OBS: Ingen Serial.begin() här för att skydda displayen på Pin 1!
}
// --- LOOP ---
void loop() {
lightSensorValue = analogRead(lightSensor);
// 1. STARS EFFECT
if (lightSensorValue > 300 && !gameInProgress) {
pulsatingStarsEffect(3, 5, 6, A3, 10, 11, 5000, 255);
return;
}
// 2. POTENTIOMETER
if (digitalRead(switchPin) == LOW && !gameInProgress) {
potentiometerMusic();
return;
}
// 3. MENY
if (!gameInProgress) {
int pulseValue = (sin(millis() * 0.003) * 127) + 128;
analogWrite(9, pulseValue);
// --- BLÅ KNAPP (INTERSTELLAR) ---
if (digitalRead(buttonPins[1]) == LOW) {
if (!isPressingBlue) { blueButtonPressStart = millis(); isPressingBlue = true; }
unsigned long elapsed = millis() - blueButtonPressStart;
if (elapsed > 1000 && elapsed <= 4000) display.showNumberDec(4 - (elapsed / 1000));
if (elapsed > 4000) {
runInterstellarMode();
isPressingBlue = false;
blueButtonPressStart = 0;
// Inget behov att återställa display här, det sker i slutet av funktionen
}
}
else {
// FIXEN ÄR HÄR: Vi kollar om vi PRECIS släppte knappen
if (isPressingBlue) {
isPressingBlue = false;
blueButtonPressStart = 0;
display.showNumberDec(highScores[difficulty]); // Återställ bara EN gång när man släpper
}
}
// --- RÖD KNAPP (HARRY POTTER) ---
if (digitalRead(buttonPins[0]) == LOW) {
if (!isPressingRed) { redButtonPressStart = millis(); isPressingRed = true; }
unsigned long elapsed = millis() - redButtonPressStart;
if (elapsed > 1000 && elapsed <= 4000) display.showNumberDec(4 - (elapsed / 1000));
if (elapsed > 4000) {
runHarryPotterMode();
isPressingRed = false;
redButtonPressStart = 0;
}
}
else {
// FIXEN ÄR HÄR OCKSÅ
if (isPressingRed) {
isPressingRed = false;
redButtonPressStart = 0;
display.showNumberDec(highScores[difficulty]);
}
}
// --- VIT KNAPP (SVÅRIGHETSGRAD / START) ---
if (digitalRead(buttonPins[2]) == LOW) {
if (!isPressingWhite) { whiteButtonPressStart = millis(); isPressingWhite = true; }
unsigned long elapsed = millis() - whiteButtonPressStart;
if (elapsed > 1000 && elapsed < 2000) display.showNumberDec(3);
else if (elapsed >= 2000 && elapsed < 3000) display.showNumberDec(2);
else if (elapsed >= 3000 && elapsed < 4000) display.showNumberDec(1);
if (elapsed > 4000) {
selectDifficulty();
isPressingWhite = false;
whiteButtonPressStart = 0;
}
}
else {
// OCH HÄR
if (isPressingWhite) {
unsigned long totalTime = millis() - whiteButtonPressStart;
isPressingWhite = false;
whiteButtonPressStart = 0;
if (totalTime < 1000) {
analogWrite(9, 0);
startNewGame();
} else {
display.showNumberDec(highScores[difficulty]);
}
}
}
}
// 4. SPELLÄGE
else {
unsigned long elapsed = millis() - startTime;
if (elapsed >= currentTimeLimit) handleMiss();
else {
unsigned long tickInterval = map(elapsed, 0, currentTimeLimit, 1000, 150);
if (millis() - lastTick >= tickInterval) { tone(piezoPin, NOTE_C5, 30); lastTick = millis(); }
}
for (int i = 0; i < 3; i++) { if (digitalRead(buttonPins[i]) == LOW && i == activeLED) handleCorrectHit(); }
}
}