///|
/// Whether a castling move is legal: the right must be held, both pieces must
/// be home, the path must be empty, and the king must not be in check, cross
/// an attacked square or land on one.
fn Position::castling_is_legal(
self : Position,
color : Color,
kingside : Bool,
) -> Bool {
if !self.castling_is_pseudo_legal(color, kingside) {
return false
}
let rank = if color == White { 0 } else { 7 }
let them = color.opposite()
if self.is_attacked(Square::make(4, rank), them) {
return false
}
let crossed : Array[Int32] = if kingside { [5] } else { [3] }
for file in crossed {
if self.is_attacked(Square::make(file, rank), them) {
return false
}
}
let destination = Square::make(if kingside { 6 } else { 2 }, rank)
!self.is_attacked(destination, them)
}
///|
/// Whether a pseudo-legal move leaves the mover's king safe.
fn Position::keeps_king_safe(self : Position, played : Move) -> Bool {
let mover = match self.piece_at(played.from) {
Some(piece) => piece
None => return false
}
match played.kind {
CastleKingside => self.castling_is_legal(mover.color(), true)
CastleQueenside => self.castling_is_legal(mover.color(), false)
_ => {
let after = self.make_move(played)
match after.king_square(mover.color()) {
Some(king) => !after.is_attacked(king, mover.color().opposite())
None => false
}
}
}
}
///|
/// The legal moves of the side to move.
///
/// A move is legal when it is pseudo-legal and, once played, the mover's own
/// king is not attacked. Castling additionally requires that the king is not
/// in check, does not cross an attacked square and does not land on one. An
/// en passant capture is checked after the captured pawn has been removed,
/// so a capture that opens a rank to the king is rejected.
pub fn Position::legal_moves(self : Position) -> Array[Move] {
let out : Array[Move] = []
for candidate in self.pseudo_legal_moves() {
if self.keeps_king_safe(candidate) {
out.push(candidate)
}
}
out
}
///|
/// Whether a move is legal.
///
/// The move must be one that `pseudo_legal_moves` generates, with the same
/// origin, target, kind and promotion piece. A move built by hand with the
/// right origin and target but the wrong kind, such as a castle written as a
/// plain king move, is not recognised. Use `legal_moves` to obtain moves
/// that satisfy this contract.
pub fn Position::is_legal(self : Position, played : Move) -> Bool {
for candidate in self.pseudo_legal_moves() {
if candidate == played {
return self.keeps_king_safe(candidate)
}
}
false
}