#include //Library for controlled LEDs #define NUM_LEDS 16 //Number of leds per panel #define DIM 3 #define LED_DATA_PIN0 13 //Pin number of panel y=0 #define LED_DATA_PIN1 10 //Pin number of panel y=1 #define LED_DATA_PIN2 7 //Pin number of panel y=2 #define LED_DATA_PIN3 4 //Pin number of panel y=3 #define DIR_INP_PIN A5 //Input pin for direction controller #define SEL_INP_PIN 2 #define SELECTION_COLOR Blue #define PLAYER1COLOR Red #define PLAYER2COLOR Green #define WIN_PLAYER1COLOR Magenta #define WIN_PLAYER2COLOR Yellow #define VICTORY_SHINE White #define BLINKING_INTERVAL 500 //Number of millisecond for half period blinking int TTTMap[DIM+1][DIM+1][DIM+1]; CRGB LEDs0[NUM_LEDS]; CRGB LEDs1[NUM_LEDS]; CRGB LEDs2[NUM_LEDS]; CRGB LEDs3[NUM_LEDS]; bool SelBlinkStatus = 0; int ShinePosition=15; unsigned long CurrentMillis, PreviousMillis; bool ButtonPushed, SelectPushed, SelectInp; int DirInp; int ActivePlayer=1; int WinningPlayer=0; int xSel=0, ySel=0, zSel=0; //Map of the corrispondance between the virtual position and the LED physical position on the plane xz //Every map corresponds to a different y int LEDMap0[DIM+1][DIM+1] = { {3,4,11,12}, {2,5,10,13}, {1,6,9,14}, {0,7,8,15} }; int LEDMap1[DIM+1][DIM+1] = { {15,14,13,12}, {8,9,10,11}, {7,6,5,4}, {0,1,2,3} }; int LEDMap2[DIM+1][DIM+1] = { {3,4,11,12}, {2,5,10,13}, {1,6,9,14}, {0,7,8,15} }; int LEDMap3[DIM+1][DIM+1] = { {15,14,13,12}, {8,9,10,11}, {7,6,5,4}, {0,1,2,3} }; void setup() { //Start a serial input Serial.begin(9600); pinMode(SEL_INP_PIN,INPUT); PreviousMillis = millis(); //Define LED model and output pin for each panel FastLED.addLeds(LEDs0, NUM_LEDS); FastLED.addLeds(LEDs1, NUM_LEDS); FastLED.addLeds(LEDs2, NUM_LEDS); FastLED.addLeds(LEDs3, NUM_LEDS); //Initialize LEDs to black for(int count=0; count BLINKING_INTERVAL){ SelBlinkStatus = !SelBlinkStatus; PreviousMillis = CurrentMillis; } if(SelBlinkStatus ==1){ switch(ySel){ case 0: LEDs0[LEDMap0[xSel][zSel]] = CRGB::SELECTION_COLOR; break; case 1: LEDs1[LEDMap1[xSel][zSel]] = CRGB::SELECTION_COLOR; break; case 2: LEDs2[LEDMap2[xSel][zSel]] = CRGB::SELECTION_COLOR; break; case 3: LEDs3[LEDMap3[xSel][zSel]] = CRGB::SELECTION_COLOR; break; } } FastLED.show(); } void VictoryAnimation(){ // The cycles involve only x and z. // The code is repeated for each y, addressing the different maps and the different pins for(int cx=0;cx<=DIM;cx++){ for(int cz=0;cz<=DIM;cz++){ switch(TTTMap[cx][0][cz]){ case 0: LEDs0[LEDMap0[cx][cz]] = CRGB::Black; break; case 1: LEDs0[LEDMap0[cx][cz]] = CRGB::PLAYER1COLOR; break; case 2: LEDs0[LEDMap0[cx][cz]] = CRGB::PLAYER2COLOR; break; case 3: LEDs0[LEDMap0[cx][cz]] = CRGB::WIN_PLAYER1COLOR; break; case 4: LEDs0[LEDMap0[cx][cz]] = CRGB::WIN_PLAYER2COLOR; break; } } } for(int cx=0;cx<=DIM;cx++){ for(int cz=0;cz<=DIM;cz++){ switch(TTTMap[cx][1][cz]){ case 0: LEDs1[LEDMap1[cx][cz]] = CRGB::Black; break; case 1: LEDs1[LEDMap1[cx][cz]] = CRGB::PLAYER1COLOR; break; case 2: LEDs1[LEDMap1[cx][cz]] = CRGB::PLAYER2COLOR; break; case 3: LEDs1[LEDMap1[cx][cz]] = CRGB::WIN_PLAYER1COLOR; break; case 4: LEDs1[LEDMap1[cx][cz]] = CRGB::WIN_PLAYER2COLOR; break; } } } for(int cx=0;cx<=DIM;cx++){ for(int cz=0;cz<=DIM;cz++){ switch(TTTMap[cx][2][cz]){ case 0: LEDs2[LEDMap2[cx][cz]] = CRGB::Black; break; case 1: LEDs2[LEDMap2[cx][cz]] = CRGB::PLAYER1COLOR; break; case 2: LEDs2[LEDMap2[cx][cz]] = CRGB::PLAYER2COLOR; break; case 3: LEDs2[LEDMap2[cx][cz]] = CRGB::WIN_PLAYER1COLOR; break; case 4: LEDs2[LEDMap2[cx][cz]] = CRGB::WIN_PLAYER2COLOR; break; } } } for(int cx=0;cx<=DIM;cx++){ for(int cz=0;cz<=DIM;cz++){ switch(TTTMap[cx][3][cz]){ case 0: LEDs3[LEDMap3[cx][cz]] = CRGB::Black; break; case 1: LEDs3[LEDMap3[cx][cz]] = CRGB::PLAYER1COLOR; break; case 2: LEDs3[LEDMap3[cx][cz]] = CRGB::PLAYER2COLOR; break; case 3: LEDs3[LEDMap3[cx][cz]] = CRGB::WIN_PLAYER1COLOR; break; case 4: LEDs3[LEDMap3[cx][cz]] = CRGB::WIN_PLAYER2COLOR; break; } } } switch(ShinePosition){ case(15): ShinePosition=0; break; default: ShinePosition=ShinePosition+1; } LEDs0[ShinePosition] = CRGB::VICTORY_SHINE; LEDs1[ShinePosition] = CRGB::VICTORY_SHINE; LEDs2[ShinePosition] = CRGB::VICTORY_SHINE; LEDs3[ShinePosition] = CRGB::VICTORY_SHINE; delay(100); FastLED.show(); } void ReadButtonInput(){ DirInp = analogRead(DIR_INP_PIN); // DirInp : analogic input for the direction command SelectInp = digitalRead(SEL_INP_PIN); if(SelectInp == 0) SelectPushed=false; if(SelectInp == 1 && SelectPushed==false) { Serial.print("\nSelect button pushed\n"); SelectPushed=true; if(TTTMap[xSel][ySel][zSel]==0){ TTTMap[xSel][ySel][zSel]=ActivePlayer; if(ActivePlayer==1){ ActivePlayer=2; } else{ ActivePlayer=1; } } CheckVictory(); } if( DirInp < 10 ){ ButtonPushed=false;} if( 1020*0.95 < DirInp && DirInp < 1020*1.05 && ButtonPushed==false){ Serial.print("\nDirection x down\n"); ButtonPushed=true; xSel=xSel-1; } if( 507*0.95 < DirInp && DirInp < 507*1.05 && ButtonPushed==false){ Serial.print("Direction x up\n"); ButtonPushed=true; xSel=xSel+1; } if( 337*0.95 < DirInp && DirInp < 337*1.05 && ButtonPushed==false){ Serial.print("Direction y down\n"); ButtonPushed=true; ySel=ySel-1; } if( 252*0.95 < DirInp && DirInp < 252*1.05 && ButtonPushed==false){ Serial.print("Direction y up\n"); ButtonPushed=true; ySel=ySel+1; } if( 201*0.95 < DirInp && DirInp < 201*1.05 && ButtonPushed==false){ Serial.print("Direction z down\n"); ButtonPushed=true; zSel=zSel-1; } if( 168*0.95 < DirInp && DirInp < 168*1.05 && ButtonPushed==false){ Serial.print("Direction z up\n"); ButtonPushed=true; zSel=zSel+1; } switch(xSel){ case DIM+1: xSel = DIM; break; case -1: xSel=0; break; } switch(ySel){ case DIM+1: ySel = DIM; break; case -1: ySel=0; break; } switch(zSel){ case DIM+1: zSel = DIM; break; case -1: zSel=0; break; } }