/* v0.62

  Changes in this version:
    1. Cleaned up code formatting and notes
    2. Changed Rotary Encoder to use hardware interuppts instead of polling.
    3. Tweaked code for most functions to better integrate the rotary encoders.
    4. All functions seems to be working as intended. Still needs plenty of testing though.
    
  v0.61
    1. Current Server LED Matrix code added. This seems to be working but needs further testing. 
      !!! The game seems to die after only a few points.
    2. Added proper, working code for the game_win and win_by switches.

  Previous versions:
  v0.60
    1. Added v1 of the 7-segment score display code.
    2. Removed the LCD code for the rotary encoders. but left lines for a single LCD for the main menu. Players can just look at the display to see what letter is shown.
    3. Could you use the Win_By switch as a power button of sorts? If it is in the win_by 0 (or off) position it shuts the board off. Winning by 0 (or 1 depending on how you look at it) isn't really a thing. You either win or win by two.
    4. Added big headers for code sections of rotary encoder & LCD. rocker switches, and new display.
    5. Made some minor tweaks to the serve brain.

v0.54:
    1. Rearranged the SERVE_BRAIN and BLUE/RED_SINGLE functions so that the logic for winning a game is decided in the SERVE_BRAIN now instead of each individual button
         This allows the ability for a change in either game_win or win_by to trigger the SERVE_BRAIN and continue the game if the new winning conditions aren't met.
    !!! This still needs to be tested, but it should also work in the opposite, where if the new conditions have already been met, the game is won.
    2. Modified the way the game_win and win_by variables work. There is now a "before" and "after" variable of each of them and if they don't match, i.e. a switch changes,
         then SERVE_BRAIN is called to reasses the state of the game.

  v0.532:
    1. Logic to "wrap around" from A to Z or vice-versa needs to be checked.
    2. Added trial code for 4x rotary encoders & 2x lcd screens for player initials.
    3. Added trial rocker switch code. Both areas of new code are noted, but seem to be working.
    4. Moved some code positions, mainly button definitions to the top so it's more accesible.
    5. Renamed several variables so they are easier to read and understand.
    6. Changed most variables so they are set when initially declared.
    7. Added lots of comments.

  v0.531:
    1. Scoring seems to be 100% working now.
    2. Added button hold code to reset game.

  Needs:

  Known Bugs:
  1. Correcting the score still needs some adjustment I think. It seems like it doesn't go back to an "S" at the start.
        
  Known Limitations:
  1. Score corrections are effectively limited to a single change.
    Technically you have the ability to "go back" to make a change (fix a "miss-click") as far as the very
    begining of the game, HOWEVER, any rallys made after the rally you went back to, will be effectively lost.

    This means that it works great for "going back" a point, but anything more than that would mean that you
    would have to replay the game from there. Going back a couple of rallys might be acceptable, but that would
    depend on the players.

    In other words, you can't change a previous rally and leave the rest of the game as it is now. For example,
    in bowling, you can go back to correct a specific ball in a specific frame and it won't have any impact
    except for the score. With Pickleball, the way that the serving and scoring is done impacts everything after it.
  2. Possibly too much code running in the loop function.
  3. The "reset" code may not function correctly after the first time.

  Notes:
  1. false = either the left side (from the server's POV) or the blue team.
    For example... team_serving = false is effectively the same as team_serving = blue_team.
    This was done intentially to ease switching between states.
  2. In this code, the term "rally" means the time period from just before a serve starts
    until and before the next serve starts or the game ends.

    The sole exception would be the first rally ("rally 0") which is prior to the game
    starting and is used for setting up the game.

*/


/**********************************************************************************************\
|                               START OF SCORE DISPLAY                                         |
\**********************************************************************************************/
//You could probably get away with removing the "dash" between the scores and just leave a sizable space instead. Especially if the scores will be in two different colors (i.e. Blue and Red).

// The score displays are laid out like this:
//            Position #:       1           2     9    3           4
//                ServerNumber
//  FRONT SIDE OF BOARD =   BlueHome1 : BlueHome2 :-:  RedAway1  : RedAway2

//  BACK SIDE OF BOARD  =    RedHome1 :  RedHome2 :-:  BlueAway1 : BlueAway2
//                ServerNumber
//            Position #:       5           6      10      7           8
// This way you always see your score first. The drawback is that the receiving team will see a score that doesn't match what the server calls out.
// I'm figuring that this can be easily overcome on a human level.
// Another option would be to allow it to be changed in a main menu

/**********************************************************************************************\
|                                                                                              |
|                                     ## INCLUDES ##                                           |
|                                                                                              |
\**********************************************************************************************/

#include <FastLED.h>
#include <Yabl.h>  //Yet Another Button Library
//#include <LiquidCrystal_I2C.h>
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
//#include <SPI.h>
#include <RotaryEncoder.h>



/**********************************************************************************************\
|                                                                                              |
|                                   ## PIN DEFINITIONS ##                                      |
|                                                                                              |
\**********************************************************************************************/

#define BUTTON_BlueTeam_PIN 35  // Pin connected to button B
#define BUTTON_RedTeam_PIN 34   // Pin connected to button R
#define BUTTON_RotEnc_A_PIN 1  // Pin connected to rotary encoder A button
#define BUTTON_RotEnc_B_PIN 1  // Pin connected to rotary encoder B button
#define BUTTON_RotEnc_C_PIN 1  // Pin connected to rotary encoder C button
#define BUTTON_RotEnc_D_PIN 1  // Pin connected to rotary encoder D button

//rotary encoder and lcd code below
#define CLK_A 12   //Blue_Lplayer_InitPos
#define DT_A  13   //Blue_Lplayer_InitPos
#define CLK_B 25  //Blue_Rplayer_InitPos
#define DT_B  26  //Blue_Rplayer_InitPos
#define CLK_C 27  //Red_Lplayer_InitPos
#define DT_C  14  //Red_Lplayer_InitPos
#define CLK_D 32  //Red_Rplayer_InitPos
#define DT_D  33  //Red_Rplayer_InitPos

