Chess game on the mbed.

Dependencies:   4DGL-uLCD-SE PinDetect SDFileSystem mbed

main.cpp

Committer:
mbavare
Date:
2016-03-14
Revision:
1:af3a896cd76d
Parent:
0:a5f9535b6e3d

File content as of revision 1:af3a896cd76d:

#include "mbed.h"
#include "uLCD_4DGL.h"
#include "SDFileSystem.h"
#include <algorithm>
#include "PinDetect.h"
#include "Speaker.h"

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

//bluetooth for the player two controller
Serial blue(p13,p14); //serial tx, rx

//Using the speaker as a pwm out
Speaker mySpeaker(p21);

//set up the chess board as an int array
int board[8][8]={0};

//Set the pieces so we can refer to them by name
const int pawn=1;
const int rook=2;
const int knight=3;
const int bishop=4;
const int queen=5;
const int king=6;

//global variables to keep track of the current player and the winner
int winner = 0;
int current_player = 1;

//class declaration for the navswitch
class Nav_Switch
{
public:
    Nav_Switch(PinName up,PinName down,PinName left,PinName right,PinName fire);
    int read();
//boolean functions to test each switch
    bool up();
    bool down();
    bool left();
    bool right();
    bool fire();
//automatic read on RHS
    operator int ();
//index to any switch array style
    bool operator[](int index) {
        return _pins[index];
    };
private:
    BusIn _pins;

};
Nav_Switch::Nav_Switch (PinName up,PinName down,PinName left,PinName right,PinName fire):
_pins(up, down, left, right, fire)
{
    _pins.mode(PullUp); //needed if pullups not on board or a bare nav switch is used - delete otherwise
    wait(0.001); //delays just a bit for pullups to pull inputs high
}
inline bool Nav_Switch::up()
{
    return !(_pins[0]);
}
inline bool Nav_Switch::down()
{
    return !(_pins[1]);
}
inline bool Nav_Switch::left()
{
    return !(_pins[2]);
}
inline bool Nav_Switch::right()
{
    return !(_pins[3]);
}
inline bool Nav_Switch::fire()
{
    return !(_pins[4]);
}
inline int Nav_Switch::read()
{
    return _pins.read();
}
inline Nav_Switch::operator int ()
{
    return _pins.read();
}

Nav_Switch p1( p9, p6, p7, p5, p8); //pin order on Sparkfun breakout

//set the MBED leds up to signal information
DigitalOut myLed1(LED1);
DigitalOut myLed2(LED2);
DigitalOut myLed3(LED3);
DigitalOut myLed4(LED4);

//functions for drawing the different chess pieces using uLCD member functions
void drawPawn(int x, int y, int Color) {
    uLCD.filled_circle(x*16+8,y*16+5,2,Color);
    uLCD.triangle(x*16+8,y*16+5,x*16+4,y*16+12,x*16+12,y*16+12,Color);    
}
void drawQueen(int x, int y, int Color) {
    uLCD.filled_rectangle(x*16+4,y*16+6, x*16+12, y*16+12, Color);
    uLCD.triangle(x*16+4,y*16+6,x*16+4,y*16+2,x*16+7,y*16+6,Color);
    uLCD.triangle(x*16+7,y*16+6,x*16+8.5,y*16+2,x*16+10,y*16+6,Color);
    uLCD.triangle(x*16+10,y*16+6,x*16+12,y*16+6,x*16+12,y*16+2,Color);
}
void drawKing(int x,int y, int Color) {
    uLCD.filled_rectangle(x*16+7,y*16+2,x*16+9,y*16+12,Color);
    uLCD.filled_rectangle(x*16+4,y*16+6,x*16+12,y*16+8,Color);    
}
void drawKnight(int x, int y, int Color) {
    uLCD.triangle(x*16+8,y*16+2,x*16+4,y*16+4,x*16+8,y*16+4,Color);
    uLCD.filled_rectangle(x*16+7, y*16+2, x*16+9, y*16+10, Color);
    uLCD.filled_rectangle(x*16+4, y*16+10, x*16+12, y*16+ 12, Color);
}
void drawRook(int x, int y, int Color) {
    uLCD.filled_rectangle(x*16+2, y*16+4, x*16+ 14, y*16+6,Color);
    uLCD.filled_rectangle(x*16+5, y*16+6, x*16+11, y*16+12, Color);
    uLCD.filled_rectangle(x*16+2, y*16+12, x*16+14,y*16+14,Color);
    uLCD.filled_rectangle(x*16+3, y*16+2, x*16+5, y*16+4,Color);
    uLCD.filled_rectangle(x*16+7, y*16+2, x*16+9, y*16+4,Color);
    uLCD.filled_rectangle(x*16+11, y*16+2,x*16+13,y*16+4,Color);
}
void drawBishop(int x, int y, int Color) {
    uLCD.filled_rectangle(x*16+4,y*16+10, x*16+12, y*16+12,Color);
    uLCD.filled_rectangle(x*16+7, y*16+4,x*16+9,y*16+10,Color);
    uLCD.triangle(x*16+5,y*16+4,x*16+11,y*16+4,x*16+8,y*16,Color);    
}

