/* Att göra / historik
- Piezo på A0
- Knapparnas LED: A4, A5, 9
- Stjärn-LEDs: 3, 5, 6, A3, 10, 11
- "Eight"-array: 3, 5, 6, 7, 8, A3, 10, 11
- Display på CLK=1, DIO=12
- Vit knapp startar spelet
*/
#include "pitches.h"
#include <EEPROM.h>
#include <TM1637Display.h>
#include <math.h>
// ======================= PIN-KONSTANTER =======================
const int ledButtonArray[] = {A4, A5, 9}; // LED vid knappar
const int ledStarsArray[] = {3, 5, 6, A3, 10, 11}; // "stjärn-LEDs" (blink i julläge)
const int ledEightArray[] = {3, 5, 6, 7, 8, A3, 10, 11};
const int ledAllArray[] = {3, 5, 6, 7, 8, A3, 10, 12, A4, A5, A3};
const int buttonPins[] = {4, 0, 13}; // 0, 1, 2 index
const int piezoPin = A0;
byte lightSensor = A1;
byte potPin = A2;
byte switchPin = 2;
#define CLK 1
#define DIO 12
// ======================= EXTERNER FRÅN SONGTAB =======================
// Dessa är definierade i din andra fil (songtab)
extern int* christmasSongs[];
extern int christmasSongLengths[];
extern const int numChristmasSongs;
extern int hesAPirateMelody[];
extern int hesAPirateNotes;
// ======================= JULLÄGE-STATE =======================
bool christmasMode = false; // Om vi är i julläge
bool christmasMelodyPlaying = false; // Om en jullåt spelas just nu
int currentChristmasSongIndex = 0; // Vilken jullåt som är aktiv
int christmasNoteIndex = 0; // Index i melodins array
unsigned long christmasLastNoteChange = 0;
unsigned long christmasCurrentNoteDuration = 0;
// För att upptäcka 4 sekunders knapp-hållning
unsigned long buttonPressStartTime[3] = {0, 0, 0};
// ======================= DISPLAY =======================
TM1637Display display = TM1637Display(CLK, DIO);
const uint8_t allON[] = {0xff, 0xff, 0xff, 0xff};
const uint8_t allOFF[] = {0x00, 0x00, 0x00, 0x00};
// ======================= SPEL-STATE =======================
int initialPitch = 500;
int pitchIncrement = 50;
int currentPitch = initialPitch;
int lightSensorValue = 0;
int potVal = 0;
int mappedPotVal = 0;
int activeLED = -1;
int previousActiveLED = -1;
unsigned long startTime = 0;
const unsigned long timeLimit = 6000; // 6 sek
bool gameInProgress = false;
bool gameLost = false;
int gameOverFlashCount = 0;
bool melodyPlayed = false; // Super Mario startmelodi spelad?
int highScore = 0;
int currentStreak = 0;
bool victoriousMelodyPlayed = false;
int tempo = 200;
int wholenote = (60000 * 4) / tempo;
int divider = 0, noteDuration = 0;
// ======================= MELODIES (MARIO m.m.) =======================
int melody[] = {
// Super Mario Bros theme (kort bit)
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,
NOTE_A5, -4, NOTE_A5, 8, NOTE_C6,8, NOTE_A5, 8,
NOTE_G5, 8, NOTE_F5, 2, NOTE_E5, -4,
NOTE_A6, -4, NOTE_A6,8, NOTE_C7,8, NOTE_A6,8,
NOTE_G6, 8, NOTE_F6, 8, NOTE_G6, 8, NOTE_F6,8, NOTE_E6, 8,
NOTE_F6, 8, NOTE_E6, 8, NOTE_F6, 8, NOTE_E6, 8, NOTE_F6, 8,
NOTE_E6, 8, NOTE_F6, 8, NOTE_E6, 8, NOTE_F6, 8,
NOTE_E6, 8, NOTE_F6, 8, NOTE_E6, 8, NOTE_F6, 8,
NOTE_E6, 8, NOTE_F6, 8, NOTE_E6, 1,
};
int gameOverNotes = sizeof(gameOverMelody) / sizeof(gameOverMelody[0]) / 2;
int victoriousMelodyNotes = sizeof(victoriousMelody) / sizeof(victoriousMelody[0]) / 2;
int melodyNotes = sizeof(melody) / sizeof(melody[0]) / 2;
// ======================= FUNKTIONSDEKLARATIONER =======================
void waitForGameStart();
void startNewGame();
void gameOver();
void flashGameLost();
void flashAllLEDs();
void turnOffAllLEDs();
void playVictoriousMelody();
void playGameOverMelody();
void playSuperMarioMelody();
void pulsatingStarsEffect(int ledPin1, int ledPin2, int ledPin3, int ledPin4, int ledPin5, int ledPin6, int pulseDuration, int maxBrightness);
void playBeep(int pitch);
void potentiometerMusic();
void fadeLEDInAndOut(int pin, int duration);
// Jul-lägesfunktioner
void checkChristmasMode();
void enterChristmasMode();
void startChristmasMelody(int index);
void stopChristmasMelody();
void updateChristmasMelody();
void playHesAPirate();
// ======================= SETUP =======================
void setup() {
// Knappar + knapp-LEDs
for (int i = 0; i < 3; i++) {
pinMode(ledButtonArray[i], OUTPUT);
pinMode(buttonPins[i], INPUT_PULLUP);
}
// Alla övriga LED-pinnar
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); // aktiv låg
randomSeed(analogRead(0)); // Random-seed
int storedHighScore;
EEPROM.get(0, storedHighScore);
highScore = storedHighScore;
// Display
display.setBrightness(5);
display.setSegments(allON);
delay(2000);
display.clear();
display.showNumberDec(0);
}
// ======================= LOOP =======================
void loop() {
// Ljussensor -> mys-läge med puls
lightSensorValue = analogRead(lightSensor);
if (lightSensorValue <= 300) {
// Pulsande stjärn-effekt (blockerande, men som tidigare)
pulsatingStarsEffect(3, 5, 6, 9, 10, 11, 5000, 255);
}
// Pot-läge när switchPin är LOW
while (digitalRead(switchPin) == LOW) {
potentiometerMusic();
}
// Kolla om vi ska in i julläge (håll in valfri knapp i 4 sek i viloläge)
checkChristmasMode();
// Om inget spel pågår, vänta på start (vit knapp)
if (!gameInProgress) {
waitForGameStart();
}
unsigned long currentTime = millis();
// ================= SPEL-LÄGE =================
if (gameInProgress) {
// Timeout?
if (activeLED != -1 && (currentTime - startTime >= timeLimit)) {
gameOver();
}
// Knappar
for (int i = 0; i < 3; i++) {
if (digitalRead(buttonPins[i]) == LOW) {
// Enda vi bryr oss om i spel-läget: om rätt knapp trycks
if (i == activeLED) {
// Släck LED vid knappen
digitalWrite(ledButtonArray[i], LOW);
// Spela kort beep
playBeep(currentPitch);
currentPitch += pitchIncrement;
// Uppdatera streak + display
currentStreak++;
display.showNumberDec(currentStreak);
// Inget aktivt LED längre
activeLED = -1;
// Om julläge: byt jullåt
if (christmasMode) {
if (christmasMelodyPlaying) {
stopChristmasMelody();
}
int newIndex = currentChristmasSongIndex;
if (numChristmasSongs > 1) {
while (newIndex == currentChristmasSongIndex) {
newIndex = random(numChristmasSongs);
}
}
startChristmasMelody(newIndex);
}
// Rundan klar
gameInProgress = false;
delay(30); // Liten debouncing
}
}
}
}
// Uppdatera jullåts-spelning (non-blocking)
updateChristmasMelody();
}
// ======================= SPELLOGIK =======================
void waitForGameStart() {
if (gameLost) {
gameLost = false;
gameOverFlashCount = 0;
turnOffAllLEDs();
currentPitch = initialPitch;
}
// Starta spel med vit knapp (index 2 i buttonPins)
if (digitalRead(buttonPins[2]) == LOW) {
startNewGame();
melodyPlayed = true;
victoriousMelodyPlayed = false;
}
}
void startNewGame() {
// Tänd alla "eight"-LEDs under aktivt spel
for (int i = 0; i < 8; i++) {
digitalWrite(ledEightArray[i], HIGH);
}
// Spela Super Mario endast i normalt läge
if (!melodyPlayed && !christmasMode) {
playSuperMarioMelody();
melodyPlayed = true;
}
turnOffAllLEDs();
startTime = millis();
gameInProgress = true;
// Slumpa ny LED (inte samma som förra)
int randomIndex;
do {
randomIndex = random(3);
} while (randomIndex == previousActiveLED);
activeLED = randomIndex;
digitalWrite(ledButtonArray[randomIndex], HIGH);
previousActiveLED = activeLED;
}
void gameOver() {
if (gameInProgress) {
if (currentStreak > highScore) {
highScore = currentStreak;
EEPROM.put(0, highScore);
if (!victoriousMelodyPlayed && !christmasMode) {
playVictoriousMelody();
victoriousMelodyPlayed = true;
}
}
}
if (!gameLost) {
gameLost = true;
if (christmasMode) {
// Julläge -> Pirates
stopChristmasMelody();
playHesAPirate();
christmasMode = false; // Tillbaka till normal-läge
} else {
playGameOverMelody();
}
for (int i = 0; i < 8; i++) {
digitalWrite(ledEightArray[i], LOW);
}
flashGameLost();
}
currentStreak = 0;
melodyPlayed = false;
gameInProgress = false;
// Visa highscore på displayen
display.showNumberDec(highScore);
}
void flashGameLost() {
for (int i = 0; i < 5; i++) {
flashAllLEDs();
delay(100);
turnOffAllLEDs();
delay(100);
}
}
void flashAllLEDs() {
for (int i = 0; i < 3; i++) {
digitalWrite(ledButtonArray[i], HIGH);
}
}
void turnOffAllLEDs() {
for (int i = 0; i < 3; i++) {
digitalWrite(ledButtonArray[i], LOW);
}
}
// ======================= MELODIFUNKTIONER =======================
void playVictoriousMelody() {
int blinkDuration = 20;
for (int thisNote = 0; thisNote < victoriousMelodyNotes * 2; thisNote += 2) {
divider = victoriousMelody[thisNote + 1];
if (divider > 0) {
noteDuration = wholenote / divider;
} else if (divider < 0) {
noteDuration = wholenote / abs(divider);
noteDuration = (int)(noteDuration * 1.5);
}
tone(piezoPin, victoriousMelody[thisNote], (unsigned long)(noteDuration * 0.9));
for (int ledIndex = 0; ledIndex < 11; ledIndex++) {
digitalWrite(ledAllArray[ledIndex], HIGH);
delay(blinkDuration);
digitalWrite(ledAllArray[ledIndex], LOW);
}
delay(noteDuration - 3 * blinkDuration);
noTone(piezoPin);
}
}
void playGameOverMelody() {
for (int thisNote = 0; thisNote < gameOverNotes * 2; thisNote += 2) {
divider = gameOverMelody[thisNote + 1];
if (divider > 0) {
noteDuration = wholenote / divider;
} else if (divider < 0) {
noteDuration = wholenote / abs(divider);
noteDuration = (int)(noteDuration * 1.5);
}
tone(piezoPin, gameOverMelody[thisNote], (unsigned long)(noteDuration * 0.9));
delay(noteDuration);
noTone(piezoPin);
}
}
void playSuperMarioMelody() {
for (int thisNote = 0; thisNote < melodyNotes * 2; thisNote += 2) {
divider = melody[thisNote + 1];
if (divider > 0) {
noteDuration = wholenote / divider;
} else if (divider < 0) {
noteDuration = wholenote / abs(divider);
noteDuration = (int)(noteDuration * 1.5);
}
tone(piezoPin, melody[thisNote], (unsigned long)(noteDuration * 0.9));
delay(noteDuration);
noTone(piezoPin);
}
}
// ======================= LJUS / POT / BEEP =======================
void pulsatingStarsEffect(int ledPin1, int ledPin2, int ledPin3, int ledPin4, int ledPin5, int ledPin6, int pulseDuration, int maxBrightness) {
unsigned long start = millis();
while (millis() - start < (unsigned long)pulseDuration) {
unsigned long elapsedTime = millis() - start;
int brightness1 = (int)(sin(elapsedTime * 0.001) * maxBrightness + maxBrightness);
int brightness2 = (int)(sin(elapsedTime * 0.001 + PI / 3) * maxBrightness + maxBrightness);
int brightness3 = (int)(sin(elapsedTime * 0.001 + 2 * PI / 3) * maxBrightness + maxBrightness);
int brightness4 = (int)(sin(elapsedTime * 0.001 + PI) * maxBrightness + maxBrightness);
int brightness5 = (int)(sin(elapsedTime * 0.001 + 4 * PI / 3) * maxBrightness + maxBrightness);
int brightness6 = (int)(sin(elapsedTime * 0.001 + 5 * PI / 3) * maxBrightness + maxBrightness);
analogWrite(ledPin1, brightness1);
analogWrite(ledPin2, brightness2);
analogWrite(ledPin3, brightness3);
analogWrite(ledPin4, brightness4);
analogWrite(ledPin5, brightness5);
analogWrite(ledPin6, brightness6);
}
analogWrite(ledPin1, 0);
analogWrite(ledPin2, 0);
analogWrite(ledPin3, 0);
analogWrite(ledPin4, 0);
analogWrite(ledPin5, 0);
analogWrite(ledPin6, 0);
}
void playBeep(int pitch) {
tone(piezoPin, pitch, 100);
delay(100);
noTone(piezoPin);
}
void potentiometerMusic() {
potVal = analogRead(potPin);
mappedPotVal = map(potVal, 0, 1023, 1, 11);
int pitch = map(potVal, 0, 1023, initialPitch, initialPitch + pitchIncrement * 5);
for (int i = 0; i < 11; i++) {
digitalWrite(ledAllArray[i], i < mappedPotVal ? HIGH : LOW);
}
if (mappedPotVal >= 0) {
tone(piezoPin, pitch, 700);
} else {
noTone(piezoPin);
}
}
void fadeLEDInAndOut(int pin, int duration) {
int fadeTime = duration;
for (int i = 0; i <= 255; i++) {
analogWrite(pin, i);
delay(fadeTime / 255);
}
for (int i = 255; i >= 0; i--) {
analogWrite(pin, i);
delay(fadeTime / 255);
}
}
// ===================== JULLÄGE – HJÄLPSFUNKTIONER =====================
void checkChristmasMode() {
if (christmasMode) return;
if (gameInProgress) return;
for (int i = 0; i < 3; i++) {
if (digitalRead(buttonPins[i]) == LOW) {
if (buttonPressStartTime[i] == 0) {
buttonPressStartTime[i] = millis();
} else if (millis() - buttonPressStartTime[i] >= 4000) {
for (int j = 0; j < 3; j++) buttonPressStartTime[j] = 0;
enterChristmasMode();
break;
}
} else {
buttonPressStartTime[i] = 0;
}
}
}
void enterChristmasMode() {
christmasMode = true;
christmasMelodyPlaying = false;
currentChristmasSongIndex = 0; // 0 = Jingle Bells
gameLost = false;
currentStreak = 0;
startNewGame();
startChristmasMelody(currentChristmasSongIndex);
}
void startChristmasMelody(int index) {
if (index < 0 || index >= numChristmasSongs) return;
currentChristmasSongIndex = index;
christmasNoteIndex = 0;
christmasMelodyPlaying = true;
christmasLastNoteChange = 0;
christmasCurrentNoteDuration = 0;
noTone(piezoPin);
}
void stopChristmasMelody() {
christmasMelodyPlaying = false;
noTone(piezoPin);
for (int i = 0; i < 6; i++) {
digitalWrite(ledStarsArray[i], LOW);
}
}
void updateChristmasMelody() {
if (!christmasMode || !christmasMelodyPlaying) return;
unsigned long now = millis();
// Blink snabbt och slumpmässigt med stjärn-LEDs
static unsigned long lastBlinkTime = 0;
if (now - lastBlinkTime > 50) {
for (int i = 0; i < 6; i++) {
digitalWrite(ledStarsArray[i], LOW);
}
int r = random(6);
digitalWrite(ledStarsArray[r], HIGH);
lastBlinkTime = now;
}
// Dags för nästa not?
if (christmasLastNoteChange == 0 || now - christmasLastNoteChange >= christmasCurrentNoteDuration) {
int* song = christmasSongs[currentChristmasSongIndex];
int notes = christmasSongLengths[currentChristmasSongIndex];
if (notes <= 0) {
stopChristmasMelody();
return;
}
if (christmasNoteIndex >= notes * 2) {
christmasNoteIndex = 0; // starta om låten
}
int thisNote = song[christmasNoteIndex];
int dividerLocal = song[christmasNoteIndex + 1];
int duration = 0;
if (dividerLocal > 0) {
duration = wholenote / dividerLocal;
} else if (dividerLocal < 0) {
duration = wholenote / abs(dividerLocal);
duration = duration + duration / 2;
}
christmasCurrentNoteDuration = duration;
christmasLastNoteChange = now;
christmasNoteIndex += 2;
if (thisNote == REST) {
noTone(piezoPin);
} else {
tone(piezoPin, thisNote, (unsigned long)(duration * 0.9));
}
}
}
void playHesAPirate() {
for (int n = 0; n < hesAPirateNotes * 2; n += 2) {
int note = hesAPirateMelody[n];
int dividerLocal = hesAPirateMelody[n + 1];
int duration = 0;
if (dividerLocal > 0) {
duration = wholenote / dividerLocal;
} else if (dividerLocal < 0) {
duration = wholenote / abs(dividerLocal);
duration = duration + duration / 2;
}
int r = random(6);
for (int i = 0; i < 6; i++) {
digitalWrite(ledStarsArray[i], (i == r) ? HIGH : LOW);
}
tone(piezoPin, note, (unsigned long)(duration * 0.9));
delay(duration);
noTone(piezoPin);
}
for (int i = 0; i < 6; i++) {
digitalWrite(ledStarsArray[i], LOW);
}
}