#define SWITCH_GameWin1_PIN 22
#define SWITCH_GameWin2_PIN 23
#define SWITCH_WinBy1_PIN 19
#define SWITCH_WinBy2_PIN 21

#define CLK_PIN 18
#define DATA_PIN 2
#define CS_PIN 5

#define BlueHome1_PIN 21
#define BlueHome2_PIN 22
#define RedAway1_PIN 19
#define RedAway2_PIN 23
#define RedHome1_PIN 19
#define RedHome2_PIN 23
#define BlueAway1_PIN 21
#define BlueAway2_PIN 22
#define ServerNumber_PIN 15


#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
#define MAX_DEVICES 4
// Each digit contains 7 segments and each segment consists of 3x pixels
// Each digit is independent of the others, with the exception that the "dashes" between the team's scores (on each side) are linked to the preceding digit.
#define NUM_LEDS1 22  // 7x segments plus 1x pixel between segments E & F
#define NUM_LEDS2 27  // 7x segments plus 1x pixel between segments E & F plus 2x pixels for spacing to the next digit then ending with 3x pixels to form the dash betwixt scores.
//!!!This will either need to change to 22 (or just reuse NUM_LEDS1) and the two strips would need to be wired in parallel or use a 64bit int to allow for the longer bitstream
#define NUM_LEDS3 44  // 2x (7x segments plus 1x pixel between segments E & F)
//!!!Will running the strips in parallel cause them to be dimmer?



/**********************************************************************************************\
|                                                                                              |
|                                   ## GLOBAL VARIABLES ##                                     |
|                                                                                              |
\**********************************************************************************************/

CRGB BlueHome1[NUM_LEDS1];
CRGB BlueHome2[NUM_LEDS2];
CRGB RedAway1[NUM_LEDS1];
CRGB RedAway2[NUM_LEDS1];
CRGB RedHome1[NUM_LEDS1];
CRGB RedHome2[NUM_LEDS2];
CRGB BlueAway1[NUM_LEDS1];
CRGB BlueAway2[NUM_LEDS1];
CRGB ServerNumber[NUM_LEDS3];

byte counter_a = 0;  //Blue_Lplayer_InitPos
byte counter_b = 1;  //Blue_Rplayer_InitPos
byte counter_c = 2;  //Red_Lplayer_InitPos
byte counter_d = 3;  //Red_Rplayer_InitPos

/*int currentStateA;
int lastStateA = digitalRead(CLK_A);
int currentStateB;
int lastStateB = digitalRead(CLK_B);
int currentStateC;
int lastStateC = digitalRead(CLK_C);
int currentStateD;
int lastStateD = digitalRead(CLK_D);
*/
bool team_serving;           // this will be initially determined by the team that presses their scoring button first
bool serve_position = true;  //means the right hand side
bool start_of_game = true;   // used for initial game setup (e.g. team_serving gets set during this time) and turned false prior to the first point.
byte blue_score = 0;         //used to keep the score for the blue (aka false)team
byte red_score = 0;          //used to keep the score for the red (aka true)team
byte server_number = 5;      // this is part of the score called prior to each serve. It starts as 2 (aka "start") and can be either 1 or 2. I chose to make this a byte variable just so it was easier to print during the game.
String last_action;          // used to keep track of what happened last. Set in the single_click functions for each team and used in the serve_brain function.
byte game_win = 11;
byte win_by = 2;
byte rally_number = 0;             // used in conjunction with the below "_hist" arrays to track the game in case a score needs to be changed. Provides the ability to "go back" to the very begining of the game, but only once. See the note in the Limitations section at the begining.
bool team_serving_hist[100] = {};  // keeps track of which team served that rally !!! not sure if the "100"'s are really needed here or not
bool serve_position_hist[100] = {1, };  // keeps track of the position of the server (left side or right side)
byte blue_score_hist[100] = {0, };  // blue team score history
byte red_score_hist[100] = {0, };  // red team score history
byte server_number_hist[100] = {2, };  // keeps track of the server number for that rally
byte current_server_hist [100] = {};

int blue1stdigit = 0;
int blue2nddigit = 0;
int red1stdigit = 0;
int red2nddigit = 0;
int bluescore_color = 171;
int redscore_color = 0;
int servernumber_color = 85;
byte current_server;

//these are the default values, but should be designed to be able to be changed in the main menu eventually.
byte PlayTo_A = 7;   // used for a very quick game
byte PlayTo_B = 9;   // usually used in conjunction with "WinBy_A" below to make it a "straight nines" game.
byte PlayTo_C = 11;  // the common "play to" score
byte WinBy_A = 0;    //aka "straight" in other words the first team to the value set by game_win wins the game.
byte WinBy_B = 1;    // this was added just because I already had spdt (3-way) rocker switches and figured I may as well use the additional feature. I doubt this will ever be used.
byte WinBy_C = 2;    // this is the common "win by 2" setting.

Button buttonBlueTeam;  // used by a member of the "blue team" during the game to keep and correct the score
Button buttonRedTeam;   // used by a member of the "red team" during the game to keep and correct the score
Button buttonRotEnc_A;  //the button (switch) on the rotary encoder when it is pressed.
Button buttonRotEnc_B;
Button buttonRotEnc_C;
Button buttonRotEnc_D;
Button SWITCH_GameWin1;
Button SWITCH_GameWin2;
Button SWITCH_WinBy1;
Button SWITCH_WinBy2;

MD_Parola P = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

RotaryEncoder *RotEnc_A = nullptr;
RotaryEncoder *RotEnc_B = nullptr;
RotaryEncoder *RotEnc_C = nullptr;
RotaryEncoder *RotEnc_D = nullptr;

/*  Use toggle switches to allow for quick adjustment of:
  1. Three-way (SPDT): For quick "play-to" changes. For instance the defaults could be 7, 9 and 11, but those can be changed in the menu.
  2. Three-way (SPDT): For winning by 2pts or just "straight". Changed to three-way (SPDT) because I already had them.
  3. Three-way (SPDT): For singles vs. doubles vs. "3-man" games. This will require a whole new set of code to accomodate.

  Wiring plan...
  game_win_rocker switch:
  position 0 (middle) to ground
  position 1 (game_win_position1) to arduino pin 17 (GW_Rocker1)
  position 2 (game_win_position2) to arduino pin 18 (GW_Rocker2)
  win_by_rocker switch:
  position 0 (middle) to ground
  position 1 (win_by_position1) to arduino pin 19 (WB_Rocker1)
  position 2 (win_by_position2) to arduino pin 20 (WB_Rocker2)
*/




