mirror of https://github.com/nealey/puzzle-box.git
All done, I think.
This commit is contained in:
parent
5f30ec894f
commit
1b7a1ecbae
|
@ -9,9 +9,6 @@
|
||||||
|
|
||||||
// Frequencies in octave 7
|
// Frequencies in octave 7
|
||||||
const uint16_t freqs[] = {
|
const uint16_t freqs[] = {
|
||||||
3520, // A
|
|
||||||
3729, // A#
|
|
||||||
3951, // B
|
|
||||||
2093, // C
|
2093, // C
|
||||||
2217, // C#
|
2217, // C#
|
||||||
2349, // D
|
2349, // D
|
||||||
|
@ -21,6 +18,9 @@ const uint16_t freqs[] = {
|
||||||
2960, // F#
|
2960, // F#
|
||||||
3136, // G
|
3136, // G
|
||||||
3322, // G#
|
3322, // G#
|
||||||
|
3520, // A
|
||||||
|
3729, // A#
|
||||||
|
3951, // B
|
||||||
};
|
};
|
||||||
|
|
||||||
const int scale[] = {
|
const int scale[] = {
|
||||||
|
@ -99,11 +99,15 @@ OCTAVE_DONE:
|
||||||
tune = NULL;
|
tune = NULL;
|
||||||
NoTone();
|
NoTone();
|
||||||
return false;
|
return false;
|
||||||
case 'a' ... 'g':
|
case 'a' ... 'b':
|
||||||
|
++octave;
|
||||||
|
case 'c' ... 'g':
|
||||||
++octave;
|
++octave;
|
||||||
note = scale[*tune - 'a'] + (12 * octave);
|
note = scale[*tune - 'a'] + (12 * octave);
|
||||||
break;
|
break;
|
||||||
case 'A' ... 'G':
|
case 'A' ... 'B':
|
||||||
|
++octave;
|
||||||
|
case 'C' ... 'G':
|
||||||
note = scale[*tune - 'A'] + (12 * octave);
|
note = scale[*tune - 'A'] + (12 * octave);
|
||||||
break;
|
break;
|
||||||
case '_':
|
case '_':
|
||||||
|
|
|
@ -27,14 +27,14 @@ private:
|
||||||
uint16_t baseDuration;
|
uint16_t baseDuration;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TUNE_STAYIN_ALIVE "E- F P a- P2 E-2 P2 C P <B- C E- P <B- C P E- P2 <B- P C P E- P F P a- P"
|
#define TUNE_STAYIN_ALIVE "E- F _ A- P2 E-2 P2 C P <B- C E- P <B- C P E- P2 <B- P C P E- P F P A- P"
|
||||||
|
|
||||||
#define TUNE_JINGLEBELLS_CHORUS1 "eee2eee2egc.d/e4"
|
#define TUNE_JINGLEBELLS_CHORUS1 "eee2eee2egc.d/e4"
|
||||||
#define TUNE_JINGLEBELLS_CHORUS2 "fff.f/feee/e/edded2g2"
|
#define TUNE_JINGLEBELLS_CHORUS2 "fff.f/feee/e/edded2g2"
|
||||||
#define TUNE_JINGLEBELLS_CHORUS3 "fff.f/feee/e/ggfdc2._"
|
#define TUNE_JINGLEBELLS_CHORUS3 "fff.f/feee/e/ggfdc2._"
|
||||||
#define TUNE_JINGLEBELLS_MELODY1 "GedcG2.G/G/GedcA4"
|
#define TUNE_JINGLEBELLS_MELODY1 "GedcG2.G/G/GedcA4"
|
||||||
#define TUNE_JINGLEBELLS_MELODY2 "AfedB2._ ggfde2._"
|
#define TUNE_JINGLEBELLS_MELODY2 "AfedB2._ggfde2._"
|
||||||
#define TUNE_JINGLEBELLS_MELODY3 "Afedgggg agfdc2g2"
|
#define TUNE_JINGLEBELLS_MELODY3 "Afedggggagfdc2g2"
|
||||||
#define TUNE_JINGLEBELLS_CHORUS TUNE_JINGLEBELLS_CHORUS1 TUNE_JINGLEBELLS_CHORUS2 TUNE_JINGLEBELLS_CHORUS1 TUNE_JINGLEBELLS_CHORUS3
|
#define TUNE_JINGLEBELLS_CHORUS TUNE_JINGLEBELLS_CHORUS1 TUNE_JINGLEBELLS_CHORUS2 TUNE_JINGLEBELLS_CHORUS1 TUNE_JINGLEBELLS_CHORUS3
|
||||||
#define TUNE_JINGLEBELLS_MELODY TUNE_JINGLEBELLS_MELODY1 TUNE_JINGLEBELLS_MELODY2 TUNE_JINGLEBELLS_MELODY1 TUNE_JINGLEBELLS_MELODY3
|
#define TUNE_JINGLEBELLS_MELODY TUNE_JINGLEBELLS_MELODY1 TUNE_JINGLEBELLS_MELODY2 TUNE_JINGLEBELLS_MELODY1 TUNE_JINGLEBELLS_MELODY3
|
||||||
#define TUNE_JINGLEBELLS TUNE_JINGLEBELLS_MELODY TUNE_JINGLEBELLS_CHORUS
|
#define TUNE_JINGLEBELLS TUNE_JINGLEBELLS_MELODY TUNE_JINGLEBELLS_CHORUS
|
||||||
|
|
13
pulse.cpp
13
pulse.cpp
|
@ -9,11 +9,18 @@ Pulse::Pulse(unsigned long period) {
|
||||||
|
|
||||||
bool Pulse::Tick() {
|
bool Pulse::Tick() {
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
|
|
||||||
if (now >= nextEventMillis) {
|
if (now >= nextEventMillis) {
|
||||||
nextEventMillis = now + period;
|
Until(period, now);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Pulse::Until(unsigned long next, unsigned long now) {
|
||||||
|
nextEventMillis = now + next;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pulse::Until(unsigned long next) {
|
||||||
|
Until(next, millis());
|
||||||
|
}
|
||||||
|
|
10
pulse.h
10
pulse.h
|
@ -1,5 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#define MILLISECOND 1L
|
||||||
|
#define SECOND (1000 * MILLISECOND)
|
||||||
|
#define MINUTE (60 * SECOND)
|
||||||
|
#define HOUR (60 * MINUTE)
|
||||||
|
#define DAY (24 * HOUR)
|
||||||
|
|
||||||
class Pulse {
|
class Pulse {
|
||||||
public:
|
public:
|
||||||
Pulse(unsigned long period);
|
Pulse(unsigned long period);
|
||||||
|
@ -7,6 +13,10 @@ public:
|
||||||
/** Tick tells you if a period has elapsed. */
|
/** Tick tells you if a period has elapsed. */
|
||||||
bool Tick();
|
bool Tick();
|
||||||
|
|
||||||
|
/** Until sets the duration of the next period. */
|
||||||
|
void Until(unsigned long next);
|
||||||
|
void Until(unsigned long next, unsigned long now);
|
||||||
|
|
||||||
unsigned long period;
|
unsigned long period;
|
||||||
unsigned long nextEventMillis;
|
unsigned long nextEventMillis;
|
||||||
};
|
};
|
||||||
|
|
443
puzzle_box.ino
443
puzzle_box.ino
|
@ -1,55 +1,69 @@
|
||||||
#include <Adafruit_GFX.h>
|
#include <Adafruit_GFX.h>
|
||||||
|
#include <Adafruit_MPR121.h>
|
||||||
#include <Adafruit_PCD8544.h>
|
#include <Adafruit_PCD8544.h>
|
||||||
#include <FastLED.h>
|
#include <FastLED.h>
|
||||||
#include <Fonts/FreeSerif9pt7b.h>
|
#include <Keyboard.h>
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include "morse.h"
|
#include "morse.h"
|
||||||
#include "musicplayer.h"
|
#include "musicplayer.h"
|
||||||
#include "paj7620.h"
|
#include "paj7620.h"
|
||||||
#include "pulse.h"
|
#include "pulse.h"
|
||||||
#include "riddler.h"
|
|
||||||
|
|
||||||
#define NUM_PUZZLES 6
|
#define NUM_PUZZLES 4
|
||||||
|
|
||||||
// WS2812 LEDs
|
// WS2812 LEDs
|
||||||
#define LEDS_PIN 1
|
#define LEDS_PIN 13
|
||||||
#define NUM_LEDS NUM_PUZZLES
|
#define NUM_LEDS NUM_PUZZLES
|
||||||
CRGB leds[NUM_LEDS];
|
CRGB leds[NUM_LEDS];
|
||||||
CHSV ColorSolved = CHSV(32, 200, 40);
|
CHSV ColorSolved = CHSV(128, 200, 40);
|
||||||
|
CHSV ColorUnsolved = CHSV(32, 200, 40);
|
||||||
|
CHSV ColorBlack = CHSV(0, 0, 0);
|
||||||
|
|
||||||
// Laser
|
// Laser
|
||||||
#define LASER_PIN 0
|
#define LASER_PIN 7
|
||||||
|
|
||||||
// Photoresistor
|
// Photoresistor
|
||||||
#define PHOTO_PIN A0
|
#define PHOTO_PIN A0
|
||||||
|
|
||||||
// Piezo buzzer
|
// Piezo buzzer
|
||||||
#define BUZZER_PIN 11
|
#define BUZZER_PIN 12
|
||||||
MusicPlayer mp = MusicPlayer(BUZZER_PIN);
|
MusicPlayer mp = MusicPlayer(BUZZER_PIN);
|
||||||
|
|
||||||
// Display pin connections. LED needs to be PWM capable.
|
// Display pin connections. LED needs to be PWM capable.
|
||||||
#define DISPLAY_WIDTH 84
|
#define DISPLAY_WIDTH 84
|
||||||
#define DISPLAY_HEIGHT 48
|
#define DISPLAY_HEIGHT 48
|
||||||
#define DISPLAY_SCE 4
|
#define DISPLAY_SCE 4
|
||||||
#define DISPLAY_RST 7
|
#define DISPLAY_RST 6
|
||||||
#define DISPLAY_DC 8
|
#define DISPLAY_DC 5
|
||||||
#define DISPLAY_LED 9
|
#define DISPLAY_LED 9
|
||||||
Adafruit_PCD8544 display = Adafruit_PCD8544(DISPLAY_DC, DISPLAY_SCE, DISPLAY_RST);
|
Adafruit_PCD8544 display = Adafruit_PCD8544(DISPLAY_DC, DISPLAY_SCE, DISPLAY_RST);
|
||||||
|
|
||||||
// Morse code stuff
|
// Morse code stuff
|
||||||
#define DIT_DURATION 100
|
#define DIT_DURATION (100 * MILLISECOND)
|
||||||
|
|
||||||
|
// Touch sensor
|
||||||
|
Adafruit_MPR121 cap;
|
||||||
|
|
||||||
|
void yay() {
|
||||||
|
mp.Play(120 * 4, TUNE_YAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void boo() {
|
||||||
|
mp.Play(120 * 4, TUNE_BOO);
|
||||||
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
FastLED.addLeds<WS2812, LEDS_PIN, GRB>(leds, NUM_LEDS);
|
|
||||||
FastLED.setBrightness(96);
|
|
||||||
|
|
||||||
// Turn on backlight
|
// Turn on backlight
|
||||||
pinMode(DISPLAY_LED, OUTPUT);
|
pinMode(DISPLAY_LED, OUTPUT);
|
||||||
analogWrite(9, 64);
|
analogWrite(DISPLAY_LED, 64);
|
||||||
|
|
||||||
// Turn on display
|
// Turn on display
|
||||||
display.begin();
|
display.begin();
|
||||||
display.setContrast(50);
|
display.setContrast(50);
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
FastLED.addLeds<WS2812, LEDS_PIN, GRB>(leds, NUM_LEDS);
|
||||||
|
//FastLED.setBrightness(96);
|
||||||
|
|
||||||
// Gesture sensor
|
// Gesture sensor
|
||||||
while (paj7620Init()) {
|
while (paj7620Init()) {
|
||||||
|
@ -58,62 +72,103 @@ void setup() {
|
||||||
display.display();
|
display.display();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (!cap.begin()) {
|
||||||
|
display.clearDisplay();
|
||||||
|
display.print("MPR121?");
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
pinMode(LASER_PIN, OUTPUT);
|
pinMode(LASER_PIN, OUTPUT);
|
||||||
pinMode(PHOTO_PIN, INPUT);
|
pinMode(PHOTO_PIN, INPUT);
|
||||||
|
|
||||||
// Riddler symbol
|
// Riddler symbol
|
||||||
display.clearDisplay();
|
poem("Touch White", "Wire", "", "Caution: Laser");
|
||||||
display.drawBitmap(
|
display.setTextSize(2);
|
||||||
(DISPLAY_WIDTH - riddler_width) / 2,
|
display.setCursor(70, 32);
|
||||||
(DISPLAY_HEIGHT - riddler_height) / 2,
|
display.print('\x19');
|
||||||
riddler_bits,
|
|
||||||
riddler_width,
|
|
||||||
riddler_height,
|
|
||||||
0xffff);
|
|
||||||
display.display();
|
display.display();
|
||||||
|
|
||||||
|
randomSeed(analogRead(PHOTO_PIN));
|
||||||
|
|
||||||
|
// Be a USB keyboard
|
||||||
|
Keyboard.begin();
|
||||||
|
|
||||||
// Hello!
|
// Hello!
|
||||||
mp.Play(120 * 4, TUNE_YAY);
|
yay();
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if it actually printed the poem
|
||||||
|
// in other words, if we just got foreground
|
||||||
|
bool poem(const char *s1, const char *s2, const char *s3, const char *s4) {
|
||||||
|
static char *last_s1 = NULL;
|
||||||
|
|
||||||
|
if (last_s1 != s1) {
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.print(s1);
|
||||||
|
display.setCursor(0, 8);
|
||||||
|
display.print(s2);
|
||||||
|
display.setCursor(0, 16);
|
||||||
|
display.print(s3);
|
||||||
|
display.setCursor(0, 24);
|
||||||
|
display.print(s4);
|
||||||
|
display.display();
|
||||||
|
last_s1 = s1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHSV solvedPoem(bool fg) {
|
||||||
|
if (fg) {
|
||||||
|
poem("Solved!", "", "", "");
|
||||||
|
}
|
||||||
|
return ColorSolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHSV loop_morse(bool fg) {
|
CHSV loop_morse(bool fg) {
|
||||||
static Pulse pulse = Pulse(DIT_DURATION);
|
static Pulse pulse = Pulse(DIT_DURATION);
|
||||||
static MorseEncoder enc = MorseEncoder("CQ CQ KD7OQI");
|
static MorseEncoder enc = MorseEncoder("CQ");
|
||||||
static uint16_t errors = 0;
|
static uint16_t errors = 0;
|
||||||
bool solved = false;
|
static bool solved = false;
|
||||||
int recv = analogRead(PHOTO_PIN);
|
int recv = analogRead(PHOTO_PIN);
|
||||||
bool error = ((recv >= 512) != enc.Transmitting);
|
bool error = ((recv >= 512) != enc.Transmitting);
|
||||||
CHSV color;
|
CHSV color;
|
||||||
|
|
||||||
if (solved) {
|
if (solved) {
|
||||||
return ColorSolved;
|
return solvedPoem(fg);
|
||||||
} else if (error) {
|
} else if (!fg) {
|
||||||
|
digitalWrite(LASER_PIN, false);
|
||||||
|
return ColorUnsolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
poem(
|
||||||
|
"Help my sensor",
|
||||||
|
"See the light;",
|
||||||
|
"Got to get the",
|
||||||
|
"Timing right!");
|
||||||
|
|
||||||
|
color = CHSV(0, 255, recv >> 2);
|
||||||
|
if (error) {
|
||||||
++errors;
|
++errors;
|
||||||
color = CHSV(0, 255, recv >> 2);
|
|
||||||
} else {
|
|
||||||
color = CHSV(128, 255, recv >> 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pulse.Tick()) {
|
if (!pulse.Tick()) {
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fg) {
|
|
||||||
display.clearDisplay();
|
|
||||||
display.setFont();
|
|
||||||
display.setCursor(0, 0);
|
|
||||||
display.print("The Morse One");
|
|
||||||
display.display();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enc.Tick()) {
|
if (enc.Tick()) {
|
||||||
digitalWrite(LASER_PIN, enc.Transmitting);
|
digitalWrite(LASER_PIN, enc.Transmitting);
|
||||||
} else {
|
} else {
|
||||||
// We've sent the whole thing
|
// We've sent the whole thing
|
||||||
if (errors < 500) {
|
if (errors < 500) {
|
||||||
solved = true;
|
solved = true;
|
||||||
|
yay();
|
||||||
|
return solvedPoem(fg);
|
||||||
}
|
}
|
||||||
enc.SetText("HO HO HO ARK");
|
boo();
|
||||||
|
enc.SetText("HO HO HO");
|
||||||
enc.Quiet(30);
|
enc.Quiet(30);
|
||||||
errors = 0;
|
errors = 0;
|
||||||
}
|
}
|
||||||
|
@ -125,8 +180,8 @@ CHSV loop_morse(bool fg) {
|
||||||
#define DOWN 25
|
#define DOWN 25
|
||||||
#define RIGHT 26
|
#define RIGHT 26
|
||||||
#define LEFT 27
|
#define LEFT 27
|
||||||
#define IN 15
|
#define IN 'B'
|
||||||
#define OUT 9
|
#define OUT 'A'
|
||||||
const char KonamiCode[] = {
|
const char KonamiCode[] = {
|
||||||
UP,
|
UP,
|
||||||
UP,
|
UP,
|
||||||
|
@ -143,20 +198,24 @@ const char KonamiCode[] = {
|
||||||
CHSV loop_konami(bool fg) {
|
CHSV loop_konami(bool fg) {
|
||||||
static bool solved = false;
|
static bool solved = false;
|
||||||
static int pos = 0;
|
static int pos = 0;
|
||||||
static Pulse pulse = Pulse(100);
|
static Pulse pulse = Pulse(100 * MILLISECOND);
|
||||||
CHSV color;
|
CHSV color;
|
||||||
uint8_t gesture;
|
uint8_t gesture;
|
||||||
|
|
||||||
if (solved) {
|
if (solved) {
|
||||||
return ColorSolved;
|
return solvedPoem(fg);
|
||||||
|
} else if (!fg) {
|
||||||
|
return ColorUnsolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
poem(
|
||||||
|
"wave it in:",
|
||||||
|
"it codifies",
|
||||||
|
"beefy men with",
|
||||||
|
"30 lives");
|
||||||
|
|
||||||
uint8_t prox = 0;
|
uint8_t prox = 0;
|
||||||
if (!paj7620ReadReg(0x6c, 1, &prox)) {
|
paj7620ReadReg(0x6c, 1, &prox);
|
||||||
display.fillRect(0, 0, 84, 4, 0);
|
|
||||||
display.fillRect(0, 0, min(prox / 3, 84), 4, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
color = CHSV(0, 255, prox);
|
color = CHSV(0, 255, prox);
|
||||||
|
|
||||||
if (!pulse.Tick()) {
|
if (!pulse.Tick()) {
|
||||||
|
@ -193,13 +252,14 @@ CHSV loop_konami(bool fg) {
|
||||||
}
|
}
|
||||||
if (out) {
|
if (out) {
|
||||||
if (out == KonamiCode[pos]) {
|
if (out == KonamiCode[pos]) {
|
||||||
display.fillRect(pos*6, 40, 6, 8, 0);
|
display.fillRect(pos * 6, 40, 6, 8, 0);
|
||||||
display.setCursor(pos * 6, 40);
|
display.setCursor(pos * 6, 40);
|
||||||
++pos;
|
++pos;
|
||||||
} else {
|
} else {
|
||||||
display.fillRect(0, 40, 84, 8, 0);
|
display.fillRect(0, 40, 84, 8, 0);
|
||||||
display.setCursor(0, 40);
|
display.setCursor(0, 40);
|
||||||
pos = 0;
|
pos = 0;
|
||||||
|
boo();
|
||||||
}
|
}
|
||||||
display.print(out);
|
display.print(out);
|
||||||
}
|
}
|
||||||
|
@ -207,27 +267,259 @@ CHSV loop_konami(bool fg) {
|
||||||
|
|
||||||
if (KonamiCode[pos] == 0) {
|
if (KonamiCode[pos] == 0) {
|
||||||
solved = true;
|
solved = true;
|
||||||
mp.Play(120 * 4, TUNE_YAY);
|
yay();
|
||||||
color = ColorSolved;
|
return solvedPoem(fg);
|
||||||
}
|
}
|
||||||
display.display();
|
display.display();
|
||||||
|
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
void beHappy() {
|
#define ROUNDS_TO_WIN 5
|
||||||
if (!mp.KeepPlaying()) {
|
const char *inputNames[] = {
|
||||||
mp.Play(76 * 4, TUNE_JINGLEBELLS);
|
"yellow",
|
||||||
|
"green",
|
||||||
|
"red",
|
||||||
|
"blue",
|
||||||
|
};
|
||||||
|
const uint8_t numInputNames = sizeof(inputNames) / sizeof(*inputNames);
|
||||||
|
|
||||||
display.clearDisplay();
|
CHSV simonSound(int num) {
|
||||||
display.setFont(&FreeSerif9pt7b);
|
if (-1 == num) {
|
||||||
display.setCursor(0, 12);
|
mp.NoTone();
|
||||||
display.print("Happy");
|
return ColorBlack;
|
||||||
display.setCursor(0, 29);
|
} else {
|
||||||
display.print("Holiday,");
|
uint16_t hue;
|
||||||
display.setCursor(0, 46);
|
switch (num) {
|
||||||
display.print("Martin!");
|
case 0:
|
||||||
display.display();
|
mp.Tone(60);
|
||||||
|
hue = HUE_YELLOW;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
mp.Tone(55);
|
||||||
|
hue = HUE_GREEN;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
mp.Tone(50);
|
||||||
|
hue = HUE_RED;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
mp.Tone(45);
|
||||||
|
hue = HUE_BLUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return CHSV(hue, 255, 64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SIMON_TONE_DURATION (300 * MILLISECOND)
|
||||||
|
#define SIMON_QUIET_DURATION (SIMON_TONE_DURATION * 2)
|
||||||
|
#define SIMON_INPUT_TIMEOUT (4 * SECOND)
|
||||||
|
CHSV loop_simon(bool fg, uint16_t touched, uint16_t justTouched) {
|
||||||
|
static uint8_t round = 0;
|
||||||
|
static uint8_t sequencePosition = 0;
|
||||||
|
static bool solved = false;
|
||||||
|
static uint8_t sequence[ROUNDS_TO_WIN] = {0};
|
||||||
|
static uint8_t state = 0;
|
||||||
|
static Pulse pulse = Pulse(0);
|
||||||
|
static CHSV color = ColorBlack;
|
||||||
|
bool ticked = pulse.Tick();
|
||||||
|
|
||||||
|
if (solved) {
|
||||||
|
return solvedPoem(fg);
|
||||||
|
} else if (!fg) {
|
||||||
|
return ColorUnsolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reset = poem(
|
||||||
|
"For a fun game",
|
||||||
|
"we can play:",
|
||||||
|
"You repeat all",
|
||||||
|
"that I say!");
|
||||||
|
if (reset) {
|
||||||
|
state = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t input = sequence[sequencePosition];
|
||||||
|
switch (state) {
|
||||||
|
case 0: { // quiet
|
||||||
|
// Beginning of a round: be quiet for a bit
|
||||||
|
color = simonSound(-1);
|
||||||
|
pulse.Until(SIMON_QUIET_DURATION);
|
||||||
|
// Pick an input at random for the next sequence item
|
||||||
|
sequence[round] = random(numInputNames);
|
||||||
|
// Start the sequencePosition loop at position 0
|
||||||
|
sequencePosition = 0;
|
||||||
|
state = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: { // Wait for a tick
|
||||||
|
if (!ticked) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
state = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: { // beep
|
||||||
|
// set the buzzer and color
|
||||||
|
color = simonSound(input);
|
||||||
|
pulse.Until(SIMON_TONE_DURATION);
|
||||||
|
state = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: { // Wait for a tick
|
||||||
|
if (!ticked) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
state = 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: { // no beep
|
||||||
|
// Stop the tone and color
|
||||||
|
color = simonSound(-1);
|
||||||
|
// Is that the last one this round?
|
||||||
|
if (sequencePosition == round) {
|
||||||
|
// Start listening for input
|
||||||
|
pulse.Until(SIMON_INPUT_TIMEOUT);
|
||||||
|
sequencePosition = 0;
|
||||||
|
state = 5;
|
||||||
|
} else {
|
||||||
|
// We're ready to play the next tone
|
||||||
|
++sequencePosition;
|
||||||
|
pulse.Until(SIMON_TONE_DURATION);
|
||||||
|
state = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 5: { // Listen for input
|
||||||
|
if (ticked) {
|
||||||
|
// Time's up!
|
||||||
|
state = 7;
|
||||||
|
}
|
||||||
|
if (justTouched) {
|
||||||
|
if (justTouched == bit(input)) {
|
||||||
|
// That's right!
|
||||||
|
color = simonSound(input);
|
||||||
|
state = 6;
|
||||||
|
} else {
|
||||||
|
// Wrong!
|
||||||
|
state = 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 6: { // We're playing the right tone, keep doing that until they let go
|
||||||
|
if (!touched) {
|
||||||
|
color = simonSound(-1);
|
||||||
|
if (sequencePosition == round) {
|
||||||
|
// They got everything this round, increase difficulty
|
||||||
|
state = 8;
|
||||||
|
} else {
|
||||||
|
// Listen for the next one
|
||||||
|
++sequencePosition;
|
||||||
|
pulse.Until(SIMON_INPUT_TIMEOUT);
|
||||||
|
state = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 7: { // Wrong!
|
||||||
|
boo();
|
||||||
|
pulse.Until(1 * SECOND);
|
||||||
|
round = 0;
|
||||||
|
sequencePosition = 0;
|
||||||
|
state = 9;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 8: { // Increase difficulty!
|
||||||
|
++round;
|
||||||
|
if (round == ROUNDS_TO_WIN) {
|
||||||
|
yay();
|
||||||
|
solved = true;
|
||||||
|
return solvedPoem(true);
|
||||||
|
}
|
||||||
|
sequencePosition = 0;
|
||||||
|
state = 9;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 9: { // wait for tick
|
||||||
|
if (!ticked) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sequencePosition = 0;
|
||||||
|
state = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// This should never happen...
|
||||||
|
state = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define KEYBOARD_INPUT_ROUNDS 6
|
||||||
|
CHSV loop_keyboard(bool fg, uint16_t justTouched) {
|
||||||
|
static bool solved = false;
|
||||||
|
static uint8_t remaining = KEYBOARD_INPUT_ROUNDS;
|
||||||
|
static uint8_t inputName = 0;
|
||||||
|
static Pulse pulse = Pulse(6 * SECOND);
|
||||||
|
|
||||||
|
if (solved) {
|
||||||
|
return solvedPoem(fg);
|
||||||
|
} else if (!fg) {
|
||||||
|
return ColorUnsolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
poem(
|
||||||
|
"Let me talk to",
|
||||||
|
"a computer,",
|
||||||
|
"You will see I",
|
||||||
|
"get much cuter");
|
||||||
|
|
||||||
|
if (pulse.Tick()) {
|
||||||
|
inputName = random(numInputNames);
|
||||||
|
Keyboard.print("\nNow touch ");
|
||||||
|
Keyboard.print(inputNames[inputName]);
|
||||||
|
Keyboard.print("...");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (justTouched) {
|
||||||
|
Keyboard.print(" ");
|
||||||
|
if (justTouched == bit(inputName)) {
|
||||||
|
Keyboard.print("correct.");
|
||||||
|
--remaining;
|
||||||
|
} else {
|
||||||
|
Keyboard.print("wrong.");
|
||||||
|
boo();
|
||||||
|
remaining = KEYBOARD_INPUT_ROUNDS;
|
||||||
|
}
|
||||||
|
if (remaining == 0) {
|
||||||
|
Keyboard.print("\nGOOD JOB, HUMAN\n");
|
||||||
|
yay();
|
||||||
|
solved = true;
|
||||||
|
return solvedPoem(true);
|
||||||
|
}
|
||||||
|
pulse.Until(0); // Pick another wire immediately
|
||||||
|
}
|
||||||
|
|
||||||
|
return ColorBlack;
|
||||||
|
}
|
||||||
|
|
||||||
|
void beHappy() {
|
||||||
|
while (true) {
|
||||||
|
if (!mp.KeepPlaying()) {
|
||||||
|
mp.Play(76 * 4, TUNE_JINGLEBELLS);
|
||||||
|
|
||||||
|
poem(
|
||||||
|
"The final act",
|
||||||
|
"enjoy you must",
|
||||||
|
"I type for you",
|
||||||
|
"Some C++!");
|
||||||
|
|
||||||
|
Keyboard.print("Happy Holidy, Martin! https://github.com/nealey/puzzle-box\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,6 +532,16 @@ void loop() {
|
||||||
|
|
||||||
mp.KeepPlaying();
|
mp.KeepPlaying();
|
||||||
|
|
||||||
|
// Read capacative touch sensors
|
||||||
|
static uint16_t lastTouched = 0;
|
||||||
|
uint16_t touched = cap.touched();
|
||||||
|
uint16_t justTouched = (lastTouched ^ touched) & touched;
|
||||||
|
|
||||||
|
if (bitRead(justTouched, 4)) {
|
||||||
|
current = (current + 1) % NUM_PUZZLES;
|
||||||
|
tone(BUZZER_PIN, 220 * (current + 1), 220);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < NUM_PUZZLES; ++i) {
|
for (int i = 0; i < NUM_PUZZLES; ++i) {
|
||||||
CHSV color = CHSV(0, 0, 0);
|
CHSV color = CHSV(0, 0, 0);
|
||||||
bool fg = (current == i);
|
bool fg = (current == i);
|
||||||
|
@ -251,6 +553,12 @@ void loop() {
|
||||||
case 1:
|
case 1:
|
||||||
color = loop_konami(fg);
|
color = loop_konami(fg);
|
||||||
break;
|
break;
|
||||||
|
case 2:
|
||||||
|
color = loop_simon(fg, touched, justTouched);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
color = loop_keyboard(fg, justTouched);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (color != lastColors[i]) {
|
if (color != lastColors[i]) {
|
||||||
lastColors[i] = color;
|
lastColors[i] = color;
|
||||||
|
@ -262,11 +570,12 @@ void loop() {
|
||||||
leds[i] = color;
|
leds[i] = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allSolved) {
|
|
||||||
beHappy();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (writeLEDs) {
|
if (writeLEDs) {
|
||||||
FastLED.show();
|
FastLED.show();
|
||||||
}
|
}
|
||||||
|
lastTouched = touched;
|
||||||
|
|
||||||
|
if (allSolved) {
|
||||||
|
beHappy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue