Skip to content

Commit

Permalink
chore: Refactor ongoing implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
kirillbobyrev committed Jun 16, 2024
1 parent 8611cb4 commit b4e7613
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 68 deletions.
7 changes: 3 additions & 4 deletions src/chess/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub const BOARD_SIZE: u8 = BOARD_WIDTH * BOARD_WIDTH;
/// Neural Network evaluators that would be able assess their potential without
/// evaluating post-states.
// TODO: Implement bijection for a move and a numeric index.
// TODO: Switch this to an enum representation (regular, en passant, castling)?
// TODO: Switch this to an enum representation (regular, en passant, castling) or add flag.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Move {
pub(super) from: Square,
Expand Down Expand Up @@ -395,14 +395,13 @@ impl fmt::Display for Player {
}
}

/// Standard [chess pieces].
/// Standard [chess pieces] types for one player.
///
/// [chess pieces]: https://en.wikipedia.org/wiki/Chess_piece
// TODO: Assert that both PieceKind and Option(PieceKind) take 1 byte.
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
pub enum PieceKind {
King = 1,
King,
Queen,
Rook,
Bishop,
Expand Down
38 changes: 6 additions & 32 deletions src/chess/generated.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,6 @@
use crate::chess::bitboard::Bitboard;
use crate::chess::zobrist::Key;
use crate::chess::core::BOARD_SIZE;

const BLACK_TO_MOVE: Key = 0;

const WHITE_CAN_CASTLE_KINGSIDE: Key = 1;
const BLACK_CAN_CASTLE_KINGSIDE: Key = 1;
const WHITE_CAN_CASTLE_QUEENSIDE: Key = 1;
const BLACK_CAN_CASTLE_QUEENSIDE: Key = 1;

const EN_PASSANT_FILES: [Key; 8] = include!(concat!(env!("OUT_DIR"), "/en_passant_zobrist_keys"));

const WHITE_KING: [Key; 64] = include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
const WHITE_QUEEN: [Key; 64] = include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
const WHITE_ROOK: [Key; 64] = include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
const WHITE_BISHOP: [Key; 64] = include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
const WHITE_KNIGHT: [Key; 64] = include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
const WHITE_PAWN: [Key; 64] = include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));

const BLACK_KING: [Key; 64] = include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
const BLACK_QUEEN: [Key; 64] = include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
const BLACK_ROOK: [Key; 64] = include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
const BLACK_BISHOP: [Key; 64] = include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
const BLACK_KNIGHT: [Key; 64] = include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
const BLACK_PAWN: [Key; 64] = include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));

// Generated in build.rs.
// TODO: Document PEXT bitboards.
const BISHOP_ATTACKS_COUNT: usize = 5248;
Expand Down Expand Up @@ -57,14 +33,12 @@ pub(super) const ROOK_ATTACK_OFFSETS: [usize; BOARD_SIZE as usize] = include!(co

pub(super) const RAYS: [Bitboard; BOARD_SIZE as usize * BOARD_SIZE as usize] =
include!(concat!(env!("CARGO_MANIFEST_DIR"), "/generated/rays.rs"));
pub(super) const BISHOP_RAYS: [Bitboard; BOARD_SIZE as usize * BOARD_SIZE as usize] = include!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/generated/bishop_rays.rs"
));
pub(super) const ROOK_RAYS: [Bitboard; BOARD_SIZE as usize * BOARD_SIZE as usize] = include!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/generated/rook_rays.rs"
));
pub(super) const BISHOP_RAYS: [Bitboard; BOARD_SIZE as usize * BOARD_SIZE as usize] = include!(
concat!(env!("CARGO_MANIFEST_DIR"), "/generated/bishop_rays.rs")
);
pub(super) const ROOK_RAYS: [Bitboard; BOARD_SIZE as usize * BOARD_SIZE as usize] = include!(
concat!(env!("CARGO_MANIFEST_DIR"), "/generated/rook_rays.rs")
);