//display_board sets up the board in the beginning of the game
void display_board(void)
{
    //this loop draws the board squares, alternating color 
    bool alternator = true;
    for(int i=0; i<8; i++)//This loop will be executed for each row once
    {
        for(int j=0; j<8; j++) //This loop will be executed 7 times for each row
        {
            if (alternator) {
                uLCD.filled_rectangle(j*16,i*16,(j*16)+16,(i*16)+16, 0xbcb6b8); //draws a light tan (white) square
                alternator = false;
            } else {
                uLCD.filled_rectangle(j*16,i*16,(j*16)+16,(i*16)+16, 0x645659); //draws a dark brown (black) square
                alternator = true;
            }
        }
        alternator = !alternator;
    }
    
    //draw the pieces in their initial placement and update the board array
    for(int i=0;i<8;i++) //set the pawns up in a for loop
    {
        drawPawn(i,1,BLACK);
        board[i][1]= -pawn;
        drawPawn(i,6,WHITE);
        board[i][6]= pawn;
    }
    
    drawRook(0,0,BLACK);
    drawRook(7,0,BLACK);
    board[0][0]=-rook;
    board[7][0]=-rook;

    drawRook(0,7,WHITE);
    drawRook(7,7,WHITE);
    board[0][7]=rook;
    board[7][7]=rook;

    drawKnight(1,0,BLACK);
    drawKnight(6,0,BLACK);
    board[1][0]=-knight;
    board[6][0]=-knight;
    
    drawKnight(1,7,WHITE);
    drawKnight(6,7,WHITE);
    board[1][7]=knight;
    board[6][7]=knight;
    
    drawBishop(2,0,BLACK);
    drawBishop(5,0,BLACK);
    board[2][0]=-bishop;
    board[5][0]=-bishop;
    
    drawBishop(2,7,WHITE);
    drawBishop(5,7,WHITE);
    board[2][7]=bishop;
    board[5][7]=bishop;
    
    drawQueen(3,0,BLACK);
    board[3][0]=-queen;
    
    drawQueen(3,7,WHITE);
    board[3][7]=queen;
    
    drawKing(4,0,BLACK);
    board[4][0]=-king;
    
    drawKing(4,7,WHITE);
    board[4][7]=king;    
}
//globals for castling - keep track of if the pieces have moved 
//false indicates they have not moved, mark true on a monement
bool king1 = false;
bool king2 = false;
bool rook11 = false;
bool rook12 = false;
bool rook21 = false;
bool rook22 = false;
bool castling = false; //set this flag high in validMove so we know in movePiece

//movePiece takes in a piece coordinate and moves it to a new spot
void movePiece(int ox, int oy, int dx, int dy, int player) {

    //first clear the piece in the destination if a piece is being captured
    if(board[dx][dy] !=0) {
        if( (dx+dy)%2==0) { //moving to a white square
            uLCD.filled_rectangle(dx*16,dy*16,(dx*16)+16,(dy*16)+16, 0xbcb6b8);

        }
        else { //moving to a dark square
            uLCD.filled_rectangle(dx*16,dy*16,(dx*16)+16,(dy*16)+16, 0x645659);
        }
    }

    //blank out the old square
    if( (ox+oy)%2==0) { //moving from a white square
        uLCD.filled_rectangle(ox*16,oy*16,(ox*16)+16,(oy*16)+16, 0xbcb6b8);

    }
    else { //moving from a dark square
        uLCD.filled_rectangle(ox*16,oy*16,(ox*16)+16,(oy*16)+16, 0x645659);
    }   
    
    //draw the piece in the new square
    switch (abs(board[ox][oy])) {  //switch based on the piece type
        case pawn:
            //if a pawn reaches the end of the line then promote it to a queen
            if(current_player == 1 && dy == 0) { //white pawn reaches end of file
                board[ox][oy] = queen;
                drawQueen(dx,dy,player);
            }
            
            else if(current_player == 2 && dy == 7) { //black pawn reaches end of file
                board[ox][oy] = -queen;
                drawQueen(dx,dy,player);
            }
            else
                drawPawn(dx,dy,player);
        break;
        case rook:
            drawRook(dx,dy,player);
            //this marks if rooks have moved or not (can only castle if false)
            if(oy == 0) {
                if(ox == 0)  //black left rook
                    rook21 = true;
                else if(ox==7) //black right rook
                    rook22 = true;
            }
            else if(oy == 7) {
                if(ox == 0)  //white rook 1
                    rook11 = true;
                else if(ox==7) //white rook 2
                    rook12 = true; 
            }
        break;    
        
        case knight:
            drawKnight(dx,dy,player);
        break; 
        
        case bishop:
            drawBishop(dx,dy,player);
        break;
        
        case queen:
            drawQueen(dx,dy,player);
        break; 
        
        case king:  //for king we also need to mark the king as moved 
            if(current_player == 1) 
                king1 = true; 
            else
                king2 = true;
            drawKing(dx,dy,player);
        break;         
    }

    //play two notes for a piece capture or one for a regular move
    if(board[dx][dy] !=0)
    {
        mySpeaker.PlayNote(500.0,0.1,0.05);
        mySpeaker.PlayNote(700.0,0.1,0.05);
    }
    else
        mySpeaker.PlayNote(600.0,0.1,0.05);

    //if the king is captured then mark then mark the correct player as winning
    if(board[dx][dy] == 6)
        winner = 2;
    else if(board[dx][dy] == -6)
        winner=1;

    //adjust the board array for the piece movement
    board[dx][dy] = board[ox][oy];
    board[ox][oy] = 0;

    //if we're castling we also need to move the rook
    //first mark castling false then make a recursive call to the movePiece function to move the rook
    if(castling) { 
        castling = false; 
        if(dx<ox) { //castling left
            movePiece(0,oy,3,oy, player); 
        }   
        else { //castling right
            movePiece(7,oy,5,oy, player);
        }
    }
}

