diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ffb65d..bb27396 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,10 +4,14 @@ project(Chess C CXX) find_package(SDL2 REQUIRED) -file(GLOB_RECURSE sources +file(GLOB_RECURSE cpp_sources CONFIGURE_DEPENDS "src/*.cpp") +file(GLOB_RECURSE c_sources + CONFIGURE_DEPENDS + "src/*.c") + file (GLOB_RECURSE headers CONFIGURE_DEPENDS "src/*.h") set (include_dirs "") @@ -16,7 +20,7 @@ foreach (_headerFile ${headers}) list (APPEND include_dirs ${_dir}) endforeach() -add_executable(Chess ${sources}) +add_executable(Chess ${cpp_sources} ${c_sources}) set_target_properties(Chess PROPERTIES CXX_STANDARD 17) # set standard level target_include_directories(Chess PRIVATE ${include_dirs}) target_compile_options(Chess PRIVATE diff --git a/src/game_logic/chess_board.cpp b/src/game_logic/chess_board.cpp deleted file mode 100644 index 541c8a4..0000000 --- a/src/game_logic/chess_board.cpp +++ /dev/null @@ -1,1008 +0,0 @@ -#include "chess_board.h" -#include "stdio.h" -#include - - -static uint8_t Board_Pieces[8][8] = {{0}}; -static uint8_t Saved_Binary_Board[8] = {0}; -static uint8_t Board_States[8][8] = {{0}}; -static Game_State_t Game_State; -static Game_State_t Last_Game_State; -static uint8_t Selected_Piece = SQUARE_EMPTY; -static uint8_t Error_Count = 0u; -static uint8_t Taken_Piece = SQUARE_EMPTY; -static bool Check[2u] = {false, false}; -static uint8_t King_Locations[2u][2u] = {{7,4}, {0,4}}; -static bool Castling_Allowed[2u][2u] = {{true, true}, {true, true}}; -static bool High_Alert = false; - - -static bool Converting_Pawn = false; -static uint8_t Converting_Pawn_Row_Col[2]; -static uint8_t Pawn_Converted_To = QUEEN_WHITE; - - -/** - * @brief Function for clearing all of the lights on the board. Except for error moves. - * @retval None - */ -static void clear_board_state(void) -{ - for (size_t i = 0; i < 8; i++) - { - for (size_t j = 0; j < 8; j++) - { - if(Board_States[i][j] != ERROR_MOVE) - { - Board_States[i][j] = LIGHT_OFF; - } - } - } - -} - -/** - * @brief Function for determining if the piece is on the white team or on the black team. - * @note Pieces should be enumerated even if white and odd if black. - * @param piece: The piece under question. - * @retval Return true if on the white team, else false. - */ -static bool white_team(uint8_t piece) -{ - return ((piece % 2u) == 0u); -} - -/** - * @brief Function for determining if two pieces are on the same team or not. - * @param piece_one: Piece one ofcoarse. - * @param piece_two: Piece two obviously. - * @retval True if on opposite teams, else false. - */ -static bool opposite_teams(uint8_t piece_one, uint8_t piece_two) -{ - return (((piece_one % 2u) == 0u) ^ ((piece_two % 2u) == 0u)); -} - -/** - * @brief Check to see if the square is safe from the other team. - * @param column: Column of potential move - * @param row: Row of the potential move - * @retval True if the square is safe, else is false - */ -bool square_is_safe(uint8_t row, uint8_t column) -{ - int8_t temp = row + (Game_State.player_turn ? -1 : 1); - - /* first check if pawns can take us */ - if ((column > 0) && ((Game_State.player_turn ? PAWN_BLACK : PAWN_WHITE) == Board_Pieces[temp][column - 1u])) - { - //can be eaten by a pawn, not safe. - return false; - } - if ((column < 7) && ((Game_State.player_turn ? PAWN_BLACK : PAWN_WHITE) == Board_Pieces[temp][column + 1u])) - { - //can be eaten by a pawn, not safe. - return false; - } - - /* Other King */ - int8_t start_r = (row == 0u) ? 0 : -1; - int8_t stop_r = (row == 7u) ? 0 : 1; - int8_t start_c = (column == 0u) ? 0 : -1; - int8_t stop_c = (column == 7u) ? 0 : 1; - for (int8_t up_down = start_r; up_down <= stop_r; up_down++) - { - for (int8_t left_right = start_c; left_right <= stop_c; left_right++) - { - int8_t x = row + left_right; - int8_t y = column + up_down; - if ((Game_State.player_turn ? KING_BLACK : KING_WHITE) == Board_Pieces[x][y]) - { - return false; - } - } - } - - for (uint8_t direction = 0u; direction < 4u; direction++) - { - int8_t up_down_step; - int8_t left_right_step; - if (direction == 0u) - { - up_down_step = 1; - left_right_step = 0; - } - else if (direction == 1u) - { - up_down_step = 0; - left_right_step = 1; - } - else if (direction == 2u) - { - up_down_step = -1; - left_right_step = 0; - } - else - { - up_down_step = 0; - left_right_step = -1; - } - /* Rooks and queens */ - bool loop = true; - int8_t x = row; - int8_t y = column; - while (loop) - { - x += left_right_step; - y += up_down_step; - if ((x < 0) || (y < 0) || (x >= 8) || (y >= 8)) - { - loop = false; - } - else if (Board_Pieces[x][y] == SQUARE_EMPTY) - { - /* Do nothing */ - } - else if (((Game_State.player_turn ? ROOK_BLACK : ROOK_WHITE) == Board_Pieces[x][y]) || ((Game_State.player_turn ? QUEEN_BLACK : QUEEN_WHITE) == Board_Pieces[x][y])) - { - return false; - } - else - { - loop = false; - } - } - } - /* Bishops and queens */ - for (uint8_t direction = 0u; direction < 4u; direction++) - { - int8_t up_down_step; - int8_t left_right_step; - if (direction == 0u) - { - up_down_step = 1; - left_right_step = 1; - } - else if (direction == 1u) - { - up_down_step = -1; - left_right_step = 1; - } - else if (direction == 2u) - { - up_down_step = -1; - left_right_step = -1; - } - else - { - up_down_step = 1; - left_right_step = -1; - } - bool loop = true; - int8_t x = row; - int8_t y = column; - while (loop) - { - uint8_t bish = (Game_State.player_turn ? BISHOP_BLACK : BISHOP_WHITE); - uint8_t queen = (Game_State.player_turn ? QUEEN_BLACK : QUEEN_WHITE); - x += left_right_step; - y += up_down_step; - if ((x < 0) || (y < 0) || (x >= 8) || (y >= 8)) - { - loop = false; - } - else if (Board_Pieces[x][y] == SQUARE_EMPTY) - { - /* do nothing */ - } - else if ((bish == Board_Pieces[x][y]) || (queen == Board_Pieces[x][y])) - { - return false; - } - else - { - loop = false; - } - } - } - /* Knights */ - for (uint8_t direction = 0u; direction < 4u; direction++) - { - int8_t up_down_step; - int8_t left_right_step; - if (direction == 0u) - { - up_down_step = 2; - left_right_step = 0; - } - else if (direction == 1u) - { - up_down_step = 0; - left_right_step = 2; - } - else if (direction == 2u) - { - up_down_step = -2; - left_right_step = 0; - } - else - { - up_down_step = 0; - left_right_step = -2; - } - - for (uint8_t i = 0u; i < 2u; i++) - { - if ((direction % 2u) == 0u) - { - left_right_step = (i == 0u) ? -1 : 1; - } - else - { - up_down_step = (i == 0u) ? -1 : 1; - } - - int8_t x = (int8_t)row + left_right_step; - int8_t y = (int8_t)column + up_down_step; - if ((x >= 0) && (y >= 0) && (x < 8) && (y < 8)) - { - if ((Game_State.player_turn ? KNIGHT_BLACK : KNIGHT_WHITE) == Board_Pieces[x][y]) - { - return false; - } - } - } - } - return true; -} - -bool Check_If_Move_Caused_Check(uint8_t piece, uint8_t row, uint8_t column) -{ - bool ret_val; - uint8_t store_current_piece = Board_Pieces[row][column]; - Board_Pieces[row][column] = piece; - //If its the white's turn we want to see if the white king is still safe. - uint8_t white_black_idx = Game_State.player_turn ? 0u : 1u; - ret_val = !square_is_safe(King_Locations[white_black_idx][0u], King_Locations[white_black_idx][1u]); - Board_Pieces[row][column] = store_current_piece; - return ret_val; -} - -void Check_If_Could_Cause_Check(uint8_t row, uint8_t column) -{ - uint8_t temp_storage = Board_Pieces[row][column]; - Board_Pieces[row][column] = SQUARE_EMPTY; - //If its the white's turn we want to see if the white king is still safe. - uint8_t white_black_idx = Game_State.player_turn ? 0u : 1u; - High_Alert = !square_is_safe(King_Locations[white_black_idx][0u], King_Locations[white_black_idx][1u]); - Board_Pieces[row][column] = temp_storage; -} - -static bool Set_Light(uint8_t piece, uint8_t row, uint8_t column, uint8_t state) -{ - bool ret_val = false; - if (!Check_If_Move_Caused_Check(piece, row, column)) - { - Board_States[row][column] = state; - ret_val = true; - } - return ret_val; -} - -/** - * @brief Function for marking potential moves for pawns. - * @param row: row to move to - * @param column: column to move to - * @retval None - */ -static bool pawn_move(uint8_t piece, uint8_t row, uint8_t column) -{ - bool ret_val = false; - if (Board_Pieces[row][column] == SQUARE_EMPTY) - { - ret_val = Set_Light(piece, row, column, POTENTIAL_MOVE); - } - return ret_val; -} - -/** - * @brief Function for "casting" a ray in any direction, vertical, horizontal, or diagonal. If the ray hits someone from the other team - * or the end of the board the array will be terminated. - * @param direction_r: Row direction - * @param direction_c: Column direction - * @param row: current row location - * @param column: current column location - * @param piece: the piece that is casting the ray. - * @retval None - */ -static bool cast_a_ray(uint8_t piece, int8_t direction_r, int8_t direction_c, uint8_t column, uint8_t row) -{ - bool ret_val = false; - bool loop = true; - int8_t x = row; - int8_t y = column; - while (loop) - { - x += direction_c; - y += direction_r; - if((x < 0) || (y < 0) || (x >= 8) || (y >= 8)) - { - loop = false; - } - else if (Board_Pieces[x][y] == SQUARE_EMPTY) - { - ret_val = Set_Light(piece, x, y, POTENTIAL_MOVE) || ret_val; - } - else if (opposite_teams(piece, Board_Pieces[x][y])) - { - ret_val = Set_Light(piece, x, y, POTENTIAL_TAKE) || ret_val; - /* once we take a piece we can no longer take anymore */ - loop = false; - } - else - { - loop = false; - } - } - return ret_val; -} - -/** - * @brief Marking a piece for taking. - * @param row: Row ofcoarse - * @param column: Column obviously - * @retval None - */ -static bool pawn_take(uint8_t piece, uint8_t row, uint8_t column) -{ - bool ret_val = false; - if ((Board_Pieces[row][column] < SQUARE_EMPTY) && opposite_teams(piece, Board_Pieces[row][column])) - { - ret_val = Set_Light(piece, row, column, POTENTIAL_TAKE); - } - return ret_val; -} - -/** - * @brief Function for marking the potential moves. - * @param piece: Piece that we are marking the potential moves for. - * @param row: Current row location of the piece. - * @param column: Current column location of the piece. - * @retval None - */ -static bool Mark_Potential_Moves(uint8_t piece, uint8_t column, uint8_t row) -{ - bool ret_val = false; - switch (piece) - { - case PAWN_WHITE : - case PAWN_BLACK: - { - int8_t direction = white_team(piece) ? -1 : 1; - uint8_t temp_row = row + direction; - if (row == (white_team(piece) ? 6u : 1u)) - { - if(Board_Pieces[temp_row][column] == SQUARE_EMPTY) - { - ret_val = pawn_move(piece, row + (direction * 2u), column) || ret_val; - } - } - - ret_val = pawn_move(piece, temp_row, column) || ret_val; - if(column != 0) - { - ret_val = pawn_take(piece, temp_row, column - 1u) || ret_val; - } - if(column != 7u) - { - ret_val = pawn_take(piece, temp_row, column + 1u) || ret_val; - } - - break; - } - case ROOK_WHITE: - case ROOK_BLACK: - { - for (uint8_t direction = 0u; direction < 4u; direction++) - { - int8_t up_down_step; - int8_t left_right_step; - if(direction == 0u) - { - up_down_step = 1; - left_right_step = 0; - } - else if(direction == 1u) - { - up_down_step = 0; - left_right_step = 1; - } - else if (direction == 2u) - { - up_down_step = -1; - left_right_step = 0; - } - else - { - up_down_step = 0; - left_right_step = -1; - } - ret_val = cast_a_ray(piece, up_down_step, left_right_step, column, row) || ret_val; - } - break; - - } - case KNIGHT_WHITE: - case KNIGHT_BLACK: - { - for (uint8_t direction = 0u; direction < 4u; direction++) - { - int8_t up_down_step; - int8_t left_right_step; - if (direction == 0u) - { - up_down_step = 2; - left_right_step = 0; - } - else if (direction == 1u) - { - up_down_step = 0; - left_right_step = 2; - } - else if (direction == 2u) - { - up_down_step = -2; - left_right_step = 0; - } - else - { - up_down_step = 0; - left_right_step = -2; - } - - for (uint8_t i = 0u; i < 2u; i++) - { - if((direction % 2u) == 0u) - { - left_right_step = (i == 0u) ? -1 : 1; - } - else - { - up_down_step = (i == 0u) ? -1 : 1; - } - - int8_t x = (int8_t)row + left_right_step; - int8_t y = (int8_t)column + up_down_step; - if ((x >= 0) && (y >= 0) && (x < 8) && (y < 8)) - { - if (Board_Pieces[x][y] == SQUARE_EMPTY) - { - ret_val = Set_Light(piece, x, y, POTENTIAL_MOVE) || ret_val; - } - else if (opposite_teams(piece, Board_Pieces[x][y])) - { - ret_val = Set_Light(piece, x, y, POTENTIAL_TAKE) || ret_val; - } - } - } - } - break; - } - case BISHOP_WHITE: - case BISHOP_BLACK: - { - for (uint8_t direction = 0u; direction < 4u; direction++) - { - int8_t up_down_step; - int8_t left_right_step; - if (direction == 0u) - { - up_down_step = 1; - left_right_step = 1; - } - else if (direction == 1u) - { - up_down_step = -1; - left_right_step = 1; - } - else if (direction == 2u) - { - up_down_step = -1; - left_right_step = -1; - } - else - { - up_down_step = 1; - left_right_step = -1; - } - ret_val = cast_a_ray(piece, up_down_step, left_right_step, column, row) || ret_val; - } - break; - } - case QUEEN_WHITE: - case QUEEN_BLACK: - { - //Mark bishop moves - ret_val = Mark_Potential_Moves((piece - 2u), column, row) || ret_val; - //Mark rook moves - ret_val = Mark_Potential_Moves((piece - 6u), column, row) || ret_val; - break; - } - case KING_WHITE: - case KING_BLACK: - { - int8_t start_r = (row == 0u) ? 0 : -1; - int8_t stop_r = (row == 7u) ? 0 : 1; - int8_t start_c = (column == 0u) ? 0 : -1; - int8_t stop_c = (column == 7u) ? 0 : 1; - for (int8_t left_right = start_r; left_right <= stop_r; left_right++) - { - for (int8_t up_down = start_c; up_down <= stop_c; up_down++) - { - int8_t x = row + left_right; - int8_t y = column + up_down; - if (square_is_safe(x, y)) - { - if (Board_Pieces[x][y] == SQUARE_EMPTY) - { - Board_States[x][y] = POTENTIAL_MOVE; - ret_val = true; - } - else if (opposite_teams(piece, Board_Pieces[x][y])) - { - Board_States[x][y] = POTENTIAL_TAKE; - ret_val = true; - } - } - } - } - uint8_t white_black_idx = white_team(piece) ? 0u : 1u; - uint8_t kings_row = white_team(piece) ? 7u : 0u; - - //Can only castle if not currently in check - if (square_is_safe(row, column)) - { - // Queen side castle - if(Castling_Allowed[white_black_idx][0u] && (Board_Pieces[kings_row][1u] == SQUARE_EMPTY) - && (Board_Pieces[kings_row][2u] == SQUARE_EMPTY) && (Board_Pieces[kings_row][3u]) == SQUARE_EMPTY) - { - //First Check to see if the king will pass through check - if(square_is_safe(kings_row, 3u) && square_is_safe(kings_row, 2u)) - { - // Yay we can castle queen side! - Board_States[kings_row][2u] = POTENTIAL_CASTLE; - ret_val = true; - } - } - - // King side castle - if (Castling_Allowed[white_black_idx][1u] && (Board_Pieces[kings_row][5u] == SQUARE_EMPTY) && (Board_Pieces[kings_row][6u] == SQUARE_EMPTY)) - { - //First Check to see if the king will pass through check - if(square_is_safe(kings_row, 5u) && square_is_safe(kings_row, 6u)) - { - // Yay we can castle king side! - Board_States[kings_row][6u] = POTENTIAL_CASTLE; - ret_val = true; - } - } - } - - break; - } - - default: - break; - } - return ret_val; -} - -bool Check_If_Player_Can_Move(bool white) -{ - for (uint8_t row = 0; row < 8u; row++) - { - for (uint8_t column = 0; column < 8u; column++) - { - if((white_team(Board_Pieces[row][column]) == white)) - { - // SDL_Log("move: Row:%d, Col:%d", row, column); - - if(Mark_Potential_Moves(Board_Pieces[row][column], column, row)) - { - // SDL_Log("Player Can still move: Row:%d, Col:%d", row, column); - clear_board_state(); - return true; - } - } - } - - } - clear_board_state(); - // SDL_Log("Player cant move"); - return false; -} - -/** - * @brief Function for switching the players turn. Incharge of handling the state machine reset. - */ -static void Switch_Turns(void) -{ - Game_State.turn_state = BEGINNING; - Game_State.player_turn = !Game_State.player_turn; - // Square is safe assumes the other team is trying to attack the square so for example at the end of - // White's turn we want to see if the black king is now in check, so we will switch teams and then - // Check if the current kings locations is safe. If it is safe then check is false, if it isnt safe then check is true. - uint8_t white_black_idx = Game_State.player_turn ? 0u : 1u; - Check[white_black_idx] = !square_is_safe(King_Locations[white_black_idx][0u], King_Locations[white_black_idx][1u]); - - //Last thing we need to check before switching turns is to check if the game is over. - bool player_can_play = Check_If_Player_Can_Move(Game_State.player_turn); - if(!player_can_play) - { - Game_State.game_over = true; - Game_State.player_turn = !Game_State.player_turn; - Game_State.error_detected = !Check[white_black_idx]; - } - -} - -/** - * @brief Function for checking the selected piece to see if we are moving the king. - * If we are then we also want to update the new location of the corresponding king. - * @param row: Current row location of the piece. - * @param column: Current column location of the piece. - * @retval None - */ -static void Check_If_Moving_King(uint8_t row, uint8_t column) -{ - uint8_t white_black_idx = Game_State.player_turn ? 0u : 1u; - if((Selected_Piece == KING_WHITE) || (Selected_Piece == KING_BLACK)) - { - King_Locations[white_black_idx][0u] = row; - King_Locations[white_black_idx][1u] = column; - Castling_Allowed[white_black_idx][0u] = false; - Castling_Allowed[white_black_idx][1u] = false; - } - // Disable the castling of the corresponding side if the rook is being moved. - else if (((Selected_Piece == ROOK_WHITE) && (row == 7u)) - || ((Selected_Piece == ROOK_BLACK) && (row == 0u))) - { - if (column == 0u) - { - Castling_Allowed[white_black_idx][0u] = false; - } - else if (column == 7u) - { - Castling_Allowed[white_black_idx][1u] = false; - } - } -} - -static void Check_If_Converting_Pawn(uint8_t row, uint8_t column) -{ - uint8_t white_black_idx = Game_State.player_turn ? 0u : 1u; - Converting_Pawn = false; - - if((Selected_Piece == PAWN_WHITE) || (Selected_Piece == PAWN_BLACK)) - { - if((row == 0u) || (row == 7u)) - { - Selected_Piece = Game_State.player_turn ? QUEEN_WHITE : QUEEN_BLACK; - Pawn_Converted_To = Selected_Piece; - Converting_Pawn = true; - Converting_Pawn_Row_Col[0] = row; - Converting_Pawn_Row_Col[1] = column; - } - } -} - -static bool Converting_Pawn_If_Applicable(uint8_t row, uint8_t column) -{ - bool ret_val = false; - if(Converting_Pawn) - { - if((row == Converting_Pawn_Row_Col[0]) && - (Converting_Pawn_Row_Col[1] == column)) - { - //Putting the piece down on the board - if(Board_Pieces[row][column] == SQUARE_EMPTY) - { - Board_Pieces[row][column] = Pawn_Converted_To; - Board_States[row][column] = LIGHT_OFF; - } - //Picking the piece back up to toggle through the options - else - { - Board_Pieces[row][column] = SQUARE_EMPTY; - Board_States[row][column] = CONVERTING_PAWN; - Pawn_Converted_To = Pawn_Converted_To - 2; - if (Pawn_Converted_To < ROOK_WHITE) - { - Pawn_Converted_To += 8u; - } - - } - ret_val = true; - } - - } - - return ret_val; -} - - -/** - * @brief Function for toggeling a square's state. - * @param j: row location that was toggled. - * @param i: column location that was toggled. - * @retval None - */ -static void Board_Square_Was_Toggled(uint8_t j, uint8_t i) -{ - if (Game_State.error_detected) - { - if (Board_States[j][i] == PIECE_ORIGIN) - { - Board_Pieces[j][i] = Selected_Piece; - Selected_Piece = SQUARE_EMPTY; - clear_board_state(); - Game_State.turn_state = BEGINNING; - } - else if (Board_States[j][i] == PIECE_NEEDS_TO_BE_HERE) - { - if (j < 8u) - { - Board_Pieces[j][i] = Selected_Piece; - Selected_Piece = SQUARE_EMPTY; - Board_States[j][i] = LIGHT_OFF; - } - else - { - Board_Pieces[j][i] = Taken_Piece; - uint8_t board_column = (j / 2u) * 2u; - if(Board_States[board_column][i] == PIECE_NEEDS_TO_BE_HERE) - { - Board_States[board_column][i] = LIGHT_OFF; - } - if(Board_States[board_column + 1u][i] == PIECE_NEEDS_TO_BE_HERE) - { - Board_States[board_column + 1u][i] = LIGHT_OFF; - } - Taken_Piece = SQUARE_EMPTY; - } - - if ((Selected_Piece == SQUARE_EMPTY) && (Taken_Piece == SQUARE_EMPTY)) - { - Game_State.turn_state = BEGINNING; - Game_State.player_turn = !Game_State.player_turn; - } - } - else if (Board_States[j][i] == ERROR_MOVE) - { - Error_Count--; - Board_States[j][i] = LIGHT_OFF; - /* All Errors have been rectified so we can go back to where we were.*/ - if(Error_Count == 0u) - { - Game_State.error_detected = false; - } - } - else - { - Error_Count++; - Board_States[j][i] = ERROR_MOVE; - } - - } - else - { - switch (Game_State.turn_state) - { - /* We are waiting till the player who's turn it is picks up a piece that is on their team */ - case BEGINNING: - { - if ((j < 8u) && (Board_Pieces[j][i] != SQUARE_EMPTY) && (white_team(Board_Pieces[j][i]) == Game_State.player_turn)) - { - if((Board_Pieces[j][i] != KING_BLACK) && (Board_Pieces[j][i] != KING_WHITE)) - { - Check_If_Could_Cause_Check(j, i); - } - Selected_Piece = Board_Pieces[j][i]; - Board_Pieces[j][i] = SQUARE_EMPTY; - (void)Mark_Potential_Moves(Selected_Piece, i, j); - Board_States[j][i] = PIECE_ORIGIN; - Game_State.turn_state = IN_PROGRESS; - } - else - { - if(!Converting_Pawn_If_Applicable(j, i)) - { - Game_State.error_detected = true; - Board_States[j][i] = ERROR_MOVE; - Error_Count++; - } - } - - break; - } - /* Person is in the middle of taking a turn for example they might already have a piece in the hand*/ - case IN_PROGRESS: - { - if (Board_States[j][i] == POTENTIAL_MOVE) - { - Check_If_Moving_King(j, i); - Check_If_Converting_Pawn(j, i); - Board_Pieces[j][i] = Selected_Piece; - Selected_Piece = SQUARE_EMPTY; - clear_board_state(); - Switch_Turns(); - } - else if (Board_States[j][i] == POTENTIAL_TAKE) - { - // Taken_Piece = Board_State[j][i]; - Board_Pieces[j][i] = SQUARE_EMPTY; - Game_State.turn_state = FINALIZING; - clear_board_state(); - Board_States[j][i] = PIECE_NEEDS_TO_BE_HERE; - } - else if (Board_States[j][i] == PIECE_ORIGIN) - { - Board_Pieces[j][i] = Selected_Piece; - Selected_Piece = SQUARE_EMPTY; - clear_board_state(); - Game_State.turn_state = BEGINNING; - } - else if (Board_States[j][i] == POTENTIAL_CASTLE) - { - Check_If_Moving_King(j, i); - Board_Pieces[j][i] = Selected_Piece; - Selected_Piece = SQUARE_EMPTY; - clear_board_state(); - if(i == 2u) - { - Board_States[j][3u] = PIECE_NEEDS_TO_BE_HERE; - Board_States[j][0u] = PIECE_NEEDS_TO_BE_REMOVED; - } - else if(i == 6u) - { - Board_States[j][5u] = PIECE_NEEDS_TO_BE_HERE; - Board_States[j][7u] = PIECE_NEEDS_TO_BE_REMOVED; - } - else - { - /* Do nothing. */ - } - - } - else if (Board_States[j][i] == PIECE_NEEDS_TO_BE_REMOVED) - { - Selected_Piece = Board_Pieces[j][i]; - Board_Pieces[j][i] = SQUARE_EMPTY; - Board_States[j][i] = LIGHT_OFF; - Game_State.turn_state = FINALIZING; - } - else - { - Game_State.error_detected = true; - Board_States[j][i] = ERROR_MOVE; - Error_Count++; - } - break; - } - /* Player still needs to do something to complete their turn, like complete castle, en pessant, or converting a pawn*/ - case FINALIZING: - { - if (Board_States[j][i] == PIECE_NEEDS_TO_BE_HERE) - { - Check_If_Moving_King(j, i); - Check_If_Converting_Pawn(j, i); - Board_Pieces[j][i] = Selected_Piece; - Selected_Piece = SQUARE_EMPTY; - Board_States[j][i] = LIGHT_OFF; - } - else - { - if(!Converting_Pawn_If_Applicable(j, i)) - { - Game_State.error_detected = true; - Board_States[j][i] = ERROR_MOVE; - Error_Count++; - } - } - - if ((Selected_Piece == SQUARE_EMPTY) && (Taken_Piece == SQUARE_EMPTY)) - { - Switch_Turns(); - } - - break; - } - default: - { - break; - } - } - } -} - -void Board_get_lights_and_state(uint8_t board_state[8][8], uint8_t board_pieces[8][8]) -{ - memcpy(&board_state[0][0], &Board_States[0][0], sizeof(Board_States)); - memcpy(&board_pieces[0][0], &Board_Pieces[0][0], sizeof(Board_Pieces)); -} - - -/** - * @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(uint8_t current_binary_board[8]) -{ - for (uint8_t j = 0u; j < 8u; 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); - } - } - } - Saved_Binary_Board[j] = current_binary_board[j]; - } -} - - -/** - * @brief Function for initializing the board - * @note - * @retval None - */ -void chess_board_init(void) -{ - for (uint8_t i = 0u; i < 8u; i++) - { - for (uint8_t j = 0u; j < 8u; j++) - { - Board_Pieces[i][j] = SQUARE_EMPTY; - } - } - Saved_Binary_Board[0] = 0xFF; - Saved_Binary_Board[1] = 0xFF; - Saved_Binary_Board[6] = 0xFF; - Saved_Binary_Board[7] = 0xFF; - - //Place black pieces - Board_Pieces[0][0] = ROOK_BLACK; - Board_Pieces[0][7] = ROOK_BLACK; - Board_Pieces[0][1] = KNIGHT_BLACK; - Board_Pieces[0][6] = KNIGHT_BLACK; - Board_Pieces[0][2] = BISHOP_BLACK; - Board_Pieces[0][5] = BISHOP_BLACK; - Board_Pieces[0][3] = QUEEN_BLACK; - Board_Pieces[0][4] = KING_BLACK; - Board_Pieces[7][0] = ROOK_WHITE; - Board_Pieces[7][7] = ROOK_WHITE; - Board_Pieces[7][1] = KNIGHT_WHITE; - Board_Pieces[7][6] = KNIGHT_WHITE; - Board_Pieces[7][2] = BISHOP_WHITE; - Board_Pieces[7][5] = BISHOP_WHITE; - Board_Pieces[7][3] = QUEEN_WHITE; - Board_Pieces[7][4] = KING_WHITE; - - for (uint8_t i = 0; i < 8; i++) - { - Board_Pieces[1][i] = PAWN_BLACK; - Board_Pieces[6][i] = PAWN_WHITE; - } -} - -Game_State_t Board_get_game_state(void) -{ - return Game_State; -} diff --git a/src/game_logic/game_state.c b/src/game_logic/game_state.c new file mode 100644 index 0000000..61fd49c --- /dev/null +++ b/src/game_logic/game_state.c @@ -0,0 +1,335 @@ +#include "game_state.h" +#include "stdio.h" +#include +#include "pawn.h" +#include "king.h" +#include "piece_logic.h" + +static uint8_t Saved_Binary_Board[8] = {0}; +static Game_State_t Game_State = { + .game_over = false, + .error_detected = false, + .player_turn = WHITE_TURN, + .turn_state = BEGINNING, + .board_state = {0}, + .board_pieces = {0}, + .selected_peice = SQUARE_EMPTY, + .castling_allowed = {{true, true},{true, true}}, +}; +static uint8_t Error_Count = 0u; +static bool Check[2u] = {false, false}; + + +/** + * @brief Function for clearing all of the lights on the board. Except for error moves. + * @retval None + */ +void clear_board_state(void) +{ + for (size_t i = 0; i < 8; i++) + { + for (size_t j = 0; j < 8; j++) + { + if(Game_State.board_state[i*8+j] != ERROR_MOVE) + { + Game_State.board_state[i*8+j] = LIGHT_OFF; + } + } + } + +} + + +bool Set_Light(uint8_t piece, uint8_t row, uint8_t column, uint8_t state) +{ + bool ret_val = false; + if (!Check_If_Move_Caused_Check(piece, row, column, &Game_State)) + { + Game_State.board_state[row*8+column] = state; + ret_val = true; + } + return ret_val; +} + + +/** + * @brief Function for switching the players turn. Incharge of handling the state machine reset. + */ +static void Switch_Turns(void) +{ + Game_State.turn_state = BEGINNING; + Game_State.player_turn = !Game_State.player_turn; + // Square is safe assumes the other team is trying to attack the square so for example at the end of + // White's turn we want to see if the black king is now in check, so we will switch teams and then + // Check if the current kings locations is safe. If it is safe then check is false, if it isnt safe then check is true. + uint8_t white_black_idx = Game_State.player_turn ? 0u : 1u; + + Check[white_black_idx] = Check_Current_Players_Check_Status(&Game_State); + + //Last thing we need to check before switching turns is to check if the game is over. + bool player_can_play = Check_If_Player_Can_Move(Game_State.player_turn, &Game_State); + if(!player_can_play) + { + Game_State.game_over = true; + Game_State.player_turn = !Game_State.player_turn; + Game_State.error_detected = !Check[white_black_idx]; + } + +} + + +/** + * @brief Function for toggeling a square's state. + * @param row_idx: row location that was toggled. + * @param column_idx: column location that was toggled. + * @retval None + */ +static void Board_Square_Was_Toggled(uint8_t row_idx, uint8_t col_idx) +{ + if (Game_State.error_detected) + { + if (Game_State.board_state[row_idx*8+col_idx] == PIECE_ORIGIN) + { + Game_State.board_pieces[row_idx*8+col_idx] = Game_State.selected_peice; + Game_State.selected_peice = SQUARE_EMPTY; + clear_board_state(); + Game_State.turn_state = BEGINNING; + } + else if (Game_State.board_state[row_idx*8+col_idx] == PIECE_NEEDS_TO_BE_HERE) + { + Game_State.board_pieces[row_idx*8+col_idx] = Game_State.selected_peice; + Game_State.selected_peice = SQUARE_EMPTY; + Game_State.board_state[row_idx*8+col_idx] = LIGHT_OFF; + + if (Game_State.selected_peice == SQUARE_EMPTY) + { + Game_State.turn_state = BEGINNING; + Game_State.player_turn = !Game_State.player_turn; + } + } + else if (Game_State.board_state[row_idx*8+col_idx] == ERROR_MOVE) + { + Error_Count--; + Game_State.board_state[row_idx*8+col_idx] = LIGHT_OFF; + /* All Errors have been rectified so we can go back to where we were.*/ + if(Error_Count == 0u) + { + Game_State.error_detected = false; + } + } + else + { + Error_Count++; + Game_State.board_state[row_idx*8+col_idx] = ERROR_MOVE; + } + + } + else + { + switch (Game_State.turn_state) + { + /* We are waiting till the player who's turn it is picks up a piece that is on their team */ + case BEGINNING: + { + if ((Game_State.board_pieces[row_idx*8+col_idx] != SQUARE_EMPTY) && (white_team(Game_State.board_pieces[row_idx*8+col_idx]) == Game_State.player_turn)) + { + Game_State.selected_peice = Game_State.board_pieces[row_idx*8+col_idx]; + Game_State.board_pieces[row_idx*8+col_idx] = SQUARE_EMPTY; + (void)Mark_Potential_Moves(Game_State.selected_peice, col_idx, row_idx, &Game_State); + Game_State.board_state[row_idx*8+col_idx] = PIECE_ORIGIN; + Game_State.turn_state = IN_PROGRESS; + } + else + { + if(!Converting_Pawn_If_Applicable(row_idx, col_idx, &Game_State)) + { + Game_State.error_detected = true; + Game_State.board_state[row_idx*8+col_idx] = ERROR_MOVE; + Error_Count++; + } + } + + break; + } + /* Person is in the middle of taking a turn for example they might already have a piece in the hand*/ + case IN_PROGRESS: + { + if (Game_State.board_state[row_idx*8+col_idx] == POTENTIAL_MOVE) + { + Check_If_Moving_King(row_idx, col_idx, &Game_State); + Check_If_Converting_Pawn(row_idx, col_idx, &Game_State); + Game_State.board_pieces[row_idx*8+col_idx] = Game_State.selected_peice; + Game_State.selected_peice = SQUARE_EMPTY; + clear_board_state(); + Switch_Turns(); + } + else if (Game_State.board_state[row_idx*8+col_idx] == POTENTIAL_TAKE) + { + Game_State.board_pieces[row_idx*8+col_idx] = SQUARE_EMPTY; + Game_State.turn_state = FINALIZING; + clear_board_state(); + Game_State.board_state[row_idx*8+col_idx] = PIECE_NEEDS_TO_BE_HERE; + } + else if (Game_State.board_state[row_idx*8+col_idx] == PIECE_ORIGIN) + { + Game_State.board_pieces[row_idx*8+col_idx] = Game_State.selected_peice; + Game_State.selected_peice = SQUARE_EMPTY; + clear_board_state(); + Game_State.turn_state = BEGINNING; + } + else if (Game_State.board_state[row_idx*8+col_idx] == POTENTIAL_CASTLE) + { + Check_If_Moving_King(row_idx, col_idx, &Game_State); + Game_State.board_pieces[row_idx*8+col_idx] = Game_State.selected_peice; + Game_State.selected_peice = SQUARE_EMPTY; + clear_board_state(); + if(col_idx == 2u) + { + Game_State.board_state[row_idx*8+3u] = PIECE_NEEDS_TO_BE_HERE; + Game_State.board_state[row_idx*8+0u] = PIECE_NEEDS_TO_BE_REMOVED; + } + else if(col_idx == 6u) + { + Game_State.board_state[row_idx*8+5u] = PIECE_NEEDS_TO_BE_HERE; + Game_State.board_state[row_idx*8+7u] = PIECE_NEEDS_TO_BE_REMOVED; + } + else + { + /* Do nothing. */ + } + + } + else if (Game_State.board_state[row_idx*8+col_idx] == PIECE_NEEDS_TO_BE_REMOVED) + { + Game_State.selected_peice = Game_State.board_pieces[row_idx*8+col_idx]; + Game_State.board_pieces[row_idx*8+col_idx] = SQUARE_EMPTY; + Game_State.board_state[row_idx*8+col_idx] = LIGHT_OFF; + Game_State.turn_state = FINALIZING; + } + else + { + Game_State.error_detected = true; + Game_State.board_state[row_idx*8+col_idx] = ERROR_MOVE; + Error_Count++; + } + break; + } + /* Player still needs to do something to complete their turn, like complete castle, en pessant, or converting a pawn*/ + case FINALIZING: + { + if (Game_State.board_state[row_idx*8+col_idx] == PIECE_NEEDS_TO_BE_HERE) + { + Check_If_Moving_King(row_idx, col_idx, &Game_State); + Check_If_Converting_Pawn(row_idx, col_idx, &Game_State); + Game_State.board_pieces[row_idx*8+col_idx] = Game_State.selected_peice; + Game_State.selected_peice = SQUARE_EMPTY; + Game_State.board_state[row_idx*8+col_idx] = LIGHT_OFF; + } + else + { + if(!Converting_Pawn_If_Applicable(row_idx, col_idx, &Game_State)) + { + Game_State.error_detected = true; + Game_State.board_state[row_idx*8+col_idx] = ERROR_MOVE; + Error_Count++; + } + } + + if (Game_State.selected_peice == SQUARE_EMPTY) + { + Switch_Turns(); + } + + break; + } + default: + { + break; + } + } + } +} + +void Board_get_lights_and_state(uint8_t * board_state, uint8_t * board_pieces) +{ + memcpy(board_state, Game_State.board_state, sizeof(Game_State.board_state)); + memcpy(board_pieces, Game_State.board_pieces, sizeof(Game_State.board_pieces)); +} + + +/** + * @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(uint8_t current_binary_board[8]) +{ + for (uint8_t j = 0u; j < 8u; 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); + } + } + } + Saved_Binary_Board[j] = current_binary_board[j]; + } +} + + +/** + * @brief Function for initializing the board + * @note + * @retval None + */ +void game_state_init(void) +{ + for (uint8_t i = 0u; i < 8u; i++) + { + for (uint8_t j = 0u; j < 8u; j++) + { + Game_State.board_pieces[i*8+j] = SQUARE_EMPTY; + } + } + Saved_Binary_Board[0] = 0xFF; + Saved_Binary_Board[1] = 0xFF; + Saved_Binary_Board[6] = 0xFF; + Saved_Binary_Board[7] = 0xFF; + + //Place black pieces + Game_State.board_pieces[0*8+0] = ROOK_BLACK; + Game_State.board_pieces[0*8+7] = ROOK_BLACK; + Game_State.board_pieces[0*8+1] = KNIGHT_BLACK; + Game_State.board_pieces[0*8+6] = KNIGHT_BLACK; + Game_State.board_pieces[0*8+2] = BISHOP_BLACK; + Game_State.board_pieces[0*8+5] = BISHOP_BLACK; + Game_State.board_pieces[0*8+3] = QUEEN_BLACK; + Game_State.board_pieces[0*8+4] = KING_BLACK; + Game_State.board_pieces[7*8+0] = ROOK_WHITE; + Game_State.board_pieces[7*8+7] = ROOK_WHITE; + Game_State.board_pieces[7*8+1] = KNIGHT_WHITE; + Game_State.board_pieces[7*8+6] = KNIGHT_WHITE; + Game_State.board_pieces[7*8+2] = BISHOP_WHITE; + Game_State.board_pieces[7*8+5] = BISHOP_WHITE; + Game_State.board_pieces[7*8+3] = QUEEN_WHITE; + Game_State.board_pieces[7*8+4] = KING_WHITE; + + for (uint8_t i = 0; i < 8; i++) + { + Game_State.board_pieces[1*8+i] = PAWN_BLACK; + Game_State.board_pieces[6*8+i] = PAWN_WHITE; + } +} + +Game_State_t Board_get_game_state(void) +{ + return Game_State; +} + + diff --git a/src/game_logic/chess_board.h b/src/game_logic/game_state.h similarity index 50% rename from src/game_logic/chess_board.h rename to src/game_logic/game_state.h index 75727f9..0e24237 100644 --- a/src/game_logic/chess_board.h +++ b/src/game_logic/game_state.h @@ -1,4 +1,12 @@ +#ifndef GAME_STATE_H +#define GAME_STATE_H + #include "stdint.h" +#include "stdbool.h" + +#ifdef __cplusplus +extern "C" { +#endif enum Board_States_t { LIGHT_OFF = 0u, @@ -30,20 +38,31 @@ enum Board_States_t { #define WHITE_TURN true #define BLACK_TURN false -enum Turn_State_t { +typedef enum { BEGINNING = 0, IN_PROGRESS, FINALIZING, -}; +}Turn_State_t; -struct Game_State_t { - bool game_over = false; - bool error_detected = false; - bool player_turn = WHITE_TURN; - Turn_State_t turn_state = BEGINNING; -}; +typedef struct { + bool game_over; + bool error_detected; + bool player_turn; + Turn_State_t turn_state; + uint8_t board_state[8*8]; + uint8_t board_pieces[8*8]; + uint8_t selected_peice; + bool castling_allowed[2u][2u]; +}Game_State_t; -void chess_board_init(void); -void Board_Changed(uint8_t current_binary_board[12]); -void Board_get_lights_and_state(uint8_t board_lights[12][8], uint8_t board_state[12][8]); +void game_state_init(void); +void Board_Changed(uint8_t current_binary_board[8]); +bool Set_Light(uint8_t piece, uint8_t row, uint8_t column, uint8_t state); +void clear_board_state(void); +void Board_get_lights_and_state(uint8_t * board_lights, uint8_t * board_state); Game_State_t Board_get_game_state(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/game_logic/piece_logic/king.c b/src/game_logic/piece_logic/king.c new file mode 100644 index 0000000..cebcf4e --- /dev/null +++ b/src/game_logic/piece_logic/king.c @@ -0,0 +1,56 @@ +#include "king.h" +#include "piece_logic.h" + +static uint8_t King_Locations[2u][2u] = {{7,4}, {0,4}}; + +bool Check_Current_Players_Check_Status(Game_State_t * game_state) +{ + bool ret_val; + uint8_t white_black_idx = game_state->player_turn ? 0u : 1u; + ret_val = !square_is_safe(King_Locations[white_black_idx][0u], King_Locations[white_black_idx][1u], game_state); + return ret_val; +} + +bool Check_If_Move_Caused_Check(uint8_t piece, uint8_t row, uint8_t column, Game_State_t * game_state) +{ + bool ret_val; + uint8_t store_current_piece = game_state->board_pieces[row*8+column]; + game_state->board_pieces[row*8+column] = piece; + //If its the white's turn we want to see if the white king is still safe. + ret_val = Check_Current_Players_Check_Status(game_state); + game_state->board_pieces[row*8+column] = store_current_piece; + return ret_val; +} + +/** + * @brief Function for checking the selected piece to see if we are moving the king. + * If we are then we also want to update the new location of the corresponding king. + * @param row: Current row location of the piece. + * @param column: Current column location of the piece. + * @retval None + */ +void Check_If_Moving_King(uint8_t row, uint8_t column, Game_State_t * game_state) +{ + uint8_t white_black_idx = game_state->player_turn ? 0u : 1u; + if((game_state->selected_peice == KING_WHITE) || (game_state->selected_peice == KING_BLACK)) + { + King_Locations[white_black_idx][0u] = row; + King_Locations[white_black_idx][1u] = column; + game_state->castling_allowed[white_black_idx][0u] = false; + game_state->castling_allowed[white_black_idx][1u] = false; + } + // Disable the castling of the corresponding side if the rook is being moved. + else if (((game_state->selected_peice == ROOK_WHITE) && (row == 7u)) + || ((game_state->selected_peice == ROOK_BLACK) && (row == 0u))) + { + if (column == 0u) + { + game_state->castling_allowed[white_black_idx][0u] = false; + } + else if (column == 7u) + { + game_state->castling_allowed[white_black_idx][1u] = false; + } + } +} + diff --git a/src/game_logic/piece_logic/king.h b/src/game_logic/piece_logic/king.h new file mode 100644 index 0000000..c39e6ec --- /dev/null +++ b/src/game_logic/piece_logic/king.h @@ -0,0 +1,6 @@ +#include "game_state.h" + +bool Check_Current_Players_Check_Status(Game_State_t * game_state); +bool Check_If_Move_Caused_Check(uint8_t piece, uint8_t row, uint8_t column, Game_State_t * game_state); +void Check_If_Moving_King(uint8_t row, uint8_t column, Game_State_t * game_state); + diff --git a/src/game_logic/piece_logic/pawn.c b/src/game_logic/piece_logic/pawn.c new file mode 100644 index 0000000..95a1f65 --- /dev/null +++ b/src/game_logic/piece_logic/pawn.c @@ -0,0 +1,93 @@ +#include "pawn.h" +#include "game_state.h" +#include "piece_logic.h" + +static bool Converting_Pawn = false; +static uint8_t Converting_Pawn_Row_Col[2]; +static uint8_t Pawn_Converted_To = QUEEN_WHITE; + +void Check_If_Converting_Pawn(uint8_t row, uint8_t column, Game_State_t * game_state) +{ + uint8_t white_black_idx = game_state->player_turn ? 0u : 1u; + Converting_Pawn = false; + + if((game_state->selected_peice == PAWN_WHITE) || (game_state->selected_peice == PAWN_BLACK)) + { + if((row == 0u) || (row == 7u)) + { + game_state->selected_peice = game_state->player_turn ? QUEEN_WHITE : QUEEN_BLACK; + Pawn_Converted_To = game_state->selected_peice; + Converting_Pawn = true; + Converting_Pawn_Row_Col[0] = row; + Converting_Pawn_Row_Col[1] = column; + } + } +} + +bool Converting_Pawn_If_Applicable(uint8_t row, uint8_t column, Game_State_t * game_state) +{ + bool ret_val = false; + if(Converting_Pawn) + { + if((row == Converting_Pawn_Row_Col[0]) && + (Converting_Pawn_Row_Col[1] == column)) + { + //Putting the piece down on the board + if(game_state->board_pieces[row*8+column] == SQUARE_EMPTY) + { + game_state->board_pieces[row*8+column] = Pawn_Converted_To; + game_state->board_state[row*8+column] = LIGHT_OFF; + } + //Picking the piece back up to toggle through the options + else + { + game_state->board_pieces[row*8+column] = SQUARE_EMPTY; + game_state->board_state[row*8+column] = CONVERTING_PAWN; + Pawn_Converted_To = Pawn_Converted_To - 2; + if (Pawn_Converted_To < ROOK_WHITE) + { + Pawn_Converted_To += 8u; + } + + } + ret_val = true; + } + + } + + return ret_val; +} + + + +/** + * @brief Function for marking potential moves for pawns. + * @param row: row to move to + * @param column: column to move to + * @retval None + */ +bool pawn_move(uint8_t piece, uint8_t row, uint8_t column, Game_State_t * game_state) +{ + bool ret_val = false; + if (game_state->board_pieces[row*8+column] == SQUARE_EMPTY) + { + ret_val = Set_Light(piece, row, column, POTENTIAL_MOVE); + } + return ret_val; +} + +/** + * @brief Marking a piece for taking. + * @param row: Row ofcoarse + * @param column: Column obviously + * @retval None + */ +bool pawn_take(uint8_t piece, uint8_t row, uint8_t column, Game_State_t * game_state) +{ + bool ret_val = false; + if ((game_state->board_pieces[row*8+column] < SQUARE_EMPTY) && opposite_teams(piece, game_state->board_pieces[row*8+column])) + { + ret_val = Set_Light(piece, row, column, POTENTIAL_TAKE); + } + return ret_val; +} diff --git a/src/game_logic/piece_logic/pawn.h b/src/game_logic/piece_logic/pawn.h new file mode 100644 index 0000000..a450834 --- /dev/null +++ b/src/game_logic/piece_logic/pawn.h @@ -0,0 +1,7 @@ +#include "game_state.h" + + +void Check_If_Converting_Pawn(uint8_t row, uint8_t column, Game_State_t * game_state); +bool Converting_Pawn_If_Applicable(uint8_t row, uint8_t column, Game_State_t * game_state); +bool pawn_move(uint8_t piece, uint8_t row, uint8_t column, Game_State_t * game_state); +bool pawn_take(uint8_t piece, uint8_t row, uint8_t column, Game_State_t * game_state); diff --git a/src/game_logic/piece_logic/piece_logic.c b/src/game_logic/piece_logic/piece_logic.c new file mode 100644 index 0000000..59f6613 --- /dev/null +++ b/src/game_logic/piece_logic/piece_logic.c @@ -0,0 +1,526 @@ +#include "piece_logic.h" +#include "king.h" +#include "pawn.h" +#include +#include + + +/** + * @brief Function for determining if the piece is on the white team or on the black team. + * @note Pieces should be enumerated even if white and odd if black. + * @param piece: The piece under question. + * @retval Return true if on the white team, else false. + */ +bool white_team(uint8_t piece) +{ + return ((piece % 2u) == 0u); +} + +/** + * @brief Function for determining if two pieces are on the same team or not. + * @param piece_one: Piece one ofcoarse. + * @param piece_two: Piece two obviously. + * @retval True if on opposite teams, else false. + */ +bool opposite_teams(uint8_t piece_one, uint8_t piece_two) +{ + return (((piece_one % 2u) == 0u) ^ ((piece_two % 2u) == 0u)); +} + +/** + * @brief Check to see if the square is safe from the other team. + * @param column: Column of potential move + * @param row: Row of the potential move + * @retval True if the square is safe, else is false + */ +bool square_is_safe(uint8_t row, uint8_t column, Game_State_t * game_state) +{ + int temp = (int)row + (game_state->player_turn ? -1 : 1); + + /* first check if pawns can take us */ + if ((column > 0) && ((game_state->player_turn ? PAWN_BLACK : PAWN_WHITE) == game_state->board_pieces[temp*8+(int)column - 1])) + { + //can be eaten by a pawn, not safe. + return false; + } + if ((column < 7) && ((game_state->player_turn ? PAWN_BLACK : PAWN_WHITE) == game_state->board_pieces[temp*8+(int)column + 1])) + { + //can be eaten by a pawn, not safe. + return false; + } + + /* Other King */ + int start_r = (row == 0u) ? 0 : -1; + int stop_r = (row == 7u) ? 0 : 1; + int start_c = (column == 0u) ? 0 : -1; + int stop_c = (column == 7u) ? 0 : 1; + for (int up_down = start_r; up_down <= stop_r; up_down++) + { + for (int left_right = start_c; left_right <= stop_c; left_right++) + { + int x = row + left_right; + int y = column + up_down; + if ((game_state->player_turn ? KING_BLACK : KING_WHITE) == game_state->board_pieces[x*8+y]) + { + return false; + } + } + } + + for (uint8_t direction = 0u; direction < 4u; direction++) + { + int8_t up_down_step; + int8_t left_right_step; + if (direction == 0u) + { + up_down_step = 1; + left_right_step = 0; + } + else if (direction == 1u) + { + up_down_step = 0; + left_right_step = 1; + } + else if (direction == 2u) + { + up_down_step = -1; + left_right_step = 0; + } + else + { + up_down_step = 0; + left_right_step = -1; + } + /* Rooks and queens */ + bool loop = true; + int x = (int)row; + int y = (int)column; + while (loop) + { + x += left_right_step; + y += up_down_step; + if ((x < 0) || (y < 0) || (x >= 8) || (y >= 8)) + { + loop = false; + } + else if (game_state->board_pieces[x*8+y] == SQUARE_EMPTY) + { + /* Do nothing */ + } + else if (((game_state->player_turn ? ROOK_BLACK : ROOK_WHITE) == game_state->board_pieces[x*8+y]) || ((game_state->player_turn ? QUEEN_BLACK : QUEEN_WHITE) == game_state->board_pieces[x*8+y])) + { + return false; + } + else + { + loop = false; + } + } + } + /* Bishops and queens */ + for (uint8_t direction = 0u; direction < 4u; direction++) + { + int8_t up_down_step; + int8_t left_right_step; + if (direction == 0u) + { + up_down_step = 1; + left_right_step = 1; + } + else if (direction == 1u) + { + up_down_step = -1; + left_right_step = 1; + } + else if (direction == 2u) + { + up_down_step = -1; + left_right_step = -1; + } + else + { + up_down_step = 1; + left_right_step = -1; + } + bool loop = true; + int x = (int)row; + int y = (int)column; + while (loop) + { + uint8_t bish = (game_state->player_turn ? BISHOP_BLACK : BISHOP_WHITE); + uint8_t queen = (game_state->player_turn ? QUEEN_BLACK : QUEEN_WHITE); + x += left_right_step; + y += up_down_step; + if ((x < 0) || (y < 0) || (x >= 8) || (y >= 8)) + { + loop = false; + } + else if (game_state->board_pieces[x*8+y] == SQUARE_EMPTY) + { + /* do nothing */ + } + else if ((bish == game_state->board_pieces[x*8+y]) || (queen == game_state->board_pieces[x*8+y])) + { + return false; + } + else + { + loop = false; + } + } + } + /* Knights */ + for (uint8_t direction = 0u; direction < 4u; direction++) + { + int8_t up_down_step; + int8_t left_right_step; + if (direction == 0u) + { + up_down_step = 2; + left_right_step = 0; + } + else if (direction == 1u) + { + up_down_step = 0; + left_right_step = 2; + } + else if (direction == 2u) + { + up_down_step = -2; + left_right_step = 0; + } + else + { + up_down_step = 0; + left_right_step = -2; + } + + for (uint8_t i = 0u; i < 2u; i++) + { + if ((direction % 2u) == 0u) + { + left_right_step = (i == 0u) ? -1 : 1; + } + else + { + up_down_step = (i == 0u) ? -1 : 1; + } + + int8_t x = (int8_t)row + left_right_step; + int8_t y = (int8_t)column + up_down_step; + if ((x >= 0) && (y >= 0) && (x < 8) && (y < 8)) + { + if ((game_state->player_turn ? KNIGHT_BLACK : KNIGHT_WHITE) == game_state->board_pieces[x*8+y]) + { + return false; + } + } + } + } + return true; +} + + +/** + * @brief Function for "casting" a ray in any direction, vertical, horizontal, or diagonal. If the ray hits someone from the other team + * or the end of the board the array will be terminated. + * @param direction_r: Row direction + * @param direction_c: Column direction + * @param row: current row location + * @param column: current column location + * @param piece: the piece that is casting the ray. + * @retval None + */ +bool cast_a_ray(uint8_t piece, int8_t direction_r, int8_t direction_c, uint8_t column, uint8_t row, Game_State_t * game_state) +{ + bool ret_val = false; + bool loop = true; + int x = row; + int y = column; + while (loop) + { + x += direction_c; + y += direction_r; + if((x < 0) || (y < 0) || (x >= 8) || (y >= 8)) + { + loop = false; + } + else if (game_state->board_pieces[x*8+y] == SQUARE_EMPTY) + { + ret_val = Set_Light(piece, (uint8_t)x, (uint8_t)y, POTENTIAL_MOVE) || ret_val; + } + else if (opposite_teams(piece, game_state->board_pieces[x*8+y])) + { + ret_val = Set_Light(piece, (uint8_t)x, (uint8_t)y, POTENTIAL_TAKE) || ret_val; + /* once we take a piece we can no longer take anymore */ + loop = false; + } + else + { + loop = false; + } + } + return ret_val; +} + + +bool Check_If_Player_Can_Move(bool white, Game_State_t * game_state) +{ + for (uint8_t row = 0; row < 8u; row++) + { + for (uint8_t column = 0; column < 8u; column++) + { + if((white_team(game_state->board_pieces[row*8+column]) == white)) + { + // SDL_Log("move: Row:%d, Col:%d", row, column); + + if(Mark_Potential_Moves(game_state->board_pieces[row*8+column], column, row, game_state)) + { + // SDL_Log("Player Can still move: Row:%d, Col:%d", row, column); + clear_board_state(); + return true; + } + } + } + + } + clear_board_state(); + // SDL_Log("Player cant move"); + return false; +} + + +/** + * @brief Function for marking the potential moves. + * @param piece: Piece that we are marking the potential moves for. + * @param row: Current row location of the piece. + * @param column: Current column location of the piece. + * @retval None + */ +bool Mark_Potential_Moves(uint8_t piece, uint8_t column, uint8_t row, Game_State_t * game_state) +{ + bool ret_val = false; + switch (piece) + { + case PAWN_WHITE : + case PAWN_BLACK: + { + int direction = white_team(piece) ? -1 : 1; + uint8_t temp_row = (uint8_t)((int)row + direction); + if (row == (white_team(piece) ? 6u : 1u)) + { + if(game_state->board_pieces[temp_row*8u+column] == SQUARE_EMPTY) + { + ret_val = pawn_move(piece, (uint8_t)((int)row + (direction * 2)), column, game_state) || ret_val; + } + } + + ret_val = pawn_move(piece, temp_row, column, game_state) || ret_val; + if(column != 0) + { + ret_val = pawn_take(piece, temp_row, column - 1u, game_state) || ret_val; + } + if(column != 7u) + { + ret_val = pawn_take(piece, temp_row, column + 1u, game_state) || ret_val; + } + + break; + } + case ROOK_WHITE: + case ROOK_BLACK: + { + for (uint8_t direction = 0u; direction < 4u; direction++) + { + int8_t up_down_step; + int8_t left_right_step; + if(direction == 0u) + { + up_down_step = 1; + left_right_step = 0; + } + else if(direction == 1u) + { + up_down_step = 0; + left_right_step = 1; + } + else if (direction == 2u) + { + up_down_step = -1; + left_right_step = 0; + } + else + { + up_down_step = 0; + left_right_step = -1; + } + ret_val = cast_a_ray(piece, up_down_step, left_right_step, column, row, game_state) || ret_val; + } + break; + + } + case KNIGHT_WHITE: + case KNIGHT_BLACK: + { + for (uint8_t direction = 0u; direction < 4u; direction++) + { + int8_t up_down_step; + int8_t left_right_step; + if (direction == 0u) + { + up_down_step = 2; + left_right_step = 0; + } + else if (direction == 1u) + { + up_down_step = 0; + left_right_step = 2; + } + else if (direction == 2u) + { + up_down_step = -2; + left_right_step = 0; + } + else + { + up_down_step = 0; + left_right_step = -2; + } + + for (uint8_t i = 0u; i < 2u; i++) + { + if((direction % 2u) == 0u) + { + left_right_step = (i == 0u) ? -1 : 1; + } + else + { + up_down_step = (i == 0u) ? -1 : 1; + } + + int8_t x = (int8_t)row + left_right_step; + int8_t y = (int8_t)column + up_down_step; + if ((x >= 0) && (y >= 0) && (x < 8) && (y < 8)) + { + if (game_state->board_pieces[x*8+y] == SQUARE_EMPTY) + { + ret_val = Set_Light(piece, (uint8_t)x, (uint8_t)y, POTENTIAL_MOVE) || ret_val; + } + else if (opposite_teams(piece, game_state->board_pieces[x*8+y])) + { + ret_val = Set_Light(piece, (uint8_t)x, (uint8_t)y, POTENTIAL_TAKE) || ret_val; + } + } + } + } + break; + } + case BISHOP_WHITE: + case BISHOP_BLACK: + { + for (uint8_t direction = 0u; direction < 4u; direction++) + { + int8_t up_down_step; + int8_t left_right_step; + if (direction == 0u) + { + up_down_step = 1; + left_right_step = 1; + } + else if (direction == 1u) + { + up_down_step = -1; + left_right_step = 1; + } + else if (direction == 2u) + { + up_down_step = -1; + left_right_step = -1; + } + else + { + up_down_step = 1; + left_right_step = -1; + } + ret_val = cast_a_ray(piece, up_down_step, left_right_step, column, row, game_state) || ret_val; + } + break; + } + case QUEEN_WHITE: + case QUEEN_BLACK: + { + //Mark bishop moves + ret_val = Mark_Potential_Moves((piece - 2u), column, row, game_state) || ret_val; + //Mark rook moves + ret_val = Mark_Potential_Moves((piece - 6u), column, row, game_state) || ret_val; + break; + } + case KING_WHITE: + case KING_BLACK: + { + int8_t start_r = (row == 0u) ? 0 : -1; + int8_t stop_r = (row == 7u) ? 0 : 1; + int8_t start_c = (column == 0u) ? 0 : -1; + int8_t stop_c = (column == 7u) ? 0 : 1; + for (int8_t left_right = start_r; left_right <= stop_r; left_right++) + { + for (int8_t up_down = start_c; up_down <= stop_c; up_down++) + { + int8_t x = row + left_right; + int8_t y = column + up_down; + if (square_is_safe(x, y, game_state)) + { + if (game_state->board_pieces[x*8+y] == SQUARE_EMPTY) + { + game_state->board_state[x*8+y] = POTENTIAL_MOVE; + ret_val = true; + } + else if (opposite_teams(piece, game_state->board_pieces[x*8+y])) + { + game_state->board_state[x*8+y] = POTENTIAL_TAKE; + ret_val = true; + } + } + } + } + uint8_t white_black_idx = white_team(piece) ? 0u : 1u; + uint8_t kings_row = white_team(piece) ? 7u : 0u; + + //Can only castle if not currently in check + if (square_is_safe(row, column, game_state)) + { + // Queen side castle + if(game_state->castling_allowed[white_black_idx][0u] && (game_state->board_pieces[kings_row*8+1u] == SQUARE_EMPTY) + && (game_state->board_pieces[kings_row*8+2u] == SQUARE_EMPTY) && (game_state->board_pieces[kings_row*8+3u]) == SQUARE_EMPTY) + { + //First Check to see if the king will pass through check + if(square_is_safe(kings_row, 3u, game_state) && square_is_safe(kings_row, 2u, game_state)) + { + // Yay we can castle queen side! + game_state->board_state[kings_row*8+2u] = POTENTIAL_CASTLE; + ret_val = true; + } + } + + // King side castle + if (game_state->castling_allowed[white_black_idx][1u] && (game_state->board_pieces[kings_row*8+5u] == SQUARE_EMPTY) && (game_state->board_pieces[kings_row*8+6u] == SQUARE_EMPTY)) + { + //First Check to see if the king will pass through check + if(square_is_safe(kings_row, 5u, game_state) && square_is_safe(kings_row, 6u, game_state)) + { + // Yay we can castle king side! + game_state->board_state[kings_row*8+6u] = POTENTIAL_CASTLE; + ret_val = true; + } + } + } + + break; + } + + default: + break; + } + return ret_val; +} + diff --git a/src/game_logic/piece_logic/piece_logic.h b/src/game_logic/piece_logic/piece_logic.h new file mode 100644 index 0000000..24d4a04 --- /dev/null +++ b/src/game_logic/piece_logic/piece_logic.h @@ -0,0 +1,13 @@ +#ifndef PIECE_LOGIC_H +#define PIECE_LOGIC_H + +#include "game_state.h" + +bool white_team(uint8_t piece); +bool opposite_teams(uint8_t piece_one, uint8_t piece_two); +bool square_is_safe(uint8_t row, uint8_t column, Game_State_t * game_state); +bool cast_a_ray(uint8_t piece, int8_t direction_r, int8_t direction_c, uint8_t column, uint8_t row, Game_State_t * game_state); +bool Check_If_Player_Can_Move(bool white, Game_State_t * game_state); +bool Mark_Potential_Moves(uint8_t piece, uint8_t column, uint8_t row, Game_State_t * game_state); + +#endif diff --git a/src/pc_app/game.cpp b/src/pc_app/game.cpp index 5b41b8b..6578388 100644 --- a/src/pc_app/game.cpp +++ b/src/pc_app/game.cpp @@ -6,7 +6,7 @@ #include #include #include -#include "chess_board.h" +#include "game_state.h" #include "user_interface_abstraction.h" static clock_t start_time, end_time; @@ -19,7 +19,7 @@ int begin_game(SDL_Renderer *renderer, SDL_Window *win) srcR.w = 800; destR.h = 800; destR.w = 800; - chess_board_init(); + game_state_init(); ui_resize(renderer, 800, 800); ui_init(renderer); srcR.x = 0; diff --git a/src/pc_app/user_interface_abstraction.cpp b/src/pc_app/user_interface_abstraction.cpp index 000b1c9..7187b31 100644 --- a/src/pc_app/user_interface_abstraction.cpp +++ b/src/pc_app/user_interface_abstraction.cpp @@ -1,5 +1,5 @@ #include "user_interface_abstraction.h" -#include "chess_board.h" +#include "game_state.h" #include #define MARGIN 300 @@ -31,7 +31,7 @@ static uint8_t Current_Binary_Board[8] = {0}; * @param board_state board state * @param game_state games state */ -static void ui_draw_end_game(SDL_Renderer *p_renderer, uint8_t board_state[8][8], Game_State_t game_state) +static void ui_draw_end_game(SDL_Renderer *p_renderer, uint8_t * board_state, Game_State_t game_state) { SDL_SetRenderTarget(p_renderer, Board_Texture); SDL_SetRenderDrawColor(p_renderer, 0x7f, 0x7f, 0x7f, 0); @@ -69,10 +69,10 @@ static void ui_draw_end_game(SDL_Renderer *p_renderer, uint8_t board_state[8][8] Rectangle.x = starting_x; for (size_t i = 0; i < 8; i++) { - if((board_state[j][i] & 0x0Fu) != SQUARE_EMPTY) + if((board_state[j*8+i] & 0x0Fu) != SQUARE_EMPTY) { uint8_t * render_color; - if((board_state[j][i] % 2u) == 0u ) + if((board_state[j*8+i] % 2u) == 0u ) { render_color = white_color; } @@ -83,7 +83,7 @@ static void ui_draw_end_game(SDL_Renderer *p_renderer, uint8_t board_state[8][8] SDL_SetRenderDrawColor(p_renderer, render_color[0], render_color[1], render_color[2], render_color[3]); SDL_RenderFillRect(p_renderer, &Rectangle); SDL_SetRenderDrawColor(p_renderer, 0x85, 0x5E, 0x42, 0x00); - SDL_RenderCopy(p_renderer, bitmapTextures[(board_state[j][i] & 0x0Fu)], NULL, &Rectangle); + SDL_RenderCopy(p_renderer, bitmapTextures[(board_state[j*8+i] & 0x0Fu)], NULL, &Rectangle); } else if (((i % 2) + (j % 2)) == 1) { @@ -109,7 +109,7 @@ static void ui_draw_end_game(SDL_Renderer *p_renderer, uint8_t board_state[8][8] * @param *p_renderer pointer to the renderer object: * @retval None */ -static void ui_draw_board(SDL_Renderer *p_renderer, uint8_t board_lights[12][8], uint8_t board_state[12][8]) +static void ui_draw_board(SDL_Renderer *p_renderer, uint8_t * board_lights, uint8_t * board_state) { SDL_SetRenderTarget(p_renderer, Board_Texture); SDL_SetRenderDrawColor(p_renderer, 0x7f, 0x7f, 0x7f, 0); @@ -131,31 +131,31 @@ static void ui_draw_board(SDL_Renderer *p_renderer, uint8_t board_lights[12][8], Rectangle.x = starting_x; for (size_t i = 0; i < 8; i++) { - if ((board_lights[j][i] == POTENTIAL_MOVE) || (board_lights[j][i] == POTENTIAL_CASTLE)) + if ((board_lights[j*8+i] == POTENTIAL_MOVE) || (board_lights[j*8+i] == POTENTIAL_CASTLE)) { 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] == PIECE_NEEDS_TO_BE_HERE) || (board_lights[j][i] == PIECE_NEEDS_TO_BE_REMOVED)) + else if ((board_lights[j*8+i] == POTENTIAL_TAKE) || (board_lights[j*8+i] == PIECE_NEEDS_TO_BE_HERE) || (board_lights[j*8+i] == PIECE_NEEDS_TO_BE_REMOVED)) { 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] == PIECE_ORIGIN) + else if (board_lights[j*8+i] == PIECE_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) + else if (board_lights[j*8+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 (board_lights[j][i] == CONVERTING_PAWN) + else if (board_lights[j*8+i] == CONVERTING_PAWN) { SDL_SetRenderDrawColor(p_renderer, 0xFF, 0x3B, 0x7A, 0x57); SDL_RenderFillRect(p_renderer, &Rectangle); @@ -170,9 +170,9 @@ static void ui_draw_board(SDL_Renderer *p_renderer, uint8_t board_lights[12][8], /* code */ } - if((board_state[j][i] & 0x0Fu) != SQUARE_EMPTY) + if((board_state[j*8+i] & 0x0Fu) != SQUARE_EMPTY) { - SDL_RenderCopy(p_renderer, bitmapTextures[(board_state[j][i] & 0x0Fu)], NULL, &Rectangle); + SDL_RenderCopy(p_renderer, bitmapTextures[(board_state[j*8+i] & 0x0Fu)], NULL, &Rectangle); } Rectangle.x += square_size; @@ -191,13 +191,13 @@ static void ui_draw_board(SDL_Renderer *p_renderer, uint8_t board_lights[12][8], Rectangle.y = starting_y; for (size_t i = 0; i < 8; i++) { - if (board_lights[j][i] == PIECE_NEEDS_TO_BE_HERE) + if (board_lights[j*8+i] == PIECE_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) + else if (board_lights[j*8+i] == ERROR_MOVE) { SDL_SetRenderDrawColor(p_renderer, 0xFF, 0xFF, 0x00, 0x00); SDL_RenderFillRect(p_renderer, &Rectangle); @@ -210,9 +210,9 @@ static void ui_draw_board(SDL_Renderer *p_renderer, uint8_t board_lights[12][8], - if ((board_state[j][i] & 0x0Fu) != SQUARE_EMPTY) + if ((board_state[j*8+i] & 0x0Fu) != SQUARE_EMPTY) { - SDL_RenderCopy(p_renderer, bitmapTextures[(board_state[j][i] & 0x0Fu)], NULL, &Rectangle); + SDL_RenderCopy(p_renderer, bitmapTextures[(board_state[j*8+i] & 0x0Fu)], NULL, &Rectangle); } @@ -236,8 +236,8 @@ static void ui_draw_board(SDL_Renderer *p_renderer, uint8_t board_lights[12][8], void ui_redraw_board(SDL_Renderer *p_renderer) { - uint8_t board_lights[8][8]; - uint8_t board_state[8][8]; + uint8_t board_lights[8*8]; + uint8_t board_state[8*8]; Game_State_t game_state = Board_get_game_state(); Board_get_lights_and_state(board_lights, board_state); if(game_state.game_over)