====== Часы Фишера для быстрых шахмат ====== ** [[slot-box:start#хочу_собрать|Проекты на Arduino и Slot Shield]] ** Обычные шахматные часы — два таймера отсчёта, работающие поочерёдно. Простой механизм привёл к появлению нового вида спорта — быстрых шахмат. Но всем, кто играл в быстрые шахматы знакома ситуация: потерянные секунды в дебюте или миттельшпиле приводят к тому, что доигрывать партию приходится «под висящим флажком». До красивой победы порой не хватает считанных мгновений. В 1989 году Роберт Фишер придумал, как уравнять шансы в цейтноте. Чемпион мира запатентовал устройство, которое поощряет игрока за сделанный ход увеличением лимита времени. Сделал ход — получил дополнительные 20 секунд на обдумывание следующего. {{ :slot-box:digital-chess-clock:digital-chess-clock.jpg |}} Алгоритм прост на бумаге, но практически не реализуем в механических часах. А с электронными часами сделать такое устройство совсем не сложно. Понадобятся две [[amp>product/troyka-led-button?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki|светодиодные кнопки]] разных цветов, [[amp>product/troyka-quad-display?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki|четырёхразрядный индикатор]] (сначала он будет показывать оставшееся время в минутах, а последнюю — отсчитывать посекундно) и [[amp>product/troyka-buzzer?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki|пьезопищалку]], которая сообщит о конце игры. * Платформы: [[amp>product/arduino-uno?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki|Arduino Uno]], [[amp>product/arduino-leonardo?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki|Arduino Leonardo]], [[amp>product/iskra-neo?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki|Iskra Neo]] * Язык программирования: Arduino (C++) ===== Что потребуется ===== [[amp>collection/digital-chess-clock?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki | Полный сет]] компонентов проекта. В сет входят: * [[amp>product/iskra-neo?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki|Iskra Neo]] * [[amp>product/arduino-troyka-slot-shield?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki|Troyka Slot Shield]] * [[amp>product/troyka-led-button?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki|светодиодные кнопки]] * [[amp>product/troyka-quad-display?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki|четырёхразрядный индикатор]] * [[amp>product/troyka-buzzer?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki|баззер]] ===== Видеоинструкция ===== {{youtube>N8A4kLf0TKM?large}} ===== Как собрать ===== Установите [[amp>product/arduino-troyka-slot-shield?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki|Troyka Slot Shield]] на [[amp>product/iskra-neo?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki|Iskra Neo]] {{ :slot-box:digital-chess-clock:step01.png |}} Перед следующими шагами переверните Slot Shield на 180 градусов Вставьте [[amp>product/troyka-quad-display?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki|светодиодные кнопки]] в левый и средний верхние слоты. {{ :slot-box:digital-chess-clock:step02.png |}} Встаьте [[amp>product/troyka-led-button?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki|зуммер]] в оставшимся свободным слот верхнего ряда. {{ :slot-box:digital-chess-clock:step03.png |}} Переверните [[amp>product/troyka-quad-display?utm_source=proj&utm_campaign=slot-proj-13&utm_medium=wiki|четырёхразрядный индикатор]] вверх ногами и вставьте в слоты, расположенные прямо под светодиодными кнопками. {{ :slot-box:digital-chess-clock:step04.png |}} ===== Скетч ===== Прошейте контроллер скетчем через [[../установка-и-настройка-arduino-ide|Arduino IDE]]. // ноты для мелодии #define NOTE_C3 131 #define NOTE_CS3 139 #define NOTE_D3 147 #define NOTE_DS3 156 #define NOTE_E3 165 #define NOTE_F3 175 #define NOTE_FS3 185 #define NOTE_G3 196 #define NOTE_GS3 208 #define NOTE_A3 220 #define NOTE_AS3 233 #define NOTE_B3 247 #define NOTE_C4 262 #define NOTE_CS4 277 #define NOTE_D4 294 #define NOTE_DS4 311 #define NOTE_E4 330 #define NOTE_F4 349 #define NOTE_FS4 370 #define NOTE_G4 392 #define NOTE_GS4 415 #define NOTE_A4 440 #define NOTE_AS4 466 #define NOTE_B4 494 #define NOTE_C5 523 // библиотека для работы с дисплеем #include // создаём объект класса QuadDisplay и передаём номера пинов CS, DI и ⎍ QuadDisplay qd(3, 2, 6); // номера цифровых пинов кнопок #define BUTTON_ONE 12 #define BUTTON_TWO 13 // номера цифровых пинов светодиодов #define LED_ONE A1 #define LED_TWO A0 // номер пина пищалки #define BUZZER A3 // таймер обратного отчёта #define TIME_START 30 // int timePlayerOne = TIME_START; int timePlayerTwo = TIME_START; // состояния игры enum State { OFF, PLAYER_ONE, PLAYER_TWO, }; // объявлеем переменную state State state; // массив мелодии при загрузке программы int startTune[] = {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}; // массив длительности нот для мелодии при старте int durationStartTune[] = {100, 200, 100, 200, 100, 400, 100, 100, 100, 100, 200, 100, 500}; // запоминаем текущее время unsigned long startMillis = millis(); void setup() { // открываем монитор Serial-порта Serial.begin(9600); // инициализация дисплея qd.begin(); // кнопки в режим входа pinMode(BUTTON_ONE, INPUT); pinMode(BUTTON_TWO, INPUT); // светодиоды в режим выхода pinMode(LED_ONE, OUTPUT); pinMode(LED_TWO, OUTPUT); // сброс игры resetGame(); } void loop() { // если нажаты обе кнопки if (!digitalRead(BUTTON_ONE) && !digitalRead(BUTTON_TWO)) { // сбрасываем игру resetGame(); } // если нажата кнопка игрока «1» if (!digitalRead(BUTTON_ONE) && state == PLAYER_ONE) { // добовляем 20 секунд к времени игрока «1» timePlayerOne = timePlayerOne + 20; // пищим бузером tone(BUZZER, NOTE_A3, 100); // ход игрока «2» state = PLAYER_TWO; // гасим светодиод игрока «1» digitalWrite(LED_ONE, LOW); // зажигаем светодиод игрока «2» digitalWrite(LED_TWO, HIGH); } else if (!digitalRead(BUTTON_TWO) && state == PLAYER_TWO) { // если нажата кнопка игрока «2» // добовляем 20 секунд к времени игрока «2» timePlayerTwo = timePlayerTwo + 20; // пищим бузером tone(BUZZER, NOTE_B3, 100);\ // ход игрока «2» state = PLAYER_ONE; // гасим светодиод игрока «1» digitalWrite(LED_TWO, LOW); // зажигаем светодиод игрока «2» digitalWrite(LED_ONE, HIGH); } // если прошла 1 секунда if (millis() - startMillis > 1000) { // проверяем какой игрок ходит switch (state) { case PLAYER_ONE: // уменьшаем время игрока «1» на 1 секунду timePlayerOne--; break; case PLAYER_TWO: // уменьшаем время игрока «2» на 1 секунду timePlayerTwo--; break; } // выводим время обоих игроков в Serial-порт Serial.print(timePlayerOne); Serial.print("\t\t"); Serial.println(timePlayerTwo); // запоминаем текущее время startMillis = millis(); } // вывод оставшигося времени обоих игроков на дисплей // два первых сегмента время игрока «1», два следующих — игрока «2» qd.displayScore(timePlayerOne < 60 ? timePlayerOne : timePlayerOne / 60, timePlayerTwo < 60 ? timePlayerTwo : timePlayerTwo / 60); // если вышло время хотя бы у одного из игроков if (timePlayerOne == 0 || timePlayerTwo == 0) { // завершаем игру endGame(); } } // функция сброса игры void resetGame() { // гасим светодиоды обоих игроков digitalWrite(LED_ONE, LOW); digitalWrite(LED_TWO, LOW); // выводим на дисплей надпись «PLAY» qd.displayDigits(QD_P, QD_L, QD_A, QD_Y); // играем привествующую мелодию melodyStart(); // выставляем время обратного отчёта timePlayerOne = TIME_START; timePlayerTwo = TIME_START; // состояние игры в режим «OFF» state = OFF; // бесконечный цикл while (1) { // если нажата кнопка игрока «1» if (!digitalRead(BUTTON_ONE)) { // пищим бузером tone(BUZZER, NOTE_A3, 100); // ход игрока «1» state = PLAYER_ONE; // зажигаем светодиод игрока «1» digitalWrite(LED_ONE, HIGH); // гасим светодиод игрока «2» digitalWrite(LED_TWO, LOW); // выходим из бесконечного цикла break; } else if (!digitalRead(BUTTON_TWO)) { // если нажата кнопка игрока «2» // пищим бузером tone(BUZZER, NOTE_B3, 100); // ход игрока «2» state = PLAYER_TWO; // зажигаем светодиод игрока «2» digitalWrite(LED_TWO, HIGH); // гасим светодиод игрока «1» digitalWrite(LED_ONE, LOW); // выходим из бесконечного цикла break; } } // вывод оставшигося времени обоих игроков на дисплей // два первых сегмента время игрока «1», два следующих — игрока «2» qd.displayScore(timePlayerOne < 60 ? timePlayerOne : timePlayerOne / 60, timePlayerTwo < 60 ? timePlayerTwo : timePlayerTwo / 60); delay(1000); } // функция окончания игры void endGame() { // играем мелодию завершения игры melodyEnd(); // гасим все свтодиоды digitalWrite(LED_ONE, LOW); digitalWrite(LED_TWO, LOW); // пока не нажата ни одна кнопка любого игрока while (digitalRead(BUTTON_ONE) && digitalRead(BUTTON_TWO)) { // крутимся в бесконечном цикле } // сбрасываем игру resetGame(); } // функция проигрывания мелодии о начале игры void melodyStart() { for (int thisNote = 0; thisNote < 13; thisNote++) { // играем следующую ноту tone(BUZZER, startTune[thisNote], durationStartTune[thisNote]); delay(durationStartTune[thisNote]); // задержка между нотами delay(25); } } // функция проигрывания мелодии о завершении игры void melodyEnd() { for (int y = 0; y <= 3; y++) { // издаём звуковой сигнал tone(BUZZER, NOTE_G3, 300); delay(200); // издаём звуковой сигнал tone(BUZZER, NOTE_C3, 300); delay(200); } } Обратите внимание Slot Shield v2 имеет другое расположение пинов. Если вы используете Slot Shield v2, то воспользуйтесь кодом ниже. // ноты для мелодии #define NOTE_C3 131 #define NOTE_CS3 139 #define NOTE_D3 147 #define NOTE_DS3 156 #define NOTE_E3 165 #define NOTE_F3 175 #define NOTE_FS3 185 #define NOTE_G3 196 #define NOTE_GS3 208 #define NOTE_A3 220 #define NOTE_AS3 233 #define NOTE_B3 247 #define NOTE_C4 262 #define NOTE_CS4 277 #define NOTE_D4 294 #define NOTE_DS4 311 #define NOTE_E4 330 #define NOTE_F4 349 #define NOTE_FS4 370 #define NOTE_G4 392 #define NOTE_GS4 415 #define NOTE_A4 440 #define NOTE_AS4 466 #define NOTE_B4 494 #define NOTE_C5 523 // библиотека для работы с дисплеем #include // создаём объект класса QuadDisplay и передаём номера пинов CS, DI и ⎍ QuadDisplay qd(6, A4, 3); // номера цифровых пинов кнопок #define BUTTON_ONE 12 #define BUTTON_TWO 13 // номера цифровых пинов светодиодов #define LED_ONE A1 #define LED_TWO A0 // номер пина пищалки #define BUZZER A3 // таймер обратного отчёта #define TIME_START 30 // int timePlayerOne = TIME_START; int timePlayerTwo = TIME_START; // состояния игры enum State { OFF, PLAYER_ONE, PLAYER_TWO, }; // объявлеем переменную state State state; // массив мелодии при загрузке программы int startTune[] = {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}; // массив длительности нот для мелодии при старте int durationStartTune[] = {100, 200, 100, 200, 100, 400, 100, 100, 100, 100, 200, 100, 500}; // запоминаем текущее время unsigned long startMillis = millis(); void setup() { // открываем монитор Serial-порта Serial.begin(9600); // инициализация дисплея qd.begin(); // кнопки в режим входа pinMode(BUTTON_ONE, INPUT); pinMode(BUTTON_TWO, INPUT); // светодиоды в режим выхода pinMode(LED_ONE, OUTPUT); pinMode(LED_TWO, OUTPUT); // сброс игры resetGame(); } void loop() { // если нажаты обе кнопки if (!digitalRead(BUTTON_ONE) && !digitalRead(BUTTON_TWO)) { // сбрасываем игру resetGame(); } // если нажата кнопка игрока «1» if (!digitalRead(BUTTON_ONE) && state == PLAYER_ONE) { // добовляем 20 секунд к времени игрока «1» timePlayerOne = timePlayerOne + 20; // пищим бузером tone(BUZZER, NOTE_A3, 100); // ход игрока «2» state = PLAYER_TWO; // гасим светодиод игрока «1» digitalWrite(LED_ONE, LOW); // зажигаем светодиод игрока «2» digitalWrite(LED_TWO, HIGH); } else if (!digitalRead(BUTTON_TWO) && state == PLAYER_TWO) { // если нажата кнопка игрока «2» // добовляем 20 секунд к времени игрока «2» timePlayerTwo = timePlayerTwo + 20; // пищим бузером tone(BUZZER, NOTE_B3, 100);\ // ход игрока «2» state = PLAYER_ONE; // гасим светодиод игрока «1» digitalWrite(LED_TWO, LOW); // зажигаем светодиод игрока «2» digitalWrite(LED_ONE, HIGH); } // если прошла 1 секунда if (millis() - startMillis > 1000) { // проверяем какой игрок ходит switch (state) { case PLAYER_ONE: // уменьшаем время игрока «1» на 1 секунду timePlayerOne--; break; case PLAYER_TWO: // уменьшаем время игрока «2» на 1 секунду timePlayerTwo--; break; } // выводим время обоих игроков в Serial-порт Serial.print(timePlayerOne); Serial.print("\t\t"); Serial.println(timePlayerTwo); // запоминаем текущее время startMillis = millis(); } // вывод оставшигося времени обоих игроков на дисплей // два первых сегмента время игрока «1», два следующих — игрока «2» qd.displayScore(timePlayerOne < 60 ? timePlayerOne : timePlayerOne / 60, timePlayerTwo < 60 ? timePlayerTwo : timePlayerTwo / 60); // если вышло время хотя бы у одного из игроков if (timePlayerOne == 0 || timePlayerTwo == 0) { // завершаем игру endGame(); } } // функция сброса игры void resetGame() { // гасим светодиоды обоих игроков digitalWrite(LED_ONE, LOW); digitalWrite(LED_TWO, LOW); // выводим на дисплей надпись «PLAY» qd.displayDigits(QD_P, QD_L, QD_A, QD_Y); // играем привествующую мелодию melodyStart(); // выставляем время обратного отчёта timePlayerOne = TIME_START; timePlayerTwo = TIME_START; // состояние игры в режим «OFF» state = OFF; // бесконечный цикл while (1) { // если нажата кнопка игрока «1» if (!digitalRead(BUTTON_ONE)) { // пищим бузером tone(BUZZER, NOTE_A3, 100); // ход игрока «1» state = PLAYER_ONE; // зажигаем светодиод игрока «1» digitalWrite(LED_ONE, HIGH); // гасим светодиод игрока «2» digitalWrite(LED_TWO, LOW); // выходим из бесконечного цикла break; } else if (!digitalRead(BUTTON_TWO)) { // если нажата кнопка игрока «2» // пищим бузером tone(BUZZER, NOTE_B3, 100); // ход игрока «2» state = PLAYER_TWO; // зажигаем светодиод игрока «2» digitalWrite(LED_TWO, HIGH); // гасим светодиод игрока «1» digitalWrite(LED_ONE, LOW); // выходим из бесконечного цикла break; } } // вывод оставшигося времени обоих игроков на дисплей // два первых сегмента время игрока «1», два следующих — игрока «2» qd.displayScore(timePlayerOne < 60 ? timePlayerOne : timePlayerOne / 60, timePlayerTwo < 60 ? timePlayerTwo : timePlayerTwo / 60); delay(1000); } // функция окончания игры void endGame() { // играем мелодию завершения игры melodyEnd(); // гасим все свтодиоды digitalWrite(LED_ONE, LOW); digitalWrite(LED_TWO, LOW); // пока не нажата ни одна кнопка любого игрока while (digitalRead(BUTTON_ONE) && digitalRead(BUTTON_TWO)) { // крутимся в бесконечном цикле } // сбрасываем игру resetGame(); } // функция проигрывания мелодии о начале игры void melodyStart() { for (int thisNote = 0; thisNote < 13; thisNote++) { // играем следующую ноту tone(BUZZER, startTune[thisNote], durationStartTune[thisNote]); delay(durationStartTune[thisNote]); // задержка между нотами delay(25); } } // функция проигрывания мелодии о завершении игры void melodyEnd() { for (int y = 0; y <= 3; y++) { // издаём звуковой сигнал tone(BUZZER, NOTE_G3, 300); delay(200); // издаём звуковой сигнал tone(BUZZER, NOTE_C3, 300); delay(200); } } ====== Что дальше? ====== Хотите собрать другой девайс? Выберите своё будущее устройство из [[slot-box:start#хочу_собрать|списка проектов на Slot Shield]]. ====== Часто задаваемые вопросы ====== > Где скачать необходимые библиотеки и как их установить? * [[https://github.com/amperka/QuadDisplay2|Библиотека для работы с дисплеем]] * [[программирование:библиотеки|Как установить библиотеку]] > У моего модуля QuadDisplay всего три ноги и расположены они слева. Можно ли использовать его в этом проекте? Да, модуль можно использовать, но скетч, библиотеки и схема сборки отличаются. Мы уже тестируем версию скетча для старого дисплея и разместим её на этой странице в ближайшие дни.