1167 lines
36 KiB
C++
1167 lines
36 KiB
C++
#include "chess_board.h"
|
|
#include <SDL.h>
|
|
#include <SDL_video.h>
|
|
#include "stdio.h"
|
|
#include <stack>
|
|
#include <string.h>
|
|
#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 POTENTIAL_CASTLE 7u
|
|
|
|
#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
|
|
|
|
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 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++)
|
|
{
|
|
for (size_t j = 0; j < 8; j++)
|
|
{
|
|
if(Board_Lights[i][j] != ERROR_MOVE)
|
|
{
|
|
Board_Lights[i][j] = LIGHT_OFF;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* @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)
|
|
{
|
|
Board_Lights[row][column] = POTENTIAL_MOVE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @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)
|
|
{
|
|
Board_Lights[row][column] = POTENTIAL_TAKE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @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 :
|
|
case PAWN_BLACK:
|
|
{
|
|
int8_t direction = White_Turn ? -1 : 1;
|
|
if (column == (White_Turn ? 6u : 1u))
|
|
{
|
|
if(Board_State[column + direction][row] == SQUARE_EMPTY)
|
|
{
|
|
pawn_move(column + (direction * 2u), row);
|
|
}
|
|
}
|
|
|
|
pawn_move(column + direction, row);
|
|
|
|
if ((row >= 1u) && opposite_teams(piece, Board_State[column + direction][row - 1u]))
|
|
{
|
|
pawn_take(column + direction, row - 1u);
|
|
}
|
|
if ((row <= 6u) && opposite_teams(piece, Board_State[column + direction][row + 1u]))
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @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:
|
|
{
|
|
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:
|
|
{
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* @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)
|
|
{
|
|
case GAME_STATE_ERROR_DETECTED:
|
|
{
|
|
if (Board_Lights[j][i] == PEICE_ORIGIN)
|
|
{
|
|
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)
|
|
{
|
|
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;
|
|
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)
|
|
{
|
|
Error_Count--;
|
|
Board_Lights[j][i] = LIGHT_OFF;
|
|
if(Error_Count == 0u)
|
|
{
|
|
Game_State = Last_Game_State;
|
|
}
|
|
}
|
|
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 ((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];
|
|
Board_State[j][i] = SQUARE_EMPTY;
|
|
Game_State++;
|
|
Board_Lights[j][i] = PEICE_ORIGIN;
|
|
}
|
|
else
|
|
{
|
|
Last_Game_State = Game_State;
|
|
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)
|
|
{
|
|
Check_If_Moving_King(j, i);
|
|
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_Peice_Spots_In_Jail();
|
|
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
|
|
{
|
|
Last_Game_State = Game_State;
|
|
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)
|
|
{
|
|
Check_If_Moving_King(j, i);
|
|
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
|
|
{
|
|
Last_Game_State = Game_State;
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
}
|