// original: https://wokwi.com/arduino/projects/304648974707458624

// changes to improve frame rate:
//  * swapped the 64x32 LED panel for four 64x8 panels
//  * track which pixels are changed: only refresh changed panels

// changes to prevent flickering projectile:
//  * removed FastLED.show() from drawGraphics()
//  * added showDirtyStrips() in opportune places


#include <FastLED.h>

constexpr uint8_t kMatrixWidth = 64;
constexpr uint8_t kMatrixHeight = 32;

#define NUM_LEDS    (kMatrixWidth * kMatrixHeight)
#define STRIP_LEDS  (NUM_LEDS / 4)
#define LED1_PIN    2
#define LED2_PIN    3
#define LED3_PIN    4
#define LED4_PIN    5
#define COLOR_ORDER GRB
#define CHIPSET     WS2811
#define BRIGHTNESS  200


CRGB leds[NUM_LEDS];
// A 1-bit flag for each strip to signal whether it needs to be refreshed
uint8_t dirtystrips = 0xf; // update all 4 strips initially

bool buttons[3];
bool buttonsHistory[3];
int button;
int counterPlayerTurn = 1;
bool hitCounter = false;

typedef struct Player{
  String name;
  int health;
  int position;
};

Player p1 = { "Megaman", 3, 1};
Player p2 = { "Cooler", 3, 1};

String welcome = "Welcome to Battle of the Heroes! Your goal is to hit the enemy castle 3 times! \n\n Blue Castle aka. " + p1.name + " can move positions with the blue button or the 's' key! \n Pink Castle aka. " + p2.name + " can move positions with the violet button or the 'k' key! \n Use the red button to or the 'b' key to shoot! \n\n The yellow point(s) in the top left corner show whose turn it is. In the top right corner you can see how much lives each of you got. \n\n Use the Potentiometer to regulate the height of your shot. \n Use the Slide Potentiometer to regulate the range of your shot, be careful, if you don't use enough power the shot might stop just in front of your opponent! \n\n NOW TO THE WEAPONS! FIGHT IS ON!";
String gameOver1 = "\n\n Well done " + p1.name + "  you knocked the opponent over and won the Game!!";
String gameOver2 = "\n\n Well done " + p2.name + "  you knocked the opponent over and won the Game!!";
String hitP1 = "\n Nice shot " + p1.name + "! You hit " + p2.name + "'s castle. They have lost one lifepoint and have ";
String hitP2 = "\n Nice shot " + p2.name + "! You hit " + p1.name + "'s castle. They have lost one lifepoint and have ";
String hitHit = " live(s) left!";

void setup() {
  Serial.begin(9600);

  FastLED.addLeds<CHIPSET, LED1_PIN, COLOR_ORDER>(leds + 0 * STRIP_LEDS,   STRIP_LEDS)  ;
  FastLED.addLeds<CHIPSET, LED2_PIN, COLOR_ORDER>(leds + 1 * STRIP_LEDS,   STRIP_LEDS)  ;
  FastLED.addLeds<CHIPSET, LED3_PIN, COLOR_ORDER>(leds + 2 * STRIP_LEDS,   STRIP_LEDS)  ;
  FastLED.addLeds<CHIPSET, LED4_PIN, COLOR_ORDER>(leds + 3 * STRIP_LEDS,   STRIP_LEDS) ;
  FastLED.setCorrection(UncorrectedColor);
  FastLED.setDither(DISABLE_DITHER);
  FastLED.setBrightness(BRIGHTNESS);

  for (int i = 6; i < 9; i++) {
    pinMode(i, INPUT_PULLUP);
  }
  drawGrass();                                          //draw the grass, castles, health bar and player turn
  drawCastles(p1.position, p2.position);
  drawPlayer(counterPlayerTurn);
  drawHealthBar();
  Serial.println(welcome);                              //print welcome message
}

void loop() {
  readButtons();

  button = buttonPressed();

  if (button != 1){                                   //if no button pressed draw aim pixel continously
    drawAim(counterPlayerTurn);
  }
  if (button > 0){                                    // if button pressed set Position off the castles
    setPositionCounter(button);

    if (button == 1){                                 //if button pressed is 1/red button initiate shoot function
      shoot();                                        //shoot function contains hit register function

      if (hitCounter == 1){                           //if castle was hit update lifepoints and reset hitcounter
        drawLifePoints(counterPlayerTurn);
        hitCounter = !hitCounter;
      }
      setCounterPlayerTurn();                         //change playerturn
    }   
    drawGrass();                                      //draw the playing field back
    drawCastles(p1.position, p2.position);
    drawPlayer(counterPlayerTurn);   
  }
}

