Josh Smith / Mbed 2 deprecated MobileChessStation

Dependencies:   mbed mbed-rtos 4DGL-uLCD-SE

main.cpp

Committer:
jsmith687
Date:
2020-04-29
Revision:
1:4af874b42e33
Parent:
0:d9dcc0c3cd64

File content as of revision 1:4af874b42e33:


#include "mbed.h"
#include "rtos.h"
#include "uLCD_4DGL.h"
#include "Speaker.h"

//piece colors
#define white true
#define black false

//button inputs
#define SELECT 0
#define FORWARD 1
#define BACKWARD 2
#define RIGHT 3
#define LEFT 4
#define MENU 5

//piece types
#define NONE 0
#define KING 1
#define QUEEN 2
#define BISHOP 3
#define KNIGHT 4
#define ROOK 5
#define PAWN 6

//seven-segment display conversions
#define ZERO 126
#define ONE 48
#define TWO 109
#define THREE 121
#define FOUR 51
#define FIVE 91
#define SIX 95
#define SEVEN 112
#define EIGHT 127
#define NINE 123

#define D1 7
#define D2 11
#define D3 13
#define D4 14

uLCD_4DGL uLCD(p28, p27, p29); // serial tx, serial rx, reset pin;

Serial pc(USBTX, USBRX);

//button control (directions optional)
DigitalIn Select(p14);
DigitalIn Menu(p15);
DigitalOut led3(LED3);
DigitalOut whiteLED(p12);
DigitalOut blackLED(p13);
Speaker speakerOut(p26);

//Joystick variables
AnalogIn JoyX(p19);
AnalogIn JoyY(p20);


volatile bool currentPlayer = white;
volatile bool gameActive = false;

bool menuOpen = false;
int menuSelectionIndex = 0;

bool playSound = true;

int boxWidth = 16;

int programTimeSetting = 600;

bool pieceSelected = false;
int currentPiece = NONE;
int positionX = 0;
int positionY = 0;
int selectedX = 0;
int selectedY = 0;
int board[8][8] = {
    {ROOK, PAWN, NONE, NONE, NONE, NONE, PAWN, ROOK},
    {KNIGHT, PAWN, NONE, NONE, NONE, NONE, PAWN, KNIGHT},
    {BISHOP, PAWN, NONE, NONE, NONE, NONE, PAWN, BISHOP},
    {KING, PAWN, NONE, NONE, NONE, NONE, PAWN, KING},
    {QUEEN, PAWN, NONE, NONE, NONE, NONE, PAWN, QUEEN},
    {BISHOP, PAWN, NONE, NONE, NONE, NONE, PAWN, BISHOP},
    {KNIGHT, PAWN, NONE, NONE, NONE, NONE, PAWN, KNIGHT},
    {ROOK, PAWN, NONE, NONE, NONE, NONE, PAWN, ROOK}
};

bool color[8][8] = {
    {white, white, white, white, white, white, black, black},
    {white, white, white, white, white, white, black, black},
    {white, white, white, white, white, white, black, black},
    {white, white, white, white, white, white, black, black},
    {white, white, white, white, white, white, black, black},
    {white, white, white, white, white, white, black, black},
    {white, white, white, white, white, white, black, black},
    {white, white, white, white, white, white, black, black}
};

Timer timerWhite;
Timer timerBlack;

void drawGameBoard();
int waitForInput();

//TIMER FUNCTIONS---------------------------------------------------------------------------------------------------------------------------------

