From 33555bc37a0589d35431c6e7dfc732cf922335c1 Mon Sep 17 00:00:00 2001 From: Sam Chudnick Date: Sun, 7 Nov 2021 07:09:44 -0500 Subject: initial commit --- .gitignore | 1 + Makefile | 10 ++ checks.c | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ chess.h | 39 ++++++ computer_player.c | 49 ++++++++ main.c | 200 ++++++++++++++++++++++++++++++ ncurses.c | 342 ++++++++++++++++++++++++++++++++++++++++++++++++++ valid.c | 254 +++++++++++++++++++++++++++++++++++++ 8 files changed, 1259 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 checks.c create mode 100644 chess.h create mode 100644 computer_player.c create mode 100644 main.c create mode 100644 ncurses.c create mode 100644 valid.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7b05f41 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +chess diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..aea03ac --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +CC ?= cc +LDFLAGS = -lncurses + +output: + ${CC} main.c ncurses.c valid.c checks.c computer_player.c $(LDFLAGS) -o chess + +clean: + rm -f chess + +install: output diff --git a/checks.c b/checks.c new file mode 100644 index 0000000..64418b2 --- /dev/null +++ b/checks.c @@ -0,0 +1,364 @@ +#include "chess.h" +int isvalid_move(int side, int start_row, int start_col, int end_row, int end_col); +int ischeck(int side, int row, int col); +int ischeck_mate(int side, int row, int col); +void find_kings(); +extern int board[ROW_SIZE][ROW_SIZE]; +extern int check, white_king_row, white_king_col, black_king_row, black_king_col; + +/* Returns 1 if king is in check, returns 0 otherwise */ +int ischeck(int side, int row, int col) { + /* Check horizontal */ + /* Check left */ + for (int i = row - 1; i >= 0; i--) { + int piece = board[i][col]; + if (piece > 0) { /* If the square is not empty */ + if ((side == WHITE && piece > 10) || (side == BLACK && piece < 10)) { /* If the piece belongs to the opponent */ + if (piece % 10 == 5 || piece % 10 == 2) { /* If the piece is a rook or queen then the king is in check*/ + return 1; + } + else { /* If its another piece, stop looking left as the king is screened by the non rook or queen piece */ + break; + } + } + else { /* If it is a friendly piece, the king is safe to the left so stop looking*/ + break; + } + } + } + + /* Check right */ + for (int i = row + 1; i < ROW_SIZE; i++) { + int piece = board[i][col]; + if (piece > 0) { /* If the square is not empty */ + if ((side == WHITE && piece > 10) || (side == BLACK && piece < 10)) { /* If the piece belongs to the opponent */ + if (piece % 10 == 5 || piece % 10 == 2) { /* If the piece is a rook or queen then the king is in check*/ + return 1; + } + else { /* If its another piece, stop looking left as the king is screened by the non rook or queen piece */ + break; + } + } + else { /* If it is a friendly piece, the king is safe to the left so stop looking*/ + break; + } + } + } + + /* Check vertical */ + /* Check down */ + for (int i = col - 1; i >= 0; i--) { + int piece = board[row][i]; + if (piece > 0) { /* If the square is not empty */ + if ((side == WHITE && piece > 10) || (side == BLACK && piece < 10)) { /* If the piece belongs to the opponent */ + if (piece % 10 == 5 || piece % 10 == 2) { /* If the piece is a rook or queen then the king is in check*/ + return 1; + } + else { /* If its another piece, stop looking left as the king is screened by the non rook or queen piece */ + break; + } + } + else { /* If it is a friendly piece, the king is safe to the left so stop looking*/ + break; + } + } + } + + /* Check up */ + for (int i = col + 1; i < ROW_SIZE; i++) { + int piece = board[row][i]; + if (piece > 0) { /* If the square is not empty */ + if ((side == WHITE && piece > 10) || (side == BLACK && piece < 10)) { /* If the piece belongs to the opponent */ + if (piece % 10 == 5 || piece % 10 == 2) { /* If the piece is a rook or queen then the king is in check*/ + return 1; + } + else { /* If its another piece, stop looking left as the king is screened by the non rook or queen piece */ + break; + } + } + else { /* If it is a friendly piece, the king is safe to the left so stop looking*/ + break; + } + } + } + + /* Check diagonal */ + /* Check up and left */ + for (int i = row + 1, j = col - 1; i < ROW_SIZE && j >= 0; i++, j--) { + int piece = board[i][j]; + if (piece > 0) { /* If the square is not empty */ + if ((side == WHITE && piece > 10) || (side == BLACK && piece < 10)) { /* If the piece belongs to the opponent */ + if (piece % 10 == 5 || piece % 10 == 4) { /* If the piece is a queen or bishop */ + return 1; + } + else if (piece == BLACK_PAWN && side == WHITE) { + return 1; + } + else { /* If its another piece, stop looking left as the king is screened by the non rook or queen piece */ + break; + } + } + else { /* If it is a friendly piece, the king is safe to the left so stop looking*/ + break; + } + } + } + + /* Check up and right */ + for (int i = row + 1, j = col + 1; i < ROW_SIZE && j < ROW_SIZE; i++, j++) { + int piece = board[i][j]; + if (piece > 0) { /* If the square is not empty */ + if ((side == WHITE && piece > 10) || (side == BLACK && piece < 10)) { /* If the piece belongs to the opponent */ + if (piece % 10 == 5 || piece % 10 == 4) { /* If the piece is a bishop */ + return 1; + } + else if (piece == BLACK_PAWN && side == WHITE) { + return 1; + } + else { /* If its another piece, stop looking left as the king is screened by the non rook or queen piece */ + break; + } + } + else { /* If it is a friendly piece, the king is safe to the left so stop looking*/ + break; + } + } + } + + /* Check down and left */ + for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) { + int piece = board[i][j]; + if (piece > 0) { /* If the square is not empty */ + if ((side == WHITE && piece > 10) || (side == BLACK && piece < 10)) { /* If the piece belongs to the opponent */ + if (piece % 10 == 5 || piece % 10 == 4) { /* If the piece is a bishop */ + return 1; + } + else if (piece == WHITE_PAWN && side == BLACK) { + return 1; + } + else { /* If its another piece, stop looking left as the king is screened by the non rook or queen piece */ + break; + } + } + else { /* If it is a friendly piece, the king is safe to the left so stop looking*/ + break; + } + } + } + + /* Check down and right */ + for (int i = row - 1, j = col + 1; i >=0 && j < ROW_SIZE; i--, j++) { + int piece = board[i][j]; + if (piece > 0) { /* If the square is not empty */ + if ((side == WHITE && piece > 10) || (side == BLACK && piece < 10)) { /* If the piece belongs to the opponent */ + if (piece % 10 == 5 || piece % 10 == 4) { /* If the piece is a bishop */ + return 1; + } + else if (piece == WHITE_PAWN && side == BLACK) { /* If white pawn is below and to right if a king, that is check */ + return 1; + } + else { /* If its another piece, stop looking left as the king is screened by the non rook or queen piece */ + break; + } + } + else { /* If it is a friendly piece, the king is safe to the left so stop looking*/ + break; + } + } + } + + /* Check for knights */ + if (board[row-2][col-1] > 0){ + if (row-2 >= 0 && row-2 < ROW_SIZE && col-1 >=0 && col-1 < ROW_SIZE) { /* Only check if the resulting row and column are valid values on the board */ + int piece = board[row-2][col-1]; + if (((side == WHITE && piece > 10) || (side == BLACK && piece < 10)) && piece % 10 == 3) { /* If it is an opponent's knight */ + return 1; + } + } + } + + if (board[row-2][col+1] > 0) { + if (row-2 >= 0 && row-2 < ROW_SIZE && col+1 >=0 && col+1 < ROW_SIZE) { + int piece = board[row-2][col+1]; + if (((side == WHITE && piece > 10) || (side == BLACK && piece < 10)) && piece % 10 == 3) { /* If it is an opponent's knight */ + return 1; + } + } + } + + if (board[row-1][col-2] > 0) { + if (row-1 >= 0 && row-1 < ROW_SIZE && col-2 >=0 && col-2 < ROW_SIZE) { + int piece = board[row-1][col-2]; + if (((side == WHITE && piece > 10) || (side == BLACK && piece < 10)) && piece % 10 == 3) { /* If it is an opponent's knight */ + return 1; + } + } + } + + if (board[row-1][col+2] > 0){ + if (row-1 >= 0 && row-1 < ROW_SIZE && col+2 >=0 && col+2 < ROW_SIZE) { + int piece = board[row-1][col+2]; + if (((side == WHITE && piece > 10) || (side == BLACK && piece < 10)) && piece % 10 == 3) { /* If it is an opponent's knight */ + return 1; + } + } + } + + if (board[row+2][col-1] > 0) { + if (row+2 >= 0 && row+2 < ROW_SIZE && col-1 >=0 && col-1 < ROW_SIZE) { + int piece = board[row+2][col-1]; + if (((side == WHITE && piece > 10) || (side == BLACK && piece < 10)) && piece % 10 == 3) { /* If it is an opponent's knight */ + return 1; + } + } + else { + } + } + + if (board[row+2][col+1] > 0) { + if (row+2 >= 0 && row+2 < ROW_SIZE && col+1 >=0 && col+1 < ROW_SIZE) { + int piece = board[row+2][col+1]; + if (((side == WHITE && piece > 10) || (side == BLACK && piece < 10)) && piece % 10 == 3) { /* If it is an opponent's knight */ + return 1; + } + } + } + + if (board[row+1][col-2] > 0) { + if (row+1 >= 0 && row+1 < ROW_SIZE && col-2 >=0 && col-2 < ROW_SIZE) { + int piece = board[row+1][col-2]; + if (((side == WHITE && piece > 10) || (side == BLACK && piece < 10)) && piece % 10 == 3) { /* If it is an opponent's knight */ + return 1; + } + } + } + if (board[row+1][col+2] > 0) { + if (row+1 >= 0 && row+1 < ROW_SIZE && col+2 >=0 && col+2 < ROW_SIZE) { + int piece = board[row+1][col+2]; + if (((side == WHITE && piece > 10) || (side == BLACK && piece < 10)) && piece % 10 == 3) { /* If it is an opponent's knight */ + return 1; + } + } + } + + return 0; +} + +int can_king_move(int side, int row, int col); +int can_piece_block(int side, int row, int col); + +/* Returns 1 if checkmate, 0 if not */ +int ischeck_mate(int side, int row, int col) { + if(!can_king_move(side, row, col)) { + if(!can_piece_block(side, row, col)) { + return 1; + } + } + return 0; +} + +int can_piece_block(int side, int row, int col) { + /* Brute force check if any move causes the king not to be in check */ + for (int i = 0; i < ROW_SIZE; i++) { + for (int j = 0; j < ROW_SIZE; j++) { + if ((board[i][j] < 10 && side == WHITE) || (board[i][j] > 10 && side == BLACK)) { + int piece = board[i][j]; + for (int k = 0; k < ROW_SIZE; k++){ + for(int l = 0; l < ROW_SIZE; l++) { + if (isvalid_move(side, i, j, k, l)) { + /* Make the move and run ischeck to see if the king is still in check afterwards */ + int destination_piece = board[k][l]; + board[k][l] = piece; + board[i][j] = 0; + + find_kings(); /* Find the kings in case they have moved */ + + /* If the king is not in check after this move, then a valid move exists, so no checkmate*/ + if (side == WHITE) { + if(!ischeck(WHITE, white_king_row, white_king_col)) { + /* Make sure to undo the test move */ + board[i][j] = piece; + board[k][l] = destination_piece; + find_kings(); + return 1; + } + } + else if (side == BLACK) { + if (!ischeck(BLACK, black_king_row, black_king_col)) { + /* Make sure to undo the test move */ + board[i][j] = piece; + board[k][l] = destination_piece; + find_kings(); + //printw("%d %d -> %d %d gets the black king out of check", i, j, k, l); + return 1; + } + } + /* Make sure to undo the test move */ + board[i][j] = piece; + board[k][l] = destination_piece; + } + } + } + } + } + } + return 0; +} + +/* Returns 0 if the King can safely move to a square to get out of check */ +int can_king_move(int side, int row, int col) { + if (!ischeck(side, row, col)) { /* If the king is not in check in the current position, checkmate is not possible */ + return 1; + } + /* Check if the king can move safely move to any square around him*/ + int temp_row, temp_col; + /* row+1, col */ + temp_row = row + 1; temp_col = col; + if (isvalid_move(side, row, col, temp_row, temp_col) && !ischeck(side, temp_row, temp_col) && temp_row >=0 && temp_row < ROW_SIZE && temp_col >=0 && temp_col < ROW_SIZE) { + return 1; + } + + /* row+1, col+1 */ + temp_row = row + 1; temp_col = col + 1; + if (isvalid_move(side, row, col, temp_row, temp_col) && !ischeck(side, temp_row, temp_col) && temp_row >=0 && temp_row < ROW_SIZE && temp_col >=0 && temp_col < ROW_SIZE) { + return 1; + } + + /* row, col+1 */ + temp_row = row; temp_col = col + 1; + if (isvalid_move(side, row, col, temp_row, temp_col) && !ischeck(side, temp_row, temp_col) && temp_row >=0 && temp_row < ROW_SIZE && temp_col >=0 && temp_col < ROW_SIZE) { + return 1; + } + + /* row-1, col-1 */ + temp_row = row - 1; temp_col = col - 1; + if (isvalid_move(side, row, col, temp_row, temp_col) && !ischeck(side, temp_row, temp_col) && temp_row >=0 && temp_row < ROW_SIZE && temp_col >=0 && temp_col < ROW_SIZE) { + return 1; + } + + /* row-1, col */ + temp_row = row - 1; temp_col = col; + if (isvalid_move(side, row, col, temp_row, temp_col) && !ischeck(side, temp_row, temp_col) && temp_row >=0 && temp_row < ROW_SIZE && temp_col >=0 && temp_col < ROW_SIZE) { + return 1; + } + + /* row-1, col+1 */ + temp_row = row - 1; temp_col = col + 1; + if (isvalid_move(side, row, col, temp_row, temp_col) && !ischeck(side, temp_row, temp_col) && temp_row >=0 && temp_row < ROW_SIZE && temp_col >=0 && temp_col < ROW_SIZE) { + return 1; + } + + /* row, col-1 */ + temp_row = row; temp_col = col - 1; + if (isvalid_move(side, row, col, temp_row, temp_col) && !ischeck(side, temp_row, temp_col) && temp_row >=0 && temp_row < ROW_SIZE && temp_col >=0 && temp_col < ROW_SIZE) { + return 1; + } + + /* row+1, col-1 */ + temp_row = row + 1; temp_col = col - 1; + if (isvalid_move(side, row, col, temp_row, temp_col) && !ischeck(side, temp_row, temp_col) && temp_row >=0 && temp_row < ROW_SIZE && temp_col >=0 && temp_col < ROW_SIZE ){ + return 1; + } + getch(); + return 0; +} diff --git a/chess.h b/chess.h new file mode 100644 index 0000000..123f2a7 --- /dev/null +++ b/chess.h @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +#define A 0 +#define B 1 +#define C 2 +#define D 3 +#define E 4 +#define F 5 +#define G 6 +#define H 7 + +#define ROW_SIZE 8 + +#define NO_CHECK 0 +#define IN_CHECK 1 + +/* Pieces */ +#define WHITE 1 +#define BLACK 2 +/* For easy identification, pieces of the same type will be congruent mod 10 + If the piece is less than 10, it is white, if it is greater than 10 it is black */ + +#define WHITE_PAWN 1 +#define WHITE_ROOK 2 +#define WHITE_KNIGHT 3 +#define WHITE_BISHOP 4 +#define WHITE_QUEEN 5 +#define WHITE_KING 6 + +#define BLACK_PAWN 11 +#define BLACK_ROOK 12 +#define BLACK_KNIGHT 13 +#define BLACK_BISHOP 14 +#define BLACK_QUEEN 15 +#define BLACK_KING 16 + diff --git a/computer_player.c b/computer_player.c new file mode 100644 index 0000000..06dc3fa --- /dev/null +++ b/computer_player.c @@ -0,0 +1,49 @@ +#include "chess.h" +#include + +extern int board[ROW_SIZE][ROW_SIZE]; +extern int white_king_row; +extern int white_king_col; +extern int black_king_row; +extern int black_king_col; +extern int check; + +int isvalid_move(int side, int old_row, int new_row, int old_col, int new_col); +int ischeck(int side, int row, int col); +void find_kings(); + +int side = BLACK; + +void computer_move() { + int valid = 0; + while (!valid) { + int start_row = random() % 7; + int start_col = random() % 7; + int end_row = random() % 7; + int end_col = random() % 7; + + if (isvalid_move(BLACK, start_row, start_col, end_row, end_col)) { + int piece = board[start_row][start_col]; + int dest_piece = board[end_row][end_col]; + board[start_row][start_col] = 0; + board[end_row][end_col] = piece; + + find_kings(); + /* If the move results in the side's king being in check, its invalid */ + if (side == WHITE && ischeck(side, white_king_row, white_king_col)) { + /* undo the move and reprompt */ + board[start_row][start_col] = piece; + board[end_row][end_col] = dest_piece; + } + else if (side == BLACK && ischeck(side, black_king_row, black_king_col)) { + board[start_row][start_col] = piece; + board[end_row][end_col] = dest_piece; + } + else { + check = NO_CHECK; /* If move is valid, then there is no check */ + valid = 1; + //check_capture(side, command[2], command[3]); + } + } + } +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..1a25217 --- /dev/null +++ b/main.c @@ -0,0 +1,200 @@ +#include "chess.h" +#include + +/* 8x8, letters are columns, numbers are rows + queen goes on left of king */ + +/* Terminal Chess */ + + +int board[ROW_SIZE][ROW_SIZE] = { + {WHITE_ROOK, WHITE_KNIGHT, WHITE_BISHOP, WHITE_QUEEN, WHITE_KING, WHITE_BISHOP, WHITE_KNIGHT, WHITE_ROOK}, + {WHITE_PAWN, WHITE_PAWN, WHITE_PAWN, WHITE_PAWN, WHITE_PAWN, WHITE_PAWN, WHITE_PAWN, WHITE_PAWN}, + {}, + {}, + {}, + {}, + {BLACK_PAWN, BLACK_PAWN, BLACK_PAWN, BLACK_PAWN, BLACK_PAWN, BLACK_PAWN, BLACK_PAWN, BLACK_PAWN}, + {BLACK_ROOK, BLACK_KNIGHT, BLACK_BISHOP, BLACK_QUEEN, BLACK_KING, BLACK_BISHOP, BLACK_KNIGHT, BLACK_ROOK}, +}; +int white_captured[16]; /* Black pieces that white has captured */ +int num_white_cap = 0; /* Number of black pieces white has captured */ +int black_captured[16]; /* White pieces that black has captured */ +int num_black_cap = 0; /* Number of white pieces black has captured */ + +int white_king_row = 0; /* Location of the white king */ +int white_king_col = 4; + +int black_king_row = 7; /* Location of the black king */ +int black_king_col = 4; + +int check = NO_CHECK; + +void print_board(void); +void move_piece(int side); +void find_kings(); + +int isvalid_move(int side, int old_row, int new_row, int old_col, int new_col); +int isblocked(int piece, int old_row, int new_row, int old_col, int new_col); +int ischeck(int side, int row, int col); +int ischeck_mate(int side, int row, int col); +int get_color(int piece); +void check_capture(int side, int row, int col); +void castle(int side); + +int get_input(); +int screen_display(); +int screen_refresh(); +int screen_close(); +int get_move(int move[4], int side); +void display_check(int side); +void display_win(int side); +WINDOW *inputwin; + +int computer_move(); + + +int main() { + screen_display(); + inputwin = newwin(1, 50, 45, 0); + while (1) { + screen_refresh(); + move_piece(WHITE); + find_kings(); + + if (ischeck(BLACK, black_king_row, black_king_col)) { + if (ischeck_mate(BLACK, black_king_row, black_king_col)) { + screen_refresh(); + display_win(WHITE); + break; + } + else { + display_check(BLACK); + check = IN_CHECK; + } + } + + screen_refresh(); + // computer_move(); + move_piece(BLACK); + find_kings(); + + if (ischeck(WHITE, white_king_row, white_king_col)) { + if (ischeck_mate(WHITE, white_king_row, white_king_col)) { + screen_refresh(); + display_win(BLACK); + break; + } + else { + display_check(WHITE); + check = IN_CHECK; + } + } + } + getch(); + screen_close(); + return 0; +} + +/* Finds and updates the locations on the board of each side's king */ +void find_kings() { + for (int row = 0; row < ROW_SIZE; row++) { + for (int col = 0; col < ROW_SIZE; col++) { + if (board[row][col] == 6) { + white_king_row = row; + white_king_col = col; + } + else if (board[row][col] == 16) { + black_king_row = row; + black_king_col = col; + } + } + } +} + +/* Gets an input from the user as to what piece to move and where to move it to and makes the move once a valid one is selected */ +void move_piece(int side) { + /* Get piece and destination from user*/ + int valid = 0; + while (!valid) { + int move[4]; + get_move(move, side); + int start_row = move[0]; + int start_col = move[1]; + int end_row = move[2]; + int end_col = move[3]; + + int v; + if ((v = isvalid_move(side, start_row, start_col, end_row, end_col))) { + + if (v == 2) { + castle(side); + break; + } + + int piece = board[start_row][start_col]; + int dest_piece = board[end_row][end_col]; + board[start_row][start_col] = 0; + board[end_row][end_col] = piece; + + find_kings(); + /* If the move results in the side's king being in check, its invalid */ + if (side == WHITE && ischeck(side, white_king_row, white_king_col)) { + /* undo the move and reprompt */ + board[start_row][start_col] = piece; + board[end_row][end_col] = dest_piece; + } + else if (side == BLACK && ischeck(side, black_king_row, black_king_col)) { + board[start_row][start_col] = piece; + board[end_row][end_col] = dest_piece; + } + else { + check = NO_CHECK; /* If move is valid, then there is no check */ + valid = 1; + //check_capture(side, command[2], command[3]); + } + } + + } +} + +void castle(int side) { + switch (side) { + case WHITE: + board[0][4] = 0; + board[0][7] = 0; + board[0][6] = WHITE_KING; + board[0][5] = WHITE_ROOK; + break; + case BLACK: + board[7][4] = 0; + board[7][7] = 0; + board[7][6] = BLACK_KING; + board[7][5] = BLACK_ROOK; + break; + } +} + +void check_capture(int side, int row, int col) { + /* If there is a piece on the given square, it is assured + to be an opponent piece as otherwise validity checking would have failed */ + int piece; + if (board[row][col] > 0) { + piece = board[row][col]; + switch (side) { + case WHITE: + white_captured[num_white_cap++] = piece; + break; + case BLACK: + black_captured[num_black_cap++] = piece; + break; + } + } +} + +/* Returns which color the piece belongs to */ +int get_color(int piece) { + if (piece <= 0) + return -1; + return (piece < 10) ? WHITE : BLACK; +} diff --git a/ncurses.c b/ncurses.c new file mode 100644 index 0000000..57047b9 --- /dev/null +++ b/ncurses.c @@ -0,0 +1,342 @@ +#include +#include "chess.h" + +#define WHITE_SQUARE 1 +#define BLACK_SQUARE 2 + +int current_square = BLACK_SQUARE; + +WINDOW *windows[ROW_SIZE][ROW_SIZE]; +WINDOW *currentwin; +WINDOW *white_statuswin; +WINDOW *black_statuswin; + +int current_y = 0; +int current_x = 0; + +int ws = 0; + +extern int board[ROW_SIZE][ROW_SIZE]; + +void get_piece(int piece, WINDOW *win) { + switch (piece) { + case 0: + mvwprintw(win, 1, 1, " "); + mvwprintw(win, 2, 1, " "); + mvwprintw(win, 3, 1, " "); + wmove(win, 2, 3); + break; + case 1: + mvwprintw(win, 1, 1, " "); + mvwprintw(win, 2, 1, " WP "); + mvwprintw(win, 3, 1, " "); + break; + case 2: + mvwprintw(win, 1, 1, " "); + mvwprintw(win, 2, 1, " WR "); + mvwprintw(win, 3, 1, " "); + break; + case 3: + mvwprintw(win, 1, 1, " "); + mvwprintw(win, 2, 1, " WN "); + mvwprintw(win, 3, 1, " "); + break; + case 4: + mvwprintw(win, 1, 1, " "); + mvwprintw(win, 2, 1, " WB "); + mvwprintw(win, 3, 1, " "); + break; + case 5: + mvwprintw(win, 1, 1, " "); + mvwprintw(win, 2, 1, " WQ "); + mvwprintw(win, 3, 1, " "); + break; + case 6: + mvwprintw(win, 1, 1, " "); + mvwprintw(win, 2, 1, " WK "); + mvwprintw(win, 3, 1, " "); + break; + + case 11: + mvwprintw(win, 1, 1, " "); + mvwprintw(win, 2, 1, " BP "); + mvwprintw(win, 3, 1, " "); + break; + case 12: + mvwprintw(win, 1, 1, " "); + mvwprintw(win, 2, 1, " BR "); + mvwprintw(win, 3, 1, " "); + break; + case 13: + mvwprintw(win, 1, 1, " "); + mvwprintw(win, 2, 1, " BN "); + mvwprintw(win, 3, 1, " "); + break; + case 14: + mvwprintw(win, 1, 1, " "); + mvwprintw(win, 2, 1, " BB "); + mvwprintw(win, 3, 1, " "); + break; + case 15: + mvwprintw(win, 1, 1, " "); + mvwprintw(win, 2, 1, " BQ "); + mvwprintw(win, 3, 1, " "); + break; + case 16: + mvwprintw(win, 1, 1, " "); + mvwprintw(win, 2, 1, " BK "); + mvwprintw(win, 3, 1, " "); + break; + } +} + +/* Print the board to standard output */ +void print_board(void) { + int row = 8; + for (int i = 0; i < ROW_SIZE; i++, row--) { + for (int j = 0; j < ROW_SIZE; j++) { + WINDOW *win = newwin(5, 8, i*5, j*8 + 4); + windows[row-1][j] = win; + refresh(); + switch (i % 2) { + case 0: + if (ws % 2 == 0) + wattron(win, COLOR_PAIR(2)); + else + wattron(win, COLOR_PAIR(1)); + break; + case 1: + if (ws % 2 == 0) + wattron(win, COLOR_PAIR(1)); + else + wattron(win, COLOR_PAIR(2)); + break; + } + ws++; + box(win, 0, 0); + get_piece(board[row-1][j], win); + wrefresh(win); + } + } + white_statuswin = newwin(5, 50, 35, 70); + black_statuswin = newwin(5, 50, 0, 70); + refresh(); + + box(white_statuswin, 0, 0); + box(black_statuswin, 0, 0); + + mvwprintw(white_statuswin, 2, 1, "White"); + mvwprintw(black_statuswin, 2, 1, "Black"); + + wrefresh(white_statuswin); + wrefresh(black_statuswin); +} + +void highlight_side(int side) { + switch (side) { + case WHITE: + wattron(white_statuswin, A_REVERSE); + mvwprintw(white_statuswin, 2, 1, "White"); + wattroff(white_statuswin, A_REVERSE); + mvwprintw(black_statuswin, 2, 1, "Black"); + wrefresh(white_statuswin); + wrefresh(black_statuswin); + break; + case BLACK: + wattron(black_statuswin, A_REVERSE); + mvwprintw(black_statuswin, 2, 1, "Black"); + wattroff(black_statuswin, A_REVERSE); + mvwprintw(white_statuswin, 2, 1, "White"); + wrefresh(white_statuswin); + wrefresh(black_statuswin); + break; + } +} + + +/* Gets a move from the player */ +void get_move(int move[4], int side) { + + highlight_side(side); + + WINDOW *hlightwin; + int c, start_row, start_col, end_row, end_col, select_start, select_dest, keep_hlight, hlight_y, hlight_x; + c = start_row = start_col = end_row = end_col = select_start = select_dest = keep_hlight = hlight_y = hlight_x = 0; + + switch (side) { + case WHITE: + current_y = 0; + current_x = 4; + currentwin = windows[0][4]; + move(0, 0); + refresh(); + break; + case BLACK: + current_y = 7; + current_x = 4; + currentwin = windows[7][4]; + break; + } + + while ((c = getch()) != EOF) { + switch (c) { + case 'h': + current_x -= 1; + if (current_x < 0) { + current_x = 0; + } + break; + case 'j': + current_y -= 1; + if (current_y < 0) { + current_y = 0; + } + break; + case 'k': + current_y += 1; + if (current_y > 7) { + current_y = 7; + } + break; + case 'l': + current_x += 1; + if (current_x > 7) { + current_x = 7; + } + break; + case 'H': case '0': + current_x = 0; + break; + case 'J': case 'G': + current_y = 0; + break; + case 'K': case 'g': + current_y = 7; + break; + case 'L': case '$': + current_x = 7; + break; + case '\n': case ' ': + switch (select_start) { + case 0: + select_start = 1; + keep_hlight = 1; + start_row = current_y; + start_col = current_x; + break; + case 1: + select_dest = 1; + end_row = current_y; + end_col = current_x; + break; + } + break; + case 'u': + if (select_start) { + select_start = 0; + start_row = start_col = 0; + get_piece(board[hlight_y][hlight_x], hlightwin); + wrefresh(hlightwin); + wmove(currentwin, 2, 3); + wrefresh(currentwin); + break; + } + } + + /* Refresh the window before updating currentwin to show the normal piece in the previous square */ + wrefresh(currentwin); + /* Apply screen effects to new square */ + currentwin = windows[current_y][current_x]; + wattron(currentwin, A_STANDOUT); + get_piece(board[current_y][current_x], currentwin); + wattroff(currentwin, A_STANDOUT); + wrefresh(currentwin); + move(0, 0); + + /* If the player has already selected a piece and returns to that square + enable keep_hlight so that the selected piece doesn't get 'unhighlighted'*/ + if (select_start && hlight_y == current_y && hlight_x == current_x) { + keep_hlight = 1; + } + if (!keep_hlight) { + /* Print the current square again so it returns to normal in memory + but don't refresh the window so it stays hidden*/ + get_piece(board[current_y][current_x], currentwin); + } + else { + keep_hlight = 0; + /* Save highlight window and board location to remove the highlight when finished */ + hlightwin = currentwin; + hlight_y = current_y; + hlight_x = current_x; + } + if (select_dest) { + break; + } + } + /* Reprint the higlighted piece to clear the highlight */ + get_piece(board[hlight_y][hlight_x], hlightwin); + wrefresh(hlightwin); + /* Move back to selected position (where you were before the above two lines) */ + wmove(currentwin, 2, 3); + wrefresh(currentwin); + move[0] = start_row; + move[1] = start_col; + move[2] = end_row; + move[3] = end_col; +} + + +void display_check(int side) { + switch (side) { + case WHITE: + mvwprintw(white_statuswin, 2, 10, "Check"); + wrefresh(white_statuswin); + break; + case BLACK: + mvwprintw(black_statuswin, 2, 10, "Check"); + wrefresh(black_statuswin); + break; + } +} + +void display_win(int side) { + switch (side) { + case WHITE: + mvwprintw(white_statuswin, 2, 10, "WIN!"); + wrefresh(white_statuswin); + break; + case BLACK: + mvwprintw(black_statuswin, 2, 10, "WIN!"); + wrefresh(black_statuswin); + break; + } +} + +int screen_display() { + initscr(); + noecho(); + cbreak(); + curs_set(0); + start_color(); + use_default_colors(); + //short int *deffg, *defbg; + //pair_content(0, deffg, defbg); + init_pair(1, COLOR_WHITE, COLOR_BLACK); + init_pair(2, COLOR_RED, COLOR_BLACK); + print_board(); + currentwin = windows[0][0]; + return 0; +} + +int screen_refresh() { + int row = 8; + print_board(); + refresh(); + return 0; +} + +int screen_close() { + endwin(); + return 0; +} diff --git a/valid.c b/valid.c new file mode 100644 index 0000000..3bfa98e --- /dev/null +++ b/valid.c @@ -0,0 +1,254 @@ +#include "chess.h" +int isvalid_move(int side, int old_row, int old_col, int new_row, int new_col); +int isblocked(int piece, int old_row, int old_col, int new_row, int new_col); +int ischeck(int side, int row, int col); +int get_color(int piece); + +int white_king_moved = 0; +int black_king_moved = 0; + +extern int board[ROW_SIZE][ROW_SIZE]; + +/* Checks if the selected move is valid by the rules of chess */ +int isvalid_move(int side, int old_row, int old_col, int new_row, int new_col) { + + /* If either the new row or new column is outside the dimensions of the board, invalid */ + if (new_row < 0 || new_row > ROW_SIZE || new_col < 0 || new_col > ROW_SIZE) { + return 0; + } + + int sp = board[old_row][old_col]; /* Piece chosen by player */ + int dp = board[new_row][new_col]; /* Piece in destination square (may or may not be null) */ + /* Invalid if the row or column is invalid, the chosen piece does not belong to the player, + the old square = dest square, or if there is a piece on the destination square that also belongs to the player*/ + if (side != get_color(sp) || ((old_col == new_col) && (old_row == new_row)) || side == get_color(dp)){ + return 0; + } + else if (isblocked(sp, old_row, old_col, new_row, new_col)) { + return 0; + } + + switch (sp % 10) { + case 1: /* Pawn */ + /* Valid if row = row + 1 for white, row = row -1 for black */ + if (side == WHITE && new_row == old_row + 1 && new_col == old_col) { + break; + } + if (side == BLACK && new_row == old_row - 1 && new_col == old_col) { + break; + } + /*valid if abs(row-row) = 1 and abs(col-col) = 1 and piece on destination square is an opponent */ + if (abs(old_row - new_row) == 1 && abs(old_col - new_col) == 1 && dp > 0) { + if ((side == WHITE && dp > 10 && new_row > old_row) || (side == BLACK && dp < 10 && new_row < old_row)) { + break; + } + } + /* Valid if abs(row-row) = 2 and col=col and destination square is empty */ + if (abs(old_row - new_row) == 2 && old_col == new_col && dp == 0) { + if ((old_row == 1 && new_row == 3 && side == WHITE) || (old_row == 6 && new_row == 4 && side == BLACK )) { + break; + } + } + return 0; + case 2: /* Rook */ + if (old_row != new_row && old_col != new_col) + return 0; + break; + case 3: /* Knight */ + if (!((abs(old_row - new_row) == 2 && abs(old_col - new_col) == 1) || (abs(old_col - new_col) == 2 && abs(old_row - new_row) == 1))) + return 0; + break; + case 4: /* Bishop */ + if (abs(old_row - new_row) != abs(old_col - new_col)) + return 0; + break; + case 5: /* Queen */ + if ((abs(old_row - new_row) != abs(old_col - new_col)) && ((old_row != new_row && old_col != new_col))) + return 0; + break; + + case 6: /* King */ + if (ischeck(side, new_row, new_col)) { /* Invalid if the move would put the king in check */ + return 0; + } + else if (side == WHITE && sp == WHITE_KING && board[0][5] == 0 && board[0][6] == 0 && old_row == 0 && new_row == 0 && new_col == 6 && !white_king_moved) { + white_king_moved = 1; + return 2; + } + else if (side == BLACK && sp == BLACK_KING && board[7][5] == 0 && board[7][6] == 0 && old_row == 7 && new_row == 7 && new_col == 6 && !black_king_moved) { + black_king_moved = 1; + return 2; + } + else if (abs(new_col - old_col) > 1 || abs(new_row - old_row) > 1) + return 0; + switch (side) { + case WHITE: + white_king_moved = 1; + break; + case BLACK: + black_king_moved = 1; + break; + } + break; + } + return 1; +} + +/* Returns whether or not the path to the destination is blocked by another piece, 1 for blocked, 0 for not-blocked */ +int isblocked(int piece, int start_row, int start_col, int end_row, int end_col) { + int diagonal = 0; + int horizontal = 0; + int vertical = 0; + /* Determine direction of travel */ + if (start_row != end_row && start_col != end_col) { + diagonal = 1; + } + else if (start_row == end_row && start_col != end_col) { + horizontal = 1; + } + else if (start_row != end_row && start_col == end_col) { + vertical = 1; + } + /* Use direction to determine path based off of piece */ + switch (piece % 10) { + case 1: /* Pawn */ + /* If pawn isn't on starting row, no need to check because previous error checking + in isvalid_move() would have already determined if its path (the 1 square in-front) is occupied*/ + if ((piece < 10 && start_row != 1) || (piece > 10 && start_row != 6)) { + break; + } + /* If the pawn is trying to move two spaces from its starting position, the only possible block + would be on the square directly in front of it */ + else if (piece < 10 && board[start_row + 1][start_col] > 0) { /* If white, check row above */ + return 1; + } + else if (piece > 10 && board[start_row - 1][start_col] > 0) { /* If black, check row below */ + return 1; + } + break; + case 2: /* Rook */ + if (horizontal && start_col < end_col) { + for (int i = start_col + 1; i < end_col; i++) { + if (board[start_row][i] > 0) { + return 1; + } + } + } + else if (horizontal && start_col > end_col) { + for (int i = start_col - 1; i > end_col; i--) { + if (board[start_row][i] > 0) { + return 1; + } + } + } + else if (vertical && start_row < end_row) { + for (int i = start_row + 1; i < end_row; i++) { + if (board[i][start_col] > 0) { + return 1; + } + } + } + else if (vertical && start_row > end_row) { + for (int i = start_row - 1; i > end_row; i--) { + if (board[i][start_col] > 0) { + return 1; + } + } + } + break; + case 3: /* Knight, a knight's path cannot be blocked */ + break; + case 4: /* Bishop */ + if (start_row > end_row && start_col > end_col) { /* If moving to the left and back */ + for (int i = start_row - 1, j = start_col - 1; j > end_col; i--, j--) { + if (board[i][j] > 0) { + return 1; + } + } + } + else if (start_row < end_row && start_col > end_col) { /* If moving to the left and forward */ + for (int i = start_row + 1, j = start_col - 1; j > end_col; i++, j--) { + if (board[i][j] > 0) { + return 1; + } + } + } + else if (start_row > end_row && start_col < end_col) { /* If moving to the right and back*/ + for (int i = start_row - 1, j = start_col + 1; j < end_col; i--, j++) { + if (board[i][j] > 0) { + return 1; + } + } + } + else if (start_row < end_row && start_col < end_col) { /* If moving to the right and forward*/ + for (int i = start_row + 1, j = start_col + 1; j < end_col; i++, j++) { + if (board[i][j] > 0) { + return 1; + } + } + } + break; + case 5: /* Queen */ + /* Queen checks are just a mix of rook and bishop checks */ + if (horizontal && start_col < end_col) { + for (int i = start_col + 1; i < end_col; i++) { + if (board[start_row][i] > 0) { + return 1; + } + } + } + else if (horizontal && start_col > end_col) { + for (int i = start_col - 1; i > end_col; i--) { + if (board[start_row][i] > 0) { + return 1; + } + } + } + else if (vertical && start_row < end_row) { + for (int i = start_row + 1; i < end_row; i++) { + if (board[i][start_col] > 0) { + return 1; + } + } + } + else if (vertical && start_row > end_row) { + for (int i = start_row - 1; i > end_row; i--) { + if (board[i][start_col] > 0) { + return 1; + } + } + } + else if (diagonal && start_row > end_row && start_col > end_col) { /* If moving to the left and back */ + for (int i = start_row - 1, j = start_col - 1; j > end_col; i--, j--) { + if (board[i][j] > 0) { + return 1; + } + } + } + else if (diagonal && start_row < end_row && start_col > end_col) { /* If moving to the left and forward */ + for (int i = start_row + 1, j = start_col - 1; j > end_col; i++, j--) { + if (board[i][j] > 0) { + return 1; + } + } + } + else if (diagonal && start_row > end_row && start_col < end_col) { /* If moving to the right and back*/ + for (int i = start_row - 1, j = start_col + 1; j < end_col; i--, j++) { + if (board[i][j] > 0) { + return 1; + } + } + } + else if (diagonal && start_row < end_row && start_col < end_col) { /* If moving to the right and forward*/ + for (int i = start_row + 1, j = start_col + 1; j < end_col; i++, j++) { + if (board[i][j] > 0) { + return 1; + } + } + } + case 6: /* King, king's path cannot be blocked as it can only move 1 square, so any blocks would be detected in isvalid_move()*/ + break; + } + return 0; +} + -- cgit v1.2.3