//this function takes in the location of a piece and the destination and outputs whether or not it is a valid move
int validMove(int ox, int oy, int dx, int dy)
{
  int source = board[ox][oy];
  int target = board[dx][dy];
  int typePiece = abs(source);
  //taking one of your own pieces is not allowed
  if (board[ox][oy] > 0 && board[dx][dy]>0 || board[ox][oy] < 0 && board[dx][dy]<0)
    return 0;

    //movement rules for pawns
    if (typePiece == pawn) {
        if(current_player == 2 && dx==ox && dy-oy==1 && target==0) return 1;
        if(current_player == 1 && dx==ox && dy-oy==-1 && target==0) return 1;
        if(abs(dx-ox)==1 && current_player == 2 && dy-oy==1 && target > 0) return 1;
        if(abs(dx-ox)==1 && current_player == 1 && dy-oy==-1 && target < 0) return 1;
        if(current_player == 2 && oy==1 && (abs(ox-dx)==0) && abs(oy-dy)==2 && target == 0) return 1;
        if(current_player == 1 && oy==6 && (abs(ox-dx)==0) &&(abs(oy-dy)==2) && target == 0) return 1;
 
    //movement rules for rooks    
    } else if (typePiece == rook) {
        if(dx==ox){ // Moving vertically
            if(dy>oy){ // Downards
                for(size_t row = (unsigned)(oy+1); row < (unsigned)dy; ++row){
                    if(board[dx][row] != 0) return 0;
                }
            } else { // Upwards
                for(size_t row = (unsigned)dy+1; row < (unsigned)(oy); ++row){
                if(board[dx][row] != 0) return 0;
                } 
            } 
            return 1;
        }
        if(dy==oy){ // Moving horizontally
            if(dx>ox){ // Rightwards
                for(size_t column = (unsigned)(ox+1); column < (unsigned)dx; ++column){
                    if(board[column][dy] != 0) return 0;
                }
            }
            if(dx<ox){ // Leftwards
                for(size_t column = (unsigned)dx+1; column < (unsigned)(ox); ++column){
                    if(board[column][dy] != 0) return 0;
                }
            }
            return 1;
        }
        return 0;

    //movement rules for the knight
    } else if (typePiece == knight) {

        if((abs(dy-oy)==2 && abs(dx-ox)==1) || (abs(dx-ox)==2 && abs(dy-oy)==1)){
            return 1;
        }
        return 0;

    //movement rules for bishops    
    } else if (typePiece == bishop) {
        if (abs(dx-ox) != abs(dy-oy)) //must move diagonally
            return 0;
        int ymult = -1;
        int xmult = -1;
        if (dx>ox)
            xmult = 1;
        if (dy>oy)
            ymult = 1;
        //check all the spots between location and destination on the diagonal for pieces
        for(int i=1; i < abs(dx-ox); ++i)
        {
            if(board[ox + i*xmult][oy+i*ymult] != 0) return 0;
        }
        return 1;

    //movement rules for queens
    } else if (typePiece ==queen) {
        // Queen
        if(abs(dx-ox) == abs(dy-oy)) { //diagonal movement
            int ymult = -1;
            int xmult = -1;
            if (dx>ox)
                xmult = 1;
            if (dy>oy)
                ymult = 1;

            for(int i=1; i < abs(dx-ox); ++i)
            {
                if(board[ox + i*xmult][oy+i*ymult] != 0) return 0;
            }
        return 1;             
        }

        
        if(dx==ox){ // Moving vertically
            if(dy>oy){ // Downards
                for(size_t row = (unsigned)(oy+1); row < (unsigned)dy; ++row){
                    if(board[dx][row] != 0) return 0;
                }
            } else { // Upwards
                for(size_t row = (unsigned)dy+1; row < (unsigned)(oy); ++row){
                    if(board[dx][row] != 0) return 0;
                }
            }
            return 1;
        }
        if(dy==oy){ // Moving horizontally
            if(dx>ox){ // Rightwards
                for(size_t column = (unsigned)(ox+1); column < (unsigned)dx; ++column){
                    if(board[column][dy] != 0) return 0;
                }
            }
            if(dx<ox){ // Leftwards
                for(size_t column = (unsigned)dx+1; column < (unsigned)(ox); ++column){
                    if(board[column][dy] != 0) return 0;
                }
            }
            return 1;
        }
        return 0;

    //movement rules for a king
    } else if (typePiece ==king) {

        //special exception for castling
        if(dy == oy && ox-2 == dx && (!king1 && current_player == 1 && !rook11 || !king2 && current_player==2 && !rook21))
        {
            if(board[ox-1][oy] ==0 && board[ox-2][oy] ==0 && board[ox-3][oy]==0) {
                castling = true;
                return 1; //king can castle to the left
            }
        }
        else if(dy == oy && ox + 2 == dx && (!king1 && current_player == 1 && !rook12 || !king2 && current_player==2 && !rook22))
        {
            if(board[ox+1][oy] ==0 && board[ox+2][oy] ==0){
                castling = true;
                return 1; //king can castle to the right
            }
        }

        if(abs(dy-oy)<=1 && abs(dx-ox)<=1) return 1;
        return 0;
    }

    return 0; 
}

