DescriptionA fidget device to help me focus by giving my hands a mindless task to do.

Process:

Image result for snes controller

My visual inspiration: a Super Nintendo Entertainment System controller

Some of the original sketches that detailed some of the form issues I would have to solve.

Designing this project was mostly a pain because of how many electronics had to be fit into such a small space. I consistently had to change the dimensions of the SPES to fit in new components like a power switch or an LED ring.

This is the ATTiny84A, a microcontroller small enough to control the SPES!

The original print of the SPES case. It is very small, and I had to make a hole in the back for a set of batteries.

 

 

 

 

 

 

 

The project was very difficult to power effectively since I was using a NeoPixel LED Ring, which uses a lot of current. My solution was to simply push the electronics as close to the front of the SPES as possible, and sneak two AA batteries in behind them.

In terms of software, I did not experience any real challenges except implementing an event-loop system to make the SPES able to change modes very quickly and reliably, as well as flash its LED’s consistently while switching game modes.

An extremely short video of the first time the event loop for flashing the LED ring worked!

The SPES in its normal place on my desk. It fits in quite well amongst the other pieces.

Discussion:

Some feedback from my classmates included:

“Some light sequences seem a bit jarring.”
I agree with this feedback, in regards to other users. However, I do not find them jarring personally, and this is a very personalized device. If I were to market this kind of product to others, I would certainly dim down any extreme color shifts and perhaps make a more cohesive user interface.

“…it would be cool to have labels.”
I also agree with this. The original plan was to have colorful buttons with letters on them, much like a Super Nintendo console controller, but acquiring the buttons would have been expensive relative to the rest of the project. I could have 3D-printed them, but their quality may have been questionable.

Overall, I am very happy with how this project turned out. It was hard work to manufacture the case for my electronics, but it looks as polished as I would have liked. The software works as was intended, but my color-blending “game” is very hard since my concept of color comes from pigmented colors like paint and colored pencils, not mixing light together. This is somewhat annoying, but helps my mind stay occupied when I am not trying to focus on something else.

I certainly did not enjoy spending all night building that case, so I learned to always start (no matter what other progress I have made) building the container earlier. I am very sensitive to the amount of sleep I get, and the day after finishing the case was absolutely terrible. The case was 3D-printed, but I ran out of material and had to remake it, which taught me to always have material handy.

The project certainly sparked a fun train of thought while I was implementing the color-blending mode. My original idea was to somehow make a different, more “traditional” game out the LED ring, but I changed it relatively late in the process. I got the idea from a game on my iPhone called Blendoku, and simply implemented its logic in a simpler format.

I would like to rebuild the SPES with a prettier body (e.g. not white) and I would like to solder the electronics absolutely perfectly so that I do have to worry about breaking hard-to-detect connections.

Schematic:

This schematic has many pushbuttons with pull-down resistors.

