From d4c34d50c9cc5eaff96041933238828b27ceb357 Mon Sep 17 00:00:00 2001 From: Brendan Coles Date: Fri, 30 Oct 2020 06:39:44 +0000 Subject: [PATCH] LibChess: Forbid King moving into check by a pawn on the home rank A player can no longer move the King piece into any position on their home rank if the move would place the King in check. A player can also no longer ignore a check position when in check by a pawn on their home rank. The player must now resolve the check during their move. --- Libraries/LibChess/Chess.cpp | 84 +++++++++++++++++++++++++----------- Libraries/LibChess/Chess.h | 1 + 2 files changed, 60 insertions(+), 25 deletions(-) diff --git a/Libraries/LibChess/Chess.cpp b/Libraries/LibChess/Chess.cpp index f452156b8ff..8c65f34b42c 100644 --- a/Libraries/LibChess/Chess.cpp +++ b/Libraries/LibChess/Chess.cpp @@ -176,6 +176,35 @@ Piece Board::set_piece(const Square& square, const Piece& piece) return m_board[square.rank][square.file] = piece; } +bool Board::is_legal_promotion(const Move& move, Colour colour) const +{ + auto piece = get_piece(move.from); + + if (move.promote_to == Type::Pawn || move.promote_to == Type::King) { + // attempted promotion to invalid piece + return false; + } + + if (piece.type != Type::Pawn && move.promote_to != Type::None) { + // attempted promotion from invalid piece + return false; + } + + unsigned promotion_rank = (colour == Colour::White) ? 7 : 0; + + if (move.to.rank != promotion_rank && move.promote_to != Type::None) { + // attempted promotion from invalid rank + return false; + } + + if (piece.type == Type::Pawn && move.to.rank == promotion_rank && move.promote_to == Type::None) { + // attempted move to promotion rank without promoting + return false; + } + + return true; +} + bool Board::is_legal(const Move& move, Colour colour) const { if (colour == Colour::None) @@ -184,6 +213,9 @@ bool Board::is_legal(const Move& move, Colour colour) const if (!is_legal_no_check(move, colour)) return false; + if (!is_legal_promotion(move, colour)) + return false; + Board clone = *this; clone.apply_illegal_move(move, colour); if (clone.in_check(colour)) @@ -218,49 +250,47 @@ bool Board::is_legal(const Move& move, Colour colour) const bool Board::is_legal_no_check(const Move& move, Colour colour) const { auto piece = get_piece(move.from); + if (piece.colour != colour) + // attempted move of opponent's piece return false; if (move.to.rank > 7 || move.to.file > 7) - return false; - - if (piece.type != Type::Pawn && move.promote_to != Type::None) - return false; - - if (move.promote_to == Type::Pawn || move.promote_to == Type::King) + // attempted move outside of board return false; if (piece.type == Type::Pawn) { int dir = (colour == Colour::White) ? +1 : -1; unsigned start_rank = (colour == Colour::White) ? 1 : 6; - unsigned other_start_rank = (colour == Colour::White) ? 6 : 1; - unsigned en_passant_rank = (colour == Colour::White) ? 4 : 3; - unsigned promotion_rank = (colour == Colour::White) ? 7 : 0; - if (move.to.rank == promotion_rank) { - if (move.promote_to == Type::Pawn || move.promote_to == Type::King || move.promote_to == Type::None) - return false; - } else if (move.promote_to != Type::None) { - return false; + if (move.from.rank == start_rank && move.to.rank == move.from.rank + (2 * dir) && move.to.file == move.from.file + && get_piece(move.to).type == Type::None && get_piece({ move.from.rank + dir, move.from.file }).type == Type::None) { + // 2 square pawn move from initial position. + return true; } - if (move.to.rank == move.from.rank + dir && move.to.file == move.from.file && get_piece(move.to).type == Type::None) { + if (move.to.rank != move.from.rank + dir) + // attempted backwards or sideways move + return false; + + if (move.to.file == move.from.file && get_piece(move.to).type == Type::None) { // Regular pawn move. return true; - } else if (move.to.rank == move.from.rank + dir && (move.to.file == move.from.file + 1 || move.to.file == move.from.file - 1)) { + } + + if (move.to.file == move.from.file + 1 || move.to.file == move.from.file - 1) { + unsigned other_start_rank = (colour == Colour::White) ? 6 : 1; + unsigned en_passant_rank = (colour == Colour::White) ? 4 : 3; Move en_passant_last_move = { { other_start_rank, move.to.file }, { en_passant_rank, move.to.file } }; if (get_piece(move.to).colour == opposing_colour(colour)) { // Pawn capture. return true; - } else if (m_last_move.has_value() && move.from.rank == en_passant_rank && m_last_move.value() == en_passant_last_move + } + if (m_last_move.has_value() && move.from.rank == en_passant_rank && m_last_move.value() == en_passant_last_move && get_piece(en_passant_last_move.to) == Piece(opposing_colour(colour), Type::Pawn)) { // En passant. return true; } - } else if (move.from.rank == start_rank && move.to.rank == move.from.rank + (2 * dir) && move.to.file == move.from.file - && get_piece(move.to).type == Type::None && get_piece({ move.from.rank + dir, move.from.file }).type == Type::None) { - // 2 square pawn move from initial position. - return true; } return false; @@ -614,15 +644,19 @@ bool Board::is_promotion_move(const Move& move, Colour colour) const if (colour == Colour::None) colour = turn(); + unsigned promotion_rank = (colour == Colour::White) ? 7 : 0; + if (move.to.rank != promotion_rank) + return false; + + if (get_piece(move.from).type != Type::Pawn) + return false; + Move queen_move = move; queen_move.promote_to = Type::Queen; if (!is_legal(queen_move, colour)) return false; - if (get_piece(move.from).type == Type::Pawn && ((colour == Colour::Black && move.to.rank == 0) || (colour == Colour::White && move.to.rank == 7))) - return true; - - return false; + return true; } bool Board::operator==(const Board& other) const diff --git a/Libraries/LibChess/Chess.h b/Libraries/LibChess/Chess.h index 3a8b0f0e8d6..d10c6ff758a 100644 --- a/Libraries/LibChess/Chess.h +++ b/Libraries/LibChess/Chess.h @@ -159,6 +159,7 @@ public: private: bool is_legal_no_check(const Move&, Colour colour) const; + bool is_legal_promotion(const Move&, Colour colour) const; bool apply_illegal_move(const Move&, Colour colour); Piece m_board[8][8];