diff --git a/assignment1-18/chess b/assignment1-18/chess new file mode 100755 index 0000000..042241c Binary files /dev/null and b/assignment1-18/chess differ diff --git a/assignment1-18/chessboard.c b/assignment1-18/chessboard.c index 661ebc8..2d348e8 100644 --- a/assignment1-18/chessboard.c +++ b/assignment1-18/chessboard.c @@ -1,10 +1,38 @@ // vim: set ts=4 sw=4 et tw=80: #include -#include "./chessboard.h" #include #include #include +#include "./chessboard.h" + +#define DEBUG_BUILD +#ifdef DEBUG_BUILD +#define DEBUG(...) fprintf(stderr, __VA_ARGS__); +#else +#define DEBUG(...) (void) 0; +#endif + +static inline void cb_move(struct chessboard* cb, + int from_row, int from_col, int to_row, int to_col); +static inline int sign(int n); +static inline enum player player(struct chessboard* cb, int row, int col); +static inline bool is_empty(struct chessboard* cb, int row, int col); +static bool check_empty_paths(struct chessboard* cb, + int from_row, int from_col, int to_row, int to_col); + +static bool can_move_pawn(struct chessboard* cb, enum player p, + int from_row, int from_col, int to_row, int to_col); +static bool can_move_rook(struct chessboard* cb, + int from_row, int from_col, int to_row, int to_col); +static bool can_move_knight(struct chessboard* cb, + int from_row, int from_col, int to_row, int to_col); +static bool can_move_bishop(struct chessboard* cb, + int from_row, int from_col, int to_row, int to_col); +static bool can_move_queen(struct chessboard* cb, + int from_row, int from_col, int to_row, int to_col); +static bool can_move_king(struct chessboard* cb, + int from_row, int from_col, int to_row, int to_col); void init_chessboard(struct chessboard * cb) { int i; @@ -65,8 +93,8 @@ void print_chessboard(struct chessboard * cb) { } -inline void cb_move(struct chessboard* cb, int from_row, int from_col, int to_row, - int to_col) { +inline void cb_move(struct chessboard* cb, + int from_row, int from_col, int to_row, int to_col) { cb->position[to_row][to_col] = cb->position[from_row][from_col]; cb->position[from_row][from_col] = EMPTY; } @@ -76,125 +104,215 @@ inline bool is_empty(struct chessboard* cb, int row, int col) { } inline enum player player(struct chessboard* cb, int row, int col) { - return cb->position[row][col] < 7 ? WHITE : BLACK; + return cb->position[row][col] < 7 ? WHITE : BLACK; } - inline int sign(int n) { return n == 0 ? 0 : (n > 0 ? 1 : -1); } -enum mstatus move_pawn(struct chessboard* cb, enum player p, int from_row, - int from_col, int to_row, int to_col) { +bool can_move_pawn(struct chessboard* cb, enum player p, + int from_row, int from_col, int to_row, int to_col) { + DEBUG("can_move_pawn: from: row %d col %d to: row %d col %d\n", + from_row, from_col, to_row, to_col); + // two forward if in the starting position, no capture is valid if (((p == WHITE && from_row == 1 && to_row == 3 && - is_empty(cb, 2, from_col)) || - (p == BLACK && from_row == 7 && to_row == 5 && - is_empty(cb, 6, from_col))) && - from_col == to_col && - is_empty(cb, to_row, to_col)) { - cb_move(cb, from_row, from_col, to_row, to_col); - return VALID; - // one forward, no capture is valid + is_empty(cb, 2, from_col)) || + (p == BLACK && from_row == 6 && to_row == 4 && + is_empty(cb, 5, from_col))) && + from_col == to_col && + is_empty(cb, to_row, to_col)) { + DEBUG("can_move_pawn: two forward, valid\n"); + return true; + // one forward, no capture is valid } else if (((p == WHITE && to_row - from_row == 1) || (p == BLACK && from_row - to_row == 1)) && - from_col == to_col && - is_empty(cb, to_row, to_col)) { - cb_move(cb, from_row, from_col, to_row, to_col); - return VALID; - // one forward, one left or right, with capture, is valid - } else if !(to_row = from_row + (p == WHITE ? 1 : -1) && - abs(from_col - to_col) == 1 && - !is_empty(cb, to_row, to_col) && - player(cb, to_row, to_col) != p) { - cb_move(cb, from_row, from_col, to_row, to_col); - return VALID; + from_col == to_col && + is_empty(cb, to_row, to_col)) { + DEBUG("can_move_pawn: one forward no capture, valid\n"); + return true; + // one forward, one left or right, with capture, is valid + } else if (to_row == from_row + (p == WHITE ? 1 : -1) && + abs(from_col - to_col) == 1 && + !is_empty(cb, to_row, to_col)) { + DEBUG("can_move_pawn: one forward diagonal capture, valid\n"); + return true; } else { - return INVALID; + DEBUG("can_move_pawn: move pawn not valid\n"); + return false; } } -enum mstatus check_empty_paths(struct chessboard* cb, int from_row, - int from_col, int to_row, int to_col) { - int ii = sign!(to_row - from_row), jj = sign(to_col - from_col); - int i = from_row + ii, j = from_col + jj; - for(; i != to_col || j != to_row; i += ii; j += jj) { - if (!is_empty(cb, i, j)) { - return INVALID; - } - } +bool check_empty_paths(struct chessboard* cb, + int from_row, int from_col, int to_row, int to_col) { + int ii = sign(to_row - from_row), jj = sign(to_col - from_col); + int i = from_row + ii, j = from_col + jj; - cb_move(cb, from_row, from_col, to_row, to_col); - return VALID; + DEBUG("check_empty_paths: from: row %d col %d, to: row %d col %d, ii: %d," + " jj: %d\n", from_row, from_col, to_row, to_col, ii, jj); + + for (; !(i == to_row && j == to_col); i += ii, j += jj) { + if (!is_empty(cb, i, j)) { + DEBUG("check_empty_paths: position row %d col %d" + " is occupied\n", i, j); + return false; + } + } + + return true; } -enum mstatus move_rook(struct chessboard* cb, enum player p, int from_row, - int from_col, int to_row, int to_col) { - if !(to_row != from_row && to_col != from_col) { - return INVALID; +bool can_move_rook(struct chessboard* cb, + int from_row, int from_col, int to_row, int to_col) { + if (to_row != from_row && to_col != from_col) { + return false; } return check_empty_paths(cb, from_row, from_col, to_row, to_col); } -enum mstatus move_bishop(struct chessboard* cb, enum player p, int from_row, - int from_col, int to_row, int to_col) { - if (from_row - to_row != from_col - to_col || from_row == to_row) { - return INVALID; +bool can_move_bishop(struct chessboard* cb, + int from_row, int from_col, int to_row, int to_col) { + if (abs(from_row - to_row) != abs(from_col - to_col)) { + return false; } return check_empty_paths(cb, from_row, from_col, to_row, to_col); } -enum mstatus move_queen(struct chessboard* cb, enum player p, int from_row, - int from_col, int to_row, int to_col) { - if (!(to_row != from_row && to_col != from_col) && - (from_row - to_row != from_col - to_col)) { - return INVALID; - } - - return check_empty_paths(cb, from_row, from_col, to_row, to_col); +bool can_move_queen(struct chessboard* cb, + int from_row, int from_col, int to_row, int to_col) { + return can_move_bishop(cb, from_row, from_col, to_row, to_col) || + can_move_rook(cb, from_row, from_col, to_row, to_col); } -enum mstatus move_knight(struct chessboard* cb, enum player p, int from_row, - int from_col, int to_row, int to_col) { - if (!(to_row == from_row + 2 && to_col == from_col + 1) && - !(to_row == from_row + 2 && to_col == from_col - 1) && - !(to_row == from_row - 2 && to_col == from_col + 1) && - !(to_row == from_row - 2 && to_col == from_col - 1) && - !(to_row == from_row + 1 && to_col == from_col + 2) && - !(to_row == from_row - 1 && to_col == from_col + 2) && - !(to_row == from_row + 1 && to_col == from_col - 2) && - !(to_row == from_row - 1 && to_col == from_col + 2)) { - return INVALID; - } +bool can_move_knight(struct chessboard* cb, + int from_row, int from_col, int to_row, int to_col) { + return (to_row == from_row + 2 && to_col == from_col + 1) || + (to_row == from_row + 2 && to_col == from_col - 1) || + (to_row == from_row - 2 && to_col == from_col + 1) || + (to_row == from_row - 2 && to_col == from_col - 1) || + (to_row == from_row + 1 && to_col == from_col + 2) || + (to_row == from_row - 1 && to_col == from_col + 2) || + (to_row == from_row + 1 && to_col == from_col - 2) || + (to_row == from_row - 1 && to_col == from_col + 2); +} - cb_move(cb, from_row, from_col, to_row, to_col); - return VALID; - ) +bool can_move_king(struct chessboard* cb, + int from_row, int from_col, int to_row, int to_col) { + if (abs(to_row - from_row) > 1) { + DEBUG("can_move_king: row movement too big (from %d to %d)\n", from_row, + to_row); + return false; + } + if (abs(to_col - from_col) > 1) { + DEBUG("can_move_king: column movement too big (from %d to %d)\n", + from_col, to_col); + return false; + } + return true; +} + +bool can_move(struct chessboard* cb, enum player p, + int from_row, int from_col, int to_row, int to_col) { + + // try to understand which piece we are moving + switch (cb->position[from_row][from_col]) { + case WHITE_PAWN: + case BLACK_PAWN: + DEBUG("can_move: moving a pawn\n"); + return can_move_pawn(cb, p, from_row, from_col, to_row, to_col); + case WHITE_ROOK: + case BLACK_ROOK: + DEBUG("can_move: moving a rook\n"); + return can_move_rook(cb, from_row, from_col, to_row, to_col); + case WHITE_BISHOP: + case BLACK_BISHOP: + DEBUG("can_move: moving a bishop\n"); + return can_move_bishop(cb, from_row, from_col, to_row, to_col); + case WHITE_QUEEN: + case BLACK_QUEEN: + DEBUG("can_move: moving a queen\n"); + return can_move_queen(cb, from_row, from_col, to_row, to_col); + case WHITE_KNIGHT: + case BLACK_KNIGHT: + DEBUG("can_move: moving a knight\n"); + return can_move_knight(cb, from_row, from_col, to_row, to_col); + case WHITE_KING: + case BLACK_KING: + DEBUG("can_move: moving a king\n"); + return can_move_king(cb, from_row, from_col, to_row, to_col); + default: + DEBUG("can_move: piece not implemented\n"); + return false; + } +} + +void find_other_king(struct chessboard *cb, enum player p, + int* k_row, int* k_col) { + for (*k_row = 0; *k_row < 8; (*k_row)++) { + for (*k_col = 0; *k_col < 8; (*k_col)++) { + if ((p == BLACK && cb->position[*k_row][*k_col] == WHITE_KING) || + (p == WHITE && cb->position[*k_row][*k_col] == BLACK_KING)) + { + DEBUG("find_other_king: %s king in position row %d col %d\n", + p == WHITE ? "black" : "white", *k_row, *k_col); + return; + } + } + } + DEBUG("find_king: king not found"); +} + +bool player_checks(struct chessboard *cb, enum player p) { + int k_row, k_col; + find_other_king(cb, p, &k_row, &k_col); + int i, j; + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + if (!is_empty(cb, i, j) && player(cb, i, j) == p) { + DEBUG("player_checks: checking row %d col %d to attack king\n", + i, j); + if (can_move(cb, p, i, j, k_row, k_col)) { + DEBUG("player_checks: row %d col %d can attack %s king\n", + i, j, p == WHITE ? "black" : "white"); + return true; + } + } + } + } + return false; } enum mstatus move( struct chessboard * cb, enum player p, - const char * from, const char * to) -{ + const char * from, const char * to) { + // if you are giving us garbage, we behave like Rome's municipal services - if (strlen(from) != 2 || strlen(to) != 2 || from[0] < 'a' || from[0] > 'h' - || to[0] < 'a' || to[0] > 'h' || from[1] < '1' || from[1] > '8' - || to[1] < '1' || to[1] > '8') { + if (strlen(from) != 2 || from[0] < 'a' || from[0] > 'h' || from[1] < '1' + || from[1] > '8') { + DEBUG("move: 'from' input '%s' is invalid\n", from); + return INVALID; + } + if (to[0] < 'a' || to[0] > 'h' || to[1] < '1' || to[1] > '8' + || strlen(to) != 2) { + DEBUG("move: 'to' input '%s' is invalid\n", to); return INVALID; } - int from_row = from[0] - 'a', to_row = to[0] - 'a'; - int from_col = from[1] - '1', to_col = to[0] - '1'; + int from_col = from[0] - 'a', to_col = to[0] - 'a'; + int from_row = from[1] - '1', to_row = to[1] - '1'; // if you are moving thin air, EH VOLEVIH if (is_empty(cb, from_row, from_col)) { + DEBUG("move: from is empty\n"); return INVALID; } // if you are moving someone else's pieces, "thou shall not steal" if (player(cb, from_row, from_col) != p) { + DEBUG("move: moving piece of opponent\n"); return INVALID; } @@ -203,32 +321,67 @@ enum mstatus move( if (cb->position[to_row][to_col] != EMPTY && cb->position[from_row][from_col] / 7 == cb->position[to_row][to_col] / 7) { + DEBUG("move: capturing piece from moving player\n"); return INVALID; } // if you try to move on the same spot, don't do it you buffoon if (from_row == to_row && from_col == to_col) { + DEBUG("move: moving to same position\n"); return INVALID; } - // try to understand which piece we are moving - switch (cb->position[from_row][from_col]) { - case WHITE_PAWN: - case BLACK_PAWN: - return move_pawn(cb, p, from_row, from_col, to_row, to_col); - case WHITE_ROOK: - case BLACK_ROOK: - return move_rook(cb, p, from_row, from_col, to_row, to_col); - case WHITE_BISHOP: - case BLACK_BISHOP: - return move_bishop(cb, p, from_row, from_col, to_row, to_col); - case WHITE_QUEEN: - case BLACK_QUEEN: - return move_queen(cb, p, from_row, from_col, to_row, to_col); - case WHITE_KNIGHT: - case BLACK_KNIGHT: - return move_knight(cb, p, from_row, from_col, to_row, to_col); - default: + bool can = can_move(cb, p, from_row, from_col, to_row, to_col); + + if (!can) { return INVALID; + } else { + cb_move(cb, from_row, from_col, to_row, to_col); + if (player_checks(cb, p)) { + // TODO: check checkmate + return CHECK; + } else { + return VALID; + } + } +} + +int main() { + struct chessboard cb; + init_chessboard(&cb); + enum player p = WHITE; + char from[5], to[5]; + from[0] = '\0'; + while(1) { + print_chessboard(&cb); + printf("%s moves.\n", p == WHITE ? "White" : "Black"); + printf("From: "); + fgets(from, 5, stdin); + // remove trailing \n: https://stackoverflow.com/q/2693776 + strtok(from, "\n"); + if (strcmp(from, "exit") == 0) { + printf("Exiting...\n"); + return 0; + } + printf("To: "); + fgets(to, 5, stdin); + strtok(to, "\n"); + enum mstatus s = move(&cb, p, from, to); + switch (s) { + case INVALID: + printf("Invalid move.\n"); + break; + case VALID: + printf("No check/checkmate.\n"); + break; + case CHECK: + printf("Check.\n"); + break; + case CHECK_MATE: + printf("Checkmate, %s wins.\n", p == WHITE ? "white" : "black"); + } + if (s != INVALID) { + p = !p; // switch player: this works since WHITE = 0 and BLACK = 1 + } } }