// Button readout
void readButtons() {
  for (int i = 0; i < 3; i++) {
    buttons[i] = !digitalRead(i + 6);
  }
}

// read which button pressed
int buttonPressed() {
  for (int i = 0; i < 3; i++) {
    if (buttons[i] != buttonsHistory[i]) {
      buttonsHistory[i] = buttons[i];
      if (buttons[i]) {
        return i + 1;
      }
    }
  }
  return 0;
}

// set the Position Counter
void setPositionCounter(int button) {
  switch (button) {
    case 2:
      p1.position++;
      break;
    case 3:
      p2.position++;
      break;
  }
  if (p1.position == 4){
    p1.position = 1;
  } 
  if (p2.position == 4){
    p2.position = 1;
  } 
}

//next 4 int set the X and Y position of the castles
int setPosX1(){
  switch (p1.position){
    case 1:
      return 3;
      break;
    case 2:
      return 9;
      break;
    case 3:
      return 15;
      break;
  }
}

int setPosY1(){
  switch (p1.position){
    case 1:
      return 20;
      break;
    case 2:
      return 19;
      break;
    case 3:
      return 22;
      break;
  }
}

int setPosX2(){
  switch (p2.position){
    case 1:
      return 60;
      break;
    case 2:
      return 54;
      break;
    case 3:
      return 48;
      break;
  }
}

int setPosY2(){
  switch (p2.position){
    case 1:
      return 20;
      break;
    case 2:
      return 19;
      break;
    case 3:
      return 22;
      break;
  }
}

void drawCastles(int counterP1, int counterP2){
  uint32_t Castle_1[3] = {
    0x000ff0ff,   
    0x000ff0ff,   
    0x000ff0ff,
  };

  uint32_t Castle_2[3] = {
    0xffff0fff,   
    0xffff0fff,   
    0xffff0fff,
  };

  uint32_t Castle_Reset[3] = {
    0x00000000,   
    0x00000000,   
    0x00000000,
  };

  //function to black out previous castles
  if (counterP1 == 1){
    drawGraphics(Castle_Reset, 1, 3, 9, 19);
    drawGraphics(Castle_Reset, 1, 3, 15, 22);
  } else if (counterP1 == 2){
    drawGraphics(Castle_Reset, 1, 3, 3, 20);
    drawGraphics(Castle_Reset, 1, 3, 15, 22);
  } else if (counterP1 == 3){
    drawGraphics(Castle_Reset, 1, 3, 3, 20);
    drawGraphics(Castle_Reset, 1, 3, 9, 19);
  }
  
  if (counterP2 == 1){
    drawGraphics(Castle_Reset, 1, 3, 53, 19);
    drawGraphics(Castle_Reset, 1, 3, 48, 22);
  } else if (counterP2 == 2){
    drawGraphics(Castle_Reset, 1, 3, 60, 20);
    drawGraphics(Castle_Reset, 1, 3, 48, 22);
  } else if (counterP2 == 3){
    drawGraphics(Castle_Reset, 1, 3, 60, 20);
    drawGraphics(Castle_Reset, 1, 3, 54, 19);
  }

  //function to draw the castles in its right position
  drawGraphics(Castle_1, 1, 3, setPosX1(), setPosY1());

  drawGraphics(Castle_2, 1, 3, setPosX2(), setPosY2());
  showDirtyStrips();
}

// set whose Player turn it is
void setCounterPlayerTurn(){
  counterPlayerTurn++;
  if (counterPlayerTurn == 3){
    counterPlayerTurn = 1;
  }
}

// draw the graphics of whose turn it is
void drawPlayer(int turn){
  uint32_t Player_1[2] = {
    0xffff00, 0x000000
  };

  uint32_t Player_2[2] = {
    0xffff00, 0xffff00,
  };

  if (turn == 1){
    drawGraphics(Player_1, 2, 1, 1, 1);
  } else if (turn == 2){
    drawGraphics(Player_2, 2, 1, 1, 1);
  }
  showDirtyStrips();
}

