// ガジェットレシピ 記憶ゲーム // Arduino UNO R3 / R4 Minima 共通(追加ライブラリは不要です) // // 配線 // LED 赤=D2 黄=D3 緑=D4 (それぞれ 1kΩ を直列に入れてGNDへ) // ボタン SW1=D5 SW2=D6 SW3=D7(INPUT_PULLUP。押すとLOW) // 受動圧電スピーカー = D8 // // 音の作り // 赤=ド(C4) 黄=ファ(F4) 緑=シ(B4)。LEDが光るときと、そのボタンを押したときに同じ音が鳴ります。 // 場面ごとに5つのメロディが鳴ります(スタート/1段クリア/ミス/全問クリア/ゲームオーバー)。 // 音を変えたいときは、下の「音の高さ」と「メロディ」だけを書き換えれば動きます。 /* ------------------------------------------------------------ ピン設定 */ constexpr byte kLedPins[] = {2, 3, 4}; // 赤・黄・緑 constexpr byte kButtonPins[] = {5, 6, 7}; // SW1・SW2・SW3 constexpr byte kBuzzerPin = 8; constexpr byte kMaxRounds = 10; // 何段まで続けるか constexpr unsigned long kInputTimeoutMs = 10000; // 無入力で失敗にするまでの時間 byte sequence[kMaxRounds]; /* ---------------------------------------------------------- 音の高さ */ // 数字は周波数(Hz)です。数が大きいほど高い音になります。 constexpr int NOTE_C2 = 65; constexpr int NOTE_C3 = 131; constexpr int NOTE_G3 = 196; constexpr int NOTE_A3 = 220; constexpr int NOTE_B3 = 247; constexpr int NOTE_C4 = 262; // ド constexpr int NOTE_D4 = 294; constexpr int NOTE_E4 = 330; constexpr int NOTE_F4 = 349; // ファ constexpr int NOTE_G4 = 392; constexpr int NOTE_B4 = 494; // シ constexpr int NOTE_G5 = 784; constexpr int NOTE_B5 = 988; constexpr int kRest = 0; // 休符 constexpr int kEnd = -1; // メロディの終わりの目印 // LEDごとの音。赤=ド、黄=ファ、緑=シ constexpr int kLedTones[] = {NOTE_C4, NOTE_F4, NOTE_B4}; /* ------------------------------------------------------------ メロディ */ // 音の並びと、その長さ。長さは「1000 ÷ この数字」ミリ秒です(4なら250ms、8なら125ms)。 const int kStartMelody[] = {NOTE_C3, kRest, NOTE_C3, kRest, NOTE_C3, kRest, NOTE_B3, kEnd}; const int kStartBeats[] = {4, 4, 4, 4, 4, 4, 2}; const int kOkMelody[] = {NOTE_B5, NOTE_G5, kEnd}; // 1段クリア const int kOkBeats[] = {8, 2}; const int kNgMelody[] = {NOTE_C2, NOTE_C2, kEnd}; // ミス const int kNgBeats[] = {8, 2}; // ↑ C2は65Hzとかなり低く、小さな圧電スピーカーでは音量が出にくい音域です。 // 聞こえにくいときは NOTE_C2 を NOTE_C3(131Hz)や NOTE_G3(196Hz)に変えると鳴りやすくなります。 const int kSuccessMelody[] = { // 全問クリア(歓びの歌) NOTE_E4, NOTE_E4, NOTE_F4, NOTE_G4, NOTE_G4, NOTE_F4, NOTE_E4, NOTE_D4, NOTE_C4, NOTE_C4, NOTE_D4, NOTE_E4, NOTE_E4, NOTE_D4, NOTE_D4, kEnd }; const int kSuccessBeats[] = {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 8, 4}; const int kFailMelody[] = { // ゲームオーバー NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, kRest, NOTE_B3, NOTE_C4, kEnd }; const int kFailBeats[] = {4, 8, 8, 4, 4, 4, 4, 4}; /* ---------------------------------------------------------------- 部品 */ void setAllLeds(bool on) { for (byte pin : kLedPins) digitalWrite(pin, on ? HIGH : LOW); } // メロディを最後(kEnd)まで鳴らす void playMelody(const int notes[], const int beats[]) { for (int i = 0; notes[i] != kEnd; i++) { const int duration = 1000 / beats[i]; if (notes[i] == kRest) { noTone(kBuzzerPin); // 休符。tone()に0を渡してはいけない } else { tone(kBuzzerPin, notes[i], duration); } const int pause = duration + duration * 3 / 10; // 音と音の間を少し空けると区切りが聞こえる delay(pause); noTone(kBuzzerPin); } } // 出題:LEDを光らせながら、その色の音を鳴らす void playSignal(byte index, int durationMs = 350) { digitalWrite(kLedPins[index], HIGH); tone(kBuzzerPin, kLedTones[index], durationMs); delay(durationMs); digitalWrite(kLedPins[index], LOW); noTone(kBuzzerPin); delay(150); } // ボタンが押されるまで待つ。押されたら 0/1/2、時間切れなら -1 を返す int waitForButton(unsigned long timeoutMs = kInputTimeoutMs) { const unsigned long startedAt = millis(); while (millis() - startedAt < timeoutMs) { for (byte i = 0; i < 3; i++) { if (digitalRead(kButtonPins[i]) == LOW) { delay(20); // チャタリング対策 digitalWrite(kLedPins[i], HIGH); // 押した色を光らせる tone(kBuzzerPin, kLedTones[i], 50); // 押した色の音を鳴らす while (digitalRead(kButtonPins[i]) == LOW) delay(5); // 離すまで待つ digitalWrite(kLedPins[i], LOW); noTone(kBuzzerPin); return i; } } } return -1; } // SW1(赤)が押されるまで、3つのLEDを点滅させて待つ void waitForStart() { while (digitalRead(kButtonPins[0]) == HIGH) { setAllLeds(true); delay(400); setAllLeds(false); delay(400); } while (digitalRead(kButtonPins[0]) == LOW) delay(5); setAllLeds(false); } // 全問クリアのときに3色を順番に走らせる void flashAll() { for (byte n = 0; n < 3; n++) { for (byte i = 0; i < 3; i++) { digitalWrite(kLedPins[i], HIGH); delay(50); digitalWrite(kLedPins[i], LOW); delay(50); } } } /* ---------------------------------------------------------------- 本体 */ void setup() { for (byte pin : kLedPins) pinMode(pin, OUTPUT); for (byte pin : kButtonPins) pinMode(pin, INPUT_PULLUP); pinMode(kBuzzerPin, OUTPUT); randomSeed(analogRead(A0)); // 毎回ちがう順番にするための種 setAllLeds(false); } void loop() { waitForStart(); playMelody(kStartMelody, kStartBeats); bool cleared = true; for (byte round = 0; round < kMaxRounds; round++) { sequence[round] = random(0, 3); // 1段ずつ問題を伸ばしていく delay(600); // 出題 for (byte i = 0; i <= round; i++) playSignal(sequence[i]); // 解答 for (byte i = 0; i <= round; i++) { const int pressed = waitForButton(); if (pressed < 0 || pressed != sequence[i]) { // 時間切れ、または違う色 cleared = false; break; } } delay(300); if (!cleared) { playMelody(kNgMelody, kNgBeats); break; } playMelody(kOkMelody, kOkBeats); } delay(500); if (cleared) { flashAll(); playMelody(kSuccessMelody, kSuccessBeats); } else { playMelody(kFailMelody, kFailBeats); } delay(1000); // このあと最初へ戻り、またSW1の押下待ちになります }