Code:

  1. /*=============================================================
  2. * SUPER PULANDO ENTERTAINMENT SYSTEM (SPES)
  3. *
  4. * By: Rolando Garcia III
  5. *
  6. * This code controls the SPES, a small handheld fidget device
  7. * inspired by a Super Nintendo Entertainment System controller.
  8. *
  9. * There are two "game" modes:
  10. *
  11. * • Fun with LED's - Just play around with mixing colors
  12. * and making a light move around!
  13. *
  14. * • Blendoku - You are given two colors and you must mix them properly to
  15. * win!
  16. =============================================================*/
  17. /*=============================================================
  18. * PIN MAPPING:
  19. *
  20. * • A button - 2
  21. * • B button - 3
  22. * • X button - 4
  23. * • Y button - 5
  24. * • Select button - 6
  25. * • Start button - 7
  26. * • NeoPixel Ring - 8
  27. * • Joystick switch - 9
  28. * • Joystick X - A0
  29. * • Joystick Y - A1
  30. *
  31. =============================================================*/
  32. #include <Adafruit_NeoPixel.h>
  33. #define APIN 2
  34. #define BPIN 3
  35. #define XPIN 4
  36. #define YPIN 5
  37. #define SELECT 6
  38. #define START 7
  39. #define LEDS 8
  40. #define STICKSW 9 //MUST BE PULLED UP TO WORK PROPERLY
  41. #define STICKX A0
  42. #define STICKY A1
  43. //Flashing event loop variables
  44. uint32_t lastTimeLightsOn;
  45. uint32_t lastTimeLightsOff;
  46. const int flashInterval = 250; //How long to flash on or off
  47. /*=============================================================
  48. * INPUTS:
  49. * • Joystick
  50. * • Joystick switch
  51. * • Start Button
  52. * • Select Button
  53. * • A
  54. * • B
  55. * • X
  56. * • Y
  57. =============================================================*/
  58. //Struct to hold a button and its properties, including debouncing properties
  59. struct button {
  60. int pin; //Pin mapping of button
  61. int lastState; //Previous state of button
  62. int buttonState; //Current state of button
  63. unsigned long lastBounceTime; //Last bounce time of button
  64. };
  65. //Initialize buttons
  66. struct button A = { APIN, LOW, LOW, millis() };
  67. struct button B = { BPIN, LOW, LOW, millis() };
  68. struct button X = { XPIN, LOW, LOW, millis() };
  69. struct button Y = { YPIN, LOW, LOW, millis() };
  70. struct button Select = { SELECT, LOW, LOW, millis() };
  71. struct button Start = { START, LOW, LOW, millis() };
  72. struct button StickSwitch = { STICKSW, HIGH, HIGH, millis() };
  73. //Button debounce function
  74. int debounce(struct button *b) {
  75. //Get the current reading
  76. int buttonReading = digitalRead(b->pin);
  77. bool result = HIGH;
  78. //If the buttonState has changed
  79. if (buttonReading != b->lastState) {
  80. //Reset the bounce timer
  81. b->lastBounceTime = millis();
  82. }
  83. //If enough time has passed, check if the state is the same
  84. if ( (millis() - (b->lastBounceTime)) > 10 ) {
  85. if (buttonReading != b->buttonState) {
  86. b->buttonState = buttonReading;
  87. // only toggle the LED if the new button state is HIGH
  88. if (b->buttonState == LOW) {
  89. result = !result;
  90. }
  91. }
  92. }
  93. b->lastState = buttonReading;
  94. return result;
  95. }
  96. //=============================================================
  97. //OUTPUT: NeoPixel Ring (16 LEDs)
  98. //=============================================================
  99. //Initialize the ring
  100. Adafruit_NeoPixel ring = Adafruit_NeoPixel(16, LEDS, NEO_RGBW + NEO_KHZ800);
  101. //Function to clear the lights of all their color
  102. void clearLights(){
  103. //Loop through all the pixels and set their RGBW values to all 0's
  104. for(int i = 0; i < 16; i++){
  105. ring.setPixelColor(i, 0, 0, 0, 0);
  106. }
  107. //Then show them so that the change is reflected
  108. ring.show();
  109. }
  110. /*=============================================================
  111. * GAME MODES:
  112. * true = Fun with LEDs
  113. * false = Blendoku
  114. =============================================================*/
  115. //Game state variables
  116. bool gameMode = true;
  117. bool lastMode = false;
  118. //Hue changing variables
  119. int color = 0; //Color being changed
  120. int red = 1; //Value of red
  121. int green = 1; //Value of green
  122. int blue = 1; //Value of blue
  123. const int change = 10; //Amount of change in color during increases or decreases
  124. //Increase the saturation of a certain color with no rollover
  125. void increaseColor() {
  126. //Determine which color is being changed
  127. //and ensure that it is not already maxed out
  128. if(color == 0 && red+change <= 255){
  129. red += change;
  130. } else if(color == 1 && green+change <= 255) {
  131. green += change;
  132. } else if(color == 2 && blue+change <= 255) {
  133. blue += change;
  134. }
  135. }
  136. //Decrease the saturation of a certain color with no rollover
  137. void decreaseColor() {
  138. //Determine which color is being changed
  139. //and ensure that it is not already minimized
  140. if(color == 0 && red-change >= 0){
  141. red -= change;
  142. } else if(color == 1 && green-change >= 0) {
  143. green -= change;
  144. } else if(color == 2 && blue-change >= 0) {
  145. blue -= change;
  146. }
  147. }
  148. //Function to randomize a passed-in hue
  149. void randomizeColors(int *redVal, int *greenVal, int *blueVal){
  150. *redVal = random(0,255);
  151. *greenVal = random(0,255);
  152. *blueVal = random(0,255);
  153. }
  154. //Resets all user's hue variables
  155. void resetColors(){
  156. red = 0;
  157. green = 0;
  158. blue = 0;
  159. }
  160. //Flash the center lights to alert the user of the game mode
  161. void flashLights(bool currentMode) {
  162. //For the funwithLEDs mode, simply flash white lights
  163. if (currentMode == true) {
  164. if(millis() - lastTimeLightsOff >= flashInterval){
  165. clearLights();
  166. lastTimeLightsOff = millis();
  167. }
  168. if(millis() - lastTimeLightsOn >= flashInterval*2){
  169. for (int j = 0; j < 16; j++) {
  170. ring.setPixelColor(j, 0, 0, 0, 255);
  171. }
  172. ring.show();
  173. lastTimeLightsOn = millis();
  174. }
  175. } else { //For the blendoku mode, flash colorful lights
  176. if(millis() - lastTimeLightsOff >= flashInterval){
  177. clearLights();
  178. lastTimeLightsOff = millis();
  179. }
  180. if(millis() - lastTimeLightsOn >= flashInterval*2){
  181. //For the blendoku game mode, flash colorful lights
  182. for (int j = 0; j < 16; j++) {
  183. ring.setPixelColor(j, random(0,255), random(0,255), random(0,255), 0);
  184. }
  185. ring.show();
  186. lastTimeLightsOn = millis();
  187. }
  188. }
  189. }
  190. //=============================================================
  191. //VARIABLES/FUNCTIONS FOR BLENDOKU MODE
  192. //=============================================================
  193. //Random Color 1
  194. int red1;
  195. int green1;
  196. int blue1;
  197. //Random Color 2;
  198. int red2;
  199. int green2;
  200. int blue2;
  201. //New blendoku game flag variable
  202. bool newBlendoku = true;
  203. //Randomizes the colors that the person has to blend
  204. void pick2RandomColors() {
  205. //Use the randomizeColors() function to do the work for you
  206. randomizeColors(&red1, &green1, &blue1);
  207. randomizeColors(&red2, &green2, &blue2);
  208. }
  209. //Shows player the two colors of the game
  210. void showPlayerProblem(){
  211. //Flash the first color
  212. for (int i = 0; i < 16; i++) {
  213. ring.setPixelColor(i, green1, red1, blue1, 0);
  214. }
  215. ring.show();
  216. delay(500);
  217. clearLights();
  218. delay(500);
  219. //Flash the second color
  220. for (int i = 0; i < 16; i++) {
  221. ring.setPixelColor(i, green2, red2, blue2, 0);
  222. }
  223. ring.show();
  224. delay(500);
  225. clearLights();
  226. delay(500);
  227. }
  228. //Function to show player's current color combination
  229. void showPlayerColors() {
  230. for (int i = 0; i < 16; i++) {
  231. ring.setPixelColor(i, green, red, blue, 0);
  232. }
  233. ring.show();
  234. }
  235. //Checks the user's solution
  236. bool checkBlendoku(){
  237. //Set the threshold for verifying two colors
  238. const int threshold = 200;
  239. //Determine the solution
  240. int solveRed = min(red2,red1) + (int)(abs(red1-red2)/2);
  241. int solveGreen = min(green2,green1) + (int)(abs(green1-green2)/2);
  242. int solveBlue = min(blue2,blue1) + (int)(abs(blue1-blue2)/2);
  243. //If the RGB value is between those of the random colors, they solved it
  244. if( (sq(red-solveRed) + sq(green-solveGreen) + sq(blue-solveBlue)) <= sq(threshold) ) {
  245. return true;
  246. }
  247. return false;
  248. }
  249. //Flash a green circle when a player solves a blendoku
  250. void success(){
  251. for(int j = 0; j < 2; j++){
  252. for (int i = 0; i < 16; i++) {
  253. ring.setPixelColor(i, 255, 0, 0, 0);
  254. }
  255. ring.show();
  256. delay(500);
  257. clearLights();
  258. delay(500);
  259. }
  260. }
  261. //Flash a red circle for a failure
  262. void failure(){
  263. for(int j = 0; j < 2; j++){
  264. for (int i = 0; i < 16; i++) {
  265. ring.setPixelColor(i, 0, 255, 0, 0);
  266. }
  267. ring.show();
  268. delay(500);
  269. clearLights();
  270. delay(500);
  271. }
  272. }
  273. //Game mode where the player must blend two colors
  274. void blendoku() {
  275. //If a new game is initiated, pick two new colors and show them
  276. if(newBlendoku || debounce(&Start) == LOW){
  277. clearLights();
  278. pick2RandomColors();
  279. resetColors();
  280. showPlayerProblem();
  281. newBlendoku = false;
  282. }
  283. //Allow the user to recheck the colors they were given
  284. //by moving the stick up or down
  285. if(analogRead(STICKY) >= 900 || analogRead(STICKY) <= 100){
  286. showPlayerProblem();
  287. }
  288. //Allow the user to switch the hue they are changing
  289. if(debounce(&StickSwitch) == LOW){
  290. color += 1;
  291. color %= 3;
  292. }
  293. if(debounce(&X) == LOW){
  294. increaseColor();
  295. }
  296. if(debounce(&B) == LOW){
  297. decreaseColor();
  298. }
  299. //Also allow them to reset their color selections and start over
  300. if(debounce(&Y) == LOW){
  301. resetColors();
  302. }
  303. showPlayerColors();
  304. //If the user confirms their solution, check it and start a new game
  305. if(debounce(&A) == LOW){
  306. if(checkBlendoku() == true){
  307. success();
  308. } else {
  309. failure();
  310. }
  311. newBlendoku = true;
  312. }
  313. }
  314. //VARIABLES/FUNCTIONS FOR FUNWITHLEDS MODE
  315. //Index of the light that is currently on
  316. int lightOn = 0;
  317. //Function to determine light index from joystick position
  318. float lightIndexFromJoystick(int stickX, int stickY){
  319. //Arctangent magics!
  320. float angle = atan2(stickY, stickX);
  321. int lightIndex = 15 - round((angle/1.57)*15);
  322. return lightIndex;
  323. }
  324. //Fun mode where the user can simply play with colors on the wheel
  325. void funWithLEDs() {
  326. clearLights();
  327. int stickX = analogRead(STICKX);
  328. int stickY = analogRead(STICKY);
  329. //Find how far the joystick is from its center
  330. int xDist = stickX - 512;
  331. int yDist = stickY - 512;
  332. double distance = sqrt( (float)xDist*xDist + (float)yDist*yDist );
  333. //Only change the light that is on if the analog stick is fully pushed
  334. if(distance > 100){
  335. ring.setPixelColor(lightOn, 0, 0, 0, 0);
  336. lightOn = lightIndexFromJoystick( stickX, stickY );
  337. }
  338. //Allow the user to switch the hue they are changing
  339. if(debounce(&A) == LOW){
  340. color += 1;
  341. color %= 3;
  342. }
  343. if(debounce(&X) == LOW){
  344. increaseColor();
  345. }
  346. if(debounce(&B) == LOW){
  347. decreaseColor();
  348. }
  349. //Also allow them to reset their color selections and start over
  350. if(debounce(&Y) == LOW){
  351. randomizeColors(&red, &green, &blue);
  352. }
  353. if(debounce(&Start) == LOW){
  354. resetColors();
  355. }
  356. ring.setPixelColor(lightOn, green, red, blue, 0);
  357. ring.show();
  358. }
  359. //Switches game modes when the Select button is pressed
  360. void switchGameMode() {
  361. //Switch the game mode
  362. gameMode = !gameMode;
  363. //Reinitialize blendoku
  364. if(gameMode == false){
  365. newBlendoku = true;
  366. }
  367. //Blank out the lights and reset the timers
  368. resetColors();
  369. lastTimeLightsOff = millis();
  370. lastTimeLightsOn = millis() + flashInterval;
  371. delay(10);
  372. }
  373. void setup() {
  374. // put your setup code here, to run once:
  375. pinMode(APIN, INPUT);
  376. pinMode(BPIN, INPUT);
  377. pinMode(XPIN, INPUT);
  378. pinMode(YPIN, INPUT);
  379. pinMode(STICKX, INPUT);
  380. pinMode(STICKY, INPUT);
  381. pinMode(STICKSW, INPUT_PULLUP); //Remember to pull up for reliable reading
  382. ring.setBrightness(30); //Lower the default brightness so as not to
  383. ring.begin(); //Initialize the LED ring
  384. ring.show();
  385. lastTimeLightsOff = millis();
  386. lastTimeLightsOn = millis() + flashInterval;
  387. }
  388. void loop() {
  389. //Check the select button
  390. if (debounce(&Select) == LOW) {
  391. lastMode = gameMode;
  392. switchGameMode();
  393. }
  394. //If the person has not confirmed the game mode, flash the lights at them
  395. if(lastMode != gameMode && debounce(&Start) == HIGH ){
  396. resetColors();
  397. flashLights(gameMode);
  398. return;
  399. } else {
  400. //Otherwise continue into the gameMode
  401. lastMode = gameMode;
  402. }
  403. //Determine the correct game mode and play it
  404. if (gameMode == true) {
  405. funWithLEDs();
  406. } else {
  407. blendoku();
  408. }
  409. }
