#include "chess_board.h" #include #include #include "stdio.h" #include #include #include "pieces.hpp" #define MARGIN 300 #define PAWN_WHITE 0u #define PAWN_BLACK 1u #define KING_WHITE 2u #define KING_BLACK 3u #define ROOK_WHITE 4u #define ROOK_BLACK 5u #define KNIGHT_WHITE 6u #define KNIGHT_BLACK 7u #define BISHOP_WHITE 8u #define BISHOP_BLACK 9u #define QUEEN_WHITE 10u #define QUEEN_BLACK 11u #define SQUARE_EMPTY 12u #define LIGHT_OFF 0u #define POTENTIAL_MOVE 1u #define POTENTIAL_TAKE 2u #define SUGGESTED_MOVE 3u #define ERROR_MOVE 4u #define PEICE_ORIGIN 5u #define PEICE_NEEDS_TO_BE_HERE 6u #define GAME_STATE_IDLE 0u #define GAME_STATE_P1_TURN_BEGINING 1u #define GAME_STATE_P1_TURN_IN_PROGRESS 2u #define GAME_STATE_P1_TURN_TAKING 3u #define GAME_STATE_P2_TURN_BEGINING 4u #define GAME_STATE_P2_TURN_IN_PROGRESS 5u #define GAME_STATE_P2_TURN_TAKING 6u #define GAME_STATE_ERROR_DETECTED 7u //#define GAME_STATE_ 4u const char File_Names[12][17] = {"pawn_white.bmp", "pawn_black.bmp", "king_white.bmp", "king_black.bmp", "tower_white.bmp", "tower_black.bmp", "horse_white.bmp", "horse_black.bmp", "bishop_white.bmp", "bishop_black.bmp", "queen_white.bmp", "queen_black.bmp", }; static int Height; static int Width; static SDL_Texture * Board_Texture; static SDL_Rect Rectangle; static int Board_Width; static uint8_t Board_State[12][8] = {{0}}; static uint8_t Current_Binary_Board[12] = {0}; static uint8_t Saved_Binary_Board[12] = {0}; static uint8_t Board_Lights[12][8] = {{0}}; SDL_Surface *bitmapSurface = NULL; SDL_Texture * bitmapTextures[12] = {NULL}; static uint8_t Game_State = GAME_STATE_P1_TURN_BEGINING; static bool White_Turn = true; static uint8_t Selected_Peice = SQUARE_EMPTY; static uint8_t Error_Count = 0u; static uint8_t Taken_Peice = SQUARE_EMPTY; static inline bool square_in_jail(uint8_t j) { return (j > 8u); } static void clear_lights(void) { for (size_t i = 0; i < 8; i++) { for (size_t j = 0; j < 8; j++) { if(Board_Lights[i][j] != ERROR_MOVE) { Board_Lights[i][j] = LIGHT_OFF; } } } } static bool white_team(uint8_t piece) { return ((piece % 2u) == 0u); } static bool opposite_teams(uint8_t piece_one, uint8_t piece_two) { return (((piece_one % 2u) == 0u) ^ ((piece_two % 2u) == 0u)); } static void pawn_move(uint8_t row, uint8_t column) { if(Board_State[row][column] == SQUARE_EMPTY) { Board_Lights[row][column] = POTENTIAL_MOVE; } } static void pawn_take(uint8_t row, uint8_t column) { if (Board_State[row][column] < SQUARE_EMPTY) { Board_Lights[row][column] = POTENTIAL_TAKE; } } static void Mark_Potential_Moves(uint8_t piece, uint8_t column, uint8_t row) { switch (piece) { case PAWN_WHITE : if (row == 6) { pawn_move(row - 2, column); } pawn_move(row - 1, column); if((column >= 1) && opposite_teams(Board_State[row][column], Board_State[row - 1][column - 1])) { pawn_take(row - 1, column - 1); } if ((column <= 6) && opposite_teams(Board_State[row][column], Board_State[row - 1][column + 1])) { pawn_take(row - 1, column + 1); } break; case PAWN_BLACK: if (row == 1) { pawn_move(row + 2, column); } pawn_move(row + 1, column); if ((column >= 1) && opposite_teams(Board_State[row][column], Board_State[row + 1][column - 1])) { pawn_take(row + 1, column - 1); } if ((column <= 6) && opposite_teams(Board_State[row][column], Board_State[row + 1][column + 1])) { pawn_take(row + 1, column + 1); } break; case ROOK_WHITE: case ROOK_BLACK: break; default: break; } } void Mark_Taken_Peices_Destination(void) { uint8_t add = (white_team(Taken_Peice) ? 8u : 10u); switch (Taken_Peice) { case PAWN_WHITE: case PAWN_BLACK: { for (uint8_t j = 0; j < 2u; j++) { for (uint8_t i = 0; i < 4u; i++) { if(Board_State[add + j][i] != Taken_Peice) { Board_State[add + j][i] = Taken_Peice; Taken_Peice = SQUARE_EMPTY; return; } } } break; } case ROOK_WHITE: case ROOK_BLACK: case KNIGHT_WHITE: case KNIGHT_BLACK: case BISHOP_WHITE: case BISHOP_BLACK: case QUEEN_WHITE: case QUEEN_BLACK: { Board_Lights[add][(Taken_Peice / 2u) + 2u] = PEICE_NEEDS_TO_BE_HERE; Board_Lights[add + 1u][(Taken_Peice / 2u) + 2u] = PEICE_NEEDS_TO_BE_HERE; break; } default: { break; } } } void Switch_Turns(void) { Game_State = (White_Turn ? GAME_STATE_P2_TURN_BEGINING : GAME_STATE_P1_TURN_BEGINING); White_Turn = !White_Turn; } void Board_Square_Was_Toggled(uint8_t j, uint8_t i, bool square_active) { switch (Game_State) { case GAME_STATE_ERROR_DETECTED: { if (Board_Lights[j][i] == PEICE_ORIGIN) { Board_State[j][i] = Selected_Peice; Selected_Peice = SQUARE_EMPTY; clear_lights(); } else if (Board_Lights[j][i] == PEICE_NEEDS_TO_BE_HERE) { if (j < 8u) { Board_State[j][i] = Selected_Peice; Selected_Peice = SQUARE_EMPTY; Board_Lights[j][i] = LIGHT_OFF; } else { Board_State[j][i] = Taken_Peice; Board_Lights[(j / 2u) * 2u][i] = LIGHT_OFF; Board_Lights[(j / 2u) * 2u + 1u][i] = LIGHT_OFF; Taken_Peice = SQUARE_EMPTY; } } else if (Board_Lights[j][i] == ERROR_MOVE) { Error_Count--; Board_Lights[j][i] = LIGHT_OFF; if(Error_Count == 0u) { if (Taken_Peice != SQUARE_EMPTY) { Game_State = White_Turn ? GAME_STATE_P1_TURN_TAKING : GAME_STATE_P2_TURN_TAKING; } if (Selected_Peice == SQUARE_EMPTY) { Game_State = White_Turn ? GAME_STATE_P1_TURN_BEGINING : GAME_STATE_P2_TURN_BEGINING; } else { Game_State = White_Turn ? GAME_STATE_P1_TURN_IN_PROGRESS : GAME_STATE_P2_TURN_IN_PROGRESS; } } } else { Error_Count++; Board_Lights[j][i] = ERROR_MOVE; } break; } case GAME_STATE_IDLE: { break; } case GAME_STATE_P2_TURN_BEGINING: case GAME_STATE_P1_TURN_BEGINING: { /* We are waiting till the player who's turn it is picks up a peice that is on their team */ if ((Board_State[j][i] != SQUARE_EMPTY) && (opposite_teams(Board_State[j][i], (White_Turn ? PAWN_BLACK : PAWN_WHITE)))) { Mark_Potential_Moves(Board_State[j][i], i, j); Selected_Peice = Board_State[j][i]; Board_State[j][i] = SQUARE_EMPTY; Game_State++; Board_Lights[j][i] = PEICE_ORIGIN; } else { Game_State = GAME_STATE_ERROR_DETECTED; Board_Lights[j][i] = ERROR_MOVE; Error_Count++; } break; } case GAME_STATE_P2_TURN_IN_PROGRESS: case GAME_STATE_P1_TURN_IN_PROGRESS: { /* We are waiting till the player who's turn it is picks up a peice that is on their team */ if (Board_Lights[j][i] == POTENTIAL_MOVE) { Board_State[j][i] = Selected_Peice; Selected_Peice = SQUARE_EMPTY; clear_lights(); Switch_Turns(); } else if (Board_Lights[j][i] == POTENTIAL_TAKE) { Taken_Peice = Board_State[j][i]; Board_State[j][i] = SQUARE_EMPTY; Game_State = (White_Turn ? GAME_STATE_P1_TURN_TAKING : GAME_STATE_P2_TURN_TAKING); Mark_Taken_Peices_Destination(); clear_lights(); Board_Lights[j][i] = PEICE_NEEDS_TO_BE_HERE; } else if (Board_Lights[j][i] == PEICE_ORIGIN) { Board_State[j][i] = Selected_Peice; Selected_Peice = SQUARE_EMPTY; clear_lights(); Game_State--; } else { Game_State = GAME_STATE_ERROR_DETECTED; Board_Lights[j][i] = ERROR_MOVE; Error_Count++; } break; } case GAME_STATE_P2_TURN_TAKING: case GAME_STATE_P1_TURN_TAKING: { if (Board_Lights[j][i] == PEICE_NEEDS_TO_BE_HERE) { if(j < 8u) { Board_State[j][i] = Selected_Peice; Selected_Peice = SQUARE_EMPTY; Board_Lights[j][i] = LIGHT_OFF; } else { Board_State[j][i] = Taken_Peice; Board_Lights[(j / 2u) * 2u][i] = LIGHT_OFF; Board_Lights[(j / 2u) * 2u + 1u][i] = LIGHT_OFF; Taken_Peice = SQUARE_EMPTY; } } else { Game_State = GAME_STATE_ERROR_DETECTED; Board_Lights[j][i] = ERROR_MOVE; Error_Count++; } if ((Selected_Peice == SQUARE_EMPTY) && (Taken_Peice == SQUARE_EMPTY)) { Switch_Turns(); } break; } default: { break; } } } /** * @brief The Board changed so now we have to see what is different and act accordingly. * @note Yes i know the design of this seems really bad but it's important to remember this is supposed to simulate the chess board I'm creating. * so I'm designing it this way because of the hardware that I'm using. * @retval None */ void Board_Changed(void) { for (uint8_t j = 0u; j < 12u; j++) { uint8_t difference = (Current_Binary_Board[j] ^ Saved_Binary_Board[j]); if (difference != 0u) { for (uint8_t i = 0u; i < 8u; i++) { if((difference & (1u << i)) != 0u) { Board_Square_Was_Toggled(j, i, ((Current_Binary_Board[j] & (1u << i)) != 0u)); } } } Saved_Binary_Board[j] = Current_Binary_Board[j]; } } /** * @brief Function for registering an incoming click * @param p_renderer: Pointer to the renderer * @param x: x location of the click * @param y: y location of the click * @retval */ void click(SDL_Renderer *p_renderer, int x, int y) { SDL_Point const point = {x, y}; const int square_size = Board_Width / 8; Rectangle.w = Board_Width + (4 * square_size); Rectangle.h = Board_Width; Rectangle.x = ((Width - Board_Width) / 2) - (2 * square_size); Rectangle.y = (Height - Board_Width) / 2; if (SDL_PointInRect(&point, &Rectangle)) { Rectangle.x = (Width - Board_Width) / 2; Rectangle.w = Board_Width; int starting_y = Rectangle.y; const int starting_x = Rectangle.x; Rectangle.w = square_size; Rectangle.h = square_size; for (size_t j = 0; j < 8; j++) { Rectangle.x = starting_x; for (size_t i = 0; i < 8; i++) { if(SDL_PointInRect(&point, &Rectangle)) { Current_Binary_Board[j] ^= (1u << i); Board_Changed(); goto draw_square; } Rectangle.x += square_size; } Rectangle.y += square_size; } Rectangle.x = ((Width - Board_Width) / 2) - (2 * square_size); /* Now we draw the jail */ for (size_t j = 8; j < 12; j++) { Rectangle.y = starting_y; for (size_t i = 0; i < 8; i++) { if (SDL_PointInRect(&point, &Rectangle)) { Current_Binary_Board[j] ^= (1u << i); Board_Changed(); goto draw_square; } Rectangle.y += square_size; } /*If we are at the end of second jail row, jump to the other side */ if (j == 9) { Rectangle.x += (Board_Width + square_size); } else { Rectangle.x += square_size; } } draw_square: SDL_SetRenderTarget(p_renderer, Board_Texture); draw_board(p_renderer); SDL_RenderCopy(p_renderer, Board_Texture, NULL, NULL); SDL_RenderPresent(p_renderer); } } /** * @brief Function for resizing the display of the board * @param p_renderer: pointer to the renderer * @param w: width of the new window * @param h: hight of the new window * @retval None */ void chess_board_resize(SDL_Renderer *p_renderer, int w, int h) { Width = w; Height = h; SDL_DestroyTexture(Board_Texture); Board_Texture = SDL_CreateTexture(p_renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h); Board_Width = ((w > h) ? h : w) - MARGIN; // get rid of rounding errors Board_Width -= Board_Width % 8; } /** * @brief Function for initializing the board * @note * @param *p_renderer pointer to the renderer: * @retval None */ void chess_board_init(SDL_Renderer *p_renderer) { for (uint8_t i = 0u; i < 12u; i++) { for (uint8_t j = 0u; j < 8u; j++) { Board_State[i][j] = SQUARE_EMPTY; } } Current_Binary_Board[0] = 0xFF; Current_Binary_Board[1] = 0xFF; Current_Binary_Board[6] = 0xFF; Current_Binary_Board[7] = 0xFF; Saved_Binary_Board[0] = 0xFF; Saved_Binary_Board[1] = 0xFF; Saved_Binary_Board[6] = 0xFF; Saved_Binary_Board[7] = 0xFF; //Place black pieces Board_State[0][0] = ROOK_BLACK; Board_State[0][7] = ROOK_BLACK; Board_State[0][1] = KNIGHT_BLACK; Board_State[0][6] = KNIGHT_BLACK; Board_State[0][2] = BISHOP_BLACK; Board_State[0][5] = BISHOP_BLACK; Board_State[0][3] = QUEEN_BLACK; Board_State[0][4] = KING_BLACK; Board_State[7][0] = ROOK_WHITE; Board_State[7][7] = ROOK_WHITE; Board_State[7][1] = KNIGHT_WHITE; Board_State[7][6] = KNIGHT_WHITE; Board_State[7][2] = BISHOP_WHITE; Board_State[7][5] = BISHOP_WHITE; Board_State[7][3] = QUEEN_WHITE; Board_State[7][4] = KING_WHITE; for (uint8_t i = 0; i < 8; i++) { Board_State[1][i] = PAWN_BLACK; Board_State[6][i] = PAWN_WHITE; } for (uint8_t i = 0; i < 12; i++) { //location of all the sprites plus the size of file names char file[25] = "sprites\\"; memcpy(&file[8], File_Names[i], 16); bitmapSurface = SDL_LoadBMP(file); bitmapTextures[i] = SDL_CreateTextureFromSurface(p_renderer, bitmapSurface); } SDL_FreeSurface(bitmapSurface); } /** * @brief Funtion for that will draw the current state of the board including pecies and colors for suggested and possible moves. * @param *p_renderer pointer to the renderer object: * @retval None */ void draw_board(SDL_Renderer *p_renderer) { SDL_SetRenderTarget(p_renderer, Board_Texture); SDL_SetRenderDrawColor(p_renderer, 0x7f, 0x7f, 0x7f, 0); SDL_RenderClear(p_renderer); SDL_RenderDrawRect(p_renderer, &Rectangle); SDL_SetRenderDrawColor(p_renderer, 0xFF, 0xFF, 0xFF, 0x00); Rectangle.w = Board_Width; Rectangle.h = Board_Width; Rectangle.x = (Width - Board_Width) / 2; Rectangle.y = (Height - Board_Width) / 2; SDL_RenderFillRect(p_renderer, &Rectangle); SDL_SetRenderDrawColor(p_renderer, 0x85, 0x5E, 0x42, 0x00); const int square_size = Board_Width / 8; int starting_x = Rectangle.x; Rectangle.w = square_size; Rectangle.h = square_size; for (size_t j = 0; j < 8; j++) { Rectangle.x = starting_x; for (size_t i = 0; i < 8; i++) { if(Board_Lights[j][i] == POTENTIAL_MOVE) { SDL_SetRenderDrawColor(p_renderer, 0x00, 0xFF, 0x00, 0x00); SDL_RenderFillRect(p_renderer, &Rectangle); SDL_SetRenderDrawColor(p_renderer, 0x85, 0x5E, 0x42, 0x00); } else if ((Board_Lights[j][i] == POTENTIAL_TAKE) || (Board_Lights[j][i] == PEICE_NEEDS_TO_BE_HERE)) { SDL_SetRenderDrawColor(p_renderer, 0xFF, 0x00, 0x00, 0x00); SDL_RenderFillRect(p_renderer, &Rectangle); SDL_SetRenderDrawColor(p_renderer, 0x85, 0x5E, 0x42, 0x00); } else if (Board_Lights[j][i] == PEICE_ORIGIN) { SDL_SetRenderDrawColor(p_renderer, 0xFF, 0x00, 0xFF, 0x00); SDL_RenderFillRect(p_renderer, &Rectangle); SDL_SetRenderDrawColor(p_renderer, 0x85, 0x5E, 0x42, 0x00); } else if (Board_Lights[j][i] == ERROR_MOVE) { SDL_SetRenderDrawColor(p_renderer, 0xFF, 0xFF, 0x00, 0x00); SDL_RenderFillRect(p_renderer, &Rectangle); SDL_SetRenderDrawColor(p_renderer, 0x85, 0x5E, 0x42, 0x00); } else if (((i % 2) + (j % 2)) == 1) { SDL_RenderFillRect(p_renderer, &Rectangle); } else { /* code */ } if((Board_State[j][i] & 0x0Fu) != SQUARE_EMPTY) { SDL_RenderCopy(p_renderer, bitmapTextures[(Board_State[j][i] & 0x0Fu)], NULL, &Rectangle); } Rectangle.x += square_size; } Rectangle.y += square_size; } Rectangle.x = ((Width - Board_Width) / 2) - (2 * square_size); Rectangle.y = (Height - Board_Width) / 2; int starting_y = Rectangle.y; SDL_SetRenderDrawColor(p_renderer, 0x6F, 0x6f, 0x6f, 0x00); /* Now we draw the jail */ for (size_t j = 8; j < 12; j++) { Rectangle.y = starting_y; for (size_t i = 0; i < 8; i++) { if (Board_Lights[j][i] == PEICE_NEEDS_TO_BE_HERE) { SDL_SetRenderDrawColor(p_renderer, 0xFF, 0x00, 0x00, 0x00); SDL_RenderFillRect(p_renderer, &Rectangle); SDL_SetRenderDrawColor(p_renderer, 0x6F, 0x6F, 0x6F, 0x00); } else if (Board_Lights[j][i] == ERROR_MOVE) { SDL_SetRenderDrawColor(p_renderer, 0xFF, 0xFF, 0x00, 0x00); SDL_RenderFillRect(p_renderer, &Rectangle); SDL_SetRenderDrawColor(p_renderer, 0x6F, 0x6F, 0x6F, 0x00); } else { SDL_RenderFillRect(p_renderer, &Rectangle); } if ((Board_State[j][i] & 0x0Fu) != SQUARE_EMPTY) { SDL_RenderCopy(p_renderer, bitmapTextures[(Board_State[j][i] & 0x0Fu)], NULL, &Rectangle); } Rectangle.y += square_size; } /*If we are at the end of second jail row, jump to the other side */ if(j == 9) { Rectangle.x += (Board_Width + square_size); } else { Rectangle.x += square_size; } } SDL_SetRenderTarget(p_renderer, NULL); SDL_RenderCopy(p_renderer, Board_Texture, NULL, NULL); }