//function draws the grass patches
void drawGrass() {
  uint32_t Grass_1_1[54] = {
    0x000ff000, 0x000ff000, 0x000ff000, 0x00000000, 0x00000000, 0x00000000,
    0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x00000000, 0x00000000,
    0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000,
  };

  uint32_t Grass_1_2[54] = {
    0x00000000, 0x00000000, 0x00000000, 0x000ff000, 0x000ff000, 0x000ff000,
    0x00000000, 0x00000000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000, 0x000ff000,
  };

  uint32_t Grass_2[30] = {
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
  };

  uint32_t Grass_3_1[21] = {
    0x00000000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
  };

  uint32_t Grass_3_2[21] = {
    0x000ff000, 0x000ff000, 0x00000000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
    0x000ff000, 0x000ff000, 0x000ff000,
  };

  drawGraphics(Grass_1_1, 6, 9, 2, 23);

  drawGraphics(Grass_1_2, 6, 9, 56, 23);

  drawGraphics(Grass_2, 3, 10, 9, 22);

  drawGraphics(Grass_2, 3, 10, 52, 22);

  drawGraphics(Grass_3_1, 3, 7, 14, 25);

  drawGraphics(Grass_3_2, 3, 7, 47, 25);
  showDirtyStrips();
}

//function draws the healthbar
void drawHealthBar(){
  uint32_t HealthBar_1[3] = {
  0x000ff0ff, 0x000ff0ff, 0x000ff0ff,
  };

  uint32_t HealthBar_2[3] = {
  0xffff0fff, 0xffff0fff, 0xffff0fff,
  };

  drawGraphics(HealthBar_1, 3, 1, 59, 1);

  drawGraphics(HealthBar_2, 3, 1, 59, 2);
}

//read slide potentiometer and return range of shot 
float shotPosX(){
  float slideVal = analogRead(A1);
  slideVal = map (slideVal, 0, 1023, 30, 70);
  return slideVal;
}

//calculate range dependent from height
float shotPosXCalc(){
  float xCorrec = map(shotPosY(), 10, 20, 0.1, 0.25);
  float minus = xCorrec * shotPosY();
  float diff = shotPosX() - minus;
  return diff;
}  

//read potentiometer and return height of shot
float shotPosY(){
  float val = analogRead(A0);
  val = map (val, 0, 1023, 10, 20);
  return val;
}

//calculate heightsteps to rise/fall each range step
float shotPosYCalc(){
  float floatCalc = (shotPosY()/(shotPosX()/2));
  return floatCalc;
}

//function to draw and delete aim at given position of castle
void drawAim(int turn){
  uint32_t aimPixel[1] = {
    0x00ff0000,
  };

  uint32_t aimPixelReset[1] = {
    0x00000000,
  };

  int aimX = map(shotPosX(), 30, 64, 3, 10);
  int aimY = map(shotPosY(), 10, 20, 1, 9);
  if (turn == 1){
    drawGraphics(aimPixel, 1, 1, setPosX1() + aimX, setPosY1() - aimY);
  } else if (turn == 2){
    drawGraphics(aimPixel, 1, 1, setPosX2() - aimX, setPosY2() - aimY);
  }
  showDirtyStrips();
  if (turn == 1){
    drawGraphics(aimPixelReset, 1, 1, setPosX1() + aimX, setPosY1() - aimY);
  } else if (turn == 2){
    drawGraphics(aimPixelReset, 1, 1, setPosX2() - aimX, setPosY2() - aimY);
  }
}

//function to shoot and following functions
void shoot(){
  float heightCalc = 0;
  float downCorrec = 0.15;                              //correction so the shot can reach enemy castle if its on the lower hill
  
  for (int i = 1; i < (shotPosX()/2); i++){             //steps go up to half of slide range

    heightCalc = heightCalc + shotPosYCalc();           //calculating the height steps - addition so the steps go up
    drawShot(i, counterPlayerTurn, heightCalc);         //initializing shot drawing funtion with range step, playerturn and height steps
  }

  for (int u = (shotPosX()/2); u < (shotPosXCalc()) + 1; u++){      //steps go from half of slide range to full slide range
    heightCalc = heightCalc - shotPosYCalc() - downCorrec;          //calculating the height steps - subtraction so the steps go down
    drawShot(u, counterPlayerTurn, heightCalc);                     //initializing shot drawing funtion with range step, playerturn and height steps

    setHitCounter(u, counterPlayerTurn, heightCalc);                //initializing hit function with range step, playerturn and height steps
    if (hitCounter == 1){                                           //if castle was hit break out the for-loop so the shot doesn't continue past the castle
      break;
    }
  }
}