void timer(void const *args) {
    //updates the small clock
    int time_left = programTimeSetting; // remaining time for either player
    int min2, min1, sec2, sec1; // decimal value for each digit
    int digits[10] = {ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE}; //contains bus words for each symbol
    
    BusOut digit_value(p18, p17, p9, p8, p7, p6, p5);
    BusOut digit_select(p21, p22, p23, p24);
    DigitalOut decimal_point(p25);
    
    while (true) {
        led3 = currentPlayer;
        whiteLED = currentPlayer;
        blackLED = !currentPlayer;
        if (!gameActive) {
            time_left = programTimeSetting;
        }
        else if (currentPlayer == black) {
            //output: programTimeSetting - whiteTime.read()
            time_left = programTimeSetting - timerBlack.read();
        }
        else {
            //output: programTimeSetting - blackTime.read()
            time_left = programTimeSetting - timerWhite.read();
        }
        
        // calculate digit values for display
        min2 = time_left / 600;
        min1 = (time_left - 600*min2) / 60;
        sec2 = (time_left - 600*min2 - 60*min1) / 10;
        sec1 = (time_left - 600*min2 - 60*min1 - 10*sec2) / 1;
        
        // write 7seg display digits
        digit_select = D4;
        digit_value = digits[min2];
        decimal_point = 0;
        Thread::wait(1);
        digit_select = D3;
        digit_value = digits[min1];
        decimal_point = 1;
        Thread::wait(1);
        digit_select = D2;
        digit_value = digits[sec2];
        decimal_point = 0;
        Thread::wait(1);
        digit_select = D1;
        digit_value = digits[sec1];
        decimal_point = 0;
        Thread::wait(1);
    }
}

//INITIALIZING FUNCTIONS--------------------------------------------------------------------------------------------------------------------------

void initializeButtons() {
    blackLED = 0;
    whiteLED = 0;
    Select.mode(PullUp);
    Menu.mode(PullUp);
}

void initializeVariables() {
    currentPlayer = white;
    positionX = 0;
    positionY = 0;
    pieceSelected = false;
    timerWhite.stop();
    timerWhite.reset();
    timerBlack.stop();
    timerBlack.reset();
}

void initializeLCD() {
    uLCD.baudrate(BAUD_3000000); //jack up baud rate to max for fast display
    uLCD.display_control(PORTRAIT);
    uLCD.text_mode(TRANSPARENT);
    uLCD.cls();
    uLCD.printf("ChessDude");
    wait(1.0);
    uLCD.cls();
    drawGameBoard();
}

void initialize() {
    initializeButtons();
    initializeVariables();
    initializeLCD();
}


//DRAW FUNCTIONS----------------------------------------------------------------------------------------------------------------------------------

void drawMenuLine(int line) {
    uLCD.filled_rectangle(1, 32*line+1, 127, 32*line+31, WHITE);
    uLCD.text_mode(TRANSPARENT);
    switch (line) {
        case 0: {
            if (playSound) 
                uLCD.text_string("SOUND: ON", 4, 2, FONT_7X8, BLACK);
            else
                uLCD.text_string("SOUND: OFF", 4, 2, FONT_7X8, BLACK);
            break;
        }
        case 1: {
            char text[13] = {"TIME: XX MIN"};
            text[6] = (char)(programTimeSetting/600);
            text[7] = (char)((programTimeSetting/60)%10);
            uLCD.text_string(text, 4, 6, FONT_7X8, BLACK);
            break;
        }
        case 2: {
            //unclear
            break;
        }
        case 3: {
            uLCD.text_string("RESTART", 4, 14, FONT_7X8, BLACK);
            break;
        }
    }
}

void drawMenuSelection(int color) {
    uLCD.line(0,32*menuSelectionIndex,128,32*menuSelectionIndex, color);
    uLCD.line(0,32*menuSelectionIndex,0,32*menuSelectionIndex+32, color);
    uLCD.line(0,32*menuSelectionIndex+32,128,32*menuSelectionIndex+32, color);
    uLCD.line(128,32*menuSelectionIndex+32,128,32*menuSelectionIndex, color);
}


void drawSelectionBox(int x, int y, int color) {
    //draw 4 lines around the box in the color selected
    if (x < 0 || x >= 8 || y < 0 || y >= 8) return;
    int startX = x*boxWidth + 1;
    int endX = (x+1)*boxWidth - 1;
    int startY = y*boxWidth + 1;
    int endY = (y+1)*boxWidth - 1;
    uLCD.line(startX, startY, startX, endY-y/7, color);
    uLCD.line(startX, endY-y/7, endX-x/7, endY-y/7, color);
    uLCD.line(endX-x/7, endY-y/7, endX-x/7, startY, color);
    uLCD.line(endX-x/7, startY, startX, startY, color);
}

