///|
/// A complete chess position.
///
/// The board is stored as twelve bitboards, one per colour and piece kind.
/// Inside each bitboard, bit `i` set means `Square(i)` holds that piece. The
/// remaining fields carry the state that FEN records: the side to move, the
/// castling rights, the en passant target, the halfmove clock and the move
/// number.
pub(all) struct Position {
priv pieces : FixedArray[UInt64]
priv turn : Color
priv castling : CastlingRights
priv ep_square : Square?
priv halfmove_clock : Int32
priv fullmove_number : Int32
} derive(Eq, @debug.Debug)
///|
pub extend Position with Eq::{equal, not_equal}
///|
pub extend Position with @debug.Debug::{to_repr}
///|
/// The index of the bitboard that stores one colour and piece kind.
fn piece_index(color : Color, kind : PieceKind) -> Int32 {
color.index() * 6 + kind.index()
}
///|
/// The FEN document of the standard starting position.
pub fn start_fen() -> String {
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
}
///|
/// Builds a position from raw fields.
///
/// `pieces` must hold twelve bitboards in colour-major order: the six white
/// kinds first, then the six black kinds, each ordered pawn, knight, bishop,
/// rook, queen, king.
pub fn Position::new(
pieces : FixedArray[UInt64],
turn : Color,
castling : CastlingRights,
ep_square : Square?,
halfmove_clock : Int32,
fullmove_number : Int32,
) -> Position {
{ pieces, turn, castling, ep_square, halfmove_clock, fullmove_number, }
}
///|
/// A position with an empty board, white to move and no castling rights.
pub fn Position::empty() -> Position {
Position::new(
FixedArray::make(12, 0UL),
White,
CastlingRights::none(),
None,
0,
1,
)
}
///|
/// The standard starting position.
pub fn Position::startpos() -> Position {
let pieces = FixedArray::make(12, 0UL)
let white_back = 0x0000000000000081UL
let black_back = 0x8100000000000000UL
pieces[piece_index(White, Rook)] = white_back
pieces[piece_index(White, Knight)] = 0x0000000000000042UL
pieces[piece_index(White, Bishop)] = 0x0000000000000024UL
pieces[piece_index(White, Queen)] = 0x0000000000000008UL
pieces[piece_index(White, King)] = 0x0000000000000010UL
pieces[piece_index(White, Pawn)] = 0x000000000000FF00UL
pieces[piece_index(Black, Rook)] = black_back
pieces[piece_index(Black, Knight)] = 0x4200000000000000UL
pieces[piece_index(Black, Bishop)] = 0x2400000000000000UL
pieces[piece_index(Black, Queen)] = 0x0800000000000000UL
pieces[piece_index(Black, King)] = 0x1000000000000000UL
pieces[piece_index(Black, Pawn)] = 0x00FF000000000000UL
Position::new(pieces, White, CastlingRights::all(), None, 0, 1)
}
///|
/// The side that is to move.
pub fn Position::turn(self : Position) -> Color {
self.turn
}
///|
/// The castling rights that are still available.
pub fn Position::castling(self : Position) -> CastlingRights {
self.castling
}
///|
/// The en passant target square, when the previous move was a double pawn
/// push.
pub fn Position::en_passant(self : Position) -> Square? {
self.ep_square
}
///|
/// The number of halfmoves since the last pawn move or capture.
pub fn Position::halfmove_clock(self : Position) -> Int32 {
self.halfmove_clock
}
///|
/// The number of the move that is about to be played, starting at `1`.
pub fn Position::fullmove_number(self : Position) -> Int32 {
self.fullmove_number
}
///|
/// The squares occupied by one colour and piece kind.
pub fn Position::pieces_of(
self : Position,
color : Color,
kind : PieceKind,
) -> Bitboard {
Bitboard::from_u64(self.pieces[piece_index(color, kind)])
}
///|
/// The squares occupied by one colour.
pub fn Position::color_occupancy(self : Position, color : Color) -> Bitboard {
let mut bits = 0UL
for kind_index = 0; kind_index < 6; kind_index = kind_index + 1 {
bits = bits | self.pieces[color.index() * 6 + kind_index]
}
Bitboard::from_u64(bits)
}
///|
/// Every occupied square.
pub fn Position::occupancy(self : Position) -> Bitboard {
self.color_occupancy(White).union(self.color_occupancy(Black))
}
///|
/// The piece on a square, or `None` when the square is empty.
pub fn Position::piece_at(self : Position, square : Square) -> Piece? {
let mask = 1UL << square.index()
for index = 0; index < 12; index = index + 1 {
if (self.pieces[index] & mask) != 0UL {
let color = if index < 6 { White } else { Black }
let kind = PieceKind::from_index(index % 6).unwrap()
return Some(Piece::new(color, kind))
}
}
None
}
///|
/// Whether the square holds no piece.
pub fn Position::is_empty_square(self : Position, square : Square) -> Bool {
match self.piece_at(square) {
Some(_) => false
None => true
}
}
///|
/// The square of one colour's king, or `None` when the king is missing.
pub fn Position::king_square(self : Position, color : Color) -> Square? {
self.pieces_of(color, King).first()
}
///|
/// The number of pieces on the board.
pub fn Position::piece_count(self : Position) -> Int32 {
self.occupancy().count()
}