/*============================================================= 
 * SUPER PULANDO ENTERTAINMENT SYSTEM (SPES)
 *
 * By: Rolando Garcia III
 * 
 * This code controls the SPES, a small handheld fidget device
 * inspired by a Super Nintendo Entertainment System controller.
 *
 * There are two "game" modes:
 *
 * • Fun with LED's - Just play around with mixing colors
 *                    and making a light move around!
 *
 * • Blendoku - You are given two colors and you must mix them properly to
 *              win!
 =============================================================*/

/*============================================================= 
 * PIN MAPPING:
 *
 * • A button - 2
 * • B button - 3
 * • X button - 4
 * • Y button - 5
 * • Select button - 6
 * • Start button - 7
 * • NeoPixel Ring - 8
 * • Joystick switch - 9
 * • Joystick X - A0
 * • Joystick Y - A1
 *
 =============================================================*/

#include <Adafruit_NeoPixel.h>
#define APIN 2
#define BPIN 3
#define XPIN 4
#define YPIN 5
#define SELECT 6
#define START 7
#define LEDS 8
#define STICKSW 9 //MUST BE PULLED UP TO WORK PROPERLY
#define STICKX A0
#define STICKY A1


//Flashing event loop variables
uint32_t lastTimeLightsOn;
uint32_t lastTimeLightsOff;
const int flashInterval = 250; //How long to flash on or off

