296 lines
7.8 KiB
C
296 lines
7.8 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#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");
|
|
} |