diff --git a/chess_board.cpp b/chess_board.cpp index 1e00520..fe2f9ea 100644 --- a/chess_board.cpp +++ b/chess_board.cpp @@ -29,6 +29,7 @@ #define ERROR_MOVE 4u #define PEICE_ORIGIN 5u #define PEICE_NEEDS_TO_BE_HERE 6u +#define POTENTIAL_CASTLE 7u #define GAME_STATE_IDLE 0u #define GAME_STATE_P1_TURN_BEGINING 1u @@ -38,7 +39,6 @@ #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", @@ -60,16 +60,30 @@ 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 uint8_t Last_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 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}}; +/** + * @brief Function for figuring out if the current column is one of the jail columns. + * @note Columns 8-12 are in the jail + * @param j: The column under question. + * @retval True if its a jail column, else false. + */ static inline bool square_in_jail(uint8_t j) { return (j > 8u); } +/** + * @brief Function for clearing all of the lights on the board. Except for error moves. + * @retval None + */ static void clear_lights(void) { for (size_t i = 0; i < 8; i++) @@ -85,16 +99,34 @@ static void clear_lights(void) } +/** + * @brief Function for determining if the peice is on the white team or on the black team. + * @note Peices should be enumerated even if white and odd if black. + * @param piece: The peice 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 peices are on the same team or not. + * @param piece_one: Peice one ofcoarse. + * @param piece_two: Peice 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 Function for marking potential moves for pawns. + * @param row: row to move to + * @param column: column to move to + * @retval None + */ static void pawn_move(uint8_t row, uint8_t column) { if(Board_State[row][column] == SQUARE_EMPTY) @@ -103,6 +135,52 @@ static void pawn_move(uint8_t row, uint8_t column) } } +/** + * @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_one: the peice that is casting the ray. + * @retval None + */ +static void cast_a_ray(int8_t direction_r, int8_t direction_c, uint8_t row, uint8_t column, uint8_t piece_one) +{ + bool loop = true; + int8_t x = column; + int8_t y = row; + while (loop) + { + x += direction_c; + y += direction_r; + if((x < 0) || (y < 0) || (x >= 8) || (y >= 8)) + { + loop = false; + } + else if (Board_State[x][y] == SQUARE_EMPTY) + { + Board_Lights[x][y] = POTENTIAL_MOVE; + } + else if (opposite_teams(piece_one, Board_State[x][y])) + { + Board_Lights[x][y] = POTENTIAL_TAKE; + /* once we take a peice we can no longer take anymore */ + loop = false; + } + else + { + loop = false; + } + } +} + +/** + * @brief Marking a peice for taking. + * @param row: Row ofcoarse + * @param column: Column obviously + * @retval None + */ static void pawn_take(uint8_t row, uint8_t column) { if (Board_State[row][column] < SQUARE_EMPTY) @@ -111,55 +189,429 @@ static void pawn_take(uint8_t row, uint8_t column) } } -static void Mark_Potential_Moves(uint8_t piece, uint8_t column, uint8_t row) +/** + * @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 column, uint8_t row) +{ + bool ret_val = true; + int8_t direction = White_Turn ? -1 : 1; + + /* first check if pawns can take us */ + if ((row >= 1u) && ((White_Turn ? PAWN_BLACK : PAWN_WHITE) == Board_State[column + direction][row - 1u])) + { + //can be eaten by a pawn, not safe. + return false; + } + if ((row <= 6u) && ((White_Turn ? PAWN_BLACK : PAWN_WHITE) == Board_State[column + direction][row + 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 = column + left_right; + int8_t y = row + up_down; + if ((White_Turn ? KING_BLACK : KING_WHITE) == Board_State[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 = column; + int8_t y = row; + while (loop) + { + x += left_right_step; + y += up_down_step; + if ((x < 0) || (y < 0) || (x >= 8) || (y >= 8)) + { + loop = false; + } + else if (Board_State[x][y] == SQUARE_EMPTY) + { + /* Do nothing */ + } + else if (((White_Turn ? ROOK_BLACK : ROOK_WHITE) == Board_State[x][y]) || ((White_Turn ? QUEEN_BLACK : QUEEN_WHITE) == Board_State[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 = column; + int8_t y = row; + while (loop) + { + x += left_right_step; + y += up_down_step; + if ((x < 0) || (y < 0) || (x >= 8) || (y >= 8)) + { + loop = false; + } + else if (Board_State[x][y] == SQUARE_EMPTY) + { + /* do nothing */ + } + else if (((White_Turn ? BISHOP_BLACK : BISHOP_WHITE) == Board_State[x][y]) || ((White_Turn ? QUEEN_BLACK : QUEEN_WHITE) == Board_State[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)column + left_right_step; + int8_t y = (int8_t)row + up_down_step; + if ((x >= 0) && (y >= 0) && (x < 8) && (y < 8)) + { + if ((White_Turn ? KNIGHT_BLACK : KNIGHT_WHITE) == Board_State[x][y]) + { + return false; + } + } + } + } + return true; +} + +/** + * @brief Function for marking the potential moves. + * @param piece: Peice that we are marking the potential moves for. + * @param row: Current row location of the peice. + * @param column: Current column location of the peice. + * @retval None + */ +static void Mark_Potential_Moves(uint8_t piece, uint8_t row, uint8_t column) { 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) + { + int8_t direction = White_Turn ? -1 : 1; + if (column == (White_Turn ? 6u : 1u)) { - pawn_move(row + 2, column); + if(Board_State[column + direction][row] == SQUARE_EMPTY) + { + pawn_move(column + (direction * 2u), row); + } } - pawn_move(row + 1, column); - if ((column >= 1) && opposite_teams(Board_State[row][column], Board_State[row + 1][column - 1])) + + pawn_move(column + direction, row); + + if ((row >= 1u) && opposite_teams(piece, Board_State[column + direction][row - 1u])) { - pawn_take(row + 1, column - 1); + pawn_take(column + direction, row - 1u); } - if ((column <= 6) && opposite_teams(Board_State[row][column], Board_State[row + 1][column + 1])) + if ((row <= 6u) && opposite_teams(piece, Board_State[column + direction][row + 1u])) { - pawn_take(row + 1, column + 1); + pawn_take(column + direction, row + 1u); } + 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; + } + cast_a_ray(up_down_step, left_right_step, row, column, piece); + } 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)column + left_right_step; + int8_t y = (int8_t)row + up_down_step; + if ((x >= 0) && (y >= 0) && (x < 8) && (y < 8)) + { + if (Board_State[x][y] == SQUARE_EMPTY) + { + Board_Lights[x][y] = POTENTIAL_MOVE; + } + else if (opposite_teams(piece, Board_State[x][y])) + { + Board_Lights[x][y] = POTENTIAL_TAKE; + } + } + } + } + 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; + } + cast_a_ray(up_down_step, left_right_step, row, column, piece); + } + break; + } + case QUEEN_WHITE: + case QUEEN_BLACK: + { + for (int8_t up_down = -1; up_down < 2; up_down++) + { + for (int8_t left_rigth = -1; left_rigth < 2; left_rigth++) + { + cast_a_ray(up_down, left_rigth, row, column, piece); + } + } + 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 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 = column + left_right; + int8_t y = row + up_down; + if (square_is_safe(x, y)) + { + if (Board_State[x][y] == SQUARE_EMPTY) + { + Board_Lights[x][y] = POTENTIAL_MOVE; + } + else if (opposite_teams(piece, Board_State[x][y])) + { + Board_Lights[x][y] = POTENTIAL_TAKE; + } + } + } + } + uint8_t white_black_idx = White_Turn ? 0u : 1u; + uint8_t kings_row = White_Turn ? 7u : 0u; + + //Can only castle if not currently in check + if(square_is_safe(column, row)) + { + // Queen side castle + if(Castling_Allowed[white_black_idx][0u] && (Board_State[kings_row][1u] == SQUARE_EMPTY) + && (Board_State[kings_row][2u] == SQUARE_EMPTY) && (Board_State[kings_row][3u]) == SQUARE_EMPTY) + { + Board_Lights[kings_row][2u] = POTENTIAL_MOVE; + } + + // King side castle + if (Castling_Allowed[white_black_idx][1u] && (Board_State[kings_row][5u] == SQUARE_EMPTY) && (Board_State[kings_row][6u] == SQUARE_EMPTY)) + { + Board_Lights[kings_row][6u] = POTENTIAL_MOVE; + } + } + + Board_Lights[column][row] = PEICE_ORIGIN; + break; + } default: break; } } -void Mark_Taken_Peices_Destination(void) +/** + * @brief Function for marking the taken peices potention moves to jail. + */ +void Mark_Taken_Peice_Spots_In_Jail(void) { uint8_t add = (white_team(Taken_Peice) ? 8u : 10u); switch (Taken_Peice) { + /* Pawns just send them to jail, we dont care where */ case PAWN_WHITE: case PAWN_BLACK: { @@ -186,24 +638,72 @@ void Mark_Taken_Peices_Destination(void) 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; + uint8_t jail_row = (Taken_Peice / 2u) + 2u; + if (Board_State[add][jail_row] != Taken_Peice) + { + Board_Lights[add][jail_row] = PEICE_NEEDS_TO_BE_HERE; + } + if (Board_State[add + 1u][jail_row] != Taken_Peice) + { + Board_Lights[add + 1u][jail_row] = PEICE_NEEDS_TO_BE_HERE; + } break; } default: { break; } - } + } } +/** + * @brief Function for switching the players turn. Incharge of handling the state machine reset. + */ 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) +/** + * @brief Function for checking the selected peice 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 peice. + * @param column: Current column location of the peice. + * @retval None + */ +void Check_If_Moving_King(uint8_t row, uint8_t column) +{ + uint8_t white_black_idx = White_Turn ? 0u : 1u; + if((Selected_Peice == KING_WHITE) || (Selected_Peice == 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_Peice == ROOK_WHITE) && (row == 7u)) + || ((Selected_Peice == 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; + } + } +} + +/** + * @brief Function for toggeling a square's state. + * @param j: row location that was toggled. + * @param i: column location that was toggled. + * @retval None + */ +void Board_Square_Was_Toggled(uint8_t j, uint8_t i) { switch (Game_State) { @@ -214,6 +714,7 @@ void Board_Square_Was_Toggled(uint8_t j, uint8_t i, bool square_active) Board_State[j][i] = Selected_Peice; Selected_Peice = SQUARE_EMPTY; clear_lights(); + Last_Game_State--; } else if (Board_Lights[j][i] == PEICE_NEEDS_TO_BE_HERE) { @@ -226,10 +727,23 @@ void Board_Square_Was_Toggled(uint8_t j, uint8_t i, bool square_active) else { Board_State[j][i] = Taken_Peice; - Board_Lights[(j / 2u) * 2u][i] = LIGHT_OFF; - Board_Lights[(j / 2u) * 2u + 1u][i] = LIGHT_OFF; + uint8_t board_column = (j / 2u) * 2u; + if(Board_Lights[board_column][i] == PEICE_NEEDS_TO_BE_HERE) + { + Board_Lights[board_column][i] = LIGHT_OFF; + } + if(Board_Lights[board_column + 1u][i] == PEICE_NEEDS_TO_BE_HERE) + { + Board_Lights[board_column + 1u][i] = LIGHT_OFF; + } Taken_Peice = SQUARE_EMPTY; } + + if ((Selected_Peice == SQUARE_EMPTY) && (Taken_Peice == SQUARE_EMPTY)) + { + Last_Game_State = (White_Turn ? GAME_STATE_P2_TURN_BEGINING : GAME_STATE_P1_TURN_BEGINING); + White_Turn = !White_Turn; + } } else if (Board_Lights[j][i] == ERROR_MOVE) { @@ -237,18 +751,7 @@ void Board_Square_Was_Toggled(uint8_t j, uint8_t i, bool square_active) 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; - } + Game_State = Last_Game_State; } } else @@ -266,7 +769,7 @@ void Board_Square_Was_Toggled(uint8_t j, uint8_t i, bool square_active) 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)))) + if ((j < 8u) && (Board_State[j][i] != SQUARE_EMPTY) && (white_team(Board_State[j][i]) == White_Turn)) { Mark_Potential_Moves(Board_State[j][i], i, j); Selected_Peice = Board_State[j][i]; @@ -276,6 +779,7 @@ void Board_Square_Was_Toggled(uint8_t j, uint8_t i, bool square_active) } else { + Last_Game_State = Game_State; Game_State = GAME_STATE_ERROR_DETECTED; Board_Lights[j][i] = ERROR_MOVE; Error_Count++; @@ -289,6 +793,7 @@ void Board_Square_Was_Toggled(uint8_t j, uint8_t i, bool square_active) /* 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) { + Check_If_Moving_King(j, i); Board_State[j][i] = Selected_Peice; Selected_Peice = SQUARE_EMPTY; clear_lights(); @@ -299,7 +804,7 @@ void Board_Square_Was_Toggled(uint8_t j, uint8_t i, bool square_active) 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(); + Mark_Taken_Peice_Spots_In_Jail(); clear_lights(); Board_Lights[j][i] = PEICE_NEEDS_TO_BE_HERE; } @@ -312,6 +817,7 @@ void Board_Square_Was_Toggled(uint8_t j, uint8_t i, bool square_active) } else { + Last_Game_State = Game_State; Game_State = GAME_STATE_ERROR_DETECTED; Board_Lights[j][i] = ERROR_MOVE; Error_Count++; @@ -325,6 +831,7 @@ void Board_Square_Was_Toggled(uint8_t j, uint8_t i, bool square_active) { if(j < 8u) { + Check_If_Moving_King(j, i); Board_State[j][i] = Selected_Peice; Selected_Peice = SQUARE_EMPTY; Board_Lights[j][i] = LIGHT_OFF; @@ -340,6 +847,7 @@ void Board_Square_Was_Toggled(uint8_t j, uint8_t i, bool square_active) } else { + Last_Game_State = Game_State; Game_State = GAME_STATE_ERROR_DETECTED; Board_Lights[j][i] = ERROR_MOVE; Error_Count++; @@ -376,7 +884,7 @@ void Board_Changed(void) { if((difference & (1u << i)) != 0u) { - Board_Square_Was_Toggled(j, i, ((Current_Binary_Board[j] & (1u << i)) != 0u)); + Board_Square_Was_Toggled(j, i); } } }