/*=============================================================
 * INPUTS:
 * • Joystick
 * • Joystick switch
 * • Start Button
 * • Select Button
 * • A
 * • B
 * • X
 * • Y
  =============================================================*/
//Struct to hold a button and its properties, including debouncing properties
struct button {

  int pin; //Pin mapping of button
  int lastState; //Previous state of button
  int buttonState; //Current state of button
  unsigned long lastBounceTime; //Last bounce time of button

};

//Initialize buttons
struct button A = { APIN, LOW, LOW, millis() };
struct button B = { BPIN, LOW, LOW, millis() };
struct button X = { XPIN, LOW, LOW, millis() };
struct button Y = { YPIN, LOW, LOW, millis() };
struct button Select = { SELECT, LOW, LOW, millis() };
struct button Start = { START, LOW, LOW, millis() };
struct button StickSwitch = { STICKSW, HIGH, HIGH, millis() };

//Button debounce function
int debounce(struct button *b) {

  //Get the current reading
  int buttonReading = digitalRead(b->pin);
  bool result = HIGH;

  //If the buttonState has changed
  if (buttonReading != b->lastState) {
    //Reset the bounce timer
    b->lastBounceTime = millis();
  }

  //If enough time has passed, check if the state is the same
  if ( (millis() - (b->lastBounceTime)) > 10 ) {

    if (buttonReading != b->buttonState) {

      b->buttonState = buttonReading;


      // only toggle the LED if the new button state is HIGH
      if (b->buttonState == LOW) {
        result = !result;
      }
    }

  }

  b->lastState = buttonReading;

  return result;

}

