#include #include #include #include #include "vector.h" #include "console.h" #include "chesshelpers.h" #include "fscomch.h" #include "chess.h" int main(int argc, char **argv) { if(argc == 2) { if(strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0) { printf("Version 1.1.0\n"); exit(0); } } //set up the board int boardA[8][8] = { { rook + 6, pawn + 6, 0, 0, 0, 0, pawn, rook }, { knight + 6, pawn + 6, 0, 0, 0, 0, pawn, knight }, { bishop + 6, pawn + 6, 0, 0, 0, 0, pawn, bishop }, { queen + 6, pawn + 6, 0, 0, 0, 0, pawn, queen }, { king + 6, pawn + 6, 0, 0, 0, 0, pawn, king }, { bishop + 6, pawn + 6, 0, 0, 0, 0, pawn, bishop }, { knight + 6, pawn + 6, 0, 0, 0, 0, pawn, knight }, { rook + 6, pawn + 6, 0, 0, 0, 0, pawn, rook } }; //convert the array to pointers int** pieces = malloc(8 * sizeof(int*)); for(int r = 0; r < 8; r++) { pieces[r] = malloc(8 * sizeof(int)); for(int c = 0; c < 8; c++) { pieces[r][c] = boardA[r][c]; } } board_t board = init_board(pieces); /* try to acquire a lock on the local matchfile */ FILE* matchFile = try_lock_match(); match_t match; char isHost; char isWhite; if (matchFile != NULL) { isHost = 1; /* we are acting as the host--await a connection */ printf("Awaiting connection\n"); match = await_match(matchFile, init_cancel_token_never()); printf("Connection completed. Match is %s\n", match.uuid); } else { isHost = 0; /* we are joining an existing match */ printf("Attempting to join existing match\n"); match = join_match(); if (strlen(match.uuid) == 36) { printf("Joined match %s\n", match.uuid); } else { printf("Error joining match. Reload the program\n"); exit(1); } } sleep(2); /* host plays white */ isWhite = isHost; char turn = 0; while (1) { //draw the board, write some info clear_screen(); draw_board(board.pieces, !isWhite); if (turn == isWhite) { /* awaiting the remote player */ printf("Waiting for %s's move\n", turn ? "black" : "white"); while (1) { char* message = read_message(match, init_cancel_token_never()); /* try to parse the message as a move */ if (strncmp(message, "move=", 5) == 0) { vector_t rOrigin = init_vector(message[5] - '0', message[6] - '0'); vector_t rDest = init_vector(message[7] - '0', message[8] - '0'); board = make_move(board, rOrigin, rDest); break; } free(message); } } else { /* get local move */ printf(turn == 0 ? "\nWhite's turn\n" : "\nBlack's turn\n"); if(is_in_check(board, turn)) { vectorpp_t allMoves = get_all_moves(board, turn, 0); char checkmate = 0; for(int i = 0; i < allMoves.length; i++) { if(allMoves.vectorps[i].length > 0) { checkmate = 0; break; } else { checkmate = 1; } } if(checkmate) { printf("You've been checkmated!\n"); break; } else { printf("You're in check!\n"); } } printf("Enter the coordinates of the piece you want to move\n"); //get the selection for the piece to be moved vector_t selection = user_input_pos(); int selectedPiece = board.pieces[selection.mx][selection.my]; //check if the piece is of the right colour, and that there is a piece there if(selectedPiece == 0 || (selectedPiece > 6 && turn == 0) || (selectedPiece <= 6 && turn == 1)) { warn_user("Invalid selection!"); continue; } printf("Selected piece: "); printf("%d", selectedPiece); printf("\n"); vectorp_t moves = get_moves_final(board, selection, selectedPiece); if(moves.length == 0) { warn_user("That piece has no valid moves!"); continue; } printf("Valid moves: "); //write all the valid moves for(int i = 0; i < moves.length; i++) { char* readable_coords = get_move_name(board, selection, moves.vectors[i]); printf(readable_coords); free(readable_coords); printf("; "); } printf("\nEnter the coordinates you would like to move that piece to, or enter < to go back\n"); //get the selection for the location to move the piece to vector_t target = user_input_pos(); if(target.mx == -1) { continue; } //check if target is in list of valid moves int moveMatch = 0; for(int i = 0; i < moves.length; i++) { if(moves.vectors[i].mx == target.mx && moves.vectors[i].my == target.my) { moveMatch = 1; break; } } if(!moveMatch) { warn_user("Invalid move"); continue; } board = make_move(board, selection, target); free(moves.vectors); printf("\n"); /* send move to remote player */ char move[10]; strcpy(move, "move="); snprintf(move + 5, 5, "%d%d%d%d", selection.mx, selection.my, target.mx, target.my); send_message(move, match); } turn = !turn; } return 0; } //this function returns moves that will place the player in check! vectorp_t get_moves(board_t board, vector_t position, piece_t piece) { int color = 0; if(piece > 6) { color = 1; piece -= 6; } if(piece == pawn) { return get_pawn_moves(board, position, color); } else if(piece == knight) { return get_knight_moves(board, position, color); } else if(piece == bishop) { return get_bishop_moves(board, position, color); } else if(piece == rook) { return get_rook_moves(board, position, color); } else if(piece == queen) { return get_queen_moves(board, position, color); } else if(piece == king) { return get_king_moves(board, position, color); } else { return init_vectorp(0, 0); } } //array of arrays representing all the moves each piece of color can make //excludeKing flag prevents recursion with castling checks vectorpp_t get_all_moves(board_t board, int color, int excludeKing) { vectorp_t* allMoves = malloc(16 * sizeof(vectorp_t)); int pieceCount = 0; for(int r = 0; r < 8; r++) { for(int c = 0; c < 8; c++) { vector_t v = init_vector(c, r); if(is_color(board, v, color) && (!excludeKing || (board.pieces[v.mx][v.my] != king && board.pieces[v.mx][v.my] != king + 6))) { allMoves[pieceCount] = get_moves(board, v, board.pieces[c][r]); pieceCount++; } } } allMoves = realloc(allMoves, pieceCount * sizeof(vectorp_t)); return init_vectorpp(allMoves, pieceCount); } //returns the position of color's king vector_t get_king_position(board_t board, int color) { for(int r = 0; r < 8; r++) { for(int c = 0; c < 8; c++) { if(board.pieces[c][r] == (king + color * 6)) { return init_vector(c, r); } } } } //returns true if the player of color is currently in check int is_in_check(board_t board, int color) { int returnV = 0; vector_t kingPos = get_king_position(board, color); //get all the moves for the opponent vectorpp_t allMoves = get_all_moves(board, !color, 1); for(int i = 0; i < allMoves.length; i++) { vectorp_t moves = allMoves.vectorps[i]; for(int j = 0; j < moves.length; j++) { if(moves.vectors[j].mx == kingPos.mx && moves.vectors[j].my == kingPos.my) { returnV = 1; } } free(moves.vectors); } free(allMoves.vectorps); return returnV; } //this function removes moves that place you in check vectorp_t get_moves_final(board_t board, vector_t position, piece_t piece) { int color = 0; if(piece > 6) { color = 1; } vectorp_t moves = get_moves(board, position, piece); //try making that move, see if it puts the player in check int* invalidMoves = malloc(moves.length * sizeof(int)); int invalidMovesCount = 0; for(int i = 0; i < moves.length; i++) { board_t tempBoard = make_move(clone_board(board), position, moves.vectors[i]); if(is_in_check(tempBoard, color)) { invalidMoves[invalidMovesCount] = i; invalidMovesCount++; } free(tempBoard.pieces); } vectorp_t finalMoves = remove_vectors(moves, invalidMoves, invalidMovesCount); free(invalidMoves); free(moves.vectors); return finalMoves; } vectorp_t get_pawn_moves(board_t board, vector_t position, int color) { vector_t* moves = malloc(6 * sizeof(vector_t)); int moveCount = 0; int forwardDir = (color == 0 ? -1 : 1); vector_t move1 = init_vector(position.mx, position.my + forwardDir); if(in_board(move1) && is_empty(board, move1)) { moves[moveCount] = move1; moveCount++; if(position.my == (color == 0 ? 6 : 1)) { vector_t move2 = init_vector(position.mx, position.my + (forwardDir * 2)); if(is_empty(board, move2)) { moves[moveCount] = move2; moveCount++; } } } vector_t captureL = init_vector(position.mx - 1, position.my + forwardDir); if(in_board(captureL) && (is_color(board, captureL, !color) || would_have_been_pawn(board, captureL, !color))) { moves[moveCount] = captureL; moveCount++; } vector_t captureR = init_vector(position.mx + 1, position.my + forwardDir); if(in_board(captureR) && (is_color(board, captureR, !color) || would_have_been_pawn(board, captureR, !color))) { moves[moveCount] = captureR; moveCount++; } return init_vectorp(moves, moveCount); } vectorp_t get_knight_moves(board_t board, vector_t position, int color) { vector_t* moves = malloc(8 * sizeof(vector_t)); int moveCount = 0; for(int i = 0; i < 8; i++) { int h = ((i == 0 || i == 1) ? 2 : (i == 2 || i == 3) ? -2 : (i == 4 || i == 5) ? 1 : -1); int v = ((i == 0 || i == 2) ? -1 : (i == 1 || i == 3) ? 1 : (i == 4 || i == 6) ? 2 : -2); vector_t move = init_vector(position.mx + h, position.my + v); if(in_board(move) && (is_empty(board, move) || is_color(board, move, !color))) { moves[moveCount] = move; moveCount++; } } return init_vectorp(moves, moveCount); } vectorp_t get_bishop_moves(board_t board, vector_t position, int color) { vector_t* moves = malloc(14 * sizeof(vector_t)); int moveCount = 0; for(int i = 0; i < 4; i++) { int h = i < 2 ? 1 : -1; int v = (i == 0 || i == 2) ? 1 : -1; vector_t checkPos = init_vector(position.mx, position.my); for(int d = 0; d < 7; d++) { checkPos.mx += h; checkPos.my += v; if(in_board(checkPos)) { if(is_empty(board, checkPos)) { moves[moveCount] = init_vector(checkPos.mx, checkPos.my); moveCount++; } else if(is_color(board, checkPos, !color)) { moves[moveCount] = init_vector(checkPos.mx, checkPos.my); moveCount++; break; } else { break; } } else { break; } } } return init_vectorp(moves, moveCount); } vectorp_t get_rook_moves(board_t board, vector_t position, int color) { vector_t* moves = malloc(14 * sizeof(vector_t)); int moveCount = 0; for(int i = 0; i < 4; i++) { int h = (i == 0 || i == 2) ? 0 : i == 1 ? 1 : -1; int v = (i == 1 || i == 3) ? 0 : i == 2 ? 1 : -1; vector_t checkPos = init_vector(position.mx, position.my); for(int d = 0; d < 7; d++) { checkPos.mx += h; checkPos.my += v; if(in_board(checkPos)) { if(is_empty(board, checkPos)) { moves[moveCount] = init_vector(checkPos.mx, checkPos.my); moveCount++; } else if(is_color(board, checkPos, !color)) { moves[moveCount] = init_vector(checkPos.mx, checkPos.my); moveCount++; break; } else { break; } } else { break; } } } return init_vectorp(moves, moveCount); } vectorp_t get_queen_moves(board_t board, vector_t position, int color) { vector_t* moves = malloc(28 * sizeof(vector_t)); int moveCount = 0; for(int i = 0; i < 8; i++) { int h; int v; //diagonal moves if(i < 4) { h = i < 2 ? 1 : -1; v = (i == 0 || i == 2) ? 1 : -1; } //straight moves else { h = (i == 4 || i == 6) ? 0 : i == 5 ? 1 : -1; v = (i == 5 || i == 7) ? 0 : i == 6 ? 1 : -1; } vector_t checkPos = init_vector(position.mx, position.my); for(int d = 0; d < 7; d++) { checkPos.mx += h; checkPos.my += v; if(in_board(checkPos)) { if(is_empty(board, checkPos)) { moves[moveCount] = init_vector(checkPos.mx, checkPos.my); moveCount++; } else if(is_color(board, checkPos, !color)) { moves[moveCount] = init_vector(checkPos.mx, checkPos.my); moveCount++; break; } else { break; } } else { break; } } } return init_vectorp(moves, moveCount); } //TODO: don't allow king to move next to opposing king vectorp_t get_king_moves(board_t board, vector_t position, int color) { //TODO: change size to 8 vector_t* moves = malloc(20 * sizeof(vector_t)); int moveCount = 0; for(int i = 0; i < 8; i++) { int h; int v; //diagonal moves if(i < 4) { h = i < 2 ? 1 : -1; v = (i == 0 || i == 2) ? 1 : -1; } //straight moves else { h = (i == 4 || i == 6) ? 0 : i == 5 ? 1 : -1; v = (i == 5 || i == 7) ? 0 : i == 6 ? 1 : -1; } vector_t move = init_vector(position.mx + h, position.my + v); if(in_board(move) && (is_empty(board, move) || is_color(board, move, !color))) { moves[moveCount] = move; moveCount++; } } //castling if(!is_in_check(board, color)) { //kingside castling if((color == 0 && board.whiteCanCastleKingside) || (color == 1 && board.blackCanCastleKingside)) { //check that the squares are free vector_t s1 = init_vector(5, color == 0 ? 7 : 0); vector_t s2 = init_vector(6, color == 0 ? 7 : 0); if(is_empty(board, s1) && is_empty(board, s2)) { //check that the squares are not threatened board_t tempBoard1 = make_move(clone_board(board), position, s1); board_t tempBoard2 = make_move(clone_board(board), position, s2); if(!is_in_check(tempBoard1, color) && !is_in_check(tempBoard2, color)) { //add the move moves[moveCount] = s2; moveCount++; } free(tempBoard1.pieces); free(tempBoard2.pieces); } } //queenside castling if((color == 0 && board.whiteCanCastleQueenside) || (color == 1 && board.blackCanCastleQueenside)) { //check that the squares are free vector_t s1 = init_vector(3, color == 0 ? 7 : 0); vector_t s2 = init_vector(2, color == 0 ? 7 : 0); if(is_empty(board, s1) && is_empty(board, s2)) { //check that the squares are not threatened board_t tempBoard1 = make_move(clone_board(board), position, s1); board_t tempBoard2 = make_move(clone_board(board), position, s2); if(!is_in_check(tempBoard1, color) && !is_in_check(tempBoard2, color)) { //add the move moves[moveCount] = s2; moveCount++; } free(tempBoard1.pieces); free(tempBoard2.pieces); } } } return init_vectorp(moves, moveCount); }