/*
* ====================================================
* GRA SIMON SAYS DLA DZIECI (4-6 lat)
* ====================================================
* Wersja dla ESP32 z LCD1602 I2C
* POPRAWIONA LOSOWOŚĆ!
* ====================================================
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "esp_random.h" // Sprzętowy generator losowy ESP32
// ============ KONFIGURACJA PINÓW ============
// Diody LED
#define LED_CZERWONA 2
#define LED_ZIELONA 4
#define LED_NIEBIESKA 5
#define LED_ZOLTA 18
// Przyciski
#define BTN_CZERWONY 13
#define BTN_ZIELONY 12
#define BTN_NIEBIESKI 14
#define BTN_ZOLTY 27
// Buzzer (opcjonalnie)
#define BUZZER 15
// ============ USTAWIENIA GRY ============
#define MAX_POZIOM 20 // Maksymalna długość sekwencji
#define CZAS_POKAZU 600 // Czas świecenia LED (ms)
#define PRZERWA 300 // Przerwa między kolorami
#define CZAS_OCZEKIWANIA 5000 // Czas na odpowiedź dziecka (5 sekund)
// ============ DŹWIĘKI (częstotliwości) ============
#define TON_CZERWONY 262 // C4
#define TON_ZIELONY 330 // E4
#define TON_NIEBIESKI 392 // G4
#define TON_ZOLTY 523 // C5
#define TON_SUKCES 880 // A5
#define TON_PORAZKA 150 // Niski ton
// ============ LCD ============
LiquidCrystal_I2C lcd(0x27, 16, 2);
// ============ ZMIENNE GRY ============
int sekwencja[MAX_POZIOM]; // Tablica z sekwencją kolorów
int poziom = 0; // Aktualny poziom
int krokGracza = 0; // Który element sekwencji gracz powtarza
int najlepszyWynik = 0; // Rekord
bool graAktywna = false; // Czy gra trwa
unsigned long seedDodatkowy = 0; // Dodatkowa entropia
// Tablice dla łatwiejszego dostępu
int ledy[] = {LED_CZERWONA, LED_ZIELONA, LED_NIEBIESKA, LED_ZOLTA};
int przyciski[] = {BTN_CZERWONY, BTN_ZIELONY, BTN_NIEBIESKI, BTN_ZOLTY};
int tony[] = {TON_CZERWONY, TON_ZIELONY, TON_NIEBIESKI, TON_ZOLTY};
// ============ WŁASNE ZNAKI LCD ============
byte buzkaUsmiech[] = {
B00000, B01010, B01010, B00000, B10001, B01110, B00000, B00000
};
byte buzkaSmutek[] = {
B00000, B01010, B01010, B00000, B01110, B10001, B00000, B00000
};
byte gwiazdka[] = {
B00100, B01110, B11111, B01110, B01010, B10001, B00000, B00000
};
byte serce[] = {
B00000, B01010, B11111, B11111, B01110, B00100, B00000, B00000
};
// ============ FUNKCJA PRAWDZIWIE LOSOWA ============
// Używa sprzętowego generatora ESP32 + dodatkowa entropia
int losowyKolor() {
// Pobierz losową liczbę ze sprzętowego generatora ESP32
uint32_t losowa = esp_random();
// Dodaj entropię z czasu
losowa ^= micros();
losowa ^= seedDodatkowy;
// Zwróć wartość 0-3
return losowa % 4;
}
// Dodatkowe mieszanie ziarna losowości
void dodajEntropie() {
seedDodatkowy ^= esp_random();
seedDodatkowy ^= micros();
seedDodatkowy += millis();
// Odczyt z kilku pinów analogowych
for (int i = 32; i <= 35; i++) {
seedDodatkowy ^= analogRead(i);
}
}
// ============ FUNKCJE POMOCNICZE ============
void inicjalizujPiny() {
for (int i = 0; i < 4; i++) {
pinMode(ledy[i], OUTPUT);
digitalWrite(ledy[i], LOW);
}
for (int i = 0; i < 4; i++) {
pinMode(przyciski[i], INPUT_PULLUP);
}
pinMode(BUZZER, OUTPUT);
}
void zapalWszystkie() {
for (int i = 0; i < 4; i++) {
digitalWrite(ledy[i], HIGH);
}
}
void zgasWszystkie() {
for (int i = 0; i < 4; i++) {
digitalWrite(ledy[i], LOW);
}
}
void zapalLED(int numer, int czasMs) {
digitalWrite(ledy[numer], HIGH);
tone(BUZZER, tony[numer], czasMs);
delay(czasMs);
digitalWrite(ledy[numer], LOW);
noTone(BUZZER);
}
void efektPowitalny() {
for (int j = 0; j < 2; j++) {
for (int i = 0; i < 4; i++) {
zapalLED(i, 150);
delay(50);
dodajEntropie(); // Zbieraj entropię podczas animacji
}
}
}
void efektSukces() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" BRAWO!!! ");
lcd.write(0);
lcd.setCursor(0, 1);
lcd.print(" SUPER ROBOTA! ");
lcd.write(2);
for (int i = 0; i < 3; i++) {
zapalWszystkie();
tone(BUZZER, TON_SUKCES, 150);
delay(200);
zgasWszystkie();
noTone(BUZZER);
delay(100);
}
delay(1000);
}
void efektPorazka() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Ups! ");
lcd.write(1);
lcd.setCursor(0, 1);
lcd.print("Sprobuj jeszcze!");
tone(BUZZER, TON_PORAZKA, 500);
for (int i = 0; i < 3; i++) {
zapalWszystkie();
delay(150);
zgasWszystkie();
delay(150);
}
noTone(BUZZER);
delay(1500);
}
// ============ FUNKCJE GRY ============
// POPRAWIONE: Generuj losowy kolor i dodaj do sekwencji
void dodajDoSekwencji() {
dodajEntropie(); // Odśwież entropię przed losowaniem
sekwencja[poziom] = losowyKolor();
// Debug - wyświetl w Serial
Serial.print("Poziom ");
Serial.print(poziom + 1);
Serial.print(": dodano kolor ");
Serial.println(sekwencja[poziom]);
poziom++;
}
// POPRAWIONE: Generuj całą nową sekwencję na początku gry
void generujNowaSekwencje() {
Serial.println("=== NOWA GRA - NOWA SEKWENCJA ===");
// Zbierz dużo entropii przed generowaniem
for (int i = 0; i < 10; i++) {
dodajEntropie();
delay(10);
}
// Wygeneruj całą sekwencję z góry
for (int i = 0; i < MAX_POZIOM; i++) {
sekwencja[i] = losowyKolor();
Serial.print("Sekwencja[");
Serial.print(i);
Serial.print("] = ");
Serial.println(sekwencja[i]);
}
Serial.println("=================================");
}
void pokazSekwencje() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" PATRZ! ");
lcd.write(3);
lcd.setCursor(0, 1);
lcd.print(" Zapamietaj!");
delay(800);
for (int i = 0; i < poziom; i++) {
lcd.setCursor(0, 1);
lcd.print("Kolor ");
lcd.print(i + 1);
lcd.print(" z ");
lcd.print(poziom);
lcd.print(" ");
zapalLED(sekwencja[i], CZAS_POKAZU);
delay(PRZERWA);
}
}
int sprawdzPrzycisk() {
for (int i = 0; i < 4; i++) {
if (digitalRead(przyciski[i]) == LOW) {
delay(50);
if (digitalRead(przyciski[i]) == LOW) {
// Dodaj entropię z czasu naciśnięcia
seedDodatkowy ^= micros();
zapalLED(i, 200);
while (digitalRead(przyciski[i]) == LOW) {
delay(10);
}
delay(100);
return i;
}
}
}
return -1;
}
bool czekajNaOdpowiedz() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Twoja kolej! ");
lcd.write(0);
krokGracza = 0;
while (krokGracza < poziom) {
lcd.setCursor(0, 1);
lcd.print("Kolor ");
lcd.print(krokGracza + 1);
lcd.print(" z ");
lcd.print(poziom);
lcd.print(" ");
unsigned long czasStart = millis();
int nacisniety = -1;
while (nacisniety == -1) {
nacisniety = sprawdzPrzycisk();
if (millis() - czasStart > CZAS_OCZEKIWANIA) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Za wolno! ");
lcd.write(1);
delay(1500);
return false;
}
if ((millis() / 500) % 2 == 0) {
lcd.setCursor(15, 0);
lcd.print("*");
} else {
lcd.setCursor(15, 0);
lcd.print(" ");
}
}
if (nacisniety != sekwencja[krokGracza]) {
return false;
}
krokGracza++;
if (krokGracza == poziom && poziom >= 3) {
lcd.setCursor(0, 1);
lcd.print(" Dobrze! ");
delay(300);
}
}
return true;
}
void ekranStartowy() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.write(3);
lcd.print(" SIMON ");
lcd.write(3);
lcd.setCursor(0, 1);
lcd.print("Nacisnij guzik!");
efektPowitalny();
bool czekaj = true;
while (czekaj) {
for (int i = 0; i < 4 && czekaj; i++) {
digitalWrite(ledy[i], HIGH);
// Zbieraj entropię podczas czekania
dodajEntropie();
delay(200);
digitalWrite(ledy[i], LOW);
for (int j = 0; j < 4; j++) {
if (digitalRead(przyciski[j]) == LOW) {
// Czas naciśnięcia jako dodatkowa entropia!
seedDodatkowy ^= micros();
seedDodatkowy += millis();
czekaj = false;
break;
}
}
}
}
delay(300);
zgasWszystkie();
}
void pokazWynik() {
int wynik = (poziom > 0) ? poziom - 1 : 0;
if (wynik > najlepszyWynik) {
najlepszyWynik = wynik;
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Twoj wynik: ");
lcd.print(wynik);
lcd.setCursor(0, 1);
lcd.print("Rekord: ");
lcd.print(najlepszyWynik);
lcd.print(" ");
lcd.write(2);
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Jeszcze raz?");
lcd.setCursor(0, 1);
lcd.print("Nacisnij guzik!");
while (true) {
dodajEntropie(); // Zbieraj entropię podczas czekania
for (int i = 0; i < 4; i++) {
if (digitalRead(przyciski[i]) == LOW) {
seedDodatkowy ^= micros(); // Entropia z momentu naciśnięcia
delay(300);
return;
}
}
delay(50);
}
}
// POPRAWIONE: Główna pętla gry
void rozgrywka() {
poziom = 0;
krokGracza = 0;
graAktywna = true;
// *** KLUCZOWA ZMIANA: Generuj nową losową sekwencję! ***
generujNowaSekwencje();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Zaczynamy! ");
lcd.write(0);
lcd.setCursor(0, 1);
lcd.print(" 3... 2... 1...");
delay(2000);
while (graAktywna && poziom < MAX_POZIOM) {
// Przejdź do następnego poziomu (sekwencja już wygenerowana)
poziom++;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Poziom: ");
lcd.print(poziom);
lcd.print(" ");
lcd.write(2);
delay(1000);
pokazSekwencje();
delay(500);
if (czekajNaOdpowiedz()) {
efektSukces();
delay(500);
} else {
efektPorazka();
graAktywna = false;
}
}
pokazWynik();
}
// ============ SETUP I LOOP ============
void setup() {
Serial.begin(115200);
Serial.println("\n\n=== SIMON SAYS START ===");
// Inicjalizacja sprzętowego generatora
// ESP32 ma wbudowany TRNG (True Random Number Generator)
Serial.print("Inicjalizacja RNG, pierwsza losowa: ");
Serial.println(esp_random());
// Zbierz początkową entropię
seedDodatkowy = esp_random();
for (int i = 0; i < 20; i++) {
dodajEntropie();
delay(5);
}
Serial.print("Seed dodatkowy: ");
Serial.println(seedDodatkowy);
inicjalizujPiny();
Wire.begin(21, 22);
lcd.init();
lcd.backlight();
lcd.createChar(0, buzkaUsmiech);
lcd.createChar(1, buzkaSmutek);
lcd.createChar(2, gwiazdka);
lcd.createChar(3, serce);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Witaj! ");
lcd.write(0);
lcd.write(3);
lcd.setCursor(0, 1);
lcd.print(" GRA KOLORY");
delay(2000);
}
void loop() {
ekranStartowy();
rozgrywka();
}Loading
esp32-devkit-c-v4
esp32-devkit-c-v4