//=============================================================
//OUTPUT: NeoPixel Ring (16 LEDs)
//=============================================================
//Initialize the ring
Adafruit_NeoPixel ring = Adafruit_NeoPixel(16, LEDS, NEO_RGBW + NEO_KHZ800);

//Function to clear the lights of all their color
void clearLights(){

  //Loop through all the pixels and set their RGBW values to all 0's
  for(int i = 0; i < 16; i++){
    ring.setPixelColor(i, 0, 0, 0, 0);
  }
  //Then show them so that the change is reflected
  ring.show();
  
}

/*=============================================================
 * GAME MODES:
 *  true = Fun with LEDs
 *  false = Blendoku
  =============================================================*/
//Game state variables
bool gameMode = true;
bool lastMode = false;

//Hue changing variables
int color = 0; //Color being changed
int red = 1; //Value of red
int green = 1; //Value of green
int blue = 1; //Value of blue
const int change = 10; //Amount of change in color during increases or decreases

//Increase the saturation of a certain color with no rollover
void increaseColor() {
  //Determine which color is being changed
  //and ensure that it is not already maxed out
  if(color == 0 && red+change <= 255){
    red += change;
  } else if(color == 1 && green+change <= 255) {
    green += change;
  } else if(color == 2 && blue+change <= 255) {
    blue += change;
  }
}

