#include #include #include "chess.h" #include "util.h" void init_board(board_t *board) { for (int i = 0; i < BOARD_VERTICAL; i++) { for (int j = 0; j < BOARD_HORIZONTAL; j++) { *board[i][j] = BOARD_DEFAULT_CONFIG[i][j]; } } } void select_player(player_e *player, unsigned int round_count) { if (round_count == 1) { *player = WHITE; return; } if (*player == WHITE) { *player = BLACK; return; } if (*player == BLACK) { *player = WHITE; return; } } void show_help() { puts( "movement help:\n\ there is a prompt asking for where to move a piece every turn\n\ format: e.g. you want to move a knight from b1 to a3:\n\ you'd type -> b1 a3 <- (without the arrows)\n" ); } // returns false if the numbers of letters are out of bounds bool is_move_in_bounds(move_t move) { if ( is_int_in_range(move.from.x, 0, 7) && is_int_in_range(move.from.y, 0, 7) && is_int_in_range(move.to.x, 0, 7) && is_int_in_range(move.to.y, 0, 7) ) { return true; } else { return false; } } bool get_move(move_t *move) { clear_stdin(); char _x_from, _x_to; int _y_from, _y_to; scanf("%c%d %c%d", &_x_from, &_y_from, &_x_to, &_y_to ); move->from.x = _x_from-96-1; // char to int conversion move->from.y = _y_from-1; move->to.x = _x_to-96-1; move->to.y = _y_to-1; return is_move_in_bounds(*move); } bool compare_pos(pos_t a, pos_t b) { return a.x == b.x && a.y == b.y; } 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 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++ ) { if ( compare_pos( new_pos, valid_moves[i].options[j] ) ) { return true; } } } break; 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 && move.piece == valid_moves[i].piece; j++ ) { /** NOTE: BOARD_HORIZONTAL assuming HORIZONTAL & VERTICAL are the same */ for (int k = 0; k < BOARD_HORIZONTAL; k++) { if ( compare_pos( new_pos, pos_multiply_scalar(valid_moves[i].options[j], k) ) ) { return true; } } } } break; } return false; } 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 * 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, 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 && move_legal) { // free ability to move return true; } else if (!none && dest_is_opponent && move_legal) { // capturing return true; } else { // otherwise, invalid move return false; } } /** * 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 check_end(); } void announce_results(end_condition_e end_condition, player_e player) { printf("The game has ended because of a "); switch (end_condition) { case CHECKMATE: printf("CHECKMATE"); break; case STALEMATE: printf("STALEMATE"); break; case CONTINUE: break; // to make the linter happy } printf("\nand the player who has won is"); switch (player) { case WHITE: printf(" WHITE"); break; case BLACK: printf(" BLACK"); break; } printf("! Congratulations.\n"); }