Загрузка данных
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Adafruit_NeoPixel.h>
// LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);
// RGB-кольцо
#define RING_PIN 6
#define RING_COUNT 16
Adafruit_NeoPixel ring(
RING_COUNT, RING_PIN, NEO_GRB + NEO_KHZ800
);
// Пины
#define POT_PIN A0
#define BUTTON_PIN 2
#define BUZZER 3
#define RED_PIN 9
#define GREEN_PIN 10
#define BLUE_PIN 11
// Настройки игры
const unsigned long GAME_TIME = 50000;
const unsigned long HOLD_TIME = 3000;
const unsigned long SHOW_TIME = 2000;
// Допустимая погрешность оттенка
const int TOLERANCE = 9;
const int TOTAL_ROUNDS = 5;
enum GameState {
WAITING,
COUNTDOWN,
SHOW_TARGET,
PLAYING,
FEEDBACK,
WIN,
LOSE
};
GameState state = WAITING;
int roundNumber = 0;
int targetHue = 0;
int selected = 0;
bool holding = false;
unsigned long gameStart = 0;
unsigned long stateStart = 0;
unsigned long holdStart = 0;
unsigned long lastMusic = 0;
unsigned long lastLCD = 0;
int musicIndex = 0;
int lastCountdown = -1;
// Музыка
int melody[] = {
523, 587, 659, 784,
659, 587, 523, 440
};
// Цвет для кольца
uint32_t getHueColor(int hue) {
int h = hue * 6;
int sector = h / 256;
int f = h % 256;
int q = 255 - f;
int r = 0;
int g = 0;
int b = 0;
switch (sector) {
case 0: r = 255; g = f; break;
case 1: r = q; g = 255; break;
case 2: g = 255; b = f; break;
case 3: g = q; b = 255; break;
case 4: r = f; b = 255; break;
default: r = 255; b = q; break;
}
return ring.Color(r, g, b);
}
// RGB-светодиод
void showTargetColor() {
uint32_t c = getHueColor(targetHue);
int r = (c >> 16) & 255;
int g = (c >> 8) & 255;
int b = c & 255;
analogWrite(RED_PIN, r);
analogWrite(GREEN_PIN, g);
analogWrite(BLUE_PIN, b);
}
// Выключение RGB
void targetOff() {
analogWrite(RED_PIN, 0);
analogWrite(GREEN_PIN, 0);
analogWrite(BLUE_PIN, 0);
}
// Отображение кольца
void showRing() {
for (int i = 0; i < RING_COUNT; i++) {
uint32_t c = getHueColor(i * 16);
int r = (c >> 16) & 255;
int g = (c >> 8) & 255;
int b = c & 255;
if (i == selected) {
// Выбранный цвет светится ярче
r = min(255, r + 70);
g = min(255, g + 70);
b = min(255, b + 70);
} else {
r = r * 35 / 255;
g = g * 35 / 255;
b = b * 35 / 255;
}
ring.setPixelColor(i, ring.Color(r, g, b));
}
ring.show();
}
// Выключение кольца
void ringOff() {
ring.clear();
ring.show();
}
// Музыка
void playMusic() {
if (millis() - lastMusic >= 250) {
lastMusic = millis();
tone(BUZZER, melody[musicIndex], 180);
musicIndex++;
if (musicIndex >= 8) {
musicIndex = 0;
}
}
}
// Начальный экран
void waitingScreen() {
noTone(BUZZER);
targetOff();
ringOff();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("COLOR CHALLENGE");
lcd.setCursor(0, 1);
lcd.print("PRESS TO START");
state = WAITING;
}
// Запуск игры
void startGame() {
roundNumber = 0;
musicIndex = 0;
lastCountdown = -1;
lcd.clear();
lcd.print("GET READY!");
state = COUNTDOWN;
stateStart = millis();
}
// Новый раунд
void newRound() {
targetHue = random(0, 256);
selected = 0;
holding = false;
targetOff();
ringOff();
showTargetColor();
lcd.clear();
lcd.print("ROUND ");
lcd.print(roundNumber + 1);
lcd.print("/5");
lcd.setCursor(0, 1);
lcd.print("MEMORIZE COLOR");
state = SHOW_TARGET;
stateStart = millis();
}
// Проверка цвета
bool checkColor() {
int selectedHue = selected * 16;
int difference = abs(targetHue - selectedHue);
if (difference > 128) {
difference = 256 - difference;
}
return difference <= TOLERANCE;
}
// Обновление выбора потенциометром
void readPotentiometer() {
int value = analogRead(POT_PIN);
selected = map(value, 0, 1023, 0, 15);
selected = constrain(selected, 0, 15);
}
// Экран игры
void updateGameLCD() {
int seconds = (GAME_TIME - (millis() - gameStart)) / 1000;
lcd.setCursor(0, 0);
lcd.print("TIME:");
lcd.print(seconds);
lcd.print("s ROUND:");
lcd.print(roundNumber + 1);
lcd.print(" ");
lcd.setCursor(0, 1);
if (holding) {
int left = 3 - (millis() - holdStart) / 1000;
lcd.print("HOLD: ");
lcd.print(left);
lcd.print(" SEC ");
} else {
lcd.print("SELECT & HOLD ");
}
}
// Проверка ответа
void checkAnswer() {
holding = false;
noTone(BUZZER);
if (checkColor()) {
roundNumber++;
lcd.clear();
lcd.print("CORRECT!");
lcd.setCursor(0, 1);
lcd.print("SCORE: ");
lcd.print(roundNumber);
lcd.print("/5");
tone(BUZZER, 1300, 200);
if (roundNumber >= TOTAL_ROUNDS) {
state = WIN;
targetOff();
ringOff();
lcd.clear();
lcd.print("YOU WIN!");
lcd.setCursor(0, 1);
lcd.print("5/5 COMPLETED");
} else {
state = FEEDBACK;
stateStart = millis();
}
} else {
lcd.clear();
lcd.print("WRONG COLOR!");
lcd.setCursor(0, 1);
lcd.print("TRY AGAIN");
tone(BUZZER, 250, 200);
state = FEEDBACK;
stateStart = millis();
}
}
// Поражение
void loseGame() {
state = LOSE;
holding = false;
targetOff();
ringOff();
lcd.clear();
lcd.print("TIME OVER!");
lcd.setCursor(0, 1);
lcd.print("GAME OVER");
tone(BUZZER, 1000);
stateStart = millis();
}
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(BUZZER, OUTPUT);
pinMode(RED_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
pinMode(BLUE_PIN, OUTPUT);
lcd.init();
lcd.backlight();
ring.begin();
ring.setBrightness(60);
ring.show();
randomSeed(analogRead(A1));
waitingScreen();
}
void loop() {
unsigned long now = millis();
// ОЖИДАНИЕ
if (state == WAITING) {
if (digitalRead(BUTTON_PIN) == LOW) {
delay(30);
if (digitalRead(BUTTON_PIN) == LOW) {
startGame();
while (digitalRead(BUTTON_PIN) == LOW) {}
}
}
}
// ОТСЧЁТ
else if (state == COUNTDOWN) {
int count = 3 - (now - stateStart) / 1000;
if (count >= 1) {
lcd.setCursor(0, 0);
lcd.print("STARTING... ");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(7, 1);
lcd.print(count);
if (count != lastCountdown) {
tone(BUZZER, 1000, 180);
lastCountdown = count;
}
}
if (now - stateStart >= 3000) {
noTone(BUZZER);
gameStart = millis();
lastMusic = millis();
newRound();
}
}
// ПОКАЗ ЦВЕТА 2 СЕКУНДЫ
else if (state == SHOW_TARGET) {
playMusic();
if (now - gameStart >= GAME_TIME) {
loseGame();
return;
}
if (now - stateStart >= SHOW_TIME) {
targetOff();
lcd.clear();
lcd.print("FIND THE COLOR");
readPotentiometer();
showRing();
state = PLAYING;
}
}
// ОСНОВНАЯ ИГРА
else if (state == PLAYING) {
if (now - gameStart >= GAME_TIME) {
loseGame();
return;
}
playMusic();
readPotentiometer();
showRing();
if (now - lastLCD >= 200) {
lastLCD = now;
updateGameLCD();
}
// Подтверждение кнопкой
if (digitalRead(BUTTON_PIN) == LOW) {
if (!holding) {
holding = true;
holdStart = now;
}
if (now - holdStart >= HOLD_TIME) {
checkAnswer();
}
} else {
holding = false;
}
}
// ПАУЗА МЕЖДУ РАУНДАМИ
else if (state == FEEDBACK) {
if (now - gameStart >= GAME_TIME) {
loseGame();
}
else if (now - stateStart >= 700) {
noTone(BUZZER);
newRound();
lastMusic = now;
}
}
// ПОБЕДА
else if (state == WIN) {
if (digitalRead(BUTTON_PIN) == LOW) {
while (digitalRead(BUTTON_PIN) == LOW) {}
waitingScreen();
}
}
// ПОРАЖЕНИЕ
else if (state == LOSE) {
if (now - stateStart >= 3000) {
noTone(BUZZER);
lcd.clear();
lcd.print("GAME OVER");
lcd.setCursor(0, 1);
lcd.print("PRESS TO RETRY");
state = WAITING;
}
}
}