//Decrease the saturation of a certain color with no rollover
void decreaseColor() {
  //Determine which color is being changed
  //and ensure that it is not already minimized
  if(color == 0 && red-change >= 0){
    red -= change;
  } else if(color == 1 && green-change >= 0) {
    green -= change;
  } else if(color == 2 && blue-change >= 0) {
    blue -= change;
  }
}

//Function to randomize a passed-in hue
void randomizeColors(int *redVal, int *greenVal, int *blueVal){

  *redVal = random(0,255);
  *greenVal = random(0,255);
  *blueVal = random(0,255);
  
}

//Resets all user's hue variables
void resetColors(){
  red = 0;
  green = 0;
  blue = 0;
}

//Flash the center lights to alert the user of the game mode
void flashLights(bool currentMode) {

  //For the funwithLEDs mode, simply flash white lights
  if (currentMode == true) {

    if(millis() - lastTimeLightsOff >= flashInterval){
      clearLights();
      lastTimeLightsOff = millis();
    }

    if(millis() - lastTimeLightsOn >= flashInterval*2){
      for (int j = 0; j < 16; j++) {
  
        ring.setPixelColor(j, 0, 0, 0, 255);
  
      }
      ring.show();
      lastTimeLightsOn = millis();
    }

  } else { //For the blendoku mode, flash colorful lights

    if(millis() - lastTimeLightsOff >= flashInterval){
      clearLights();
      lastTimeLightsOff = millis();
    }
    
    if(millis() - lastTimeLightsOn >= flashInterval*2){
      //For the blendoku game mode, flash colorful lights
      for (int j = 0; j < 16; j++) {
  
        ring.setPixelColor(j, random(0,255), random(0,255), random(0,255), 0);
  
      }
      ring.show();
      lastTimeLightsOn = millis();
    }

  }

}

