Загрузка данных



#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;
    }
  }
}