// Библиотека для доступа к энергонезависимой памяти EEPROM #include // Библиотека для работы с текстовым дисплеем по шине I²C #include // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); // Константа для хранения адреса ячейки в памяти EEPROM // для сохранения и считывания данных игры constexpr int EEPROM_ADDRESS = 200; // Создаём звуковой шрифт из нот со своими частотами constexpr int NOTE_C3 = 131; constexpr int NOTE_D3 = 147; constexpr int NOTE_E3 = 165; constexpr int NOTE_F3 = 175; constexpr int NOTE_G3 = 196; constexpr int NOTE_A3 = 220; constexpr int NOTE_B3 = 247; constexpr int NOTE_C4 = 262; constexpr int NOTE_D4 = 294; constexpr int NOTE_E4 = 330; constexpr int NOTE_F4 = 349; constexpr int NOTE_G4 = 392; constexpr int NOTE_A4 = 440; constexpr int NOTE_B4 = 494; constexpr int NOTE_C5 = 523; // Даём понятное имя пину с пищалкой constexpr int BUZZER_PIN = 2; // Даём понятные имена пинам со светодиодами constexpr int LED_1_PIN = 3; constexpr int LED_2_PIN = 4; constexpr int LED_3_PIN = 5; constexpr int LED_4_PIN = 6; // Даём понятные имена пинам с кнопками constexpr int BUTTON_1_PIN = A3; constexpr int BUTTON_2_PIN = A2; constexpr int BUTTON_3_PIN = A1; constexpr int BUTTON_4_PIN = A0; // Массив нот для мелодии «Начало игры» constexpr int NOTE_GAME_BEGIN[] = { NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4, NOTE_G4, NOTE_F4, NOTE_E4, NOTE_F4, NOTE_G4 }; // Массив длительности нот для мелодии «Начало игры constexpr int NOTE_DURATION_GAME_BEGIN[] = { 8, 4, 8, 4, 8, 4, 8, 8, 8, 8, 4, 8, 2 }; // Количество нот в мелодии «Начало игры constexpr int NOTE_COUNT_GAME_BEGIN = 13; // Массив нот для мелодии «Выигрыш» constexpr int NOTE_GAME_WIN[] = { NOTE_C4, NOTE_C4, NOTE_G4, NOTE_C5, NOTE_G4, NOTE_C5 }; // Массив длительности нот для мелодии «Выигрыш» constexpr int NOTE_DURATION_GAME_WIN[] = { 8, 8, 8, 4, 8, 4 }; // Количество нот в мелодии «Выигрыш» constexpr int NOTE_COUNT_GAME_WIN = 6; // Массив пинов со светодиодами constexpr int LED_NUMBER[] = { LED_1_PIN, LED_2_PIN, LED_3_PIN, LED_4_PIN }; // Массив нот для светодиодов constexpr int LED_NOTE[] = { NOTE_F3, NOTE_G3, NOTE_A3, NOTE_B3 }; // Массив пинов с кнопками constexpr int BUTTON_NUMBER[] = { BUTTON_1_PIN, BUTTON_2_PIN, BUTTON_3_PIN, BUTTON_4_PIN }; // Количество светодиодов и кнопок в массиве constexpr int LED_BUTTON_COUNTS = 4; // Переменные для текущего и лучшего счёта int score = 0; int scoreBest = 0; // Константа максимального счёта constexpr int SCORE_MAX = 128; // Массив последовательности случайно сгенерированных комбинаций int randomArray[SCORE_MAX]; // Массив последовательности нажатия кнопок int inputArray[SCORE_MAX]; // Создаём перечисления состояний игры enum { GAME_BEGIN, GAME_PLAY, GAME_OVER } stateGame; void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Назначаем пины светодиодов в режим выхода // А пины кнопок в режим входа с подтяжкой к питанию for (int i = 0; i < LED_BUTTON_COUNTS; i++) { pinMode(LED_NUMBER[i], OUTPUT); pinMode(BUTTON_NUMBER[i], INPUT_PULLUP); } // Задаём зерно генерации случайных чисел randomSeed(analogRead(A6)); // Считываем лучший результат игры из EEPROM EEPROM.get(EEPROM_ADDRESS, scoreBest); // Устанавливаем режим «Начало игры» stateGame = GAME_BEGIN; } void loop() { // Если установлен режим «Начало игры» if (stateGame == GAME_BEGIN) { // Обнуляем текущий результат score = 0; // Выводим приветственную заставку на дисплей printLCDGameBegin(); // Выводим приветственную музыку playMelodyGameBegin(); // Даём пользователю время на просмотр заставки delay(1000); // Очищаем дисплей lcd.clear(); // Устанавливаем режим «Процесс игры» stateGame = GAME_PLAY; } // Если установлен режим «Процесс игры» if (stateGame == GAME_PLAY) { // и функция playGame выдаёт ложное значение, // переходим в режим «Конец игры». if (playGame() == false) { stateGame = GAME_OVER; } } // Если установлен режим «Конец игры» if (stateGame == GAME_OVER) { // Выводим конечную заставку на дисплей printLCDGameOver(); // Выводим завершающую музыку playMelodyGameOver(); stateGame = GAME_BEGIN; delay(1000); } } // Функция обработки игрового процесса bool playGame() { lcd.setCursor(2, 0); lcd.print(""); lcd.setCursor(0, 1); lcd.print("Score:"); while (true) { // Отображаем на дисплее текущее количество очков lcd.setCursor(6, 1); lcd.print(score); // Зажигаем все светодиоды ledsSetAll(HIGH); // Включаем победную мелодию playMelodyGameWin(); // Гасим все светодиоды ledsSetAll(LOW); // Ждём delay(1000); // Демонстрируем случайную последовательность светодиодов generateRandomArray(); // Игра продолжается и счёт увеличивается, // пока функция readInputArray истинна. if (readInputArray()) { score++; } else { return false; } } } // Функция вывода начальной заставки на экран void printLCDGameBegin() { // Очищаем дисплей lcd.clear(); // Выводим текст начальной заставки lcd.setCursor(2, 0); lcd.print(""); lcd.setCursor(4, 1); lcd.print("DIY Game"); } // Функция вывода конечной заставки на экран void printLCDGameOver() { lcd.clear(); lcd.setCursor(0, 1); lcd.print("Score:"); lcd.print(score); // Если счёт превзошёл предыдущий рекорд, обновляем значение рекорда if (score > scoreBest) { scoreBest = score; // Сохраняем лучший результат игры в EEPROM EEPROM.put(EEPROM_ADDRESS, scoreBest); } // Выводим лучший результат игры за все попытки lcd.setCursor(0, 0); lcd.print("Best: "); lcd.print(scoreBest); } // Функция вывода мелодии «Начало игры void playMelodyGameBegin() { // Выполняем перебор нот for (int i = 0; i < NOTE_COUNT_GAME_BEGIN; i++) { int led = random(0, LED_BUTTON_COUNTS); // Зажигаем случайно выбранный светодиод digitalWrite(LED_NUMBER[led], HIGH); // Играем ноту с определённой длительностью tone(BUZZER_PIN, NOTE_GAME_BEGIN[i], 1000 / NOTE_DURATION_GAME_BEGIN[i]); // Делаем задержку для выделения нот в мелодии: // Длительность текущей ноты плюс 30% delay((1000 / NOTE_DURATION_GAME_BEGIN[i]) * 1.3); // Гасим случайно выбранный светодиод digitalWrite(LED_NUMBER[led], LOW); } } // Функция вывода мелодии «Выигрыш» void playMelodyGameWin() { // Выполняем перебор нот for (int i = 0; i < NOTE_COUNT_GAME_WIN; i++) { // Играем ноту с определённой длительностью tone(BUZZER_PIN, NOTE_GAME_WIN[i], 1000 / NOTE_DURATION_GAME_WIN[i]); // Делаем задержку для выделения нот в мелодии: // Длительность текущей ноты плюс 30% delay((1000 / NOTE_DURATION_GAME_WIN[i]) * 1.3); } } // Функция вывода мелодии «Проигрыш» void playMelodyGameOver() { for (int i = 0; i <= 2; i++) { // Зажигаем все светодиоды ledsSetAll(HIGH); // Издаём звуковой сигнал tone(BUZZER_PIN, NOTE_G3, 300); delay(200); // Гасим все светодиоды ledsSetAll(LOW); // Издаём звуковой сигнал tone(BUZZER_PIN, NOTE_C3, 300); delay(200); } } // Функция вывода произвольной комбинации светодиодов void generateRandomArray() { // Генерируем случайное число в диапазоне от 0 до 3 randomArray[score] = random(0, LED_BUTTON_COUNTS); // Зажигаем по очереди всю последовательность светодиодов for (int scoreNow = 0; scoreNow <= score; scoreNow++) { ledBeep(randomArray[scoreNow]); } } // Функция считывания угадываемой комбинации кнопок bool readInputArray() { int scoreNow = 0; // Пока количество нажатий на кнопки не превысило количество очков while (scoreNow <= score) { // Проверяем каждую кнопку for (int i = 0; i < LED_BUTTON_COUNTS; i++) { if (!digitalRead(BUTTON_NUMBER[i])) { // Подаём светодиодный звуковой сигнал нажатой кнопки ledBeep(i); // Присваиваем массиву кода кнопок, текущий номер кнопки inputArray[scoreNow] = i; delay(250); // Если текущая нажатая клавиша не совпадает со случайно сгенерированной if (inputArray[scoreNow] != randomArray[scoreNow]) { // Функция ложна return false; } scoreNow++; } } } return true; } // Функция включение или отключение всех светодиодов void ledsSetAll(bool state) { for (int i = 0; i < LED_BUTTON_COUNTS; i++) { digitalWrite(LED_NUMBER[i], state); } } // Функция светодиодного звукового сигнала void ledBeep(int led) { // Зажигаем выбранный светодиод digitalWrite(LED_NUMBER[led], HIGH); // Подаём звуковой сигнал зажжённого светодиода tone(BUZZER_PIN, LED_NOTE[led], 100); delay(400); // Гасим выбранный светодиод digitalWrite(LED_NUMBER[led], LOW); delay(100); }