//function to draw and black out the shot on its given position depending on castle position
void drawShot(int step, int turn, float heightCalc){
  uint32_t shotPixel[1] = {
    0x00ff0000,
  };

  uint32_t shotPixelReset[1] = {
    0x00000000,
  };

  if (turn == 1){
    if(setPosX1()+step<64){                                                           //only draw shot until end of screen so it doesn't continue on the other side
    drawGraphics(shotPixel, 1, 1, setPosX1() + step, setPosY1() - heightCalc);        //draw shot dependent on position of castle added with range and height
    }
  } else if (turn == 2){
     if(setPosX2() - step>1){
    drawGraphics(shotPixel, 1, 1, setPosX2() - step, setPosY2() - heightCalc);  
     }
  }
  showDirtyStrips();

  if (turn == 1){
    if(setPosX1()+step<64){
    drawGraphics(shotPixelReset, 1, 1, setPosX1() + step, setPosY1() - heightCalc);
    }
  } else if (turn == 2){
      if(setPosX2() - step>1){
    drawGraphics(shotPixelReset, 1, 1, setPosX2() - step, setPosY2() - heightCalc);
      }
    }
}

//function to register hits
void setHitCounter(int step, int turn, float heightCalc){
  int shotX1;
  int shotY1;
  int shotX2;
  int shotY2;

  if (turn == 1){
    shotX1 = setPosX1() + step;                       //set x position of shot from player1
    shotY1 = setPosY1() - heightCalc;                 //set y position of shot from player1

    if (shotX1 == setPosX2()){                        // if shot is on same position or 1 or 2 pixels below as enemy castle register as hit
      if (shotY1 == setPosY2()){
        hitCounter = !hitCounter;
      } else if(shotY1 == setPosY2() + 1){
        hitCounter = !hitCounter;
      } else if(shotY1 == setPosY2() + 2){
        hitCounter = !hitCounter;
      }
    }
  } else if (turn == 2){
      shotX2 = setPosX2() - step;                    //set x position of shot from player2
      shotY2 = setPosY2() - heightCalc;              //set y position of shot from player2

      if (shotX2 == setPosX1()){                     // if shot is on same position or 1 or 2 pixels below as enemy castle register as hit
        if (shotY2 == setPosY1()){
        hitCounter = !hitCounter;
      } else if(shotY2 == setPosY1() + 1){
        hitCounter = !hitCounter;
      } else if(shotY2 == setPosY1() + 2){
        hitCounter = !hitCounter;
      }
    }
  }
}