void drawPiece(int x, int y, int piece) {
    //draws a piece in a specific box (including NONE, which draws a black rectangle to erase)
    uLCD.text_mode(TRANSPARENT);
    int textColor = WHITE;
    if (color[x][y] == black) textColor = RED;
    switch (piece) {
        case NONE: {
            uLCD.filled_rectangle(x*boxWidth+2, y*boxWidth+2, x*boxWidth+boxWidth-2, y*boxWidth+boxWidth-2, BLACK);
            break;
        }
        case KING: {
            //uLCD.text_char('K', x*2.3+1, y*2+1, textColor);
            int king[14*14] = {
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, 
                BLACK, BLACK, textColor, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, 
                BLACK, BLACK, textColor, textColor, BLACK, BLACK, textColor, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, 
                textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, textColor, textColor, BLACK, BLACK, textColor, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, textColor, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK
            };
            uLCD.BLIT(x*boxWidth+1, y*boxWidth+1, boxWidth-2-x/8, boxWidth-2-y/8, king);
            break;
        }
        case QUEEN: {
            //uLCD.text_char('Q', x*2.3+1, y*2+1, textColor);
            int queen[14*14] = {
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, textColor, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, 
                BLACK, BLACK, BLACK, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, textColor, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, textColor, textColor, BLACK, textColor, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, 
                BLACK, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, textColor, textColor, BLACK, textColor, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, textColor, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, 
                BLACK, BLACK, textColor, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK
            };
            uLCD.BLIT(x*boxWidth+1, y*boxWidth+1, boxWidth-2-x/8, boxWidth-2-y/8, queen);
            break;
        }
        case BISHOP: {
            //uLCD.text_char('B', x*2.3+1, y*2+1, textColor);
            int bishop[14*14] = {
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK
            };
            uLCD.BLIT(x*boxWidth+1, y*boxWidth+1, boxWidth-2-x/8, boxWidth-2-y/8, bishop);
            break;
        }
        case KNIGHT: {
            //uLCD.text_char('N', x*2.3+1, y*2+1, textColor);
            int knight[14*14] = {
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, textColor, BLACK, textColor, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, BLACK, BLACK, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, textColor, BLACK, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, textColor, textColor, BLACK, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, BLACK, BLACK, BLACK, BLACK, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK
            };
            uLCD.BLIT(x*boxWidth+1, y*boxWidth+1, boxWidth-2-x/8, boxWidth-2-y/8, knight);
            break;
        }
        case ROOK: {
            //uLCD.text_char('R', x*2.3+1, y*2+1, textColor);
            int rook[14*14] = {
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, textColor, textColor, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, 
                BLACK, BLACK, BLACK, textColor, textColor, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, textColor, textColor, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, textColor, textColor, textColor, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK
            };
            uLCD.BLIT(x*boxWidth+1, y*boxWidth+1, boxWidth-2-x/8, boxWidth-2-y/8, rook);
            break;
        }
        case PAWN: {
            //uLCD.text_char('P', x*2.3+1, y*2+1, textColor);
            int pawn[14*14] = {
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, BLACK, BLACK, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, textColor, textColor, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, BLACK, BLACK, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, textColor, textColor, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, 
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK
            };
            uLCD.BLIT(x*boxWidth+1, y*boxWidth+1, boxWidth-2-x/8, boxWidth-2-y/8, pawn);
            break;
        }
        default:
            return;
    }
}

void drawGameBoard() {
    uLCD.cls();
    for (int i = 0; i < 9; i++) {
        uLCD.line(i*boxWidth-i/8, 0, i*boxWidth-i/8, 128, 0xFFFFFF);
        uLCD.line(0, i*boxWidth-i/8, 128, i*boxWidth-i/8, 0xFFFFFF);
    }
    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
            drawPiece(i, j, board[i][j]);
        }
    }
    if (pieceSelected) {
        drawSelectionBox(selectedX, selectedY, RED);
    }
    drawSelectionBox(positionX, positionY, GREEN);
}


