diff --git a/src/chess.c b/src/chess.c index 5b58d8f..8350afc 100644 --- a/src/chess.c +++ b/src/chess.c @@ -1,4 +1,5 @@ #include +#include #include "chess.h" #include "util.h" @@ -79,33 +80,59 @@ pos_t pos_multiply_scalar(pos_t a, int b) { return (pos_t){a.x*b, a.y*b}; } +// check whether a position is out of bounds of the board +bool is_pos_oob(pos_t pos) { + return pos.x < 0 || pos.x > BOARD_HORIZONTAL || + pos.y < 0 || pos.y > BOARD_VERTICAL; +} + /** * check the wanted pos_t against all valid move options * of the current piece */ bool is_move_legal(move_t move) { + pos_t new_pos = { + move.from.x+move.to.x, + move.from.y+move.to.y + }; + + // OOB check + if (is_pos_oob(new_pos)) { + return false; + } + valid_move_t valid_moves[PIECE_COUNT] = DEFAULT_VALID_MOVES; switch (move.piece) { case NONE: - case PAWN: - case KNIGHT: - case KING: + case WPAWN: + case WKNIGHT: + case WKING: + case BPAWN: + case BKNIGHT: + case BKING: for (int i = 0; i < PIECE_COUNT; i++) { for (int j = 0; j < valid_moves[i].piece_move_count && move.piece == valid_moves[i].piece; j++ ) { - pos_t new_pos = {move.from.x+move.to.x, move.from.y+move.to.y}; - if (compare_pos(new_pos, valid_moves[i].options[j]) == true) { + if ( + compare_pos( + new_pos, + valid_moves[i].options[j] + ) + ) { return true; } } } break; - case ROOK: - case BISHOP: - case QUEEN: + case WROOK: + case WBISHOP: + case WQUEEN: + case BROOK: + case BBISHOP: + case BQUEEN: for (int i = 0; i < PIECE_COUNT; i++) { for (int j = 0; j < valid_moves[i].piece_move_count && @@ -114,15 +141,13 @@ bool is_move_legal(move_t move) { ) { /** NOTE: BOARD_HORIZONTAL assuming HORIZONTAL & VERTICAL are the same */ for (int k = 0; k < BOARD_HORIZONTAL; k++) { - pos_t new_pos = { - move.from.x+move.to.x, - move.from.y+move.to.y - }; - if (compare_pos( - new_pos, - pos_multiply_scalar(valid_moves[i].options[j], k) - ) == true) { - return true; + if ( + compare_pos( + new_pos, + pos_multiply_scalar(valid_moves[i].options[j], k) + ) + ) { + return true; } } } @@ -133,22 +158,36 @@ bool is_move_legal(move_t move) { return false; } -bool is_none(board_t board, pos_t pos) { +bool is_dest_none(board_t board, pos_t pos) { return board[pos.y][pos.x] == NONE; } + +bool is_dest_opponent(board_t board, player_e player, move_t move) { + switch (player) { + case WHITE: + return board[move.to.y][move.to.x] < 0; + break; + case BLACK: + return board[move.to.y][move.to.x] > 0; + break; + } +} + /** the move can be: * 1. legal x illegal - * 2. the 'to' pos could be occupied x non-occupied - * 2a. it could be capturable x non-capturable + * 2a. the 'to' pos could be occupied x non-occupied + * 2b. it could be capturable x non-capturable + * 2c. it could be out of bounds */ -bool validate_move(board_t board, move_t move) { - bool none = is_none(board, move.to); +bool validate_move(board_t board, player_e player, move_t move) { + bool none = is_dest_none(board, move.to); + bool dest_is_opponent = is_dest_opponent(board, player, move); bool move_legal = is_move_legal(move); - if (none == true && move_legal == true) { + if (none && move_legal) { // free ability to move return true; - } else if (none == false && move_legal == true) { + } else if (!none && dest_is_opponent && move_legal) { // capturing return true; } else { @@ -157,18 +196,76 @@ bool validate_move(board_t board, move_t move) { } } -/** TODO: capturing and promotions */ -bool perform_move(board_t *board, player_e player, move_t move) { - bool move_valid = validate_move(*board, move); - if (move_valid == true) { +/** + * checks and sorts out a capture edge case of a move + * sorts out = moves the piece into the 'captured' area and puts a NONE in its' place + * (if the 'captured' piece is not a king, because you cannot capture a king) +*/ +bool check_captures(board_t *board, player_e player, captured_t *captured, move_t move) { + if (is_dest_opponent(*board, player, move)) { + switch (player) { + case WHITE: + captured->white[captured->white_n++] = abs(*board[move.to.y][move.to.x]); + *board[move.to.y][move.to.x] = NONE; + break; + case BLACK: + captured->white[captured->white_n++] = abs(*board[move.to.y][move.to.x]); + *board[move.to.y][move.to.x] = NONE; + break; + } + + return true; + } else { + return false; + } +} + +// checks and sorts out a promotion edge case of a move +bool check_promotions(board_t *board, player_e player, move_t *move) { + pos_t new_pos = { + move->from.x+move->to.x, + move->from.y+move->to.y + }; + + // white player achieved a promotion + if (new_pos.y < 0 && player == WHITE && move->piece == WPAWN) { + *board[move->from.y][move->from.x] = WQUEEN; + move->piece = WQUEEN; + return true; + } + + // black player achieved a promotion + if (new_pos.y > BOARD_VERTICAL && player == BLACK && move->piece == BPAWN) { + *board[move->from.y][move->from.x] = BQUEEN; + move->piece = BQUEEN; + return true; + } + + return false; +} + +// checks for mates +/** FIXME: unimplemented */ +end_condition_e check_end() { + return CONTINUE; +} + +end_condition_e perform_move(board_t *board, player_e player, captured_t *captured, move_t move) { + if (validate_move(*board, player, move)) { + if ( + *board[move.to.y][move.to.x] != WKING || // accounting for both players + *board[move.to.y][move.to.x] != BKING // you cannot capture a king + ) { + check_captures(board, player, captured, move); + } + + check_promotions(board, player, &move); + *board[move.to.y][move.to.x] = move.piece; // move piece to wanted place *board[move.from.y][move.from.x] = NONE; // erase the remnants } - return move_valid; -} -end_condition_e check_edge_cases(board_t board) { - return CHECKMATE; + return check_end(); } void announce_results(end_condition_e end_condition, player_e player) { @@ -181,6 +278,7 @@ void announce_results(end_condition_e end_condition, player_e player) { case STALEMATE: printf("STALEMATE"); break; + case CONTINUE: break; // to make the linter happy } printf("\nand the player who has won is"); diff --git a/src/chess.h b/src/chess.h index a06b807..7ff1454 100644 --- a/src/chess.h +++ b/src/chess.h @@ -19,18 +19,28 @@ #define BOARD_VERTICAL 8 // type abstractions -typedef int board_t[BOARD_HORIZONTAL][BOARD_VERTICAL]; +typedef enum { WHITE, BLACK } player_e; -#define PIECE_COUNT 7 // how many unique pieces are there +typedef int board_t[BOARD_VERTICAL][BOARD_HORIZONTAL]; + +#define PIECE_COUNT 13 // how many unique pieces are there #define MAX_PIECE_MOVE_COUNT 27 // the max possible num of options where to move in one moment typedef enum { NONE, - PAWN, - ROOK, - KNIGHT, - BISHOP, - QUEEN, - KING + + WPAWN, + WROOK, + WKNIGHT, + WBISHOP, + WQUEEN, + WKING, + + BPAWN, + BROOK, + BKNIGHT, + BBISHOP, + BQUEEN, + BKING } piece_e; typedef struct { @@ -38,8 +48,6 @@ typedef struct { int y; } pos_t; -typedef enum { WHITE, BLACK } player_e; - typedef struct { // what // where from @@ -50,15 +58,17 @@ typedef struct { } move_t; // the default arrangment of pieces on the board +// negative numbers are black, positive ones white #define BOARD_DEFAULT_CONFIG \ (board_t){\ - {2,3,4,5,6,4,3,2},\ - {1,1,1,1,1,1,1,1},\ - {0,0,0,0,0,0,0,0},\ - {0,0,0,0,0,0,0,0},\ - {0,0,0,0,0,0,0,0},\ - {1,1,1,1,1,1,1,1},\ - {2,3,4,5,6,4,3,2}\ + { BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK },\ + { BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN },\ + { NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE },\ + { NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE },\ + { NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE },\ + { NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE },\ + { WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN },\ + { WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK }\ } typedef struct { @@ -87,34 +97,77 @@ typedef struct { */ #define DEFAULT_VALID_MOVES (valid_move_t[PIECE_COUNT]){\ {NONE, {}, 0},\ - {PAWN, {\ + \ + {WPAWN, {\ { 0, -1}, {-1, -1}, {+1, -1}, { 0, -2}\ }, 4},\ - {ROOK, {\ + {WROOK, {\ { 0, -1}, { 0, +1}, {-1, 0}, {+1, 0}\ }, 4},\ - {KNIGHT, {\ + {WKNIGHT, {\ {-1, -2}, {+1, -2}, {-2, -1}, {-2, +1},\ {+2, -1}, {+2, +1}, {-1, -2}, {-1, +2}\ }, 8},\ - {BISHOP, {\ + {WBISHOP, {\ {-1, -1}, {-1, +1}, {+1, -1}, {+1, +1}\ }, 4},\ - {QUEEN, {\ + {WQUEEN, {\ {0, -1}, {0, +1}, {-1, 0}, {+1, 0},\ {-1, -1}, {-1, +1}, {+1, -1}, {+1, +1}\ }, 8},\ - {KING, {\ + {WKING, {\ + { 0, -1}, { 0, +1}, {-1, 0}, {+1, 0},\ + {-1, -1}, {-1, +1}, {+1, -1}, {+1, +1}\ + }, 8},\ + \ + {BPAWN, {\ + { 0, -1}, {-1, -1}, {+1, -1}, { 0, -2}\ + }, 4},\ + {BROOK, {\ + { 0, -1}, { 0, +1}, {-1, 0}, {+1, 0}\ + }, 4},\ + {BKNIGHT, {\ + {-1, -2}, {+1, -2}, {-2, -1}, {-2, +1},\ + {+2, -1}, {+2, +1}, {-1, -2}, {-1, +2}\ + }, 8},\ + {BBISHOP, {\ + {-1, -1}, {-1, +1}, {+1, -1}, {+1, +1}\ + }, 4},\ + {BQUEEN, {\ + {0, -1}, {0, +1}, {-1, 0}, {+1, 0},\ + {-1, -1}, {-1, +1}, {+1, -1}, {+1, +1}\ + }, 8},\ + {BKING, {\ { 0, -1}, { 0, +1}, {-1, 0}, {+1, 0},\ {-1, -1}, {-1, +1}, {+1, -1}, {+1, +1}\ }, 8}\ } +/** + * states in which the game can end + * CONTINUE => the game does not end, it continues + * CHECKMATE => one side wins + * STALEMATE => it's a draw +*/ typedef enum { + CONTINUE, CHECKMATE, STALEMATE } end_condition_e; +/** + * a tracker for which pieces have been captured by who + * white contains opponent pieces WHITE have been captured by them + * black contains opponent pieces BLACK have been captured by them +*/ +#define PIECE_COUNT_CAPTURABLE_PER_PLAYER 15 // 2*8 - 1 +typedef struct { + piece_e white[PIECE_COUNT_CAPTURABLE_PER_PLAYER]; + unsigned int white_n; + piece_e black[PIECE_COUNT_CAPTURABLE_PER_PLAYER]; + unsigned int black_n; +} captured_t; + // initializes the board with the default configuration of pieces void init_board(board_t *board); @@ -126,10 +179,7 @@ void select_player(player_e *player, unsigned int round_count); bool get_move(move_t *move); // performs the move if is it possible -bool perform_move(board_t *board, player_e player, move_t move); - -// checks for captures, prommotions and mates -end_condition_e check_edge_cases(board_t board); +end_condition_e perform_move(board_t *board, player_e player, captured_t *captured, move_t move); // if there is one of the mates, end the game and show who has won void announce_results(end_condition_e end_condition, player_e player); diff --git a/src/main.c b/src/main.c index 2e5144b..135e893 100644 --- a/src/main.c +++ b/src/main.c @@ -22,6 +22,7 @@ int main() { player_e player; unsigned int round_count = 0; end_condition_e end_condition; + captured_t captured = {.white_n = 0, .black_n = 0}; bool running = true; // algo begin @@ -37,9 +38,7 @@ int main() { get_move(&move); - perform_move(&board, player, move); - - end_condition = check_edge_cases(board); + end_condition = perform_move(&board, player, &captured, move); if (end_condition == CHECKMATE || end_condition == STALEMATE) { running = false; }