/*
int blue1stdigit = 0;
int blue2nddigit = 0;
int red1stdigit = 0;
int red2nddigit = 0;
//int serverdigit = 5;
byte bluescore_color = CRGB::Blue;
byte redscore_color = CRGB::Red;
byte servernumber_color = CRGB::Red;
 */
//First Digit


/**********************************************************************************************\
|                                                                                              |
|                                          ## CONSTANTS ##                                     |
|                                                                                              |
\**********************************************************************************************/
//Below is the traditional layout for a 7-segment display.

//    / AAA \
//    F     B
//    F     B
//    F     B
//    + GGG +
//    E     C
//    E     C
//    E     C
//    \ DDD /

// LED's have been run through the segments like this: G->B->A->F->E->D->C
// Since the number is binary, the digits are read starting from the right and moving left
// The first two charachters "0b" are to designate that the following is a binary number, then that is followed by padding to fill out the full 32 bits
const uint32_t first_digit[10] = {
  0b00000000001111111110111111111000,  //0 <-- this is the 7-segment number represented by this pattern.
  0b00000000001110000000000000111000,  //1
  0b00000000000001111110000111111111,  //2     You'll notice that every column has a 0 in the 13th place (when read from the right as binary is read)
  0b00000000001111110000000111111111,  //3
  0b00000000001110000000111000111111,  //4     This X or blank pixel is there because of the physical way the LED's were wired. It was easier to just
  0b00000000001111110000111111000111,  //5     add a 0 in that spot in the code, than to have to physcially cut and solder the strip.
  0b00000000001111111110111111000111,  //6
  0b00000000001110000000000111111000,  //7
  0b00000000001111111110111111111111,  //8
  0b00000000001111110000111111111111   //9
};//**********CCCDDDEEEXFFFAAABBBGGG<---- Then these are the traditional segment labels in a 7-segment display. The X is an LED that is never used, and the * are the 0's used to pad the line out to 32 bits

const uint32_t second_digit[10] = {
  0b00000111001111111110111111111000,  //0
  0b00000111001110000000000000111000,  //1
  0b00000111000001111110000111111111,  //2
  0b00000111001111110000000111111111,  //3
  0b00000111001110000000111000111111,  //4
  0b00000111001111110000111111000111,  //5
  0b00000111001111111110111111000111,  //6
  0b00000111001110000000000111111000,  //7
  0b00000111001111111110111111111111,  //8
  0b00000111001111110000111111111111   //9
};//*****---XXCCCDDDEEEXFFFAAABBBGGG <-- For the 2nd or right hand digit, I've added 2x always blank LED's followed by 3x always on LED's for the "dash" betwixt the two scores, so it will look like... 00-00


const uint64_t server_digit[6] = {
  0b0000000000000000000000000000000000000000000000000000000000000000,  //0 N/A because this isn't a valid server number, but it's here to "pad" the numbers so you can still use this as normal. Otherwise when the server number comes in as 1 and if there were only the three lines containing 1, 2, and 5, it would display 2 which is in position 1.
  0b0000000000000000000011100000000000001110001110000000000000111000,  //1 Server 1
  0b0000000000000000000000011111100001111111110001111110000111111111,  //2 Server 2
  0b0000000000000000000000000000000000000000000000000000000000000000,  //3 N/A
  0b0000000000000000000000000000000000000000000000000000000000000000,  //4 N/A
  0b0000000000000000000011111100001111110001111111110000111111000111,  //5 Server 5 but to be interpreted as S for Start.
};//********************CCCDDDEEEXFFFAAABBBGGGCCCDDDEEEXFFFAAABBBGGG

const char *Player1_Initial[26] = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
const char *Player2_Initial[26] = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
const char *Player3_Initial[26] = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
const char *Player4_Initial[26] = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };


/**********************************************************************************************\
|                                                                                              |
|                                          ## FUNCTIONS ##                                     |
|                                                                                              |
\**********************************************************************************************/