//MENU FUNCTIONS----------------------------------------------------------------------------------------------------------------------------------

void closeMenu() {
    uLCD.cls();
    drawGameBoard();
    menuOpen = false;
    menuSelectionIndex = 0;
}

void openMenu() {
    uLCD.cls();
    for (int i = 0; i < 4; i++) {
        drawMenuLine(i);
    }
    drawMenuSelection(GREEN);
    menuOpen = true;
}

void menuSelectHandler() {
    switch (menuSelectionIndex) {
        case 0:
            playSound = !playSound;
            break;
        case 1:
            switch (programTimeSetting) {
                case 300:
                    programTimeSetting = 600;
                    break;
                case 600:
                    programTimeSetting = 900;
                    break;
                case 900:
                    programTimeSetting = 1200;
                    break;
                case 1200:
                    programTimeSetting = 1800;
                    break;
                case 1800:
                    programTimeSetting = 3600;
                    break;
                case 3600:
                    programTimeSetting = 300;
                    break;
                default:
                    programTimeSetting = 600;
                    break;
            }
            break;
        case 2:
            //unclear
        case 3:
            initialize();
            return;
        default:
            break;
    }
    drawMenuLine(menuSelectionIndex);
}

void moveMenuSelection(int direction) {
    if (direction == RIGHT || direction == LEFT) return;
    drawMenuSelection(BLACK);
    if (direction == FORWARD) {
        menuSelectionIndex++;
        if (menuSelectionIndex > 3) menuSelectionIndex = 0;
    }
    if (direction == BACKWARD) {
        menuSelectionIndex--;
        if (menuSelectionIndex < 0) menuSelectionIndex = 3;
    }
    drawMenuSelection(GREEN);
}

void menuHandler() {
    int move = waitForInput();
    if (move == MENU) {
        closeMenu();
        return;
    }
    if (move == SELECT) {
        menuSelectHandler();
        return;
    }
    moveMenuSelection(move);
}

//GAME FUNCTIONS----------------------------------------------------------------------------------------------------------------------------------

void removeSelectionBox() {
    switch (currentPiece) {
        case KING:
            for (int i = selectedX - 1; i <= selectedX + 1; i++) {
                for (int j = selectedY - 1; j <= selectedY + 1; j++) {
                    drawSelectionBox(i, j, BLACK);
                }
            }
            break;
        case QUEEN:
            for (int i = 0; i < 8; i++) {
                drawSelectionBox(selectedX, i, BLACK);
                drawSelectionBox(i, selectedY, BLACK);
                drawSelectionBox(selectedX + i, selectedY + i, BLACK);
                drawSelectionBox(selectedX - i, selectedY - i, BLACK);
            }
            break;
        case BISHOP:
            for (int i = 0; i < 8; i++) {
                drawSelectionBox(selectedX + i, selectedY + i, BLACK); 
                drawSelectionBox(selectedX - i, selectedY - i, BLACK); 
            }
            break;
        case KNIGHT:
            drawSelectionBox(selectedX, selectedY, BLACK);
            drawSelectionBox(selectedX + 2, selectedY + 1, BLACK);
            drawSelectionBox(selectedX + 1, selectedY + 2, BLACK);
            drawSelectionBox(selectedX - 2, selectedY + 1, BLACK);
            drawSelectionBox(selectedX - 1, selectedY + 2, BLACK);
            drawSelectionBox(selectedX + 2, selectedY - 1, BLACK);
            drawSelectionBox(selectedX + 1, selectedY - 2, BLACK);
            drawSelectionBox(selectedX - 2, selectedY - 1, BLACK);
            drawSelectionBox(selectedX - 1, selectedY - 2, BLACK);
            break;
        case ROOK:
            for (int i = 0; i < 8; i++) {
                drawSelectionBox(selectedX, i, BLACK);
                drawSelectionBox(i, selectedY, BLACK);
            }
            break;
        case PAWN:
            drawSelectionBox(selectedX, selectedY, BLACK);
            drawSelectionBox(selectedX, selectedY + 1, BLACK);
            drawSelectionBox(selectedX, selectedY + 2, BLACK);
            drawSelectionBox(selectedX + 1, selectedY + 1, BLACK);
            drawSelectionBox(selectedX - 1, selectedY + 1, BLACK);
            break;
    }
    drawSelectionBox(positionX, positionY, GREEN);
}

