///|
/// The castling right that a corner square belongs to.
fn castling_right_at(square : Square) -> (Color, Bool)? {
match square.index() {
0 => Some((White, false))
7 => Some((White, true))
56 => Some((Black, false))
63 => Some((Black, true))
_ => None
}
}
///|
fn set_piece(
pieces : FixedArray[UInt64],
color : Color,
kind : PieceKind,
square : Square,
) -> Unit {
let index = piece_index(color, kind)
pieces[index] = pieces[index] | (1UL << square.index())
}
///|
fn clear_piece(
pieces : FixedArray[UInt64],
color : Color,
kind : PieceKind,
square : Square,
) -> Unit {
let index = piece_index(color, kind)
pieces[index] = pieces[index] & (1UL << square.index()).lnot()
}
///|
fn revoke_right(
rights : CastlingRights,
color : Color,
kingside : Bool,
) -> CastlingRights {
if kingside {
rights.with_kingside(color, false)
} else {
rights.with_queenside(color, false)
}
}
///|
fn relocate_rook(
pieces : FixedArray[UInt64],
color : Color,
rank : Int32,
from_file : Int32,
to_file : Int32,
) -> Unit {
let index = piece_index(color, Rook)
let origin = Square::make(from_file, rank)
let destination = Square::make(to_file, rank)
pieces[index] = pieces[index] & (1UL << origin.index()).lnot()
pieces[index] = pieces[index] | (1UL << destination.index())
}
///|
/// Applies a move and returns the resulting position.
///
/// The move is applied as written, so this does not check king safety: a
/// move that leaves the mover in check still produces the position it
/// describes. The origin square must hold a piece, which is always true for
/// a move that came out of `pseudo_legal_moves`.
///
/// The step updates everything FEN records: the piece bitboards, a capture
/// on the target square, the pawn removed by an en passant capture, the
/// promoted piece, the rook of a castling, the castling rights lost by a
/// king or rook move or by the capture of a rook on its home square, the en
/// passant target after a double pawn push, the halfmove clock and the move
/// number.
pub fn Position::make_move(self : Position, played : Move) -> Position {
let mover = match self.piece_at(played.from) {
Some(piece) => piece
None => {
let message = StringBuilder()
message.write_string("no piece on ")
message.write_string(played.from.to_string())
message.write_string(" to move to ")
message.write_string(played.to.to_string())
abort(message.to_string())
}
}
let pieces = FixedArray::make(12, 0UL)
for index = 0; index < 12; index = index + 1 {
pieces[index] = self.pieces[index]
}
let captured = self.piece_at(played.to)
match captured {
Some(piece) => clear_piece(pieces, piece.color(), piece.kind(), played.to)
None => ()
}
if played.kind == EnPassant {
let behind = if mover.color() == White { -1 } else { 1 }
match played.to.offset(0, behind) {
Some(square) =>
clear_piece(pieces, mover.color().opposite(), Pawn, square)
None => ()
}
}
clear_piece(pieces, mover.color(), mover.kind(), played.from)
let placed = match played.promotion {
Some(kind) => kind
None => mover.kind()
}
set_piece(pieces, mover.color(), placed, played.to)
match played.kind {
CastleKingside =>
relocate_rook(pieces, mover.color(), played.from.rank(), 7, 5)
CastleQueenside =>
relocate_rook(pieces, mover.color(), played.from.rank(), 0, 3)
_ => ()
}
let mut castling = self.castling
match mover.kind() {
King =>
castling = castling
.with_kingside(mover.color(), false)
.with_queenside(mover.color(), false)
Rook =>
match castling_right_at(played.from) {
Some((color, kingside)) =>
castling = revoke_right(castling, color, kingside)
None => ()
}
_ => ()
}
match castling_right_at(played.to) {
Some((color, kingside)) =>
castling = revoke_right(castling, color, kingside)
None => ()
}
let en_passant = match played.kind {
DoublePawnPush =>
Some(
Square::make(
played.from.file(),
(played.from.rank() + played.to.rank()) / 2,
),
)
_ => None
}
let halfmove_clock = if mover.kind() == Pawn ||
captured != None ||
played.kind == EnPassant {
0
} else {
self.halfmove_clock + 1
}
let fullmove_number = if self.turn == Black {
self.fullmove_number + 1
} else {
self.fullmove_number
}
Position::new(
pieces,
self.turn.opposite(),
castling,
en_passant,
halfmove_clock,
fullmove_number,
)
}