====== Часы Фишера для быстрых шахмат ======
** [[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 всего три ноги и расположены они слева. Можно ли использовать его в этом проекте?
Да, модуль можно использовать, но скетч, библиотеки и схема сборки отличаются.
Мы уже тестируем версию скетча для старого дисплея и разместим её на этой странице в ближайшие дни.