//=============================================================
//VARIABLES/FUNCTIONS FOR BLENDOKU MODE
//=============================================================
//Random Color 1
int red1;
int green1;
int blue1;
//Random Color 2;
int red2;
int green2;
int blue2;

//New blendoku game flag variable
bool newBlendoku = true;

//Randomizes the colors that the person has to blend
void pick2RandomColors() {

  //Use the randomizeColors() function to do the work for you
  randomizeColors(&red1, &green1, &blue1);
  randomizeColors(&red2, &green2, &blue2);
  
}

//Shows player the two colors of the game
void showPlayerProblem(){

  //Flash the first color
  for (int i = 0; i < 16; i++) {

    ring.setPixelColor(i, green1, red1, blue1, 0);

  }
  ring.show();
  delay(500);

  clearLights();
  delay(500);

  //Flash the second color
  for (int i = 0; i < 16; i++) {

    ring.setPixelColor(i, green2, red2, blue2, 0);

  }
  ring.show();
  delay(500);

  clearLights();
  delay(500);
  
}

//Function to show player's current color combination
void showPlayerColors() {

  for (int i = 0; i < 16; i++) {

    ring.setPixelColor(i, green, red, blue, 0);

  }
  ring.show();
  
}

//Checks the user's solution 
bool checkBlendoku(){

  //Set the threshold for verifying two colors
  const int threshold = 200;

  //Determine the solution
  int solveRed = min(red2,red1) + (int)(abs(red1-red2)/2);
  int solveGreen = min(green2,green1) + (int)(abs(green1-green2)/2);
  int solveBlue = min(blue2,blue1) + (int)(abs(blue1-blue2)/2);
  
  //If the RGB value is between those of the random colors, they solved it
  if( (sq(red-solveRed) + sq(green-solveGreen) + sq(blue-solveBlue)) <= sq(threshold) ) {
    return true;
  }
  
  return false;
  
}

//Flash a green circle when a player solves a blendoku
void success(){

  for(int j = 0; j < 2; j++){
    for (int i = 0; i < 16; i++) {
  
      ring.setPixelColor(i, 255, 0, 0, 0);
  
    }
    ring.show();
    delay(500);
  
    clearLights();
    delay(500);
  }
  
}

//Flash a red circle for a failure
void failure(){

  for(int j = 0; j < 2; j++){
    for (int i = 0; i < 16; i++) {
  
      ring.setPixelColor(i, 0, 255, 0, 0);
  
    }
    ring.show();
    delay(500);
  
    clearLights();
    delay(500);
  }
  
}

