// Подключаем библиотеку для работы с мелодиями в формате RTTTL #include // Подключаем библиотеку для работы с четырёхразрядным дисплеем #include // Подключаем библиотеку для работы с кнопками #include // Создаём объект дисплея на шине SPI и пине 10 QuadDisplay qd(10); // Создаём объект кнопки на пине 2 TroykaButton button(2); // Даём понятное имя пину A2 с пищалкой constexpr uint8_t BUZZER_PIN = A2; // Даём понятное имя пину 5 со светодиодом constexpr uint8_t LED_PIN = 5; // Мелодия «Старт» в формате RTTTL const char* gameStart = "gameStart:d=4,o=5,b=100:" "16e6,16e6,32p,8e6,16c6,8e6,8g6,8p," "8g,8p"; // Мелодия «Игра проиграна» в формате RTTTL const char* gameOver = "gameOver:d=4,o=5,b=90:" "32c6,32c6,32c6,8p,16b,16f6,16p,16f6," "16f.6,16e.6,16d6,16c6,16p,16e,16p,16c"; // Мелодия «Игра выиграна» в формате RTTTL const char* gameWin = "gameWin:d=4,o=5,b=180:" "8g3,8c4,8e4,8g4,8c,8e,3g,3e," "8e3,8c4,8e#4,8A#4,8c,8e#,3a#,3e#," "8b#3,8d4,8f4,8b#4,8d,8f,3b#,32p," "8b#,32p,8b#,32p,8b#,1c6"; // Создаём перечисление состояний игры с соответствующей переменной enum { GAME_START, // «Начало игры» GAME_PLAY, // «Процесс игры» GAME_END, // «Конец игры» GAME_OVER, // «Игра проиграна» GAME_WIN // «Игра выиграна» } gameState; // Создаём перечисление разрядов дисплея enum { D1 = 0, // Первый разряд D2 = 1, // Второй разряд D3 = 2, // Третий разряд D4 = 3 // Четвёртый разряд }; // Создаём переменную для хранения значения на разряде дисплея uint8_t digitCounter; // Создаём переменную для хранения значения на разряде дисплея в битовом виде uint8_t digitMaskQD; // Создаём массив для хранения значений на разрядах дисплея в битовом виде uint8_t digitMasksQD[4]; // Создаём массив для хранения статуса установки значений на разрядах дисплея // true: разряд установлен, false: разряд не установлен bool digitStates[4]; // Создаём переменную для работы таймера обновления значений на разрядах дисплея long timeLastUpdateDigit = 0; // Создаём переменную для работы таймера остановки значений на разрядах дисплея long timeLastStopDigit = 0; // Создаём константу для хранения времени обновления значений счётчика на разрядах дисплея constexpr int timeUpdateDigit = 100; // Создаем переменную для хранения времени остановки значений счётчика на разрядах дисплея int timeStopDigit; void setup() { // Инициализируем дисплей qd.begin(); // Инициализируем кнопку button.begin(); // Настраиваем пин с пищалкой в режим выхода pinMode(BUZZER_PIN, OUTPUT); // Настраиваем пин со светодиодом в режим выхода pinMode(LED_PIN, OUTPUT); // Задаём зерно генерации случайных чисел randomSeed(analogRead(A6)); // Устанавливаем режим «Начало игры» gameState = GAME_START; } void loop() { // Считываем состояние с кнопки button.read(); // Выбираем действие в зависимости от текущего режима игры switch (gameState) { // Если установлен режим «Начало игры» case GAME_START: { // Переходим в функцию обработки режима «Начало игры» handleGameStart(); break; } // Если установлен режим «Процесс игры» case GAME_PLAY: { // Переходим в функцию обработки режима «Процесс игры» handleGamePlay(); break; } // Если установлен режим «Конец игры» case GAME_END: { // Переходим в функцию обработки режима «Конец игры» handleGameEnd(); break; } // Если установлен режим «Игра проиграна» case GAME_OVER: { // Переходим в функцию обработки режима «Игры проиграна» handleGameOver(); break; } // Если установлен режим «Игра выиграна» case GAME_WIN: { // Переходим в функцию обработки режима «Игры выиграна» handleGameWin(); break; } } } // Функция обработки режима «Начало игры» void handleGameStart() { // Зажигаем светодиод digitalWrite(LED_PIN, HIGH); // Выводим приветствие на дисплей qd.displayDigits(QD_P, QD_L, QD_A, QD_Y); // Играем приветственную мелодию anyrtttl::blocking::play(BUZZER_PIN, gameStart); // Ожидаем нажатие на кнопку do { // Считываем состояние с кнопки button.read(); } while (!button.isClick()); // Гасим светодиод digitalWrite(LED_PIN, LOW); // Сбрасываем настройки по умолчанию resetDefaults(); // Генерируем случайное число timeStopDigit = timeRandom(); // Устанавливаем режим «Процесс игры» gameState = GAME_PLAY; } // Функция обработки режима «Процесс игры» void handleGamePlay() { // В режиме «Процесс игры» работают два таймера: // timerUpdateDigit(): таймер обновления значений символов, // который отвечает за время поочередного обновления цифр от 0 до 9 // timerStopDigit(): таймер остановки значений символов, // который отвечает за время остановки обновления цифр от 0 до 9 // Если таймер обновления символов досчитал, // получаем последующее число от 0 до 9 // Передаем константе состояние таймера обновления символов const auto timerUpdateDone = timerUpdateDigit(); // Если таймер досчитал, т.е. прошла 1 секунда if (timerUpdateDone) { // Увеличиваем цифру на один digitCounter++; // Если цифра стала более 9 if (digitCounter > 9) { // Обнуляем цифру до ноля digitCounter = 0; } // Конвертируем число в QD-символов в битовом виде digitMaskQD = intToQD(digitCounter); } // Передаем константе состояние таймера остановки значений const auto timerStopDone = timerStopDigit(); // Если таймер остановки значений счётчика досчитал, // проверяем какой разряд стоит на очередь установки if (timerStopDone) { // Если очередь второго разряда if (digitStates[D2] == false) { // Фиксируем разряд статусом true: установлен digitStates[D2] = true; // Присваиваем текущий символ на счётчике в массив хранения значений результата digitMasksQD[D2] = digitMaskQD; // Генерируем случайное число timeStopDigit = timeRandom(); // Если очередь третьего разряда } else if (digitStates[D3] == false) { // Фиксируем разряд статусом true: установлен digitStates[D3] = true; // Присваиваем текущий символ на счётчике в массив хранения значений результата digitMasksQD[D3] = digitMaskQD; // Генерируем случайное число timeStopDigit = timeRandom(); // Если очередь четвёртого разряда } else if (digitStates[D4] == false) { // Фиксируем разряд статусом true: установлен digitStates[D4] = true; // Присваиваем текущий символ на счётчике в массив хранения значений результата digitMasksQD[D4] = digitMaskQD; } } // Присваиваем текущий символ на счётчике всем не установленным разрядам for (int i = D2; i <= D4; i++) { if (!digitStates[i]) { digitMasksQD[i] = digitMaskQD; } } // Если все разряды установлены if (checkDigitStates()) { // Устанавливаем режим «Конец игры» gameState = GAME_END; } // Выводим текущие символы разрядов на дисплей printDigits(); } // Функция обработки режима «Конец игры» void handleGameEnd() { // Выводим текущие символы разрядов на дисплей printDigits(); // Ждём 1000 мс для наглядности комбинации на дисплее delay(1000); // Проверяем значения разрядов на совпадения if (checkDigitsWin()) { // Устанавливаем режим «Игра выиграна» gameState = GAME_WIN; // Если вдруг совпала выигрышная комбинация, // устанавливаем читерскую инструкцию digitMasksQD[D4] = intToQD(--digitCounter); // Устанавливаем режим «Игра проиграна» gameState = GAME_OVER; } else { // Устанавливаем режим «Игра проиграна» gameState = GAME_OVER; } // Выводим текущие символы разрядов на дисплей после проверки на совпадения printDigits(); // Ждём 1000 мс для наглядности комбинации на дисплее delay(1000); } // Функция обработки режима «Игра проиграна» void handleGameOver() { // Печатаем на дисплей про проигрыш qd.displayDigits(QD_F, QD_A, QD_I, QD_L); // Играем мелодию проигрыша anyrtttl::blocking::play(BUZZER_PIN, gameOver); // Ожидаем нажатие на кнопку do { button.read(); } while (!button.isClick()); // Устанавливаем режим «Начало игры» gameState = GAME_START; } // Функция обработки режима «Игра выиграна» void handleGameWin() { // Печатаем на дисплей про выигрыш qd.displayDigits(QD_C, QD_O, QD_O, QD_L); // Играем мелодию выигрыша anyrtttl::blocking::play(BUZZER_PIN, gameWin); // Ожидаем нажатие на кнопку do { button.read(); } while (!button.isClick()); // Устанавливаем режим «Начало игры» gameState = GAME_START; } // Функция-таймер обновления значений счётчика на разрядах дисплея bool timerUpdateDigit() { // Запоминаем текущее время long timeNow = millis(); // Если прошёл интервал времени по обновлению значений счётчика if (timeNow - timeLastUpdateDigit > timeUpdateDigit) { // Обновляем текущее время timeLastUpdateDigit = timeNow; // Да, таймер досчитал return true; } // Нет, таймер не досчитал return false; } // Функция-таймер остановки значений счётчика на разрядах дисплея bool timerStopDigit() { // Запоминаем текущее время long timeNow = millis(); // Если прошёл интервал времени по остановки значений счётчика if (timeNow - timeLastStopDigit > timeStopDigit) { timeLastStopDigit = timeNow; // Да, таймер досчитал return true; } // Нет, таймер не досчитал return false; } // Функция конвертации целого числа int в QD-символ дисплея int intToQD(int digitInt) { // Создаём переменную для хранения QD-символа int digitQD; // Конвертируем целое число int в QD-символ дисплея switch (digitInt) { case 0: digitQD = QD_0; break; case 1: digitQD = QD_1; break; case 2: digitQD = QD_2; break; case 3: digitQD = QD_3; break; case 4: digitQD = QD_4; break; case 5: digitQD = QD_5; break; case 6: digitQD = QD_6; break; case 7: digitQD = QD_7; break; case 8: digitQD = QD_8; break; case 9: digitQD = QD_9; break; } // Возвращаем QD-символ дисплея return digitQD; } // Функция вывода символов на дисплей void printDigits() { qd.displayDigits(QD_NONE, digitMasksQD[D2], digitMasksQD[D3], digitMasksQD[D4]); } // Функция проверки выигрышной комбинации bool checkDigitsWin() { if (digitMasksQD[D2] == digitMasksQD[D3] && digitMasksQD[D3] == digitMasksQD[D4]) { return true; } else { return false; } } // Функция проверки статуса установки разрядов на дисплее bool checkDigitStates() { for (int i = D2; i <= D4; i++) { if (digitStates[i] == false) { return false; } } return true; } // Функция сброса статуса установки разрядов на дисплее void resetDigitStates() { for (int i = D2; i <= D4; i++) { digitStates[i] = false; } } // Функция сброса настроек по умолчанию void resetDefaults() { // Засекаем текущее время для таймеров timeLastUpdateDigit = millis(); timeLastStopDigit = millis(); // Сбрасываем состояние разрядов resetDigitStates(); // Присваиваем переменной для хранения значения разряда число 0 digitCounter = 0; // Присваиваем переменной для хранения // значения разряда в символьном представление QD знак «-» digitMaskQD = QD_MINUS; } // Функция генерации случайно числа от 1000 до 3000 int timeRandom() { return random(1000, 3000); }