assigment1-18: done up to check (no checkmate)
This commit is contained in:
parent
a022bf6d0a
commit
635486c9b5
2 changed files with 247 additions and 94 deletions
BIN
assignment1-18/chess
Executable file
BIN
assignment1-18/chess
Executable file
Binary file not shown.
|
@ -1,10 +1,38 @@
|
||||||
// vim: set ts=4 sw=4 et tw=80:
|
// vim: set ts=4 sw=4 et tw=80:
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "./chessboard.h"
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#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) {
|
void init_chessboard(struct chessboard * cb) {
|
||||||
int i;
|
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,
|
inline void cb_move(struct chessboard* cb,
|
||||||
int to_col) {
|
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[to_row][to_col] = cb->position[from_row][from_col];
|
||||||
cb->position[from_row][from_col] = EMPTY;
|
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) {
|
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) {
|
inline int sign(int n) {
|
||||||
return n == 0 ? 0 : (n > 0 ? 1 : -1);
|
return n == 0 ? 0 : (n > 0 ? 1 : -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum mstatus move_pawn(struct chessboard* cb, enum player p, int from_row,
|
bool can_move_pawn(struct chessboard* cb, enum player p,
|
||||||
int from_col, int to_row, int to_col) {
|
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
|
// two forward if in the starting position, no capture is valid
|
||||||
if (((p == WHITE && from_row == 1 && to_row == 3 &&
|
if (((p == WHITE && from_row == 1 && to_row == 3 &&
|
||||||
is_empty(cb, 2, from_col)) ||
|
is_empty(cb, 2, from_col)) ||
|
||||||
(p == BLACK && from_row == 7 && to_row == 5 &&
|
(p == BLACK && from_row == 6 && to_row == 4 &&
|
||||||
is_empty(cb, 6, from_col))) &&
|
is_empty(cb, 5, from_col))) &&
|
||||||
from_col == to_col &&
|
from_col == to_col &&
|
||||||
is_empty(cb, to_row, to_col)) {
|
is_empty(cb, to_row, to_col)) {
|
||||||
cb_move(cb, from_row, from_col, to_row, to_col);
|
DEBUG("can_move_pawn: two forward, valid\n");
|
||||||
return VALID;
|
return true;
|
||||||
// one forward, no capture is valid
|
// one forward, no capture is valid
|
||||||
} else if (((p == WHITE && to_row - from_row == 1) ||
|
} else if (((p == WHITE && to_row - from_row == 1) ||
|
||||||
(p == BLACK && from_row - to_row == 1)) &&
|
(p == BLACK && from_row - to_row == 1)) &&
|
||||||
from_col == to_col &&
|
from_col == to_col &&
|
||||||
is_empty(cb, to_row, to_col)) {
|
is_empty(cb, to_row, to_col)) {
|
||||||
cb_move(cb, from_row, from_col, to_row, to_col);
|
DEBUG("can_move_pawn: one forward no capture, valid\n");
|
||||||
return VALID;
|
return true;
|
||||||
// one forward, one left or right, with capture, is valid
|
// one forward, one left or right, with capture, is valid
|
||||||
} else if !(to_row = from_row + (p == WHITE ? 1 : -1) &&
|
} else if (to_row == from_row + (p == WHITE ? 1 : -1) &&
|
||||||
abs(from_col - to_col) == 1 &&
|
abs(from_col - to_col) == 1 &&
|
||||||
!is_empty(cb, to_row, to_col) &&
|
!is_empty(cb, to_row, to_col)) {
|
||||||
player(cb, to_row, to_col) != p) {
|
DEBUG("can_move_pawn: one forward diagonal capture, valid\n");
|
||||||
cb_move(cb, from_row, from_col, to_row, to_col);
|
return true;
|
||||||
return VALID;
|
|
||||||
} else {
|
} 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,
|
bool check_empty_paths(struct chessboard* cb,
|
||||||
int from_col, int to_row, int to_col) {
|
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 ii = sign(to_row - from_row), jj = sign(to_col - from_col);
|
||||||
int i = from_row + ii, j = from_col + jj;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cb_move(cb, from_row, from_col, to_row, to_col);
|
DEBUG("check_empty_paths: from: row %d col %d, to: row %d col %d, ii: %d,"
|
||||||
return VALID;
|
" 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,
|
bool can_move_rook(struct chessboard* cb,
|
||||||
int from_col, int to_row, int to_col) {
|
int from_row, int from_col, int to_row, int to_col) {
|
||||||
if !(to_row != from_row && to_col != from_col) {
|
if (to_row != from_row && to_col != from_col) {
|
||||||
return INVALID;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return check_empty_paths(cb, from_row, from_col, to_row, to_col);
|
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,
|
bool can_move_bishop(struct chessboard* cb,
|
||||||
int from_col, int to_row, int to_col) {
|
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) {
|
if (abs(from_row - to_row) != abs(from_col - to_col)) {
|
||||||
return INVALID;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return check_empty_paths(cb, from_row, from_col, to_row, to_col);
|
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,
|
bool can_move_queen(struct chessboard* cb,
|
||||||
int from_col, int to_row, int to_col) {
|
int from_row, int from_col, int to_row, int to_col) {
|
||||||
if (!(to_row != from_row && to_col != from_col) &&
|
return can_move_bishop(cb, from_row, from_col, to_row, to_col) ||
|
||||||
(from_row - to_row != from_col - to_col)) {
|
can_move_rook(cb, from_row, from_col, to_row, to_col);
|
||||||
return INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
return check_empty_paths(cb, from_row, from_col, to_row, to_col);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum mstatus move_knight(struct chessboard* cb, enum player p, int from_row,
|
bool can_move_knight(struct chessboard* cb,
|
||||||
int from_col, int to_row, int to_col) {
|
int from_row, int from_col, int to_row, int to_col) {
|
||||||
if (!(to_row == from_row + 2 && to_col == from_col + 1) &&
|
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 - 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) ||
|
||||||
!(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;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
cb_move(cb, from_row, from_col, to_row, to_col);
|
bool can_move_king(struct chessboard* cb,
|
||||||
return VALID;
|
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(
|
enum mstatus move(
|
||||||
struct chessboard * cb, enum player p,
|
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 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'
|
if (strlen(from) != 2 || from[0] < 'a' || from[0] > 'h' || from[1] < '1'
|
||||||
|| to[0] < 'a' || to[0] > 'h' || from[1] < '1' || from[1] > '8'
|
|| from[1] > '8') {
|
||||||
|| to[1] < '1' || to[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;
|
return INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
int from_row = from[0] - 'a', to_row = to[0] - 'a';
|
int from_col = from[0] - 'a', to_col = to[0] - 'a';
|
||||||
int from_col = from[1] - '1', to_col = to[0] - '1';
|
int from_row = from[1] - '1', to_row = to[1] - '1';
|
||||||
|
|
||||||
// if you are moving thin air, EH VOLEVIH
|
// if you are moving thin air, EH VOLEVIH
|
||||||
if (is_empty(cb, from_row, from_col)) {
|
if (is_empty(cb, from_row, from_col)) {
|
||||||
|
DEBUG("move: from is empty\n");
|
||||||
return INVALID;
|
return INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if you are moving someone else's pieces, "thou shall not steal"
|
// if you are moving someone else's pieces, "thou shall not steal"
|
||||||
if (player(cb, from_row, from_col) != p) {
|
if (player(cb, from_row, from_col) != p) {
|
||||||
|
DEBUG("move: moving piece of opponent\n");
|
||||||
return INVALID;
|
return INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,32 +321,67 @@ enum mstatus move(
|
||||||
if (cb->position[to_row][to_col] != EMPTY &&
|
if (cb->position[to_row][to_col] != EMPTY &&
|
||||||
cb->position[from_row][from_col] / 7 ==
|
cb->position[from_row][from_col] / 7 ==
|
||||||
cb->position[to_row][to_col] / 7) {
|
cb->position[to_row][to_col] / 7) {
|
||||||
|
DEBUG("move: capturing piece from moving player\n");
|
||||||
return INVALID;
|
return INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if you try to move on the same spot, don't do it you buffoon
|
// if you try to move on the same spot, don't do it you buffoon
|
||||||
if (from_row == to_row && from_col == to_col) {
|
if (from_row == to_row && from_col == to_col) {
|
||||||
|
DEBUG("move: moving to same position\n");
|
||||||
return INVALID;
|
return INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to understand which piece we are moving
|
bool can = can_move(cb, p, from_row, from_col, to_row, to_col);
|
||||||
switch (cb->position[from_row][from_col]) {
|
|
||||||
case WHITE_PAWN:
|
if (!can) {
|
||||||
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:
|
|
||||||
return INVALID;
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue