#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// --- DEFINICJE PINÓW (wg Twojego opisu) ---
#define LED_CZERWONA 2
#define LED_ZIELONA 4
#define LED_NIEBIESKA 5
#define LED_ZOLTA 18
#define BTN_CZERWONY 13
#define BTN_ZIELONY 12
#define BTN_NIEBIESKI 14
#define BTN_ZOLTY 27
#define BUZZER 15
// --- KONFIGURACJA GRY ---
#define MAX_LEVEL 50 // Maksymalna liczba poziomów
const int LCD_ADDR = 0x27; // Adres I2C wyświetlacza (czasem 0x3F)
const int LCD_COLS = 16;
const int LCD_ROWS = 2;
// Inicjalizacja LCD
LiquidCrystal_I2C lcd(LCD_ADDR, LCD_COLS, LCD_ROWS);
// Tablice pomocnicze do łatwiejszego zarządzania w pętlach
// Kolejność w tablicach: 0=Czerwony, 1=Zielony, 2=Niebieski, 3=Żółty
const int ledPins[] = {LED_CZERWONA, LED_ZIELONA, LED_NIEBIESKA, LED_ZOLTA};
const int btnPins[] = {BTN_CZERWONY, BTN_ZIELONY, BTN_NIEBIESKI, BTN_ZOLTY};
// Częstotliwości dźwięków dla każdego koloru (C, E, G, C oktawę wyżej)
const int gameTones[] = {261, 329, 392, 523};
int gameSequence[MAX_LEVEL]; // Tablica przechowująca sekwencję gry
int currentLevel = 1; // Aktualny poziom
int inputStep = 0; // Krok wprowadzania przez gracza
void setup() {
Serial.begin(115200);
// Konfiguracja pinów LED
for (int i = 0; i < 4; i++) {
pinMode(ledPins[i], OUTPUT);
digitalWrite(ledPins[i], LOW);
}
// Konfiguracja pinów Przycisków (INPUT_PULLUP - druga noga przycisku do GND)
for (int i = 0; i < 4; i++) {
pinMode(btnPins[i], INPUT_PULLUP);
}
// Konfiguracja Buzzera
pinMode(BUZZER, OUTPUT);
// Inicjalizacja LCD
// Dla ESP32 domyślne piny I2C to SDA=21, SCL=22
Wire.begin();
lcd.init();
lcd.backlight();
// Ziarno losowości (czytamy szum z niepodłączonego pinu analogowego)
randomSeed(analogRead(34));
introSequence();
delay(1000);
startGame();
}
void loop() {
// W pętli loop nic nie robimy, cała logika jest sterowana funkcjami
// wywoływanymi sekwencyjnie. Gdy gra się kończy, resetujemy ją.
}
// --- FUNKCJE LOGIKI GRY ---
void startGame() {
currentLevel = 1;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Start Gry!");
delay(1000);
generateSequence(); // Generuj pełną losową sekwencję na zapas
while(currentLevel <= MAX_LEVEL) {
updateLevelDisplay();
playSequence(); // Odtwórz sekwencję
if (!readPlayerInput()) {
// Jeśli gracz się pomylił
gameOver();
return;
}
// Jeśli gracz zgadł, zwiększ poziom i krótka pauza
currentLevel++;
delay(1000);
}
// Jeśli gracz przeszedł MAX_LEVEL
victory();
}
// Generuje losową sekwencję kolorów
void generateSequence() {
for (int i = 0; i < MAX_LEVEL; i++) {
gameSequence[i] = random(0, 4); // Losuje liczby 0-3
}
}
// Wyświetla aktualny poziom
void updateLevelDisplay() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Poziom: ");
lcd.print(currentLevel);
lcd.setCursor(0, 1);
lcd.print("Patrz uwaznie...");
}
// Odtwarza sekwencję dla aktualnego poziomu
void playSequence() {
for (int i = 0; i < currentLevel; i++) {
int colorIndex = gameSequence[i];
flashLED(colorIndex);
delay(250); // Przerwa między mignięciami
}
lcd.setCursor(0, 1);
lcd.print("Twoja kolej! ");
}
// Zapala LED i gra dźwięk
void flashLED(int colorIndex) {
digitalWrite(ledPins[colorIndex], HIGH);
tone(BUZZER, gameTones[colorIndex]);
delay(300); // Czas trwania sygnału
digitalWrite(ledPins[colorIndex], LOW);
noTone(BUZZER);
}
// Odczytuje ruchy gracza
bool readPlayerInput() {
for (int i = 0; i < currentLevel; i++) {
int expectedColor = gameSequence[i];
int pressedColor = -1;
// Czekaj na naciśnięcie przycisku
while (pressedColor == -1) {
for (int j = 0; j < 4; j++) {
if (digitalRead(btnPins[j]) == LOW) { // LOW oznacza wciśnięty (PULLUP)
pressedColor = j;
// Efekt wciśnięcia (LED + Dźwięk)
digitalWrite(ledPins[j], HIGH);
tone(BUZZER, gameTones[j]);
// Czekaj na puszczenie przycisku (prosty debouncing)
while (digitalRead(btnPins[j]) == LOW) { delay(10); }
digitalWrite(ledPins[j], LOW);
noTone(BUZZER);
}
}
}
// Sprawdź czy poprawny przycisk
if (pressedColor != expectedColor) {
return false; // Błąd
}
}
return true; // Sekwencja poprawna
}
void gameOver() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("KONIEC GRY!");
lcd.setCursor(0, 1);
lcd.print("Wynik: ");
lcd.print(currentLevel - 1);
// Dźwięk porażki
tone(BUZZER, 150);
delay(500);
tone(BUZZER, 100);
delay(1000);
noTone(BUZZER);
// Mruganie wszystkimi LEDami
for(int i=0; i<3; i++){
for(int j=0; j<4; j++) digitalWrite(ledPins[j], HIGH);
delay(200);
for(int j=0; j<4; j++) digitalWrite(ledPins[j], LOW);
delay(200);
}
delay(2000);
startGame(); // Restart
}
void victory() {
lcd.clear();
lcd.print("WYGRALES!");
// Fanfary...
startGame();
}
void introSequence() {
lcd.setCursor(0, 0);
lcd.print("SIMON SAYS");
lcd.setCursor(0, 1);
lcd.print("ESP32 Edition");
// Efekt Knight Rider na LEDach
for (int i = 0; i < 4; i++) {
digitalWrite(ledPins[i], HIGH);
tone(BUZZER, gameTones[i] * 2, 100);
delay(100);
digitalWrite(ledPins[i], LOW);
}
for (int i = 2; i >= 0; i--) {
digitalWrite(ledPins[i], HIGH);
tone(BUZZER, gameTones[i] * 2, 100);
delay(100);
digitalWrite(ledPins[i], LOW);
}
noTone(BUZZER);
}