pub(super) const KNIGHT_ATTACKS: [Bitboard; BOARD_SIZE as usize] = include!(concat!(
env!("CARGO_MANIFEST_DIR"),
Expand Down
4 changes: 3 additions & 1 deletion src/chess/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ pub mod attacks;
pub mod bitboard;
pub mod core;
pub mod position;
pub mod zobrist;
pub mod transposition;

mod zobrist_keys;
mod generated;
62 changes: 33 additions & 29 deletions src/chess/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,11 @@ use anyhow::{bail, Context};
use crate::chess::attacks;
use crate::chess::bitboard::{Bitboard, Board, Pieces};
use crate::chess::core::{
CastleRights,
File,
Move,
MoveList,
Piece,
Player,
Promotion,
Rank,
Square,
BOARD_WIDTH,
CastleRights, File, Move, MoveList, Piece, Player, Promotion, Rank, Square, BOARD_WIDTH,
};
use crate::chess::transposition;
use crate::chess::zobrist_keys;


/// State of the chess game: board, half-move counters and castling rights,
/// etc. It has 1:1 relationship with [Forsyth-Edwards Notation] (FEN).
Expand Down Expand Up @@ -104,7 +98,7 @@ impl Position {
self.side_to_move
}

pub(crate) fn they(&self) -> Player {
pub(crate) fn them(&self) -> Player {
self.us().opponent()
}

Expand All @@ -117,7 +111,7 @@ impl Position {
}

fn occupied_squares(&self) -> Bitboard {
self.occupancy(self.us()) | self.occupancy(self.they())
self.occupancy(self.us()) | self.occupancy(self.them())
}

/// Parses board from Forsyth-Edwards Notation and checks its correctness.
Expand Down Expand Up @@ -282,12 +276,12 @@ impl Position {
}

pub(super) fn attack_info(&self) -> attacks::AttackInfo {
let (us, they) = (self.us(), self.they());
let (our_pieces, their_pieces) = (self.pieces(us), self.pieces(they));
let (us, them) = (self.us(), self.them());
let (our_pieces, their_pieces) = (self.pieces(us), self.pieces(them));
let king: Square = our_pieces.king.as_square();
let (our_occupancy, their_occupancy) = (our_pieces.all(), their_pieces.all());
let occupancy = our_occupancy | their_occupancy;
attacks::AttackInfo::new(they, their_pieces, king, our_occupancy, occupancy)
attacks::AttackInfo::new(them, their_pieces, king, our_occupancy, occupancy)
}

/// Calculates a list of legal moves (i.e. the moves that do not leave our
Expand Down Expand Up @@ -323,14 +317,14 @@ impl Position {
// debug_assert!(validate(&self).is_ok(), "{}", self.fen());
// TODO: Try caching more e.g. all()s? Benchmark to confirm that this is an
// improvement.
let (us, they) = (self.us(), self.they());
let (our_pieces, their_pieces) = (self.pieces(us), self.pieces(they));
let (us, them) = (self.us(), self.them());
let (our_pieces, their_pieces) = (self.pieces(us), self.pieces(them));
let king: Square = our_pieces.king.as_square();
let (our_occupancy, their_occupancy) = (our_pieces.all(), their_pieces.all());
let occupied_squares = our_occupancy | their_occupancy;
let their_or_empty = !our_occupancy;
let attack_info =
attacks::AttackInfo::new(they, their_pieces, king, our_occupancy, occupied_squares);
attacks::AttackInfo::new(them, their_pieces, king, our_occupancy, occupied_squares);
// Moving the king to safety is always a valid move.
generate_king_moves(king, attack_info.safe_king_squares, &mut moves);
// If there are checks, the moves are restricted to resolving them.
Expand Down Expand Up @@ -388,7 +382,7 @@ impl Position {
generate_pawn_moves(
our_pieces.pawns,
us,
they,
them,
their_pieces,
their_occupancy,
their_or_empty,
Expand Down Expand Up @@ -420,7 +414,7 @@ impl Position {
pub fn make_move(&mut self, next_move: &Move) {

Check warning on line 414 in src/chess/position.rs

View workflow job for this annotation

GitHub Actions / Build docs

missing documentation for a method
debug_assert!(self.is_legal());
// TODO: debug_assert!(self.is_legal_move(move));
let (us, they) = (self.us(), self.they());
let (us, them) = (self.us(), self.them());
let our_backrank = Rank::backrank(us);
let (our_pieces, their_pieces) = match self.us() {
Player::White => (&mut self.board.white_pieces, &mut self.board.black_pieces),
Expand Down Expand Up @@ -448,7 +442,7 @@ impl Position {
if their_pieces.all().contains(next_move.to) {
// Capturing a piece resets the clock.
self.halfmove_clock = 0;
match (they, next_move.to) {
match (them, next_move.to) {
(Player::White, Square::H1) => self.castling.remove(CastleRights::WHITE_SHORT),
(Player::White, Square::A1) => self.castling.remove(CastleRights::WHITE_LONG),
(Player::Black, Square::H8) => self.castling.remove(CastleRights::BLACK_SHORT),
Expand Down Expand Up @@ -546,7 +540,8 @@ impl Position {
self.in_check() && self.generate_moves().is_empty()
}

#[must_use] pub fn is_stalemate(&self) -> bool {
#[must_use]
pub fn is_stalemate(&self) -> bool {

Check warning on line 544 in src/chess/position.rs

View workflow job for this annotation

GitHub Actions / Build docs

missing documentation for a method
!self.in_check() && self.generate_moves().is_empty()
}

Expand All @@ -559,6 +554,15 @@ impl Position {
pub fn gives_check(&self, next_move: &Move) -> bool {

Check warning on line 554 in src/chess/position.rs

View workflow job for this annotation

GitHub Actions / Test Suite (macos-latest, stable, true)

unused variable: `next_move`

Check warning on line 554 in src/chess/position.rs

View workflow job for this annotation

GitHub Actions / Test Suite (macos-latest, nightly, true)

unused variable: `next_move`

Check warning on line 554 in src/chess/position.rs

View workflow job for this annotation

GitHub Actions / Test Suite (ubuntu-latest, 1.78.0, false)

unused variable: `next_move`

Check warning on line 554 in src/chess/position.rs

View workflow job for this annotation

GitHub Actions / Test Suite (ubuntu-latest, nightly, true)

unused variable: `next_move`

Check warning on line 554 in src/chess/position.rs

View workflow job for this annotation

GitHub Actions / Test Suite (ubuntu-latest, stable, false)

unused variable: `next_move`

Check warning on line 554 in src/chess/position.rs

View workflow job for this annotation

GitHub Actions / Test Suite (windows-latest, stable, true)

unused variable: `next_move`

Check warning on line 554 in src/chess/position.rs

View workflow job for this annotation

GitHub Actions / Test Suite (windows-latest, nightly, true)

unused variable: `next_move`
todo!()
}

pub fn compute_hash(&self) -> transposition::Key {
let mut key = 0;
if self.side_to_move == Player::Black {
key ^= zobrist_keys::BLACK_TO_MOVE;
}

key
}
}

impl TryFrom<&str> for Position {
Expand Down Expand Up @@ -694,9 +698,9 @@ fn validate(position: &Position) -> anyhow::Result<()> {
// A pawn that was just pushed by our opponent should be in front of
// en_passant_square.
let pushed_pawn = en_passant_square
.shift(position.they().pawn_push_direction())
.shift(position.them().pawn_push_direction())
.unwrap();
if !position.pieces(position.they()).pawns.contains(pushed_pawn) {
if !position.pieces(position.them()).pawns.contains(pushed_pawn) {
bail!("en passant square is not beyond pushed pawn")
}
// If en-passant was played and there's a check, doubly pushed pawn
Expand All @@ -721,8 +725,8 @@ fn validate(position: &Position) -> anyhow::Result<()> {
}
}
// Doubly pushed pawn can not block a diagonal check.
for attacker in (position.pieces(position.they()).queens
| position.pieces(position.they()).bishops)
for attacker in (position.pieces(position.them()).queens
| position.pieces(position.them()).bishops)
.iter()
{
let xray = attacks::bishop_ray(attacker, king);
Expand Down Expand Up @@ -812,7 +816,7 @@ fn generate_bishop_moves(
fn generate_pawn_moves(
pawns: Bitboard,
us: Player,
they: Player,
them: Player,
their_pieces: &Pieces,
their_occupancy: Bitboard,
their_or_empty: Bitboard,
Expand Down Expand Up @@ -848,9 +852,9 @@ fn generate_pawn_moves(
}
// Generate en passant moves.
if let Some(en_passant_square) = en_passant_square {
let en_passant_pawn = en_passant_square.shift(they.pawn_push_direction()).unwrap();
let en_passant_pawn = en_passant_square.shift(them.pawn_push_direction()).unwrap();
// Check if capturing en passant resolves the check.
let candidate_pawns = attacks::pawn_attacks(en_passant_square, they) & pawns;
let candidate_pawns = attacks::pawn_attacks(en_passant_square, them) & pawns;
if checkers.contains(en_passant_pawn) {
for our_pawn in candidate_pawns.iter() {
if pins.contains(our_pawn) {
Expand Down
1 change: 1 addition & 0 deletions src/chess/zobrist.rs → src/chess/transposition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! [Transposition Table](https://www.chessprogramming.org/Transposition_Table

use super::position::Position;
use core::hash;

Check warning on line 6 in src/chess/transposition.rs

View workflow job for this annotation

GitHub Actions / Test Suite (macos-latest, stable, true)

unused import: `core::hash`

Check warning on line 6 in src/chess/transposition.rs

View workflow job for this annotation

GitHub Actions / Test Suite (macos-latest, nightly, true)

unused import: `core::hash`

Check warning on line 6 in src/chess/transposition.rs

View workflow job for this annotation

GitHub Actions / Test Suite (ubuntu-latest, 1.78.0, false)

unused import: `core::hash`

Check warning on line 6 in src/chess/transposition.rs

View workflow job for this annotation

GitHub Actions / Test Suite (ubuntu-latest, nightly, true)

unused import: `core::hash`

Check warning on line 6 in src/chess/transposition.rs

View workflow job for this annotation

GitHub Actions / Test Suite (ubuntu-latest, stable, false)

unused import: `core::hash`

Check warning on line 6 in src/chess/transposition.rs

View workflow job for this annotation

GitHub Actions / Test Suite (windows-latest, stable, true)

unused import: `core::hash`

Check warning on line 6 in src/chess/transposition.rs

View workflow job for this annotation

GitHub Actions / Test Suite (windows-latest, nightly, true)

unused import: `core::hash`
use std::hash::{Hash, Hasher};

pub type Key = u64;
Expand Down
37 changes: 37 additions & 0 deletions src/chess/zobrist_keys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use crate::chess::transposition::Key;

pub(super) const BLACK_TO_MOVE: Key = 0x9e06bad39d761293;

pub(super) const WHITE_CAN_CASTLE_KINGSIDE: Key = 0xf05ac573dd61d323;
pub(super) const BLACK_CAN_CASTLE_KINGSIDE: Key = 0x680988787a43d289;
pub(super) const WHITE_CAN_CASTLE_QUEENSIDE: Key = 0x41d8b55ba5feb78b;
pub(super) const BLACK_CAN_CASTLE_QUEENSIDE: Key = 0x2f941f8dfd3e3d1f;

pub(super) const EN_PASSANT_FILES: [Key; 8] =
include!(concat!(env!("OUT_DIR"), "/en_passant_zobrist_keys"));

pub(super) const WHITE_KING: [Key; 64] =
include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
pub(super) const WHITE_QUEEN: [Key; 64] =
include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
pub(super) const WHITE_ROOK: [Key; 64] =
include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
pub(super) const WHITE_BISHOP: [Key; 64] =
include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
pub(super) const WHITE_KNIGHT: [Key; 64] =
include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
pub(super) const WHITE_PAWN: [Key; 64] =
include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));

pub(super) const BLACK_KING: [Key; 64] =
include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
pub(super) const BLACK_QUEEN: [Key; 64] =
include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
pub(super) const BLACK_ROOK: [Key; 64] =
include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
pub(super) const BLACK_BISHOP: [Key; 64] =
include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
pub(super) const BLACK_KNIGHT: [Key; 64] =
include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
pub(super) const BLACK_PAWN: [Key; 64] =
include!(concat!(env!("OUT_DIR"), "/white_king_zobrist_keys"));
2 changes: 1 addition & 1 deletion src/evaluation/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ fn piece_value(pieces: &crate::chess::bitboard::Pieces) -> i32 {
}

pub(crate) fn material_advantage(position: &Position) -> Score {
let (us, them) = (position.us(), position.they());
let (us, them) = (position.us(), position.them());
let (our_pieces, their_pieces) = (position.pieces(us), position.pieces(them));
let advantage = piece_value(our_pieces) - piece_value(their_pieces);
Score::from(advantage)
Expand Down
2 changes: 1 addition & 1 deletion tools/src/bin/extract_lc0_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ fn extract_training_samples(archive: impl BufRead) -> io::Result<Vec<V6TrainingD
Ok(samples)
}

// TODO: Flip the planes.
// TODO: Flip the planes since lc0 does the bit flipping for whatever reason.
fn extract_planes(sample: &V6TrainingData) -> Vec<u64> {
vec![
// Our pieces.
Expand Down

0 comments on commit b4e7613

Please sign in to comment.