diff --git a/Dan/devDemo/devDemo.ino b/Dan/devDemo/devDemo.ino new file mode 100644 index 0000000..176a3b2 --- /dev/null +++ b/Dan/devDemo/devDemo.ino @@ -0,0 +1,282 @@ +enum modeStates {MODEA, MODEB}; +byte currentMode; + +enum commandStates {INERT, SEND_PERSIST, SEND_SPARKLE, RESOLVING}; +byte commandState;//this is the state sent to neighbors +byte internalState;//this state is internal, and does not get sent + +Color colors[8] = {RED, GREEN, BLUE, ORANGE, MAGENTA, YELLOW, CYAN, WHITE}; +byte currentColor; +byte transitionColor; +byte sparkleBrightness[6] = {0, 0, 0, 0, 0, 0}; + +Timer sendTimer; +byte sendIncrement = 200; +Timer displayTimer; +int displayIncrement = 1785;//the amount of time it takes to complete a full fade up + +Timer animTimer; +int animIncrement; + +byte animFrame = 0; + +byte sendData; + +void setup() { + // put your setup code here, to run once: + currentMode = 0; + commandState = INERT; + internalState = INERT; + currentColor = 0; + animIncrement = 150; +} + +void loop() { + // decide which loop to run + switch (internalState) { + case INERT: + inertLoop(); + break; + case SEND_PERSIST: + sendPersistLoop(); + break; + case SEND_SPARKLE: + sendSparkleLoop(); + break; + case RESOLVING: + resolvingLoop(); + break; + } + + //communicate full state + sendData = (currentMode << 5) + (commandState << 3) + (currentColor);//bit-shifted data to fit in 6 bits + setValueSentOnAllFaces(sendData); + + //do display work + switch (internalState) { + case INERT: + inertDisplay(); + break; + case SEND_PERSIST: + sendPersistDisplay(); + break; + case SEND_SPARKLE: + sendSparkleDisplay(); + break; + case RESOLVING: + resolvingDisplay(); + break; + } +} + +void inertLoop() { + //if single clicked, move to SEND_PERSIST + if (buttonSingleClicked()) { + changeInternalState(SEND_PERSIST); + currentColor = nextColor(currentColor);; + } + + //if double clicked, move to SEND_SPARKLE + if (buttonDoubleClicked()) { + changeInternalState(SEND_SPARKLE); + currentColor = rand(7);//generate a random color + } + + //now we evaluate neighbors. if our neighbor is in either send state, move to that send state + FOREACH_FACE(f) { + if (!isValueReceivedOnFaceExpired(f)) { + byte neighborData = getLastValueReceivedOnFace(f); + if (getCommandState(neighborData) == SEND_PERSIST) { + changeInternalState(SEND_PERSIST); + currentColor = getColor(neighborData);//you are going to take on the color of the commanding neighbor + } + if (getCommandState(neighborData) == SEND_SPARKLE) { + changeInternalState(SEND_SPARKLE); + currentColor = rand(7);//generate a random color + } + } + } +} + +void sendPersistLoop() { + //first, check if it's been long enough to send the command + if (sendTimer.isExpired()) { + commandState = internalState; + } + + //now check neighbors. If they have all moved into SEND_PERSIST or RESOLVING, you can move to RESOLVING + //Only do this check if we are past the full display time + if (displayTimer.isExpired()) { + bool canResolve = true;//default to true, set to false in the face loop + FOREACH_FACE(f) { + if (!isValueReceivedOnFaceExpired(f)) {//something is here + byte neighborData = getLastValueReceivedOnFace(f); + if (getCommandState(neighborData) != SEND_PERSIST && getCommandState(neighborData) != RESOLVING) {//it is neither of the acceptable states + canResolve = false; + } + } + }//end of face loop + + //if we've survived and are stil true, we transition to resolving + if (canResolve) { + changeInternalState(RESOLVING); + commandState = RESOLVING; + } + } +} + +void sendSparkleLoop() { + //first, check if it's been long enough to send the command + if (sendTimer.isExpired()) { + commandState = internalState; + } + + //now check neighbors. If they have all moved into SEND_SPARKLE or RESOLVING, you can move to RESOLVING + //Only do this check if we are past the full display time + if (displayTimer.isExpired()) { + bool canResolve = true;//default to true, set to false in the face loop + FOREACH_FACE(f) { + if (!isValueReceivedOnFaceExpired(f)) {//something is here + byte neighborData = getLastValueReceivedOnFace(f); + if (getCommandState(neighborData) != SEND_SPARKLE && getCommandState(neighborData) != RESOLVING) {//it is neither of the acceptable states + canResolve = false; + } + } + }//end of face loop + + //if we've survived and are stil true, we transition to resolving + if (canResolve) { + changeInternalState(RESOLVING); + commandState = RESOLVING; + } + } +} + +void resolvingLoop() { + //check neighbors. If they have all moved into RESOLVING or INERT, you can move to INERT + bool canInert = true;//default to true, set to false in the face loop + FOREACH_FACE(f) { + if (!isValueReceivedOnFaceExpired(f)) {//something is here + byte neighborData = getLastValueReceivedOnFace(f); + if (getCommandState(neighborData) != RESOLVING && getCommandState(neighborData) != INERT) {//it is neither of the acceptable states + canInert = false; + } + } + }//end of face loop + + //if we've survived and are stil true, we transition to resolving + if (canInert) { + changeInternalState(INERT); + commandState = INERT; + } +} + +byte getMode(byte data) { + return (data >> 5);//the value in the first bit +} + +byte getCommandState (byte data) { + return ((data >> 3) & 3);//the value in the second and third bits +} + +byte getColor(byte data) { + return (data & 7);//the value in the fourth, fifth, and sixth bits +} + +void inertDisplay() { + if (animTimer.isExpired()) { //is it time to animate? + animTimer.set(animIncrement); + animFrame ++; + //this animation is just setting pixels to different dimness levels based on frame and position + FOREACH_FACE(f) { + byte dimnessLevel = (5 - ((f + animFrame) % 6)) * (5 - ((f + animFrame) % 6)) * 10; + setColorOnFace(dim(colors[currentColor], dimnessLevel), f); + } + + if (animFrame > 5) { + animFrame -= 6; + } + } +} + +void sendPersistDisplay() { + if (animTimer.isExpired()) { + animTimer.set(animIncrement); + animFrame ++; + int dimnessLevel = animFrame; + if (dimnessLevel > 255) { + dimnessLevel = 255; + } + setColor(dim(colors[currentColor], dimnessLevel)); + } +} + +void sendSparkleDisplay() { + if (animTimer.isExpired()) { + animTimer.set(animIncrement); + animFrame++; + FOREACH_FACE(f) { + //in this loop, we increment brightnesses, start flashes, and end flashes + if (sparkleBrightness[f] == 255) { //this face is at full brightness. Set to 0 + sparkleBrightness[f] = 0; + } else if (sparkleBrightness[f] > 0) { //not at 255, but in process. Increment by 17 + sparkleBrightness[f] += 17; + } else if (sparkleBrightness[f] == 0) { //at 0. If we are on a multiple of five frame, randomly decide to start or not start it + if (animFrame % 3 == 0) { //acceptable starting frame + sparkleBrightness[f] += rand(1) * 17;//this is either 0 or 17 + } + }//end of if/else + + //now that we've addressed the brightness of this pixel, we set it + setColorOnFace(dim(WHITE, sparkleBrightness[f]), f); + } + } +} + +void resolvingDisplay() { + +} + +void changeInternalState(byte state) { + //this is here for the moment of transition. mostly housekeeping + switch (state) { + case INERT: + animIncrement = 150; + animTimer.set(animIncrement); + animFrame = 0; + setColor(OFF); + break; + case SEND_PERSIST: + animIncrement = 7; + animTimer.set(animIncrement); + sendTimer.set(sendIncrement); + displayTimer.set(displayIncrement); + animFrame = 0; + setColor(OFF); + break; + case SEND_SPARKLE: + animIncrement = 15; + animTimer.set(animIncrement); + sendTimer.set(sendIncrement); + displayTimer.set(displayIncrement); + animFrame = 0; + setColor(OFF); + break; + case RESOLVING: + animFrame = 0; + break; + } + + internalState = state; +} + +byte nextColor(byte col) { + byte nextCol = col; + nextCol++; + if (nextCol == 8) { + nextCol = 0; + } + return nextCol; +} + + diff --git a/Dan/fractureUpgrade2/fractureUpgrade2.ino b/Dan/fractureUpgrade2/fractureUpgrade2.ino index 36809ae..c8e81df 100644 --- a/Dan/fractureUpgrade2/fractureUpgrade2.ino +++ b/Dan/fractureUpgrade2/fractureUpgrade2.ino @@ -1,21 +1,21 @@ byte gameState; byte team; -Color playerColors[] = {ORANGE, YELLOW, GREEN, CYAN}; -byte teamHues[] = {22, 42, 85, 128}; +byte teamHues[] = {22, 49, 82, 99}; enum fractureStates {NOMINAL, FRACTURED, RESOLVING}; - bool neighborStates[6]; //animation variables -bool isFlashing = false; -byte flashHue = 0; -Timer flashTimer; +Timer animTimer; +byte saturation = 255; +byte brightness = 255; + +Timer happyTimer; +bool flashOn = true; void setup() { gameState = NOMINAL; setColor(makeColorHSB(teamHues[team], 255, 255)); - FOREACH_FACE(f) { if (!isValueReceivedOnFaceExpired(f)) { neighborStates[f] = true; @@ -23,8 +23,6 @@ void setup() { neighborStates[f] = false; } } - - setFaceColor(0,RED); } void loop() { @@ -50,6 +48,7 @@ void loop() { break; } + //this is where you update your face list FOREACH_FACE(f) { if (!isValueReceivedOnFaceExpired(f)) { neighborStates[f] = true; @@ -58,6 +57,33 @@ void loop() { } } + //last, we want to resolve any flashes that have been initiated + if (animTimer.isExpired() && (saturation != 255 || brightness != 255)) { + if (saturation != 255) { + saturation += 17; + } + if (brightness != 255) { + brightness += 17; + } + animTimer.set(50); + setColor(makeColorHSB(teamHues[team], saturation, brightness)); + } + + if (happyTimer.isExpired()) { + if (saturation == 255 && brightness == 255) { //this only happens if no other flashes are happening + if (happinessCheck()) { + if (flashOn) { + setColor(OFF); + flashOn = false; + } else { + setColor(makeColorHSB(teamHues[team], 255, 255)); + flashOn = true; + } + } + } + happyTimer.set(500); + } + setValueSentOnAllFaces((gameState * 10) + team); } @@ -72,21 +98,14 @@ void nominalLoop() { } if (gameState == FRACTURED) { - //begin the red flash, change the state - isFlashing = true; - flashHue = 0; - setColor(makeColorHSB(flashHue, 255, 255)); - flashTimer.set(200); + //begin the off flash, change the state + setColor(OFF); + brightness = 0; } } void fracturedLoop() { //first we do the flash animation - if (isFlashing) { - - } - - FOREACH_FACE(f) { if (!isValueReceivedOnFaceExpired(f) && neighborStates[f] == false) { //new neighbor gameState = RESOLVING; @@ -97,6 +116,7 @@ void fracturedLoop() { if (gameState == RESOLVING) { setColor(WHITE); + saturation = 0; } } @@ -113,3 +133,23 @@ void resolvingLoop() { } } +bool happinessCheck() { + bool isHappy = true; + byte numNeighbors = 0; + + FOREACH_FACE(f) { + byte neighbor = getLastValueReceivedOnFace(f) % 10; + if (!isValueReceivedOnFaceExpired(f)) { //this means there is something there + numNeighbors++; + if (neighbor == team) { //this means this neighbor is the same color as us. Oh no! + isHappy = false; + } + } + } + if (numNeighbors < 2) { //if numNeighbors never reached 2, it's not happy + isHappy = false; + } + + return isHappy; +} + diff --git a/Dan/lifecycleTest/lifecycleTest.ino b/Dan/lifecycleTest/lifecycleTest.ino index cba8f9a..f4f71f4 100644 --- a/Dan/lifecycleTest/lifecycleTest.ino +++ b/Dan/lifecycleTest/lifecycleTest.ino @@ -2,7 +2,7 @@ enum blinkStates {SLEEP, WAKE, PROD, GAMEA, GAMEB, MENU, PROGRAMA, PROGRAMB, LEA byte blinkState; bool menuToggle = true; byte currentGame = GAMEB; -byte programType = PROGRAMA; +byte programType = PROGRAMB; bool programInitiator = false; Timer animTimer; int animFrame = 0; @@ -57,8 +57,7 @@ void loop() { } if (idleTimer.isExpired() && blinkState != SLEEP) { - setColor(OFF); - blinkState = SLEEP; + changeState(SLEEP); buttonDoubleClicked(); buttonMenuPressed(); } @@ -68,12 +67,12 @@ void loop() { void sleepLoop() { if (buttonSingleClicked()) { //someone has hit the button. This is method 1 of waking up - blinkState = WAKE; + changeState(WAKE); } FOREACH_FACE(f) { //this looks for neighbors in WAKE state. Method 2 of waking up if (!isValueReceivedOnFaceExpired(f) && getLastValueReceivedOnFace(f) == PROD) { - blinkState = WAKE; + changeState(WAKE); idleTimer.set(60000); } } @@ -91,16 +90,11 @@ void wakeLoop() { } if (animFrame > 3) { //you are halfway through wakeup. Start waking neighbors - blinkState = PROD; + changeState(PROD); } if (animFrame == 15) { //last frame of animation - blinkState = currentGame; - if (blinkState == GAMEA) { - setColor(ORANGE); - } else if (blinkState == GAMEB) { - setColor(RED); - } + changeState(currentGame); } } @@ -120,15 +114,8 @@ void gameLoop() { //now we need to start looking out for menu press, but only when alone if (buttonMenuPressed()) {//you have to do this first to set the flag to false on every check if (isAlone()) { - blinkState = MENU; + changeState(MENU); menuIdleTimer.set(10000); - setColorOnFace(dim(GREEN, 16), 0); - setColorOnFace(dim(GREEN, 16), 1); - setColorOnFace(dim(GREEN, 16), 2); - setColorOnFace(dim(BLUE, 16), 3); - setColorOnFace(dim(BLUE, 16), 4); - setColorOnFace(dim(BLUE, 16), 5); - animFrame = 0; } } @@ -136,19 +123,14 @@ void gameLoop() { FOREACH_FACE(f) { if (!isValueReceivedOnFaceExpired(f)) {//you found a neighbor if (getLastValueReceivedOnFace(f) == KO) { //KO command - blinkState = YAWN; + changeState(YAWN); menuIdleTimer.set(3000);//secondhand sleep timer - animFrame = 0; } else if (getLastValueReceivedOnFace(f) == PROGRAMA) { - blinkState = LEARNA; + changeState(LEARNA); learnTimer.set(6000); - setColor(GREEN); - animFrame = 0; } else if (getLastValueReceivedOnFace(f) == PROGRAMB) { - blinkState = LEARNB; + changeState(LEARNB); learnTimer.set(6000); - setColor(GREEN); - animFrame = 0; } }//end of found neighbor statement }//end of foreachface loop @@ -188,22 +170,17 @@ void menuLoop() { if (buttonDoubleClicked()) { //type selected, move to appropriate mode if (menuToggle) {//head to the correct programming mode - blinkState = programType; + changeState(programType); programInitiator = true; + menuIdleTimer.set(6000); } else if (!menuToggle) { - blinkState = YAWN; - menuIdleTimer.set(10000); - animFrame = 0; + changeState(YAWN); + menuIdleTimer.set(3000); } } if (menuIdleTimer.isExpired()) { - blinkState = currentGame; - if (blinkState == GAMEA) { - setColor(ORANGE); - } else if (blinkState == GAMEB) { - setColor(RED); - } + changeState(currentGame); } } @@ -217,50 +194,48 @@ void programLoop() { } if (buttonDoubleClicked()) {//return to menu if double clicked - blinkState = MENU; - setColorOnFace(dim(GREEN, 16), 0);//reset all the dimness, let the next thing correct for choice - setColorOnFace(dim(GREEN, 16), 1); - setColorOnFace(dim(GREEN, 16), 2); - setColorOnFace(dim(BLUE, 16), 3); - setColorOnFace(dim(BLUE, 16), 4); - setColorOnFace(dim(BLUE, 16), 5); - animFrame = 0; + changeState(MENU); menuIdleTimer.set(10000); programInitiator = false; } - //here we need to determine if it's safe to move to READY state - //to qualify, all neighbors must be in PROGRAMX or READY. No learners left - if (!isAlone()) {//we have some neighbors, lets look at them - bool readyCheck = false; + //in order to move from program to ready mode + //you need to check all neighbors + //IF you have neighbors in LEARNA, LEARNB, GAMEA, or GAMEB, stay in program mode + //only once you have no neighbors of that type can you move on + if (!isAlone) { + byte neighborsInNeed = 0; FOREACH_FACE(f) { - if (!isValueReceivedOnFaceExpired(f)) {//only do this check on spots WITH neighbors - if (getLastValueReceivedOnFace(f) == PROGRAMA || getLastValueReceivedOnFace(f) == PROGRAMB || getLastValueReceivedOnFace(f) == READY) { - //this face is good - readyCheck = true; - } else { - //in some other state - readyCheck = false; - } + byte neighborState = getLastValueReceivedOnFace(f); + if (!isValueReceivedOnFaceExpired(f)) { //there's something here + switch (neighborState) { + case LEARNA: + case LEARNB: + case GAMEA: + case GAMEB: + neighborsInNeed++; + break; + }//end of switch } } - //so the face loop is over. If the readyCheck is true, we're golden - if (readyCheck) { - blinkState = READY; - setColor(dim(GREEN, 16));//just set it to a dim green - if (programInitiator) { //so for the one who begins things, THIS is where they change currentGame - currentGame = programType; + + if (neighborsInNeed == 0) { //no neighbors need programming anymore. Move to ready + changeState(READY); + if (programInitiator) {//this is where the program initiator actually changes game + switch (programType) { + case PROGRAMA: + currentGame = GAMEA; + break; + case PROGRAMB: + currentGame = GAMEB; + break; + } } } - } + }//end of isAlone check if (menuIdleTimer.isExpired()) { - blinkState = currentGame; - if (blinkState == GAMEA) { - setColor(ORANGE); - } else if (blinkState == GAMEB) { - setColor(RED); - } + changeState(currentGame); programInitiator = false; } } @@ -279,39 +254,37 @@ void learnLoop() { if (learnTimer.isExpired()) {//this means the game has been "learned" and we can move to PROGRAM state if (blinkState == LEARNA) { currentGame = GAMEA; - blinkState = PROGRAMA; + changeState(PROGRAMA); } else if (blinkState == LEARNB) { currentGame = GAMEB; - blinkState = PROGRAMB; + changeState(PROGRAMB); } } } void readyLoop() { - //so now we look at all neighbors and determine if all are in READY or GAME so we can transition to game state - bool gameCheck = false; - FOREACH_FACE(f) { - if (!isValueReceivedOnFaceExpired(f)) {//only check occupied faces - if (getLastValueReceivedOnFace(f) == READY || getLastValueReceivedOnFace(f) == currentGame) { - //this face is good - gameCheck = true; - } else { - //this face is no good - gameCheck = false; - } - } - } - - if (gameCheck) { //survived the loop while still true - blinkState = currentGame; - if (currentGame == GAMEA) { - setColor(ORANGE); - } else if (currentGame = GAMEB) { - setColor(RED); - } - animFrame = 0; - idleTimer.set(1000); - } + //all we gotta do here is move to game state when it's safe + //mimicing the logic from program, we wait until no neighbors are programming or learning +// if (!isAlone) { +// byte neighborsInProgress = 0; +// FOREACH_FACE(f) { +// byte neighborState = getLastValueReceivedOnFace(f); +// if (!isValueReceivedOnFaceExpired(f)) { //there's something here +// switch (neighborState) { +// case LEARNA: +// case LEARNB: +// case PROGRAMA: +// case PROGRAMB: +// neighborsInProgress++; +// break; +// }//end of switch +// } +// } +// +// if (neighborsInProgress == 0) { //no neighbors need programming anymore. Move to ready +// changeState(currentGame); +// } +// }//end of isAlone check } void yawnLoop() { @@ -322,7 +295,7 @@ void yawnLoop() { } if (animFrame == 3) { - blinkState = KO; + changeState(KO); } if (animFrame == 30) { @@ -330,22 +303,53 @@ void yawnLoop() { } if (buttonDoubleClicked()) {//return to menu if double clicked - blinkState = MENU; - setColorOnFace(dim(GREEN, 16), 0);//reset all the dimness, let the next thing correct for choice - setColorOnFace(dim(GREEN, 16), 1); - setColorOnFace(dim(GREEN, 16), 2); - setColorOnFace(dim(BLUE, 16), 3); - setColorOnFace(dim(BLUE, 16), 4); - setColorOnFace(dim(BLUE, 16), 5); - animFrame = 0; + changeState(MENU); menuIdleTimer.set(10000); } if (menuIdleTimer.isExpired()) { - blinkState = SLEEP; - setColor(OFF); + changeState(SLEEP); buttonDoubleClicked(); buttonMenuPressed(); } } +void changeState(byte newState) { + switch (newState) { + case SLEEP: + setColor(OFF); + break; + case GAMEA: + setColor(ORANGE); + break; + case GAMEB: + setColor(RED); + break; + case MENU: + setColorOnFace(dim(GREEN, 16), 0); + setColorOnFace(dim(GREEN, 16), 1); + setColorOnFace(dim(GREEN, 16), 2); + setColorOnFace(dim(BLUE, 16), 3); + setColorOnFace(dim(BLUE, 16), 4); + setColorOnFace(dim(BLUE, 16), 5); + animFrame = 0; + break; + case PROGRAMA: + case PROGRAMB: + case LEARNA: + case LEARNB: + setColor(GREEN); + animFrame = 0; + break; + case READY: + setColor(dim(GREEN, 16)); + break; + case YAWN: + setColor(BLUE); + animFrame = 0; + break; + } + + blinkState = newState; +} +