diff --git a/TODO b/TODO index d1838c6..a886bea 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,4 @@ 1. Algebraic notation: notation_move_to_string, documentation 2. Board: board_is_move_legal, add other piece's moves -3. Chess: output special error message for each type of illegal move so it's clear what went wrong \ No newline at end of file +3. Chess: output special error message for each type of illegal move so it's clear what went wrong +4. board_get_pawn_moves - add en passant \ No newline at end of file diff --git a/src/board.c b/src/board.c index 04c6f18..d5df37d 100644 --- a/src/board.c +++ b/src/board.c @@ -322,11 +322,11 @@ int board_is_tile_occupied(const Board* board, Position pos) { /* -Returns all possible legal positions either in diagonal or in straight lines +Returns all possible legal positions in diagonal, in straight lines, in both as well as knight-type for specified tile. -If mode is 0 - returns positions in straight lines, 1 - in diagonals, 2 - in both +Returns NULL in case of an error */ -Position* board_get_moves(const Board* board, const Tile* piece_tile, int mode, unsigned int* pos_count) { +Position* board_get_positions(const Board* board, const Tile* piece_tile, MoveMode mode, unsigned int* pos_count) { if (!board || !piece_tile || !pos_count) { return NULL; } @@ -338,7 +338,30 @@ Position* board_get_moves(const Board* board, const Tile* piece_tile, int mode, } switch (mode) { - case 0: { + case MODEKNIGHT: { + for (unsigned int j = 0; j < 8; j++) { + int vx = KNIGHTDELTAS[j].x; + int vy = KNIGHTDELTAS[j].y; + + int xtmp = piece_tile->position.row + vx; + int ytmp = piece_tile->position.column + vy; + if (board_is_tile_in_bounds(board, position_new(xtmp, ytmp))) { + Tile* tile = board_get_tile(board, position_new(xtmp, ytmp)); + if (tile->piece.type == NONE) { + legal_moves[i] = tile->position; + i++; + } else if (tile->piece.color != piece_tile->piece.color) { + legal_moves[i] = tile->position; + i++; + } else { + break; + } + } + } + break; + } + + case MODESTRAIGHTS: { for (unsigned int j = 0; j < 4; j++) { int vx = CARDINALS[j].x; int vy = CARDINALS[j].y; @@ -365,7 +388,7 @@ Position* board_get_moves(const Board* board, const Tile* piece_tile, int mode, break; } - case 1: { + case MODEDIAGONALS: { for (unsigned int j = 0; j < 4; j++) { int vx = DIAGONALS[j].x; int vy = DIAGONALS[j].y; @@ -392,7 +415,7 @@ Position* board_get_moves(const Board* board, const Tile* piece_tile, int mode, break; } - case 2: { + case MODESTRAIGHTSDIAGONALS: { for (unsigned int j = 0; j < 4; j++) { int vx = CARDINALS[j].x; int vy = CARDINALS[j].y; @@ -455,21 +478,184 @@ Position* board_get_moves(const Board* board, const Tile* piece_tile, int mode, } -// Returns all possible legal rook moves. Returns NULL if given tile does not contain a rook piece + +// Returns all possible legal knight positions. +Position* board_get_knight_moves(const Board* board, const Tile* knight_tile, unsigned int* pos_count) { + if (!board || !knight_tile || !pos_count) { + return NULL; + } + + return board_get_positions(board, knight_tile, MODEKNIGHT, pos_count); +} + + +// Returns all possible legal rook moves. Position* board_get_rook_moves(const Board* board, const Tile* rook_tile, unsigned int* pos_count) { if (!board || !rook_tile || !pos_count) { return NULL; } - if (rook_tile->piece.type != ROOK) { + return board_get_positions(board, rook_tile, MODESTRAIGHTS, pos_count); +} + + +// Returns all possible legal bishop positions. +Position* board_get_bishop_moves(const Board* board, const Tile* bishop_tile, unsigned int* pos_count) { + if (!board || !bishop_tile || !pos_count) { + return NULL; + } + + return board_get_positions(board, bishop_tile, MODEDIAGONALS, pos_count); +} + + +// Returns all possible legal queen positions. +Position* board_get_queen_moves(const Board* board, const Tile* queen_tile, unsigned int* pos_count) { + if (!board || !queen_tile || !pos_count) { + return NULL; + } + + return board_get_positions(board, queen_tile, MODESTRAIGHTSDIAGONALS, pos_count); +} + + +// Returns all possible legal king positions. +Position* board_get_king_moves(const Board* board, const Tile* king_tile, unsigned int* pos_count) { + if (!board || !king_tile || !pos_count) { + return NULL; + } + + Position* queen_positions = board_get_queen_moves(board, king_tile, pos_count); + Position* king_positions = malloc(sizeof(Position) * (*pos_count)); + if (!king_positions) { + free(queen_positions); + return NULL; + } + + unsigned int king_pos_count = 0; + for (unsigned int i = 0; i < *pos_count; i++) { + if (position_distance(king_tile->position, queen_positions[i]) > 1) { + continue; + } + + king_positions[king_pos_count] = queen_positions[i]; + king_pos_count++; + } + + free(queen_positions); + *pos_count = king_pos_count; + king_positions = realloc(king_positions, sizeof(Position) * king_pos_count); + + return NULL; +} + + +// Returns all possible legal pawn positions. +Position* board_get_pawn_moves(const Board* board, const Tile* pawn_tile, unsigned int* pos_count) { + if (!board || !pawn_tile || !pos_count) { return NULL; } + + Position* positions = malloc(sizeof(Position) * board->rows * board->columns); + if (!positions) { + return NULL; + } + + + // 1 and 2 tile advancements and takes + Position pos = pawn_tile->position; + for (int i = 1; i <= 2; i++) { + pos = pawn_tile->position; + + // Advances + if (pawn_tile->piece.color == WHITE) { + pos.row = pos.row + i; + } else { + pos.row = pos.row - i; + } + + + if (board_is_tile_in_bounds(board, pos)) { + Tile* tile = board_get_tile(board, pos); + if (tile->piece.type == NONE) { + // Add this position + positions[*pos_count] = pos; + (*pos_count)++; + } + } + + + // Takes + if (i % 2 == 0) { + pos.column = pos.column + 1; + } else { + pos.column = pos.column - 1; + } - return board_get_moves(board, rook_tile, 0, pos_count); + if (board_is_tile_in_bounds(board, pos)) { + Tile* tile = board_get_tile(board, pos); + if (tile->piece.type != NONE && tile->piece.color != pawn_tile->piece.color) { + // Add this position + positions[*pos_count] = pos; + (*pos_count)++; + } + } + } + + + positions = realloc(positions, sizeof(Position) * (*pos_count)); + + return positions; } +// Returns all possible legal positions for a piece standing on piece_tile. Returns NULL in case of an error +Position* board_get_positions_for(const Board* board, const Tile* piece_tile, unsigned int* pos_count) { + if (!board || !piece_tile || !pos_count) { + return NULL; + } + + // Check if there is a piece or not + if (piece_tile->piece.type == NONE) { + return NULL; + } + + switch (piece_tile->piece.type) { + PAWN: { + return board_get_pawn_moves(board, piece_tile, pos_count); + } + + KNIGHT: { + return board_get_knight_moves(board, piece_tile, pos_count); + } + + ROOK: { + return board_get_rook_moves(board, piece_tile, pos_count); + } + + BISHOP: { + return board_get_bishop_moves(board, piece_tile, pos_count); + } + + QUEEN: { + return board_get_queen_moves(board, piece_tile, pos_count); + } + + KING: { + return board_get_king_moves(board, piece_tile, pos_count); + } + + default: { + return NULL; + } + } + + return NULL; +} + + + /* Checks if a move is legal or not (and reason) on this board. @@ -520,7 +706,27 @@ MoveLegality board_is_move_legal(const Board* board, Move move, Color color_play } case QUEEN: { - return LEGAL; + unsigned int pos_count = 0; + Position* legal_positions = board_get_queen_moves( + board, + board_get_tile(board, move.origin), + &pos_count + ); + + for (unsigned int i = 0; i < pos_count; i++) { + if (legal_positions[i].row == move.target.row && + legal_positions[i].column == move.target.column + ) + { + // This position is allowed to be moved to + free(legal_positions); + return LEGAL; + } + } + + // Couldn't find such a position in legal placements + free(legal_positions); + return ILLINVALIDPIECEMOVEMENT; } case ROOK: { @@ -542,20 +748,81 @@ MoveLegality board_is_move_legal(const Board* board, Move move, Color color_play } } + // Couldn't find such a position in legal placements free(legal_positions); - return LEGAL; + return ILLINVALIDPIECEMOVEMENT; } case KNIGHT: { - return LEGAL; + unsigned int pos_count = 0; + Position* legal_positions = board_get_knight_moves( + board, + board_get_tile(board, move.origin), + &pos_count + ); + + for (unsigned int i = 0; i < pos_count; i++) { + if (legal_positions[i].row == move.target.row && + legal_positions[i].column == move.target.column + ) + { + // This position is allowed to be moved to + free(legal_positions); + return LEGAL; + } + } + + // Couldn't find such a position in legal placements + free(legal_positions); + return ILLINVALIDPIECEMOVEMENT; } case BISHOP: { - return LEGAL; + unsigned int pos_count = 0; + Position* legal_positions = board_get_bishop_moves( + board, + board_get_tile(board, move.origin), + &pos_count + ); + + for (unsigned int i = 0; i < pos_count; i++) { + if (legal_positions[i].row == move.target.row && + legal_positions[i].column == move.target.column + ) + { + // This position is allowed to be moved to + free(legal_positions); + return LEGAL; + } + } + + // Couldn't find such a position in legal placements + free(legal_positions); + return ILLINVALIDPIECEMOVEMENT; } case PAWN: { - return LEGAL; + unsigned int pos_count = 0; + Position* legal_positions = board_get_pawn_moves( + board, + board_get_tile(board, move.origin), + &pos_count + ); + + for (unsigned int i = 0; i < pos_count; i++) { + if (legal_positions[i].row == move.target.row && + legal_positions[i].column == move.target.column + ) + { + // This position is allowed to be moved to + free(legal_positions); + return LEGAL; + } + } + + // Couldn't find such a position in legal placements + free(legal_positions); + return ILLINVALIDPIECEMOVEMENT; } default: { diff --git a/src/board.h b/src/board.h index f9ba6f8..939e569 100644 --- a/src/board.h +++ b/src/board.h @@ -107,22 +107,50 @@ int board_is_tile_occupied(const Board* board, Position pos); void board_print(const Board* board, FILE* out); +typedef enum MoveMode { + MODESTRAIGHTS, + MODEDIAGONALS, + MODESTRAIGHTSDIAGONALS, + MODEKNIGHT +} MoveMode; + + /* -Returns all possible legal positions either in diagonal or in straight lines +Returns all possible legal positions in diagonal, in straight lines, in both as well as knight-type for specified tile. -If mode is 0 - returns positions in straight lines, 1 - in diagonals, 2 - in both +Returns NULL in case of an error */ -Position* board_get_moves(const Board* board, const Tile* piece_tile, int mode, unsigned int* pos_count); +Position* board_get_positions(const Board* board, const Tile* piece_tile, MoveMode mode, unsigned int* pos_count); -// Returns all possible legal king positions. Returns NULL if given tile does not contain a king piece +// Returns all possible legal king positions. Position* board_get_king_moves(const Board* board, const Tile* king_tile, unsigned int* pos_count); -// Returns all possible legal king positions. Returns NULL if given tile does not contain a rook piece +// Returns all possible legal rook positions. Position* board_get_rook_moves(const Board* board, const Tile* rook_tile, unsigned int* pos_count); +// Returns all possible legal queen positions. +Position* board_get_queen_moves(const Board* board, const Tile* queen_tile, unsigned int* pos_count); + + +// Returns all possible legal bishop positions. +Position* board_get_bishop_moves(const Board* board, const Tile* bishop_tile, unsigned int* pos_count); + + +// Returns all possible legal knight positions. +Position* board_get_knight_moves(const Board* board, const Tile* knight_tile, unsigned int* pos_count); + + +// Returns all possible legal pawn positions. +Position* board_get_pawn_moves(const Board* board, const Tile* pawn_tile, unsigned int* pos_count); + + +// Returns all possible legal positions for a piece standing on piece_tile. Returns NULL in case of an error +Position* board_get_positions_for(const Board* board, const Tile* piece_tile, unsigned int* pos_count); + + // Move legality status typedef enum MoveLegality { LEGAL,