//Game mode where the player must blend two colors
void blendoku() {

  //If a new game is initiated, pick two new colors and show them
  if(newBlendoku || debounce(&Start) == LOW){
    clearLights();
    pick2RandomColors();
    resetColors();
    showPlayerProblem();
    newBlendoku = false;
  }

  //Allow the user to recheck the colors they were given
  //by moving the stick up or down
  if(analogRead(STICKY) >= 900 || analogRead(STICKY) <= 100){
    showPlayerProblem();
  }

  //Allow the user to switch the hue they are changing
  if(debounce(&StickSwitch) == LOW){
    color += 1;
    color %= 3;
  }

  if(debounce(&X) == LOW){
    increaseColor();
  }

  if(debounce(&B) == LOW){
    decreaseColor();
  }

  //Also allow them to reset their color selections and start over
  if(debounce(&Y) == LOW){
    resetColors();
  }

  showPlayerColors();

  //If the user confirms their solution, check it and start a new game
  if(debounce(&A) == LOW){
    
    if(checkBlendoku() == true){
      success();
    } else {
      failure();
    }
    newBlendoku = true;
    
  }


}

//VARIABLES/FUNCTIONS FOR FUNWITHLEDS MODE

//Index of the light that is currently on
int lightOn = 0;

//Function to determine light index from joystick position
float lightIndexFromJoystick(int stickX, int stickY){

  //Arctangent magics!
  float angle = atan2(stickY, stickX);
  int lightIndex = 15 - round((angle/1.57)*15);

  return lightIndex;
}

//Fun mode where the user can simply play with colors on the wheel
void funWithLEDs() {

  clearLights();
  int stickX = analogRead(STICKX);
  int stickY = analogRead(STICKY);

  //Find how far the joystick is from its center
  int xDist = stickX - 512;
  int yDist = stickY - 512;

  double distance = sqrt( (float)xDist*xDist + (float)yDist*yDist );

  //Only change the light that is on if the analog stick is fully pushed
  if(distance > 100){
    ring.setPixelColor(lightOn, 0, 0, 0, 0);
    lightOn = lightIndexFromJoystick( stickX, stickY );
  }

  //Allow the user to switch the hue they are changing
  if(debounce(&A) == LOW){
    color += 1;
    color %= 3;
  }

  if(debounce(&X) == LOW){
    increaseColor();
  }

  if(debounce(&B) == LOW){
    decreaseColor();
  }

  //Also allow them to reset their color selections and start over
  if(debounce(&Y) == LOW){
    randomizeColors(&red, &green, &blue);
  }

  if(debounce(&Start) == LOW){
    resetColors();
  }
  
  ring.setPixelColor(lightOn, green, red, blue, 0);
  ring.show();

}

//Switches game modes when the Select button is pressed
void switchGameMode() {
  //Switch the game mode
  gameMode = !gameMode;

  //Reinitialize blendoku
  if(gameMode == false){
    newBlendoku = true;
  }

  //Blank out the lights and reset the timers
  resetColors();
  lastTimeLightsOff = millis();
  lastTimeLightsOn = millis() + flashInterval;
  delay(10);
}

void setup() {
  // put your setup code here, to run once:
  pinMode(APIN, INPUT);
  pinMode(BPIN, INPUT);
  pinMode(XPIN, INPUT);
  pinMode(YPIN, INPUT);
  pinMode(STICKX, INPUT);
  pinMode(STICKY, INPUT);
  pinMode(STICKSW, INPUT_PULLUP); //Remember to pull up for reliable reading

  ring.setBrightness(30); //Lower the default brightness so as not to 
  ring.begin(); //Initialize the LED ring
  ring.show();

  lastTimeLightsOff = millis();
  lastTimeLightsOn = millis() + flashInterval;
}

void loop() {
  
  //Check the select button
  if (debounce(&Select) == LOW) {
    lastMode = gameMode;
    switchGameMode();
  }

  //If the person has not confirmed the game mode, flash the lights at them
  if(lastMode != gameMode && debounce(&Start) == HIGH ){
    resetColors();
    flashLights(gameMode);
    return;
  } else {
    //Otherwise continue into the gameMode
    lastMode = gameMode;
  }

  //Determine the correct game mode and play it
  if (gameMode == true) {

    funWithLEDs();

  } else {

    blendoku();
    
  }
}