int waitForInput() {
    //main loop which waits for input while in game
    float calx = 0.0;  // joystick hysteresis calibration (optional)
    float caly = 0.0;
    while (true) {
        if (JoyY > (0.6+caly)) {
            while (JoyY > (0.6+caly)) { Thread::wait(10); }
            return FORWARD;
        }
        if (JoyY < (0.4+caly)) {
            while (JoyY < (0.4+caly)) { Thread::wait(10); }
            return BACKWARD;
        }
        if (JoyX > (0.6+calx)) {
            while (JoyX > (0.6+calx)) { Thread::wait(10); }
            return RIGHT;
        }
        if (JoyX < (0.4+calx)) {
            while (JoyX < (0.4+calx)) { Thread::wait(10); }
            return LEFT;
        }
        if (!Select) {
            while (!Select) { Thread::wait(10); }
            return SELECT;
        }
        if (!Menu) {
            while (!Menu) { Thread::wait(10); }
            return MENU;
        }
        Thread::wait(10);
    }
}

void displayPossibleMoves() {
    //highlights squares where a selected piece can move
    //use a for loop to iterate over all possible positions (diagonals, lines, etc.)
        //if piece is already and not opposite team don't highlight
        //make sure not to highlight edge conditions (off board)
    /*switch (currentPiece) {
        case NONE:
            //return false;
        case KING:
            //make sure it's opposite player's team or none
            //check for castling (queenside and short)
            for (int i = positionX - 1; i <= positionX + 1; i++) {
                for (int j = positionY - 1; j <= positionY + 1; j++) {
                    //if (checkValidMove(KING, i, j)) {
                    //    drawSelectionBox(i, j, YELLOW);
                    //}
                }
            }
            if (board[positionX][positionY] == NONE)
                //return true;
            if (color[positionX][positionY] != currentPlayer)
                //return true;
            //return false;
        case QUEEN:
            //check it's on the lines
            //check it's on diagonal
            //make sure no pieces are in the way
            //return false;
        case BISHOP:
            //check it's on diagonals
            //make sure no pieces are in the way
            //return false;
        case KNIGHT:
            //check that it's knight position
            //make sure it's opposite player's team or none
            //return false;
        case ROOK:
            //check it's on the lines
            //make sure no pieces are in the way
            //return false;
        case PAWN:
            //check en passant
            //check no pieces in way
            //check for take on diagonal
            //return false;
        default:
            //return false;
    }*/
}

void showGameEnd() {
    //shows the end of the game
    uLCD.cls();
}