/*#####################################################################
|                          SETUP FUNCTION START                       |
#####################################################################*/
void setup() {
  Serial.begin(115200);
  Serial.println("Is the serial port working?");
  while (!Serial);
/*------------------- Start of Button/Switch Area -------------------*/
  buttonBlueTeam.inverted(false);  // need to verify, but I believe false here means that the button is treated as NO (normally open) vs. NC (normally closed)
  buttonRedTeam.inverted(false);
  buttonRotEnc_A.inverted(false);
  buttonRotEnc_B.inverted(false);
  buttonRotEnc_C.inverted(false);
  buttonRotEnc_D.inverted(false);
  SWITCH_GameWin1.inverted(true);
  SWITCH_GameWin2.inverted(true);
  SWITCH_WinBy1.inverted(true);
  SWITCH_WinBy2.inverted(true);

  buttonBlueTeam.attach(BUTTON_BlueTeam_PIN, INPUT_PULLUP);         // pin configured to pull-up mode
  buttonRedTeam.attach(BUTTON_RedTeam_PIN, INPUT_PULLUP);           // pin configured to pull-up mode
  buttonRotEnc_A.attach(BUTTON_RotEnc_A_PIN, INPUT_PULLUP);  // pin configured to pull-up mode
  buttonRotEnc_B.attach(BUTTON_RotEnc_B_PIN, INPUT_PULLUP);  // pin configured to pull-up mode
  buttonRotEnc_C.attach(BUTTON_RotEnc_C_PIN, INPUT_PULLUP);  // pin configured to pull-up mode
  buttonRotEnc_D.attach(BUTTON_RotEnc_D_PIN, INPUT_PULLUP);  // pin configured to pull-up mode
  SWITCH_GameWin1.attach(SWITCH_GameWin1_PIN, INPUT_PULLUP);
  SWITCH_GameWin2.attach(SWITCH_GameWin2_PIN, INPUT_PULLUP);
  SWITCH_WinBy1.attach(SWITCH_WinBy1_PIN, INPUT_PULLUP);
  SWITCH_WinBy2.attach(SWITCH_WinBy2_PIN, INPUT_PULLUP);

  buttonBlueTeam.callback(BLUE_SINGLE, SINGLE_TAP);  // this determines the function (BLUE_SINGLE in this case) called by the type of press/tap (SINGLE_TAP in this case).
  buttonRedTeam.callback(RED_SINGLE, SINGLE_TAP);
  buttonRotEnc_A.callback(RE_A_SINGLE, SINGLE_TAP);
  buttonRotEnc_B.callback(RE_B_SINGLE, SINGLE_TAP);
  buttonRotEnc_C.callback(RE_C_SINGLE, SINGLE_TAP);
  buttonRotEnc_D.callback(RE_D_SINGLE, SINGLE_TAP);
  buttonBlueTeam.callback(BLUE_DOUBLE, DOUBLE_TAP);
  buttonRedTeam.callback(RED_DOUBLE, DOUBLE_TAP);
  buttonRotEnc_A.callback(RE_A_DOUBLE, DOUBLE_TAP);
  buttonRotEnc_B.callback(RE_B_DOUBLE, DOUBLE_TAP);
  buttonRotEnc_C.callback(RE_C_DOUBLE, DOUBLE_TAP);
  buttonRotEnc_D.callback(RE_D_DOUBLE, DOUBLE_TAP);
  buttonBlueTeam.callback(RESET_GAME, HOLD);
  buttonRedTeam.callback(RESET_GAME, HOLD);
  SWITCH_GameWin1.callback(GameWin2, PRESS);
  SWITCH_GameWin1.callback(GameWin1, RELEASE);
  SWITCH_GameWin2.callback(GameWin3, PRESS);
  SWITCH_GameWin2.callback(GameWin1, RELEASE);
  SWITCH_WinBy1.callback(WinBy2, PRESS);
  SWITCH_WinBy1.callback(WinBy1, RELEASE);
  SWITCH_WinBy2.callback(WinBy3, PRESS);
  SWITCH_WinBy2.callback(WinBy1, RELEASE);
/*------------------- End of Button/Switch Area -------------------*/

/*------------------- Start of Rotary Encoder Area -------------------*/
  // use FOUR3 mode when CLK_A, DT_A signals are always HIGH in latch position.
  RotEnc_A = new RotaryEncoder(CLK_A, DT_A, RotaryEncoder::LatchMode::FOUR3);
  RotEnc_B = new RotaryEncoder(CLK_B, DT_B, RotaryEncoder::LatchMode::FOUR3);
  RotEnc_C = new RotaryEncoder(CLK_C, DT_C, RotaryEncoder::LatchMode::FOUR3);
  RotEnc_D = new RotaryEncoder(CLK_D, DT_D, RotaryEncoder::LatchMode::FOUR3);
  // use FOUR0 mode when CLK_A, DT_A signals are always LOW in latch position.
  //encoder = new RotaryEncoder(CLK_A, DT_A, RotaryEncoder::LatchMode::FOUR0);
  // use TWO03 mode when CLK_A, DT_A signals are both LOW or HIGH in latch position.
  //encoder = new RotaryEncoder(CLK_A, DT_A, RotaryEncoder::LatchMode::TWO03);
  
  // register interrupt routine
  attachInterrupt(digitalPinToInterrupt(CLK_A), checkPosition, CHANGE);
  attachInterrupt(digitalPinToInterrupt(DT_A), checkPosition, CHANGE);
  attachInterrupt(digitalPinToInterrupt(CLK_B), checkPosition, CHANGE);
  attachInterrupt(digitalPinToInterrupt(DT_B), checkPosition, CHANGE);
  attachInterrupt(digitalPinToInterrupt(CLK_C), checkPosition, CHANGE);
  attachInterrupt(digitalPinToInterrupt(DT_C), checkPosition, CHANGE);
  attachInterrupt(digitalPinToInterrupt(CLK_D), checkPosition, CHANGE);
  attachInterrupt(digitalPinToInterrupt(DT_D), checkPosition, CHANGE);
 /*------------------- End of Rotary Encoder Area -------------------*/

 /*------------------- Start of Player Display LED's Area -------------------*/
  FastLED.addLeds<WS2812B, BlueHome1_PIN, GRB>(BlueHome1, NUM_LEDS1);
  FastLED.addLeds<WS2812B, BlueHome2_PIN, GRB>(BlueHome2, NUM_LEDS2);
  FastLED.addLeds<WS2812B, RedAway1_PIN, GRB>(RedAway1, NUM_LEDS1);
  FastLED.addLeds<WS2812B, RedAway2_PIN, GRB>(RedAway2, NUM_LEDS1);
  FastLED.addLeds<WS2812B, RedHome1_PIN, GRB>(RedHome1, NUM_LEDS1);
  FastLED.addLeds<WS2812B, RedHome2_PIN, GRB>(RedHome2, NUM_LEDS2);
  FastLED.addLeds<WS2812B, BlueAway1_PIN, GRB>(BlueAway1, NUM_LEDS1);
  FastLED.addLeds<WS2812B, BlueAway2_PIN, GRB>(BlueAway2, NUM_LEDS1);
  FastLED.addLeds<WS2812B, ServerNumber_PIN, GRB>(ServerNumber, NUM_LEDS3);
  FastLED.show(true);  //this clears the screen
  //delay(1000); // not really needed, but here for testing

  P.begin(4);
  P.setZone(0, 0, 0);
  P.setZone(1, 1, 1);
  P.setZone(2, 2, 2);
  P.setZone(3, 3, 3);
  P.displayZoneText(0, Player1_Initial[counter_a], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
  P.displayZoneText(1, Player2_Initial[counter_b], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
  P.displayZoneText(2, Player3_Initial[counter_c], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
  P.displayZoneText(3, Player4_Initial[counter_d], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);

  BlueFirstDigit(blue1stdigit, CHSV(bluescore_color, 255, 255));  // these lines will display 00 - 00 at the very start of the game. These may need to be changed so they are fed by the team's score instead.
  BlueSecondDigit(blue2nddigit, CHSV(bluescore_color, 255, 255));
  RedFirstDigit(red1stdigit, CHSV(redscore_color, 255, 255));
  RedSecondDigit(red2nddigit, CHSV(redscore_color, 255, 255));
  ServerDigit(server_number, CHSV(servernumber_color, 255, 255));  // This line should display 5 or "S" at the start, then either 1 or 2 depending on which server it is.
  //FastLED.show();
  //delay(3000); // not really needed, but here for testing
 /*------------------- End of Player Display LED's Area -------------------*/
  Serial.print("game_win: ") && Serial.println(game_win);
  Serial.print("win_by: ") && Serial.println(win_by);
}
/*#####################################################################
|                         END OF SETUP FUNCTION                       |
#####################################################################*/


/*#####################################################################
|                         BUTTON FUNCTION START                       |
#####################################################################*/
void BLUE_SINGLE() {
  Serial.println() && Serial.println("##### START OF BLUE_SINGLE");
  Serial.print("Rally number: ") && Serial.println(rally_number);
  if (start_of_game == true) {                // if this is the the first press of a button during this game
    team_serving = false;                     // then set the serving team to be the blue team (in this case)
    team_serving_hist[rally_number] = false;  // add false to the array at position "rally_number" (which in this case should be 0). That may be able to be hard coded.
    start_of_game = false;                    // change start_of_game to false so that the next button press adds a point
    GAME_START();                             // go to the GAME_START function
  } else if (team_serving == false) {         // come here ONLY if start_of_game is false AND the blue team served this rally
    blue_score++;                             // add a point to the blue team's score
    blue1stdigit = blue_score / 10;
    blue2nddigit = blue_score - (blue1stdigit * 10);
    last_action = "point_won";  // tell the serve_brain (coming up) that the blue team won the point/last rally
    SERVE_BRAIN();              // go to the SERVE_BRAIN function
  }
  else {                         //otherwise if the blue team WASN'T serving
    last_action = "rally_lost";  // tell the serve_brain (coming up) that the blue team lost the last rally
    SERVE_BRAIN();               // go to the SERVE_BRAIN function
  }
  rally_number++;
  Serial.println("END OF BLUE_SINGLE #####");
}


void RED_SINGLE() {
  Serial.println() && Serial.println("##### START OF RED_SINGLE");
  Serial.print("Rally number: ") && Serial.println(rally_number);
  if (start_of_game == true) {
    team_serving = true;
    team_serving_hist[rally_number] = true;
    start_of_game = false;
    GAME_START();
  } else if (team_serving == true) {
    red_score++;
    red1stdigit = red_score / 10;
    red2nddigit = red_score - (red1stdigit * 10);
    last_action = "point_won";
    SERVE_BRAIN();
  }
  else {
    last_action = "rally_lost";
    SERVE_BRAIN();
  }
  rally_number++;
  Serial.println("END OF RED_SINGLE #####");
}


void BLUE_DOUBLE() {
  Serial.println() && Serial.println("##### START OF BLUE_DOUBLE");
  if (rally_number <= 1) {                   // these two lines keep the score corrections (aka double-clicks or "going back")
    rally_number = 0;                        // from going past the start of the history ("rally 0")
  } else rally_number = (rally_number - 2);  // otherwise if the rally number isn't already zero, set the rally number back two rally's.
  Serial.print("Rally number: ") && Serial.println(rally_number);
  if ((start_of_game == true) || (blue_score <= 0)) {    // NEED TO VERIFY THIS LOGIC - this is here to help with restarting the game and to keep the blue team's score from going negative
    GAME_START();                                        // go to the GAME_START function
  } else {                                               // otherwise if the game hasn't started (rally isn't 0)
    team_serving = team_serving_hist[rally_number];      // set these variables back to what they were 2 rally's ago. It's two rally's not one because the rally number has already been
    server_number = server_number_hist[rally_number];    // incremented before starting this function and you actually want to go back to the rally before that.
    serve_position = serve_position_hist[rally_number];  // For example... rally#3 ends -> rally# increases to 4 but that rally hasn't actually started yet
    blue_score = blue_score_hist[rally_number];          // -> you realize that rally#3 ended incorrectly -> so you go back to, but don't change, the end of rally#2
    red_score = red_score_hist[rally_number];            // -> then you score rally#3 correctly. So you go from the start of rally#4 to the end of rally#2 (or back two rally's)
    current_server = current_server_hist [rally_number];
    blue1stdigit = blue_score / 10;
    blue2nddigit = blue_score - (blue1stdigit * 10);
    red1stdigit = red_score / 10;
    red2nddigit = red_score - (red1stdigit * 10);
    DISPLAY_SCORE();  // go to the DISPLAY_SCORE function
  }
  Serial.println("END OF BLUE_DOUBLE #####");
}


void RED_DOUBLE() {
  Serial.println() && Serial.println("##### START OF RED_DOUBLE");
  if (rally_number <= 1) {
    rally_number = 0;
  } else rally_number = (rally_number - 2);
  Serial.print("Rally number: ") && Serial.println(rally_number);
  if ((start_of_game == true) || (red_score <= 0)) {
    GAME_START();
  } else {
    team_serving = team_serving_hist[rally_number];
    server_number = server_number_hist[rally_number];
    serve_position = serve_position_hist[rally_number];
    blue_score = blue_score_hist[rally_number];
    red_score = red_score_hist[rally_number];
    current_server = current_server_hist [rally_number];
    blue1stdigit = blue_score / 10;
    blue2nddigit = blue_score - (blue1stdigit * 10);
    red1stdigit = red_score / 10;
    red2nddigit = red_score - (red1stdigit * 10);
    DISPLAY_SCORE();
  }
  Serial.println("END OF RED_DOUBLE #####");
}


void RE_A_SINGLE() {  //RotaryEncoder_A single click button function. These are just here as placeholders.
                      //One idea is that when one of them is clicked it changes that teams score color. The other could maybe change the brightness?
  //null
}


void RE_B_SINGLE() {
  //null
}


void RE_C_SINGLE() {
  //null
}


void RE_D_SINGLE() {
  //null
}


void RE_A_DOUBLE() {
  //null
}


void RE_B_DOUBLE() {
  //null
}


void RE_C_DOUBLE() {
  //null
}


void RE_D_DOUBLE() {  //RotaryEncoder_A single click button function
  //null
}
/*#####################################################################
|                       END OF BUTTON FUNCTIONS                       |
#####################################################################*/

/*#####################################################################
|                       GAME FLOW FUNCTIONS START                     |
#####################################################################*/
void GAME_START() {
  Serial.println() && Serial.println("##### START OF GAME_START");
  Serial.print("START OF GAME. Team serving first: ") && Serial.println(team_serving);
  Serial.print("Rally number: ") && Serial.println(rally_number);
  SERVE_BRAIN();
  //DISPLAY_SCORE();  // go to the DISPLAY_SCORE function
  Serial.println("END OF GAME_START #####");
}



void SERVE_BRAIN() {
  Serial.print("Rally number: ") && Serial.println(rally_number);
  if (start_of_game == true) {
    //null
  }
  else {
Serial.println() && Serial.println("##### START OF SERVE_BRAIN");
  Serial.print("Serve_brain: Last_action = ") && Serial.println(last_action);
   if (last_action == "point_won") {                                                                                                                 // if the last rally ended with the serving team winning the rally
      if (((blue_score >= game_win) && (blue_score >= (red_score + win_by))) || ((red_score >= game_win) && (red_score >= (blue_score + win_by)))) {  //if a team's score is at or above the score needed to win AND is greater than or equal to the number of points needed to win by
        //last_action = "game_won"; //set the last_action to "game_won" which may not be needed
        GAME_WON();  // go to the GAME_WON function
      } else {
        serve_position = !serve_position;  // the serving position changes to the other serving position. It doesn't really matter what serving position it is, just that it changes to the other one.
      }
    }
    if (last_action == "rally_lost") {                     // otherwise if the serving team lost the rally
      if ((server_number == 2) || (server_number == 5)) {  // otherwise if the server was already the second server_number
        team_serving = !team_serving;                      // then the serving team needs to be changed
        server_number = 1;                                 // and server one always starts serving for their team
        serve_position = true;                             // and always from the right hand (true) position
        //last_serve_position = serve_position;
        Serial.println("Side Out");
      } else if (server_number == 1) {     // AND it was their first server
        server_number = 2;                 // change to that team's second server in
        serve_position = !serve_position;  // the other serving position
        Serial.println("Second Server");
      }
    }
      
      if (team_serving == false) {
        if (blue_score%2 == 0) {
          if (serve_position == true) {
            current_server = 10;
          }
        else if (serve_position == false) {
          current_server = 21;
        }
        }
        else if (!blue_score%2 == 0) {
          if (serve_position == true) {
          current_server = 20;
          }
          else if (serve_position == false) {
            current_server = 11;
          }
        }
      }
      else if (team_serving == true) {
        if (red_score%2 == 0) {
          if (serve_position == true) {
            current_server = 43;
          }
          else if (serve_position == false) {
            current_server = 32;
          }
        }
        else if (!red_score%2 == 0) {
          if (serve_position == true) {
            current_server = 33;
          }
          else if (serve_position == false) {
            current_server = 42;
          }
        }
      }

  //Capture all the values for this rally in case of a score correction later.
    team_serving_hist [rally_number] = team_serving;
    server_number_hist [rally_number] = server_number;
    serve_position_hist [rally_number] = serve_position;
    blue_score_hist [rally_number] = blue_score;
    red_score_hist [rally_number] = red_score;
    current_server_hist [rally_number] = current_server;

    DISPLAY_SCORE();  // go to the DISPLAY_SCORE function
    Serial.println("END OF SERVE_BRAIN #####");
  }
}


void DISPLAY_SCORE() {
  //this whole section is only for debugging puporses
  Serial.println() && Serial.println("##### START OF DISPLAY_SCORE");
  if (start_of_game == true) {
      //null
  }
  else {
    if (team_serving == false) {
      if (serve_position == false) {
        Serial.print("1.Serving team: ") && Serial.print(team_serving) && Serial.print(" #Serving Side# Score: ") && Serial.print(blue_score) && Serial.print(" - ") && Serial.print(red_score) && Serial.print(" - ") && Serial.println(server_number) && Serial.println();
      }
      else { Serial.print("2.Serving team: ") && Serial.print(team_serving) && Serial.print("     Score: ") && Serial.print(blue_score) && Serial.print(" - ") && Serial.print(red_score) && Serial.print(" - ") && Serial.print(server_number) && Serial.println(" #Serving Side#");
      }
    }
    if (team_serving == true) {
      if (serve_position == false) { Serial.print("3.Serving team: ") && Serial.print(team_serving) && Serial.print(" #Serving Side# Score: ") && Serial.print(red_score) && Serial.print(" - ") && Serial.print(blue_score) && Serial.print(" - ") && Serial.println(server_number) && Serial.println();
      } 
      else { Serial.print("4.Serving team: ") && Serial.print(team_serving) && Serial.print("     Score: ") && Serial.print(red_score) && Serial.print(" - ") && Serial.print(blue_score) && Serial.print(" - ") && Serial.print(server_number) && Serial.println(" #Serving Side#");
      }
    }  //Serial.print("5.Rally number: ")&&Serial.println(rally_number);
    Serial.print("6.Team_serving_hist: ") && Serial.println(team_serving_hist[rally_number]); Serial.print("7.Serve_position_hist: ") && Serial.println(serve_position_hist[rally_number]);
    if (team_serving == false) {
      Serial.print("8.Blue_score_hist: ") && Serial.println(blue_score_hist[rally_number]); Serial.print("9.Red_score_hist: ") && Serial.println(red_score_hist[rally_number]);
    }
    if (team_serving == true) { Serial.print("10.Red_score_hist: ") && Serial.println(red_score_hist[rally_number]); Serial.print("11.Blue_Score_hist: ") && Serial.println(blue_score_hist[rally_number]);
    }
    Serial.print("12.Server_num_hist: ") && Serial.println(server_number_hist[rally_number]);
    //debugging above. There is nothing in here otherwise.
    //  rally_number++;

    BlueFirstDigit(blue1stdigit, CHSV(bluescore_color, 255, 255));
    BlueSecondDigit(blue2nddigit, CHSV(bluescore_color, 255, 255));
    RedFirstDigit(red1stdigit, CHSV(redscore_color, 255, 255));
    RedSecondDigit(red2nddigit, CHSV(redscore_color, 255, 255));
    ServerDigit(server_number, CHSV(servernumber_color, 255, 255));
    FastLED.show();

    DISPLAY_PLAYER_POSITION();
    Serial.print("13.Next rally number: ") && Serial.println(rally_number);
    Serial.println("END OF DISPLAY_SCORE #####");
  }
}
/*P.displayZoneText(0, Player1_Initial[counter_a], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
  P.displayZoneText(1, Player2_Initial[counter_b], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
  P.displayZoneText(2, Player3_Initial[counter_c], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
  P.displayZoneText(3, Player4_Initial[counter_d], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);*/


void DISPLAY_PLAYER_POSITION() {
  //P.displayReset();
  //Determine the serving position for player
  if (start_of_game ==true){ 
    P.displayZoneText(0, Player1_Initial [counter_a], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
    P.displayZoneText(1, Player2_Initial [counter_b], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
    P.displayZoneText(2, Player3_Initial [counter_c], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
    P.displayZoneText(3, Player4_Initial [counter_d], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
  }
  else {
    switch (current_server) {
      case 10:
        P.displayZoneText(0, Player1_Initial[counter_a], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(1, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(2, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(3, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        break;
      case 11:
        P.displayZoneText(1, Player1_Initial[counter_a], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(0, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(2, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(3, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        break;
      case 20:
        P.displayZoneText(0, Player2_Initial [counter_b], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(1, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(2, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(3, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        break;
      case 21:
        P.displayZoneText(1, Player2_Initial [counter_b], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(0, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(2, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(3, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        break;
      case 32:
        P.displayZoneText(2, Player3_Initial [counter_c], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(1, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(0, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(3, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        break;
      case 33:
        P.displayZoneText(3, Player3_Initial [counter_c], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(1, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(2, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(0, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        break;
      case 42:
        P.displayZoneText(2, Player4_Initial [counter_d], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(1, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(0, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(3, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        break;
      case 43:
        P.displayZoneText(3, Player4_Initial [counter_d], PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(1, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(2, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        P.displayZoneText(0, "", PA_CENTER, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT, PA_NO_EFFECT);
        break;
    }
  }
}


void GAME_WON() {
  Serial.println() && Serial.println("##### START OF GAME_WON");
  /*  there isn't currently anything worthwhile here yet, but this is where
    custom code for winning the game will go. Maybe something like a congrats
    message
  */
  Serial.print("Winner is: ") && Serial.print(team_serving) && Serial.println();

  last_action = "game_won";

  Serial.println("END OF GAME_WON #####");
}


void DO_NOTHING() {
  //null
}


void RESET_GAME() {
  Serial.println() && Serial.println("##### RESET_GAME #####");  // Either player button being held causes the scores to be reset and start back at RALLY.
  // this may need to change as I've read reports (and I think I remember from the ping pong scoreboard) that this only works one time.
  // maybe running a wire from a pin to to the reset pin and turning it high would work instead. Not sure if the teensy 4.1 has a "reset pin" though,
  delay(1000);
  //asm volatile("  jmp 0");
}
/*#####################################################################
|                       END OF GAME FLOW FUNCTIONS                    |
#####################################################################*/

/*#####################################################################
|                       GAME OPTION FUNCTIONS START                   |
#####################################################################*/
void GameWin1() {
  game_win = PlayTo_A;  //7 
  SERVE_BRAIN();
  Serial.print("game_win = ")&&Serial.println(game_win);
}


void GameWin2() {
  game_win = PlayTo_B;  //9 
  SERVE_BRAIN();
  Serial.print("game_win = ")&&Serial.println(game_win);
}

void GameWin3() {
  game_win = PlayTo_C;  //11 
  SERVE_BRAIN();
  Serial.print("game_win = ")&&Serial.println(game_win);
}


void WinBy1() {
  win_by = WinBy_A;  //0 
  SERVE_BRAIN();
  Serial.print("win_by = ")&&Serial.println(win_by);
}


void WinBy2() {
  win_by = WinBy_B;  //1 
  SERVE_BRAIN();
  Serial.print("win_by = ")&&Serial.println(win_by);
}


void WinBy3() {
  win_by = WinBy_C;  //2 
  SERVE_BRAIN();
  Serial.print("win_by = ")&&Serial.println(win_by);
}


void checkPosition() {
  RotEnc_A->tick(); // just call tick() to check the state.
  RotEnc_B->tick();
  RotEnc_C->tick();
  RotEnc_D->tick();
}


void limit_A() {
  if ((int)(RotEnc_A->getDirection()) < 0) { //The encoder is rotating CCW so the counter should be decremented
    counter_a --;
    if (counter_a > 26) { // if counter tries to go below 'A' (i.e. tries to go to -1)
      counter_a = 25; // then wrap around back to 'Z'
      }
  }
  else { //Otherwise the encoder is rotating CW so the counter should be incremented
    counter_a ++;
    if (counter_a > 25) { // if counter tries to go past 'Z'
      counter_a = 0; // then wrap around back to 'A'
    }
  }
  DISPLAY_PLAYER_POSITION();
  Serial.print("counter_a = ")&&Serial.println(counter_a);
}


void limit_B() {
  if ((int)(RotEnc_B->getDirection()) < 0) { 
    counter_b --;
    if (counter_b > 26) {
      counter_b = 25;
    }
  }
  else {   
    counter_b ++;
    if (counter_b > 25) {
      counter_b = 0;
    }
  }
  DISPLAY_PLAYER_POSITION();
  Serial.print("counter_b = ")&&Serial.println(counter_b);
}


void limit_C() {
  if ((int)(RotEnc_C->getDirection()) < 0) { //The encoder is rotating CCW so the counter should be decremented
    counter_c --;
    if (counter_c > 26) { // if counter tries to go below 'A' (i.e. tries to go to -1)
      counter_c = 25; // then wrap around back to 'Z'
    }
  }
  else { //Otherwise the encoder is rotating CW so the counter should be incremented
    counter_c ++;
    if (counter_c > 25) { // if counter tries to go past 'Z'
      counter_c = 0; // then wrap around back to 'A'
    }
  }
  DISPLAY_PLAYER_POSITION();
  Serial.print("counter_c = ")&&Serial.println(counter_c);
}


void limit_D() {
  if ((int)(RotEnc_D->getDirection()) < 0) { 
    counter_d --;
    if (counter_d > 26) {
      counter_d = 25;
    }
  }
  else {   
    counter_d ++;
    if (counter_d > 25) {
      counter_d = 0;
    }
  }
  DISPLAY_PLAYER_POSITION();
  Serial.print("counter_d = ")&&Serial.println(counter_d);
}
/*#####################################################################
|                       END OF GAME OPTION FUNCTIONS                  |
#####################################################################*/

/*#####################################################################
|                       SCORE DISPLAY FUNCTIONS START                 |
#####################################################################*/
void BlueFirstDigit(int val, CHSV color) {  //I still reason that you can do away with the first "display" value as well as NUM_LEDS in the lower statements. You'll still need it on this line however.
  for (int i = 0; i < NUM_LEDS1; i++) {
    color.v = bitRead(first_digit[val], i) * 255;
    BlueHome1[i] = color;
    BlueAway1[i] = color;
  }
}


void BlueSecondDigit(int val, CHSV color) {  // This is for the 2x digits that have a dash "---" after them.
  for (int i = 0; i < NUM_LEDS2; i++) {
    color.v = bitRead(second_digit[val], i) * 255;
    BlueHome2[i] = color;
  }
  for (int i = 0; i < NUM_LEDS1; i++) {
    color.v = bitRead(second_digit[val], i) * 255;
    BlueAway2[i] = color;
  }
}


void RedFirstDigit(int val, CHSV color) {
  for (int i = 0; i < NUM_LEDS1; i++) {
    color.v = bitRead(first_digit[val], i) * 255;
    RedHome1[i] = color;
    RedAway1[i] = color;
  }
}


void RedSecondDigit(int val, CHSV color) {  // This is for the 2x digits that have a dash "---" after them.
  for (int i = 0; i < NUM_LEDS2; i++) {
    color.v = bitRead(second_digit[val], i) * 255;
    RedHome2[i] = color;
  }
  for (int i = 0; i < NUM_LEDS1; i++) {
    color.v = bitRead(second_digit[val], i) * 255;
    RedAway2[i] = color;
  }
}


void ServerDigit(int val, CHSV color) {
  for (int i = 0; i < NUM_LEDS3; i++) {
    color.v = bitRead(server_digit[val], i) * 255;
    ServerNumber[i] = color;
  }
}
/*#####################################################################
|                       END OF SCORE DISPLAY FUNCTIONS                |
#####################################################################*/

/*#####################################################################
|                       LOOP FUNCTION START                           |
#####################################################################*/
void loop() {
  SWITCH_GameWin1.update();
  SWITCH_GameWin2.update();
  SWITCH_WinBy1.update();
  SWITCH_WinBy2.update();
  buttonBlueTeam.update();
  buttonRedTeam.update();
  buttonRotEnc_A.update();
  buttonRotEnc_B.update();
  buttonRotEnc_C.update();
  buttonRotEnc_D.update();

  //ServerDigit(server_number, CHSV(servernumber_color, 255, 255));
  //BlueFirstDigit(blue1stdigit, CHSV(bluescore_color, 255, 255));
  //BlueSecondDigit(blue2nddigit, CHSV(bluescore_color, 255, 255));
  //RedFirstDigit(red1stdigit, CHSV(redscore_color, 255, 255));
  //RedSecondDigit(red2nddigit, CHSV(redscore_color, 255, 255));

  static int pos_A = 0;
  static int pos_B = 0;
  static int pos_C = 0;
  static int pos_D = 0;
  int newPos_A = RotEnc_A->getPosition();
  int newPos_B = RotEnc_B->getPosition();
  int newPos_C = RotEnc_C->getPosition();
  int newPos_D = RotEnc_D->getPosition();
  if (pos_A != newPos_A) {
    limit_A();
    //SERVE_BRAIN();
  }
  if (pos_B != newPos_B) {
    limit_B();
    //SERVE_BRAIN();
  }
  if (pos_C != newPos_C) {
    limit_C();
    //SERVE_BRAIN();
  }
  if (pos_D != newPos_D) {
    limit_D();
    //SERVE_BRAIN();
  }
  pos_A = newPos_A;
  pos_B = newPos_B;
  pos_C = newPos_C;
  pos_D = newPos_D;

P.displayAnimate();

/*
//Uncomment these lines for testing the LED digits
  for (int blue_score = 0; blue_score < 20; blue_score++) { //Just a line of code for testing the digits to ensure they are displaying the numbers properly
  blue1stdigit = blue_score / 10;
  blue2nddigit = blue_score - (blue1stdigit * 10);
  BlueFirstDigit(blue1stdigit, CHSV(171, 255, 255)); 
  BlueSecondDigit(blue2nddigit, CHSV(171, 255, 255));
  FastLED.show();
  delay(500); 
}
for (int red_score = 0; red_score < 20; red_score++) {
  red1stdigit = red_score / 10;
  red2nddigit = red_score - (red1stdigit * 10);
  RedFirstDigit(red1stdigit, CHSV(0, 255, 255)); 
  RedSecondDigit(red2nddigit, CHSV(0, 255, 255));
  FastLED.show();
  delay(500);
}
for (int server_number = 0;  server_number <6 ; server_number++) {
  ServerDigit(server_number, CHSV(85, 255, 255));
  FastLED.show();
  delay(500);
}
//Uncomment above for testing the LED digits
*/
}
/*#####################################################################
|                       END OF LOOP FUNCTION                          |
#####################################################################*/