//naming the pins for the joystick
PinDetect up(p26);
PinDetect center(p25);
PinDetect left(p24);
PinDetect down(p23);
PinDetect right(p22);


//variables for the joystick interrupt functions
volatile int x = 4;
volatile int y = 6;
volatile int x2 = 4;
volatile int y2 = 2;
volatile bool center_hit = false;

//joystick interrupt functions that control which square is selected
void up_hit_callback(void) {
    --y;
    if(y<0)
        y=7;
}  
void down_hit_callback(void) {
    y = (y+1)%8;
}
void left_hit_callback(void) {
    --x;
    if(x<0)
        x=7;
}
void right_hit_callback(void) {
    x = (x+1)%8;
}
void center_hit_callback(void) {
    center_hit = true;
}

int main() {

    //set up the interrupt functions for the joystick
    center.attach_deasserted(&center_hit_callback);
    left.attach_deasserted(&left_hit_callback);
    right.attach_deasserted(&right_hit_callback);
    up.attach_deasserted(&up_hit_callback);
    down.attach_deasserted(&down_hit_callback);
    center.setSampleFrequency();
    left.setSampleFrequency();
    right.setSampleFrequency();
    down.setSampleFrequency();
    up.setSampleFrequency();

    //setup the board
    display_board();

    //set the default cursor locations
    int oldx = 4;
    int oldx2 = 4;
    int oldy = 6;
    int oldy2 = 1;

    //control variables for p1 and p2
    int selectedx;
    int selectedy;
    bool selected=false;
    int selectedx2;
    int selectedy2;
    bool selected2 = false;

    //draw the initial cursor
    uLCD.rectangle(oldx*16,oldy*16,(oldx*16)+16,(oldy*16)+16, GREEN);
    
    //the overall game loop
    while(1) {
        //the loop for player one
        while(current_player==1) {
            myLed4 = 0; //this led indicates okayer
            myLed1 = selected; //this led indicates whether or not a piece has been selected
            
            //if the selected square has moved
            if(x != oldx || y != oldy) {
                mySpeaker.PlayNote(969.0,0.05,0.05); //play a noise for cursor movement

                //delete the old selector
                if(oldx % 2 == oldy %2)
                    uLCD.rectangle(oldx*16,oldy*16,(oldx*16)+16,(oldy*16)+16, 0xbcb6b8); 
                else
                    uLCD.rectangle(oldx*16,oldy*16,(oldx*16)+16,(oldy*16)+16, 0x645659); 

                //draw the new cursor
                uLCD.rectangle(x*16,y*16,(x*16)+16,(y*16)+16, GREEN);
                
                //record the old cursor location
                oldx = x;
                oldy = y;  
            }

            if (center_hit) {
                if(board[oldx][oldy] > 0 || selected) {
                    if(selected) { //if a piece has already been selected
                        //check move, make move and change turn if valid
                        if(selectedx == oldx && selectedy==oldy) { //deselect piece by clicking on it again
                            selected = false;
                        } else { 
                            if (validMove(selectedx, selectedy, oldx, oldy) ==1) { //if the move is valid make the move and swith turns
                                movePiece(selectedx, selectedy, oldx, oldy, WHITE); 
                                selected = false;
                                current_player = 2;
                                
                                //erase the player one selector
                                if(oldx % 2 == oldy %2)
                                    uLCD.rectangle(oldx*16,oldy*16,(oldx*16)+16,(oldy*16)+16, 0xbcb6b8); 
                                else
                                    uLCD.rectangle(oldx*16,oldy*16,(oldx*16)+16,(oldy*16)+16, 0x645659); 
                                 
                                //draw the player two selector    
                                uLCD.rectangle(oldx2*16,oldy2*16,(oldx2*16)+16,(oldy2*16)+16, BLUE);
                            }
                        }
                    } else { //if no piece is selected select the piece being clicked on
                        selected = true;
                        selectedx =  oldx;
                        selectedy = oldy;
                    }

                }    
                center_hit =false; //set the center hit flag to false
            }    
                
        }
            //the loop for player two
            while(current_player==2 && winner!=1)
            {
                myLed4 = 1; //this indicates that it is player twos turn
                myLed1 = selected2;  //shows whether or not a piece is selected
                //variables for the bluetooth 
                char bnum=0;
                char bhit=0;

                bool one_hit = false; 

                //this reads input from the bluetooth and adjusts the location of the cursor or registers a selection
                if (blue.getc()=='!') {
                    if (blue.getc()=='B') { //button data packet
                        bnum = blue.getc(); //button number
                        bhit = blue.getc(); //1=hit, 0=release
                        if (blue.getc()==char(~('!' + 'B' + bnum + bhit))) { //checksum OK?
                            switch (bnum) {
                                case '1': //number button 1
                                if (bhit=='1') {
                                    one_hit = true; 
                                } 
                                break;
                                case '5': //button 5 up arrow
                                if (bhit=='1') {
                                       //y2 = (y2-1)%8;
                                    --y2;
                                    if(y2<0)
                                        y2=7;
                                }
                                break;
                                case '6': //button 6 down arrow
                                if (bhit=='1') {
                                    y2 = (y2+1)%8;
                                } 
                                break;
                                case '7': //button 7 left arrow
                                if (bhit=='1') {
                                        //x2 = (x2-1)%8;
                                    --x2;
                                    if(x2<0)
                                        x2=7;
                                } 
                                break;
                                case '8': //button 8 right arrow
                                if (bhit=='1') {
                                    x2 = (x2+1)%8;
                                } 
                                break;

                            }

                            //sames as in player 1, play a sound and move the selection box if necessary
                            if(x2 != oldx2 || y2 != oldy2)
                            {
                                mySpeaker.PlayNote(800.0,0.05,0.05);
                                if(oldx % 2 == oldy2 %2)
                                    uLCD.rectangle(oldx2*16,oldy2*16,(oldx2*16)+16,(oldy2*16)+16, 0xbcb6b8); 
                                else
                                    uLCD.rectangle(oldx2*16,oldy2*16,(oldx2*16)+16,(oldy2*16)+16, 0x645659); 

                                uLCD.rectangle(x2*16,y2*16,(x2*16)+16,(y2*16)+16, BLUE);
                                oldx2 = x2;
                                oldy2 = y2;  
                            }

                            //if the selection button was pressed select/ deselect the piece or attempt to make a move
                            if (one_hit) {
                                if(board[oldx2][oldy2] < 0 || selected2) {
                                    if(selected2) {
                                        //check move, make move and change turn if valid
                                        if(selectedx2 == oldx2 && selectedy2 == oldy2) {
                                            selected2 = false; 
                                        }
                                        else {
                                            if(validMove(selectedx2, selectedy2, oldx2, oldy2)) {
                                                movePiece(selectedx2, selectedy2, oldx2, oldy2, BLACK); 
                                                selected2 = false;
                                                current_player = 1;
                                                if(oldx % 2 == oldy2 %2)
                                                    uLCD.rectangle(oldx2*16,oldy2*16,(oldx2*16)+16,(oldy2*16)+16, 0xbcb6b8); 
                                                else
                                                    uLCD.rectangle(oldx2*16,oldy2*16,(oldx2*16)+16,(oldy2*16)+16, 0x645659); 
                                                uLCD.rectangle(oldx*16,oldy*16,(oldx*16)+16,(oldy*16)+16, GREEN);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        selected2 = true;
                                        selectedx2 =  oldx2;
                                        selectedy2 = oldy2;
                                    }
                                    one_hit =false;   
                                }
                            } 
                        }
                    }      
                }
            } 
            //if someone won the game then display the winner and play the video
            if(winner) {
                uLCD.cls();
                uLCD.printf("Player %i wins", winner);
                wait(1);
                while(1) {

                    uLCD.media_init();
                    uLCD.set_sector_address(0x0000, 0x00);
                    uLCD.display_video(0,0);
                }
            }           
        }
    }