bool checkValidMove(int x, int y) {
    if (board[x][y] != NONE && color[x][y] == currentPlayer) return false;  //handles moving onto own piece (and same space)
    switch (currentPiece) {
        case NONE:
            return false;
        case KING:
            //make sure it's opposite player's team or none
            //check for castling (queenside and short)
            if (abs(x-selectedX) <= 1 && abs(y-selectedY) <= 1) {
                if (board[x][y] == NONE || color[x][y] != currentPlayer) {
                    return true;
                }
            }
            return false;
        case QUEEN:
            //check it's on the lines
            //check it's on diagonal
            //make sure no pieces are in the way
            if (x == selectedX) {
                //check y direction in a line
                if (selectedY < y) {
                    //if position is forward
                    for (int i = selectedY; i < y; i++) {
                        if (board[selectedX][i] != NONE) return false;
                    }
                }
                else {
                    //if position is backward
                    for (int i = selectedY; i > y; i--) {
                        if (board[selectedX][i] != NONE) return false;
                    }
                } 
                return true;
            }
            else if (y == selectedY) {
                //check x direction in a line
                if (selectedX < x) {
                    //if position is forward
                    for (int i = selectedX; i < x; i++) {
                        if (board[i][selectedY] != NONE) return false;
                    }
                }
                else {
                    //if position is backward
                    for (int i = selectedX; i > x; i--) {
                        if (board[i][selectedY] != NONE) return false;
                    }
                }
                return true;
            }
            else if (abs(x - selectedX) == abs(y - selectedY)) {
                //check diagonals
                if (selectedX < x) {
                    if (selectedY < y) {
                        for (int i = 1; i < x - selectedX; i++) {
                            if (board[selectedX + i][selectedY + i] != NONE) return false;
                        }
                    }
                    else {
                        for (int i = 1; i < x - selectedX; i++) {
                            if (board[selectedX + i][selectedY - i] != NONE) return false;
                        }
                    }
                }
                if (selectedX > x) {
                    if (selectedY < y) {
                        for (int i = 1; i < selectedX - x; i++) {
                            if (board[selectedX - i][selectedY + i] != NONE) return false;
                        }
                    }
                    else {
                        for (int i = 1; i < selectedX - x; i++) {
                            if (board[selectedX - i][selectedY - i] != NONE) return false;
                        }
                    }
                }
                return true;
            }
            else {
                return true;
            }
        case BISHOP:
            //check it's on diagonals
            //make sure no pieces are in the way
            if (abs(x - selectedX) == abs(y - selectedY)) {
                //check diagonals
                if (selectedX < x) {
                    if (selectedY < y) {
                        for (int i = 1; i < x - selectedX; i++) {
                            if (board[selectedX + i][selectedY + i] != NONE) return false;
                        }
                    }
                    else {
                        for (int i = 1; i < x - selectedX; i++) {
                            if (board[selectedX + i][selectedY - i] != NONE) return false;
                        }
                    }
                }
                if (selectedX > x) {
                    if (selectedY < y) {
                        for (int i = 1; i < selectedX - x; i++) {
                            if (board[selectedX - i][selectedY + i] != NONE) return false;
                        }
                    }
                    else {
                        for (int i = 1; i < selectedX - x; i++) {
                            if (board[selectedX - i][selectedY - i] != NONE) return false;
                        }
                    }
                }
                return true;
            }
            else {
                return false;
            }
        case KNIGHT:
            //check that it's knight position
            //make sure it's opposite player's team or none
            if (abs(y - selectedY) == 2) {
                if (abs(x - selectedX) == 1) {
                    return true;
                }
            }
            else if (abs(y - selectedY) == 1) {
                if (abs(x - selectedX) == 2) {
                    return true;
                }
            }
            return false;
        case ROOK:
            //check it's on the lines
            //make sure no pieces are in the way
            if (x == selectedX) {
                //check y direction in a line
                if (selectedY < y) {
                    //if position is forward
                    for (int i = selectedY; i < y; i++) {
                        if (board[selectedX][i] != NONE) return false;
                    }
                }
                else {
                    //if position is backward
                    for (int i = selectedY; i > y; i--) {
                        if (board[selectedX][i] != NONE) return false;
                    }
                }
                return true;
            }
            else if (y == selectedY) {
                //check x direction in a line
                if (selectedX < x) {
                    //if position is forward
                    for (int i = selectedX; i < x; i++) {
                        if (board[i][selectedY] != NONE) return false;
                    }
                }
                else {
                    //if position is backward
                    for (int i = selectedX; i > x; i--) {
                        if (board[i][selectedY] != NONE) return false;
                    }
                }
                return true;
            }
            else {
                return false;
            }
        case PAWN:
            //check en passant
            //check no pieces in way
            //check for take on diagonal
            //check for 2 moves on first row
            if (currentPlayer == white && y <= selectedY) return false;
            if (currentPlayer == black && y >= selectedY) return false;
            if (x != selectedX) {
                if (abs(x - selectedX) != 1) return false;  //must be one diagonal space away
                if (abs(y - selectedY) != 1) return false; 
                if (board[x][y] == NONE) return false;  //must be taking a piece
                return true;
            }
            else {
                if (currentPlayer == white) {
                    if (selectedY == 1) {
                        if (y == 2 && board[x][y] == NONE) return true;
                        else if (y == 3 && board[x][y] == NONE && board[x][y - 1] == NONE) return true;
                        else return false;
                    }
                    else {
                        if (y == selectedY + 1) {
                            if (board[x][y] == NONE) {
                                return true;
                            }
                        }
                        return false;
                    }
                }
                else {
                    if (selectedY == 6) {
                        if (y == 5 && board[x][y] == NONE) return true;
                        else if (y == 4 && board[x][y] == NONE && board[x][y + 1] == NONE) return true;
                        else return false;
                    }
                    else {
                        if (y == selectedY - 1) {
                            if (board[x][y] == NONE) {
                                return true;
                            }
                        }
                        return false;
                    }
                }
            }
        default:
            return false;
    }
}