//function update health bar and life points - calculates game end
void drawLifePoints(int turn){
  uint32_t HealthBarReset[4] = {
   0x00000000, 0x00000000, 0x00000000, 0x00000000,
  };

  uint32_t P1[42] = {
    0x0fffffff, 0x0fffffff, 0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff,
    0x0fffffff, 0x00000000, 0x0fffffff, 0x00000000, 0x00000000, 0x0fffffff, 0x0fffffff,
    0x0fffffff, 0x0fffffff, 0x0fffffff, 0x00000000, 0x0fffffff, 0x00000000, 0x0fffffff,
    0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff,
    0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff,
    0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff,
  };

  uint32_t P2[42] = {
    0x0fffffff, 0x0fffffff, 0x0fffffff, 0x00000000, 0x0fffffff, 0x0fffffff, 0x0fffffff,
    0x0fffffff, 0x00000000, 0x0fffffff, 0x00000000, 0x0fffffff, 0x00000000, 0x0fffffff,
    0x0fffffff, 0x0fffffff, 0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff,
    0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff, 0x0fffffff, 0x00000000,
    0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff, 0x00000000, 0x00000000,
    0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff, 0x0fffffff, 0x0fffffff,
  };

  uint32_t WON[120] = { 
    0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff, 0x00000000, 0x0fffffff, 0x0fffffff, 0x0fffffff, 0x0fffffff, 0x0fffffff, 0x00000000, 0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff, 
    0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff, 0x00000000, 0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff, 0x00000000, 0x0fffffff, 0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff, 
    0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff, 0x00000000, 0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff, 0x00000000, 0x0fffffff, 0x00000000, 0x0fffffff, 0x00000000, 0x00000000, 0x0fffffff, 
    0x0fffffff, 0x00000000, 0x00000000, 0x0fffffff, 0x00000000, 0x00000000, 0x0fffffff, 0x00000000, 0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff, 0x00000000, 0x0fffffff, 0x00000000, 0x00000000, 0x0fffffff, 0x00000000, 0x0fffffff, 
    0x00000000, 0x0fffffff, 0x00000000, 0x0fffffff, 0x00000000, 0x0fffffff, 0x00000000, 0x00000000, 0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff, 0x00000000, 0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff, 0x0fffffff, 
    0x00000000, 0x00000000, 0x0fffffff, 0x00000000, 0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff, 0x0fffffff, 0x0fffffff, 0x0fffffff, 0x0fffffff, 0x00000000, 0x0fffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0fffffff, 
  };

  static int resetP1 = 0;
  static int resetP2 = 0;

  if (turn == 1){
    resetP2 = resetP2 + 1;                                        //counter raises by 1
    drawGraphics(HealthBarReset, 4, 1, 62 - resetP2, 2);          //draw the Healthbar blackout initially to the right of the healthbar adjusted with the loss of lifepoints 
    p2.health = p2.health - 1;                                    //decrease health by 1 and print message that the enemy castle was hit
    Serial.println(hitP1 + p2.health + hitHit);
  } else if (turn == 2){
    resetP1 = resetP1 + 1;
    drawGraphics(HealthBarReset, 4, 1, 62 - resetP1, 1);
    p1.health = p1.health - 1;
    Serial.println(hitP2 + p1.health + hitHit);
  }

  if (resetP1 == 3){                                              //if lifepoint loss counter reaches 3 the game ends, print out gameover message and draw the winning message
    FastLED.clear(); dirtystrips = 0xf;
    Serial.println(gameOver2);
    drawGraphics(P2, 7, 6, 17, 10);
    drawGraphics(WON, 20, 6, 26, 10);
  } else if (resetP2 == 3){
    FastLED.clear(); dirtystrips = 0xf;
    Serial.println(gameOver1);
    drawGraphics(P1, 7, 6, 17, 10);
    drawGraphics(WON, 20, 6, 26, 10);
  }
  showDirtyStrips();
}

// Convert X and Y coordinates to simple matrix index
uint16_t xy(uint8_t x, uint8_t y) {
  int i = x + (y * kMatrixWidth);
  return i;
}

void drawGraphics(uint32_t * graphicsArray, uint32_t width, uint32_t height, uint32_t xCoord, uint32_t yCoord) {
  // In order to draw images you need width, height, x and y coords of the graphic.
  // I recommend creating objects for graphics with this data as attributes.
  for (int h = 0; h < height; h++) {
    for (int w = 0; w < width; w++) {

      // Access graphics array
      uint32_t arrayIndex = w + (h * width);
      uint32_t color = graphicsArray[arrayIndex];

      // Write to leds array
      plotLED(xy(xCoord + w, yCoord + h), color);
    }
  }
}


// write a pixel and set the appropriate strip's dirty bit
void plotLED(int16_t lednum, CRGB col) {
  leds[lednum] = col;
  uint8_t bitset = 1;
  while (lednum >= STRIP_LEDS)  
    bitset <<= 1, lednum -= STRIP_LEDS;  
  dirtystrips |= bitset;
}


// show() only strips modified since the last showDirtyStrips()
void showDirtyStrips() {
  // Serial.print(dirtystrips, BIN); Serial.print(" ");
  // FastLED.show(); dirtystrips = 0; return;
  uint8_t strip = 0;
  while (dirtystrips) {
    if (dirtystrips & 1)
      FastLED[strip].showLeds(BRIGHTNESS);
    dirtystrips >>= 1;
    strip++;
  }
}