void movePiece() {
    //new position is viable, update location and change current player
    if (!gameActive) gameActive = true;
    if (board[positionX][positionY] == KING) {
        showGameEnd();
        return;
    }
    removeSelectionBox();
    drawPiece(selectedX, selectedY, NONE);
    board[positionX][positionY] = board[selectedX][selectedY];
    color[positionX][positionY] = currentPlayer;
    board[selectedX][selectedY] = NONE;
    drawPiece(positionX, positionY, board[positionX][positionY]);
    pieceSelected = false;
    if (currentPlayer == white) {
        timerWhite.stop();
        timerBlack.start();
    }
    if (currentPlayer == black) {
        timerBlack.stop();
        timerWhite.start();
    }
    if (playSound) speakerOut.PlayNote(2000, .1, .25);
    currentPlayer = !currentPlayer;
}

void moveSelectionBox(int direction) {
    if (direction == NONE) {
        drawSelectionBox(positionX, positionY, GREEN);
        return;
    }
    if (direction == FORWARD && positionY == 7) return;
    if (direction == BACKWARD && positionY == 0) return;
    if (direction == RIGHT && positionX == 7) return;
    if (direction == LEFT && positionX == 0) return;
    if (pieceSelected && positionX == selectedX && positionY == selectedY) {
        drawSelectionBox(positionX, positionY, RED);
    }
    else {
        drawSelectionBox(positionX, positionY, BLACK);
    }
    if (direction == FORWARD) {
        positionY++;
    }
    if (direction == BACKWARD) {
        positionY--;
    }
    if (direction == RIGHT) {
        positionX++;
    }
    if (direction == LEFT) {
        positionX--;
    }
    if (playSound) speakerOut.PlayNote(500, .1, .25);
    drawSelectionBox(positionX, positionY, GREEN);
}

bool checkValidSelection() {
    if (board[positionX][positionY] != NONE) {
        if (color[positionX][positionY] == currentPlayer) {
            return true;
        }
    }
    return false;
}

void selectPiece() {
    selectedX = positionX;
    selectedY = positionY;
    pieceSelected = true;
    currentPiece = board[positionX][positionY];
    displayPossibleMoves();
}

void deselectPiece() {
    pieceSelected = false;
    currentPiece = NONE;
    removeSelectionBox();
    moveSelectionBox(NONE);
}

void selectHandler() {
    if (pieceSelected) {
        if (positionX == selectedX && positionY == selectedY) {
            deselectPiece();
        }
        if (checkValidMove(positionX, positionY)) {
            movePiece();
        }
    }
    else {
        if (checkValidSelection()) {
            selectPiece();
        }
    }
}

void gameHandler() {
    if (menuOpen) return;
    int move = waitForInput();
    switch (move) {
        case MENU:
            openMenu();
            return;
        case SELECT:
            selectHandler();
            break;
        default:
            moveSelectionBox(move);
            break;
    }
}

//MAIN----------------------------------------------------------------------------------------------------------------------------------

int main() {
    
    initialize();
    
    Thread timerThread(timer);
    
    while (true) {
        if (menuOpen) menuHandler();
        else gameHandler();
    }
 
}