diff --git a/Cargo.lock b/Cargo.lock index 619a74b..f20454a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "anstream" version = "0.6.15" @@ -51,6 +66,27 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -63,6 +99,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chess" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ed299b171ec34f372945ad6726f7bc1d2afd5f59fb8380f64f48e2bab2f0ec8" +dependencies = [ + "arrayvec", + "failure", + "nodrop", + "rand 0.7.3", +] + [[package]] name = "clap" version = "4.5.18" @@ -94,7 +142,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -109,6 +157,28 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "failure" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" +dependencies = [ + "backtrace", + "failure_derive", +] + +[[package]] +name = "failure_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -120,6 +190,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" + [[package]] name = "heck" version = "0.5.0" @@ -138,6 +214,36 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "object" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +dependencies = [ + "memchr", +] + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -165,6 +271,18 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + [[package]] name = "rand" version = "0.8.5" @@ -172,8 +290,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] @@ -183,9 +311,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", ] +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + [[package]] name = "rand_core" version = "0.6.4" @@ -195,12 +329,37 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + [[package]] name = "shard" version = "0.1.0" dependencies = [ + "chess", "clap", - "rand", + "rand 0.8.5", ] [[package]] @@ -209,6 +368,17 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.77" @@ -220,12 +390,30 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + [[package]] name = "unicode-ident" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "utf8parse" version = "0.2.2" @@ -329,5 +517,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] diff --git a/Cargo.toml b/Cargo.toml index 2cb47b0..24a45af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] +chess = "3.2.0" clap = { version = "4.5.18", features = ["derive"] } rand = "0.8.5" diff --git a/fuzzy_testing/main.py b/fuzzy_testing/main.py deleted file mode 100644 index 545e217..0000000 --- a/fuzzy_testing/main.py +++ /dev/null @@ -1,62 +0,0 @@ -import chess -import subprocess -import random - -def generate_random_fen(): - """Generates a random legal board position (FEN string).""" - board = chess.Board() - move_count = random.randint(0, 100) # Random number of moves to play - - for _ in range(move_count): - if board.is_game_over(): - break - legal_moves = list(board.legal_moves) - board.push(random.choice(legal_moves)) # Play random legal move - - return board.fen() - -def get_legal_moves_from_executable(fen): - """Runs the executable with the given FEN string and gets legal moves.""" - # Modify this command to point to your executable's path - result = subprocess.run(['/home/adarsh/Coding/shard/target/release/shard', fen], capture_output=True, text=True) - - # Assuming the executable outputs moves as space-separated UCI strings - moves = result.stdout.strip().split() - return moves - -def run_fuzzy_test(): - """Performs fuzzy testing by comparing executable output with python-chess.""" - i = 0 - subprocess.run(["cargo", "build", "--release"]) - while True: - print(f"Test iteration {i + 1}") - - # Generate random FEN - random_fen = generate_random_fen() - print("Testing FEN:", random_fen) - - # Get legal moves using python-chess - board = chess.Board(random_fen) - python_moves = [move.uci() for move in board.legal_moves] - - # Get legal moves from executable - executable_moves = get_legal_moves_from_executable(random_fen) - - # Compare results - python_moves_set = set(python_moves) - executable_moves_set = set(executable_moves) - - if python_moves_set != executable_moves_set: - print(f"Discrepancy found in test {i + 1}!") - print(f"FEN: {random_fen}") - print(f"python-chess moves: {sorted(python_moves_set)}") - print(f"Executable moves: {sorted(executable_moves_set)}") - print(f"Moves in python-chess not present in executable: {sorted(python_moves_set - executable_moves_set)}") - print(f"Moves in executable not present in python-chess: {sorted(executable_moves_set - python_moves_set)}") - break - else: - print(f"Test {i + 1} passed!\n") - i += 1 - -if __name__ == "__main__": - run_fuzzy_test() diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..a874e22 --- /dev/null +++ b/run.sh @@ -0,0 +1,2 @@ +#!/bin/bash +RUSTFLAGS="-C target-cpu=native" cargo build --release diff --git a/src/definitions/bitboards.rs b/src/definitions/bitboards.rs deleted file mode 100644 index 3bfd3e4..0000000 --- a/src/definitions/bitboards.rs +++ /dev/null @@ -1,89 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use crate::definitions::square::Square; - -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] -pub struct BitBoard(pub u64); - -impl BitBoard { - // Create a new empty BitBoard - pub fn new(board_state: u64) -> Self { - BitBoard(board_state) - } - - // Set a bit at the given square index - pub fn set(&mut self, square: Square) { - self.0 |= 1 << square as u64; - } - - // Clear a bit at the given square index - pub fn clear(&mut self, square: Square) { - self.0 &= !(1 << square as u64); - } - - // Check if a bit is set at the given square index - pub fn get(&self, square: Square) -> bool { - (self.0 >> square as u64) & 1 == 1 - } - - pub fn pop(&mut self) -> Square { - // Find the least significant bit that is set - let square = self.0.trailing_zeros() as u8; - - // Unset the bit - self.0 &= !(1 << square); - - // Return the square - square.into() - } - - pub fn count(&self) -> u32 { - self.0.count_ones() - } -} - -impl Display for BitBoard { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let files = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; - let ranks = ['8', '7', '6', '5', '4', '3', '2', '1']; - - for (rank_index, rank) in ranks.iter().enumerate() { - write!(f, "{} ", rank)?; // Print rank label - for (file_index, _) in files.iter().enumerate() { - let square = (7 - rank_index) * 8 + file_index; - if self.get(square.into()) { - write!(f, "X ")?; - } else { - write!(f, ". ")?; - } - } - writeln!(f)?; - } - write!(f, " ")?; - for file in files.iter() { - write!(f, "{} ", file)?; - } - writeln!(f) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_bitboard() { - let mut board = BitBoard::default(); - board.set(Square::D2); - board.set(Square::D3); - board.set(Square::D4); - assert_eq!(board, BitBoard(134744064)); - assert_eq!(board.count(), 3); - assert_eq!(board.pop(), Square::D2); - assert_eq!(board.count(), 2); - assert_eq!(board.pop(), Square::D3); - assert_eq!(board.count(), 1); - assert_eq!(board.pop(), Square::D4); - assert_eq!(board.count(), 0); - } -} diff --git a/src/definitions/board.rs b/src/definitions/board.rs deleted file mode 100644 index df54b7c..0000000 --- a/src/definitions/board.rs +++ /dev/null @@ -1,1528 +0,0 @@ -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::time::{Duration, Instant}; - -use crate::definitions::bitboards::BitBoard; -use crate::definitions::castling::{CastleType, Castling}; -use crate::definitions::color::Color; -use crate::definitions::move_::Move; -use crate::definitions::piece::Piece; -use crate::definitions::rank::Rank; -use crate::definitions::square::Square; -use crate::definitions::undo::Undo; -use crate::utils::identification::*; -use crate::utils::offsets::*; -use crate::utils::zobrist::ZOBRIST_TABLE; - -use std::collections::HashSet; - -use super::piece; - -#[derive(Debug)] -pub struct Board { - /// Array of 64 squares - pub pieces: [Piece; 64], - /// Bitboards for each piece type: White, Black, Both - pub pawn_bitboards: [BitBoard; 3], - /// Squares of the kings for: White, Black - pub king_square: [Square; 2], - pub side_to_move: Color, - pub en_passant_square: Square, - pub fifty_move_counter: u32, - /// Half moves - pub ply: u32, - /// Half moves in the whole game - pub history_ply: u32, - /// 4 bits for each castling permission, as defines in [Castling] - pub castle_permission: u32, - /// Zobrist key - pub position_key: u64, - /// Number of pieces on the board for each piece type - pub piece_count: [u32; 13], - /// Total number of pieces on the board - pub piece_count_total: u32, - /// List of pieces on the board for each piece type, - /// e.g. to add knights to the board, - /// we can do pieces\[WN\]\[0\] = E1 then piece_list\[WN\]\[1\] = E4 - pub piece_list: [HashSet; 13], - /// Number of non-pawn pieces on the board. White, Black, Both - pub big_piece_count: [u32; 3], - /// Number of rooks and queens on the board. White, Black, Both - pub major_piece_count: [u32; 3], - /// Number of bishops and knights on the board. White, Black, Both - pub minor_piece_count: [u32; 3], - /// History of the game - pub history: Vec, - /// Material scores for White, Black, Both - pub material_scores: [u32; 3], -} - -#[rustfmt::skip] -macro_rules! impl_type_for_board { - ($type:ty) => { - impl From<$type> for Board { - fn from(fen: $type) -> Self { - let mut board = Board::empty(); - let parts: Vec<&str> = fen.split_whitespace().collect(); - - // Parse pieces placement - let mut square_index = 0; - for ch in parts[0].chars() { - match ch { - 'r' => { - board.pieces[flip_board(square_index)] = Piece::BR; - } - 'n' => { - board.pieces[flip_board(square_index)] = Piece::BN; - } - 'b' => { - board.pieces[flip_board(square_index)] = Piece::BB; - } - 'q' => { - board.pieces[flip_board(square_index)] = Piece::BQ; - } - 'k' => { - board.pieces[flip_board(square_index)] = Piece::BK; - } - 'p' => { - board.pieces[flip_board(square_index)] = Piece::BP; - } - 'R' => { - board.pieces[flip_board(square_index)] = Piece::WR; - } - 'N' => { - board.pieces[flip_board(square_index)] = Piece::WN; - } - 'B' => { - board.pieces[flip_board(square_index)] = Piece::WB; - } - 'Q' => { - board.pieces[flip_board(square_index)] = Piece::WQ; - } - 'K' => { - board.pieces[flip_board(square_index)] = Piece::WK; - } - 'P' => { - board.pieces[flip_board(square_index)] = Piece::WP; - } - '/' => continue, // Move to next rank - digit if digit.is_digit(10) => { - // Skip squares based on number - let empty_spaces = digit.to_digit(10).unwrap(); - square_index += empty_spaces as usize; - continue; - } - _ => {} - } - square_index += 1; - } - - // Parse side to move - assert!(parts[1] == "w" || parts[1] == "b"); - board.side_to_move = if parts[1] == "w" { - Color::White - } else { - Color::Black - }; - - // Parse castling rights - board.castle_permission = 0; - for character in parts[2].chars() { - match character { - 'K' => board.castle_permission |= Castling::WK as u32, - 'Q' => board.castle_permission |= Castling::WQ as u32, - 'k' => board.castle_permission |= Castling::BK as u32, - 'q' => board.castle_permission |= Castling::BQ as u32, - _ => {} - } - } - assert!(board.castle_permission <= 15); - - // Parse en passant square - board.en_passant_square = match parts[3] { - "-" => Square::None, - square => { - let file = match square.chars().nth(0).unwrap() { - 'a' => 0, 'b' => 1, 'c' => 2, 'd' => 3, 'e' => 4, 'f' => 5, 'g' => 6, 'h' => 7, - _ => 0 - }; - let rank = square.chars().nth(1).unwrap().to_digit(10).unwrap() - 1; - Square::from((rank * 8 + file) as u8) - } - }; - - // Parse halfmove clock and fullmove number - board.fifty_move_counter = parts.get(4) - .and_then(|s| s.parse::().ok()) // Try parsing; return None on failure - .unwrap_or(0); // Default to 0 if parsing fails - board.ply = parts.get(5) - .and_then(|s| s.parse::().ok()) - .unwrap_or(0) * 2; // Default to 0 if parsing fails - board.history_ply = board.ply; // FIXME: This might be wrong - - board.position_key = board.get_hash(); - board.update_piece_metadata(); - board - } - } - }; -} - -fn flip_board(square_index: usize) -> usize { - let rank = square_index / 8; - let file = square_index % 8; - ((7 - rank) * 8) + (file) // Flip the file by subtracting from 7 -} - -impl_type_for_board!(&str); -impl_type_for_board!(String); - -impl Default for Board { - fn default() -> Self { - let mut pieces = [Piece::None; 64]; - - pieces[0] = Piece::WR; - pieces[1] = Piece::WN; - pieces[2] = Piece::WB; - pieces[3] = Piece::WQ; - pieces[4] = Piece::WK; - pieces[5] = Piece::WB; - pieces[6] = Piece::WN; - pieces[7] = Piece::WR; - for piece in pieces.iter_mut().take(16).skip(8) { - *piece = Piece::WP; // Black Pawns - } - - pieces[56] = Piece::BR; - pieces[57] = Piece::BN; - pieces[58] = Piece::BB; - pieces[59] = Piece::BQ; - pieces[60] = Piece::BK; - pieces[61] = Piece::BB; - pieces[62] = Piece::BN; - pieces[63] = Piece::BR; - for piece in pieces.iter_mut().take(56).skip(48) { - *piece = Piece::BP; // White Pawns - } - - let mut temp = Board { - pieces, - pawn_bitboards: [BitBoard(0); 3], // FIXME: Initialize pawn bitboards - king_square: [Square::E1, Square::E8], // Standard starting positions for kings - side_to_move: Color::White, // White to move - en_passant_square: Square::None, // No en passant at start - fifty_move_counter: 0, // Initial counter - ply: 0, // Initial ply - history_ply: 0, // Initial history ply - castle_permission: 0b1111, // Both sides can castle both ways - position_key: 0, // Initial Zobrist key - piece_count: [0; 13], // Update counts after initializing pieces - piece_count_total: 0, // Update total count after initializing pieces - piece_list: std::array::from_fn(|_| HashSet::new()), // Initialize empty piece lists - big_piece_count: [0; 3], // Big piece counts initialized to 0 - major_piece_count: [0; 3], // Major piece counts initialized to 0 - minor_piece_count: [0; 3], // Minor piece counts initialized to 0 - history: Vec::new(), // Empty history - material_scores: [0; 3], // Material scores initialized to 0 - }; - - temp.position_key = temp.get_hash(); - temp.update_piece_metadata(); // Update piece metadata - temp - } -} - -impl fmt::Display for Board { - #[rustfmt::skip] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // Print the board row by row - for rank in (0..8).rev() { // Print from rank 8 to 1 - write!(f, "{} ", rank + 1)?; // Print the rank number - for file in 0..8 { - let square_index = (rank) * 8 + file; - let piece = self.pieces[square_index]; - let piece_char = match piece { - Piece::WR => 'R', Piece::WN => 'N', Piece::WB => 'B', Piece::WQ => 'Q', Piece::WK => 'K', Piece::WP => 'P', - Piece::BR => 'r', Piece::BN => 'n', Piece::BB => 'b', Piece::BQ => 'q', Piece::BK => 'k', Piece::BP => 'p', - Piece::None => '.', - }; - write!(f, "{} ", piece_char)?; // Print the piece character - } - writeln!(f)?; // Move to the next line - } - writeln!(f, " a b c d e f g h")?; // Print the file labels - writeln!(f, "Side to move: {}", self.side_to_move)?; - writeln!(f, "En passant square: {}", self.en_passant_square)?; - writeln!(f, "Castle permission: {:#06b}", self.castle_permission)?; - writeln!(f, "Fifty move counter: {}", self.fifty_move_counter)?; - writeln!(f, "Ply: {}", self.ply)?; - writeln!(f, "Position key: {:016X}", self.position_key)?; - writeln!(f, "Piece count: {:?}", self.piece_count_total)?; - Ok(()) - } -} - -impl Hash for Board { - fn hash(&self, state: &mut H) { - state.write_u64(self.position_key); - } -} - -impl Board { - pub fn new(value: T) -> Self - where - T: Into, - { - let mut temp = value.into(); - temp.update_piece_metadata(); - temp - } - - pub fn empty() -> Self { - let mut temp = Board { - pieces: [Piece::None; 64], - pawn_bitboards: [BitBoard(0); 3], - king_square: [Square::None, Square::None], - side_to_move: Color::White, - en_passant_square: Square::None, - fifty_move_counter: 0, - ply: 0, - history_ply: 0, - castle_permission: 0b1111, - position_key: 0, - piece_count: [0; 13], - piece_count_total: 0, - piece_list: std::array::from_fn(|_| HashSet::new()), - big_piece_count: [0; 3], - major_piece_count: [0; 3], - minor_piece_count: [0; 3], - history: Vec::new(), - material_scores: [0; 3], - }; - - temp.position_key = temp.get_hash(); - temp - } - - fn get_hash(&self) -> u64 { - let mut hash = 0; - - for square_index in 0..64 { - let piece = self.pieces[square_index]; - if piece != Piece::None { - hash ^= ZOBRIST_TABLE[piece as usize][square_index]; - } - } - - if self.side_to_move == Color::Black { - hash ^= ZOBRIST_TABLE[12][0]; - } - - if self.en_passant_square != Square::None { - hash ^= ZOBRIST_TABLE[12][self.en_passant_square as usize]; - } - - hash ^= ZOBRIST_TABLE[13][self.castle_permission as usize]; - - hash - } - - fn update_hash_given_piece(&mut self, piece: Piece, square: Square) { - self.position_key ^= ZOBRIST_TABLE[piece as usize][square as usize]; - } - - fn update_hash_given_castle_permission(&mut self, castle_permission: u32) { - self.position_key ^= ZOBRIST_TABLE[13][castle_permission as usize]; - } - - fn update_hash_given_playing_side(&mut self, side: Color) { - // FIXME: should be side_to_move - self.position_key ^= ZOBRIST_TABLE[12][0]; - } - - fn update_hash_given_en_passant_square(&mut self, en_passant_square: Square) { - if en_passant_square != Square::None { - // FIXME: shouldnt happend - self.position_key ^= ZOBRIST_TABLE[12][en_passant_square as usize]; - } - } - - pub fn reset(&mut self) { - *self = Board::default(); - } - - pub fn update_piece_metadata(&mut self) { - self.piece_count = [0; 13]; - self.piece_count_total = 0; - self.big_piece_count = [0; 3]; - self.major_piece_count = [0; 3]; - self.minor_piece_count = [0; 3]; - self.material_scores = [0; 3]; - - for (index, piece) in self.pieces.iter().enumerate() { - if *piece == Piece::None { - continue; - } - - let square: Square = index.into(); - - if is_piece_big(piece) { - self.big_piece_count[piece_color(piece) as usize] += 1; - } else if is_piece_major(piece) { - self.major_piece_count[piece_color(piece) as usize] += 1; - } else if is_piece_minor(piece) { - self.minor_piece_count[piece_color(piece) as usize] += 1; - } - - self.material_scores[piece_color(piece) as usize] += piece_value(piece); - self.piece_list[*piece as usize].insert(square); - self.piece_count[*piece as usize] += 1; - self.piece_count_total += 1; - - match *piece { - Piece::WK => self.king_square[Color::White as usize] = square, - Piece::BK => self.king_square[Color::Black as usize] = square, - Piece::WP => { - self.pawn_bitboards[Color::White as usize].set(square); - self.pawn_bitboards[Color::Both as usize].set(square); - } - Piece::BP => { - self.pawn_bitboards[Color::Black as usize].set(square); - self.pawn_bitboards[Color::Both as usize].set(square); - } - _ => (), - } - } - } - - fn get_piece(&self, square: Square) -> Piece { - assert_ne!(square, Square::None); - self.pieces[square as usize] - } - - fn get_piece_at_index(&self, index: usize) -> Piece { - self.get_piece(index.into()) - } - - pub fn set_square(&mut self, square: Square, piece: Piece) { - assert_ne!(square, Square::None); - self.pieces[square as usize] = piece; - self.update_hash_given_piece(piece, square); - self.material_scores[piece_color(&piece) as usize] += piece_value(&piece); - - if is_piece_big(&piece) { - self.big_piece_count[piece_color(&piece) as usize] += 1; - } else if is_piece_major(&piece) { - self.major_piece_count[piece_color(&piece) as usize] += 1; - } else if is_piece_minor(&piece) { - self.minor_piece_count[piece_color(&piece) as usize] += 1; - } else { - self.pawn_bitboards[piece_color(&piece) as usize].set(square); - self.pawn_bitboards[Color::Both as usize].set(square); - } - - self.piece_list[piece as usize].insert(square); - self.piece_count[piece as usize] += 1; - self.piece_count_total += 1; - - if piece == Piece::WK { - self.king_square[Color::White as usize] = square; - } else if piece == Piece::BK { - self.king_square[Color::Black as usize] = square; - } - } - - fn set_square_at_index(&mut self, index: usize, piece: Piece) { - self.set_square(index.into(), piece); - } - - pub fn clear_square(&mut self, square: Square) { - let piece = self.get_piece(square); - if piece == Piece::None { - return; - } - - self.pieces[square as usize] = Piece::None; - self.update_hash_given_piece(piece, square); - self.material_scores[piece_color(&piece) as usize] -= piece_value(&piece); - - if is_piece_big(&piece) { - self.big_piece_count[piece_color(&piece) as usize] -= 1; - } else if is_piece_major(&piece) { - self.major_piece_count[piece_color(&piece) as usize] -= 1; - } else if is_piece_minor(&piece) { - self.minor_piece_count[piece_color(&piece) as usize] -= 1; - } else { - self.pawn_bitboards[piece_color(&piece) as usize].clear(square); - self.pawn_bitboards[Color::Both as usize].clear(square); - } - - self.piece_list[piece as usize].remove(&square); - self.piece_count[piece as usize] -= 1; - self.piece_count_total -= 1; - - if piece == Piece::BK || piece == Piece::WK { - self.king_square[piece_color(&piece) as usize] = Square::None; - } - } - - fn clear_square_at_index(&mut self, index: usize) { - self.clear_square(index.into()); - } - - pub fn transfer_piece(&mut self, from: Square, to: Square) { - let piece = self.get_piece(from); - self.clear_square(from); - self.set_square(to, piece); - } - - /// Get the piece at a square with an offset. - /// The board is like this - /// ``` - /// (0, 7)───┐ ┌────(7, 7) - /// ┌▼┬─┬─┬─┬─┬─┬─┬▼┐ - /// ├─┼─┼─┼─┼─┼─┼─┼─┤ - /// ├─┼─┼─┼─┼─┼─┼─┼─┤ - /// ├─┼─┼─┼─┼─┼─┼─┼─┤ - /// ├─┼─┼─┼─┼─┼─┼─┼─┤ - /// ├─┼─┼─┼─┼─┼─┼─┼─┤ - /// ├─┼─┼─┼─┼─┼─┼─┼─┤ - /// ├─┼─┼─┼─┼─┼─┼─┼─┤ - /// └▲┴─┴─┴─┴─┴─┴─┴▲┘ - /// (0, 0)──┘ B C D E F G └───(7, 0) - /// - fn get_piece_with_offset(&self, square: &Square, x_offset: isize, y_offset: isize) -> Piece { - let square_x_corr: isize = *square as isize % 8; - let square_y_corr: isize = *square as isize / 8; - - if !((0..8).contains(&(square_x_corr + x_offset)) - && (0..8).contains(&(square_y_corr + y_offset))) - { - return Piece::None; - } - - self.pieces[(square_x_corr + x_offset + (square_y_corr + y_offset) * 8) as usize] - } - - // Is this square attacked by the side to move? - pub fn is_square_attacked(&self, square: &Square, attacker: &Color) -> bool { - // Pawns - if *attacker == Color::White { - if self.get_piece_with_offset(square, -1, -1) == Piece::WP - || self.get_piece_with_offset(square, 1, -1) == Piece::WP - { - return true; - } - } else if self.get_piece_with_offset(square, -1, 1) == Piece::BP - || self.get_piece_with_offset(square, 1, 1) == Piece::BP - { - return true; - } - - // Knights - let knight_piece = if *attacker == Color::White { - Piece::WN - } else { - Piece::BN - }; - - for (x_offset, y_offset) in KNIGHT_OFFSETS.iter() { - if self.get_piece_with_offset(square, *x_offset, *y_offset) == knight_piece { - return true; - } - } - - // Kings - let king_piece = if *attacker == Color::White { - Piece::WK - } else { - Piece::BK - }; - - for (x_offset, y_offset) in KING_OFFSETS.iter() { - if self.get_piece_with_offset(square, *x_offset, *y_offset) == king_piece { - return true; - } - } - - // Rooks and Queens (horizontal and vertical lines) - let rook_piece = if *attacker == Color::White { - Piece::WR - } else { - Piece::BR - }; - - let queen_piece = if *attacker == Color::White { - Piece::WQ - } else { - Piece::BQ - }; - - for (x_offset, y_offset) in ROOK_OFFSETS.iter() { - let mut step = 1; - while let Some(index) = square.new_coor(x_offset * step, y_offset * step) { - let piece = self.get_piece_at_index(index); - if piece == rook_piece || piece == queen_piece { - return true; - } else if piece != Piece::None { - break; - } - step += 1; - } - } - - // Bishops and Queens (diagonals) - let bishop_piece = if *attacker == Color::White { - Piece::WB - } else { - Piece::BB - }; - - for (x_offset, y_offset) in BISHOP_OFFSETS.iter() { - let mut step = 1; - while let Some(index) = square.new_coor(x_offset * step, y_offset * step) { - let piece = self.get_piece_at_index(index); - if piece == bishop_piece || piece == queen_piece { - return true; - } else if piece != Piece::None { - break; - } - step += 1; - } - } - - false - } - - pub fn get_pseudo_legal_moves(&self) -> Vec { - let mut moves = Vec::new(); - - if self.side_to_move == Color::White { - // White King - for white_king in &self.piece_list[Piece::WK as usize] { - if *white_king == Square::None { - break; - } - - for (x_offset, y_offset) in KING_OFFSETS.iter() { - if self.is_square_attacked( - &white_king.new_square(*x_offset, *y_offset), - &Color::Black, - ) { - continue; - } - - if let Some(index) = white_king.new_coor(*x_offset, *y_offset) { - let piece = self.get_piece_at_index(index); - if piece == Piece::None { - #[rustfmt::skip] - moves.push(Move::new(*white_king, index.into(), Piece::None, false, false, Piece::None, CastleType::None, Piece::WK)); - } else if piece_color(&piece) != Color::White { - #[rustfmt::skip] - moves.push(Move::new(*white_king, index.into(), piece, false, false, Piece::None, CastleType::None, Piece::WK)); - } - } - } - } - - // White pawns - for white_pawn in &self.piece_list[Piece::WP as usize] { - // The pawn is not on board - if *white_pawn == Square::None { - break; - } - - if self.get_piece_with_offset(white_pawn, 0, 1) == Piece::None { - // Move one square forward - if white_pawn.get_rank() == Rank::R7 { - // Promotion - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(0, 1), Piece::None, false, false, Piece::WQ, CastleType::None, Piece::WP)); - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(0, 1), Piece::None, false, false, Piece::WR, CastleType::None, Piece::WP)); - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(0, 1), Piece::None, false, false, Piece::WB, CastleType::None, Piece::WP)); - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(0, 1), Piece::None, false, false, Piece::WN, CastleType::None, Piece::WP)); - } else { - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(0, 1), Piece::None, false, true, Piece::None, CastleType::None, Piece::WP)); - } - - // Move two squares forward - if white_pawn.get_rank() == Rank::R2 - && self.get_piece_with_offset(white_pawn, 0, 2) == Piece::None - { - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(0, 2), Piece::None, false, true, Piece::None, CastleType::None, Piece::WP)); - } - } - - // Pawn kill - // CHECK: if this works with enpassant + kill - let piece_diagonal_left = self.get_piece_with_offset(white_pawn, -1, 1); - let piece_diagonal_right = self.get_piece_with_offset(white_pawn, 1, 1); - if white_pawn.get_rank() == Rank::R7 { - if piece_diagonal_left != Piece::None - && piece_color(&piece_diagonal_left) == Color::Black - { - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(-1, 1), piece_diagonal_left, false, false, Piece::WQ, CastleType::None, Piece::WP)); - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(-1, 1), piece_diagonal_left, false, false, Piece::WR, CastleType::None, Piece::WP)); - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(-1, 1), piece_diagonal_left, false, false, Piece::WB, CastleType::None, Piece::WP)); - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(-1, 1), piece_diagonal_left, false, false, Piece::WN, CastleType::None, Piece::WP)); - } - - if piece_diagonal_right != Piece::None - && piece_color(&piece_diagonal_right) == Color::Black - { - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(1, 1), piece_diagonal_right, false, false, Piece::WQ, CastleType::None, Piece::WP)); - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(1, 1), piece_diagonal_right, false, false, Piece::WR, CastleType::None, Piece::WP)); - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(1, 1), piece_diagonal_right, false, false, Piece::WB, CastleType::None, Piece::WP)); - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(1, 1), piece_diagonal_right, false, false, Piece::WN, CastleType::None, Piece::WP)); - } - } else { - if piece_diagonal_left != Piece::None - && piece_color(&piece_diagonal_left) == Color::Black - { - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(-1, 1), piece_diagonal_left, false, false, Piece::None, CastleType::None, Piece::WP)); - } - - if piece_diagonal_right != Piece::None - && piece_color(&piece_diagonal_right) == Color::Black - { - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(1, 1), piece_diagonal_right, false, false, Piece::None, CastleType::None, Piece::WP)); - } - } - - // En passant - // if pawn to the side is BP and the en passant square is behind that - if self.en_passant_square != Square::None { - if self.get_piece_with_offset(white_pawn, -1, 0) == Piece::BP - && white_pawn.new_square(-1, 1) == self.en_passant_square - { - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(-1, 1), Piece::BP, true, false, Piece::None, CastleType::None, Piece::WP)); - } else if self.get_piece_with_offset(white_pawn, 1, 0) == Piece::BP - && white_pawn.new_square(1, 1) == self.en_passant_square - { - #[rustfmt::skip] - moves.push(Move::new(*white_pawn, white_pawn.new_square(1, 1), Piece::BP, true, false, Piece::None, CastleType::None, Piece::WP)); - } - } - } - - // White Rook - for white_rook in &self.piece_list[Piece::WR as usize] { - if *white_rook == Square::None { - break; - } - - for (x_offset, y_offset) in ROOK_OFFSETS.iter() { - let mut step = 1; - while let Some(index) = white_rook.new_coor(x_offset * step, y_offset * step) { - let piece = self.get_piece_at_index(index); - if piece == Piece::None { - // Rook movement - #[rustfmt::skip] - moves.push(Move::new(*white_rook, index.into(), Piece::None, false, false, Piece::None, CastleType::None, Piece::WR)); - } else if piece_color(&piece) == Color::White { - break; - } else { - // Rook capture - #[rustfmt::skip] - moves.push(Move::new(*white_rook, index.into(), piece, false, false, Piece::None, CastleType::None, Piece::WR)); - break; - } - step += 1; - } - } - - // Rook castle queen side - if *white_rook == Square::A1 - && (self.castle_permission & Castling::WQ as u32 != 0) - && self.is_square_empty(Square::B1) - && self.is_square_empty(Square::C1) - && self.is_square_empty(Square::D1) - && !self.is_square_attacked(&Square::E1, &Color::Black) - && !self.is_square_attacked(&Square::C1, &Color::Black) - && !self.is_square_attacked(&Square::D1, &Color::Black) - { - #[rustfmt::skip] - moves.push(Move::new(Square::E1, Square::C1, Piece::None, false, false, Piece::None, CastleType::QueenSide, Piece::WK)); - } - - // Rook castle king side - if *white_rook == Square::H1 - && (self.castle_permission & Castling::WK as u32 != 0) - && self.is_square_empty(Square::F1) - && self.is_square_empty(Square::G1) - && !self.is_square_attacked(&Square::E1, &Color::Black) - && !self.is_square_attacked(&Square::F1, &Color::Black) - && !self.is_square_attacked(&Square::G1, &Color::Black) - { - #[rustfmt::skip] - moves.push(Move::new(Square::E1, Square::G1, Piece::None, false, false, Piece::None, CastleType::KingSide, Piece::WK)); - } - } - - // White Bishop - for white_bishop in &self.piece_list[Piece::WB as usize] { - if *white_bishop == Square::None { - break; - } - - for (x_offset, y_offset) in BISHOP_OFFSETS.iter() { - let mut step = 1; - while let Some(index) = white_bishop.new_coor(x_offset * step, y_offset * step) - { - let piece = self.get_piece_at_index(index); - if piece == Piece::None { - // Bishop movement - #[rustfmt::skip] - moves.push(Move::new(*white_bishop, index.into(), Piece::None, false, false, Piece::None, CastleType::None, Piece::WB)); - } else if piece != Piece::None { - if piece_color(&piece) == Color::White { - break; - } - // Bishop capture - #[rustfmt::skip] - moves.push(Move::new(*white_bishop, index.into(), piece, false, false, Piece::None, CastleType::None, Piece::WB)); - break; - } - step += 1; - } - } - } - - // White Knight - for white_knight in &self.piece_list[Piece::WN as usize] { - if *white_knight == Square::None { - break; - } - - for (x_offset, y_offset) in KNIGHT_OFFSETS.iter() { - if let Some(index) = white_knight.new_coor(*x_offset, *y_offset) { - let piece = self.get_piece_at_index(index); - if piece == Piece::None { - #[rustfmt::skip] - moves.push(Move::new(*white_knight, index.into(), Piece::None, false, false, Piece::None, CastleType::None, Piece::WN)); - } else if piece_color(&piece) != Color::White { - #[rustfmt::skip] - moves.push(Move::new(*white_knight, index.into(), piece, false, false, Piece::None, CastleType::None, Piece::WN)); - } - } - } - } - - // White Queen - for white_queen in &self.piece_list[Piece::WQ as usize] { - if *white_queen == Square::None { - break; - } - - for (x_offset, y_offset) in BISHOP_OFFSETS.iter() { - let mut step = 1; - while let Some(index) = white_queen.new_coor(x_offset * step, y_offset * step) { - let piece = self.get_piece_at_index(index); - if piece == Piece::None { - // Bishop-type movement - #[rustfmt::skip] - moves.push(Move::new(*white_queen, index.into(), Piece::None, false, false, Piece::None, CastleType::None, Piece::WQ)); - } else if piece != Piece::None { - if piece_color(&piece) == Color::White { - break; - } - // Bishop-type capture - #[rustfmt::skip] - moves.push(Move::new(*white_queen, index.into(), piece, false, false, Piece::None, CastleType::None, Piece::WQ)); - break; - } - step += 1; - } - } - - for (x_offset, y_offset) in ROOK_OFFSETS.iter() { - let mut step = 1; - while let Some(index) = white_queen.new_coor(x_offset * step, y_offset * step) { - let piece = self.get_piece_at_index(index); - if piece == Piece::None { - // Rook-type movement - #[rustfmt::skip] - moves.push(Move::new(*white_queen, index.into(), Piece::None, false, false, Piece::None, CastleType::None, Piece::WQ)); - } else if piece != Piece::None { - if piece_color(&piece) == Color::White { - break; - } - // Rook-type capture - #[rustfmt::skip] - moves.push(Move::new(*white_queen, index.into(), piece, false, false, Piece::None, CastleType::None, Piece::WQ)); - break; - } - step += 1; - } - } - } - } else if self.side_to_move == Color::Black { - // Black King - for black_king in &self.piece_list[Piece::BK as usize] { - if *black_king == Square::None { - break; - } - - for (x_offset, y_offset) in KING_OFFSETS.iter() { - if self.is_square_attacked( - &black_king.new_square(*x_offset, *y_offset), - &Color::White, - ) { - continue; - } - - if let Some(index) = black_king.new_coor(*x_offset, *y_offset) { - let piece = self.get_piece_at_index(index); - if piece == Piece::None { - #[rustfmt::skip] - moves.push(Move::new(*black_king, index.into(), Piece::None, false, false, Piece::None, CastleType::None, Piece::BK)); - } else if piece_color(&piece) != Color::Black { - #[rustfmt::skip] - moves.push(Move::new(*black_king, index.into(), piece, false, false, Piece::None, CastleType::None, Piece::BK)); - } - } - } - } - - // Black pawns - for black_pawn in &self.piece_list[Piece::BP as usize] { - if *black_pawn == Square::None { - break; - } - - if self.get_piece_with_offset(&black_pawn, 0, -1) == Piece::None { - // Move one square forward - if black_pawn.get_rank() == Rank::R2 { - // Promotion - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(0, -1), Piece::None, false, false, Piece::BQ, CastleType::None, Piece::BP)); - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(0, -1), Piece::None, false, false, Piece::BR, CastleType::None, Piece::BP)); - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(0, -1), Piece::None, false, false, Piece::BB, CastleType::None, Piece::BP)); - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(0, -1), Piece::None, false, false, Piece::BN, CastleType::None, Piece::BP)); - } else { - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(0, -1), Piece::None, false, true, Piece::None, CastleType::None, Piece::BP)); - } - - // Move two squares forward - if black_pawn.get_rank() == Rank::R7 - && self.get_piece_with_offset(&black_pawn, 0, -2) == Piece::None - { - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(0, -2), Piece::None, false, true, Piece::None, CastleType::None, Piece::BP)); - } - } - - // Pawn kill - let piece_diagonal_left = self.get_piece_with_offset(black_pawn, -1, -1); - let piece_diagonal_right = self.get_piece_with_offset(black_pawn, 1, -1); - if black_pawn.get_rank() == Rank::R2 { - if piece_diagonal_left != Piece::None - && piece_color(&piece_diagonal_left) == Color::White - { - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(-1, -1), piece_diagonal_left, false, false, Piece::BQ, CastleType::None, Piece::BP)); - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(-1, -1), piece_diagonal_left, false, false, Piece::BR, CastleType::None, Piece::BP)); - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(-1, -1), piece_diagonal_left, false, false, Piece::BB, CastleType::None, Piece::BP)); - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(-1, -1), piece_diagonal_left, false, false, Piece::BN, CastleType::None, Piece::BP)); - } - - if piece_diagonal_right != Piece::None - && piece_color(&piece_diagonal_right) == Color::White - { - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(1, -1), piece_diagonal_right, false, false, Piece::BQ, CastleType::None, Piece::BP)); - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(1, -1), piece_diagonal_right, false, false, Piece::BR, CastleType::None, Piece::BP)); - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(1, -1), piece_diagonal_right, false, false, Piece::BB, CastleType::None, Piece::BP)); - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(1, -1), piece_diagonal_right, false, false, Piece::BN, CastleType::None, Piece::BP)); - } - } else { - if piece_diagonal_left != Piece::None - && piece_color(&piece_diagonal_left) == Color::White - { - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(-1, -1), piece_diagonal_left, false, false, Piece::None, CastleType::None, Piece::BP)); - } - - if piece_diagonal_right != Piece::None - && piece_color(&piece_diagonal_right) == Color::White - { - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(1, -1), piece_diagonal_right, false, false, Piece::None, CastleType::None, Piece::BP)); - } - } - - // En passant - if self.en_passant_square != Square::None { - if self.get_piece_with_offset(&black_pawn, -1, 0) == Piece::WP - && black_pawn.new_square(-1, -1) == self.en_passant_square - { - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(-1, -1), Piece::WP, true, false, Piece::None, CastleType::None, Piece::BP)); - } else if self.get_piece_with_offset(&black_pawn, 1, 0) == Piece::WP - && black_pawn.new_square(1, -1) == self.en_passant_square - { - #[rustfmt::skip] - moves.push(Move::new(*black_pawn, black_pawn.new_square(1, -1), Piece::WP, true, false, Piece::None, CastleType::None, Piece::BP)); - } - } - } - - // Black Rook - for black_rook in &self.piece_list[Piece::BR as usize] { - if *black_rook == Square::None { - break; - } - - for (x_offset, y_offset) in ROOK_OFFSETS.iter() { - let mut step = 1; - while let Some(index) = black_rook.new_coor(x_offset * step, y_offset * step) { - let piece = self.get_piece_at_index(index); - if piece == Piece::None { - // Rook movement - #[rustfmt::skip] - moves.push(Move::new(*black_rook, index.into(), Piece::None, false, false, Piece::None, CastleType::None, Piece::BR)); - } else if piece_color(&piece) == Color::Black { - break; - } else { - #[rustfmt::skip] - moves.push(Move::new(*black_rook, index.into(), piece, false, false, Piece::None, CastleType::None, Piece::BR)); - break; - } - step += 1; - } - } - - // Rook castle queen side - if *black_rook == Square::A8 - && (self.castle_permission & Castling::BQ as u32 != 0) - && self.is_square_empty(Square::B8) - && self.is_square_empty(Square::C8) - && self.is_square_empty(Square::D8) - && !self.is_square_attacked(&Square::E8, &Color::White) - && !self.is_square_attacked(&Square::C8, &Color::White) - && !self.is_square_attacked(&Square::D8, &Color::White) - // Cheking is rook is attacked after castle, FIXME - { - #[rustfmt::skip] - moves.push(Move::new(Square::E8, Square::C8, Piece::None, false, false, Piece::None, CastleType::QueenSide, Piece::BK)); - } - - // Rook castle king side - if *black_rook == Square::H8 - && (self.castle_permission & Castling::BK as u32 != 0) - && self.is_square_empty(Square::F8) - && self.is_square_empty(Square::G8) - && !self.is_square_attacked(&Square::E8, &Color::White) - && !self.is_square_attacked(&Square::F8, &Color::White) - && !self.is_square_attacked(&Square::G8, &Color::White) - { - #[rustfmt::skip] - moves.push(Move::new(Square::E8, Square::G8, Piece::None, false, false, Piece::None, CastleType::KingSide, Piece::BK)); - } - } - - // Black Bishop - for black_bishop in &self.piece_list[Piece::BB as usize] { - if *black_bishop == Square::None { - break; - } - - for (x_offset, y_offset) in BISHOP_OFFSETS.iter() { - let mut step = 1; - while let Some(index) = black_bishop.new_coor(x_offset * step, y_offset * step) - { - let piece = self.get_piece_at_index(index); - if piece == Piece::None { - // Bishop movement - #[rustfmt::skip] - moves.push(Move::new(*black_bishop, index.into(), Piece::None, false, false, Piece::None, CastleType::None, Piece::BB)); - } else if piece != Piece::None { - if piece_color(&piece) == Color::Black { - break; - } - // Bishop capture - #[rustfmt::skip] - moves.push(Move::new(*black_bishop, index.into(), piece, false, false, Piece::None, CastleType::None, Piece::BB)); - break; - } - step += 1; - } - } - } - - // Black Knight - for black_knight in &self.piece_list[Piece::BN as usize] { - if *black_knight == Square::None { - break; - } - - for (x_offset, y_offset) in KNIGHT_OFFSETS.iter() { - if let Some(index) = black_knight.new_coor(*x_offset, *y_offset) { - let piece = self.get_piece_at_index(index); - if piece == Piece::None { - #[rustfmt::skip] - moves.push(Move::new(*black_knight, index.into(), Piece::None, false, false, Piece::None, CastleType::None, Piece::BN)); - } else if piece_color(&piece) != Color::Black { - #[rustfmt::skip] - moves.push(Move::new(*black_knight, index.into(), piece, false, false, Piece::None, CastleType::None, Piece::BN)); - } - } - } - } - - // Black Queen - for black_queen in &self.piece_list[Piece::BQ as usize] { - if *black_queen == Square::None { - break; - } - - for (x_offset, y_offset) in BISHOP_OFFSETS.iter() { - let mut step = 1; - while let Some(index) = black_queen.new_coor(x_offset * step, y_offset * step) { - let piece = self.get_piece_at_index(index); - if piece == Piece::None { - // Bishop-type movement - #[rustfmt::skip] - moves.push(Move::new(*black_queen, index.into(), Piece::None, false, false, Piece::None, CastleType::None, Piece::BQ)); - } else if piece != Piece::None { - if piece_color(&piece) == Color::Black { - break; - } - // Capture - #[rustfmt::skip] - moves.push(Move::new(*black_queen, index.into(), piece, false, false, Piece::None, CastleType::None, Piece::BQ)); - break; - } - step += 1; - } - } - - // Rook-type movement for queen - for (x_offset, y_offset) in ROOK_OFFSETS.iter() { - let mut step = 1; - while let Some(index) = black_queen.new_coor(x_offset * step, y_offset * step) { - let piece = self.get_piece_at_index(index); - if piece == Piece::None { - #[rustfmt::skip] - moves.push(Move::new(*black_queen, index.into(), Piece::None, false, false, Piece::None, CastleType::None, Piece::BQ)); - } else if piece != Piece::None { - if piece_color(&piece) == Color::Black { - break; - } - #[rustfmt::skip] - moves.push(Move::new(*black_queen, index.into(), piece, false, false, Piece::None, CastleType::None, Piece::BQ)); - break; - } - step += 1; - } - } - } - } - - moves - } - - pub fn get_legal_moves(&mut self) -> Vec { - let pseudo_moves = self.get_pseudo_legal_moves(); - let mut legal_moves = Vec::new(); - - for mv in pseudo_moves { - // Make the move - self.make_move(mv); - - // Check if the king is in check after the move - let opposite_color = self.side_to_move.flip(); - let king_square = self.king_square[opposite_color as usize]; - if !self.is_square_attacked(&king_square, &self.side_to_move) { - // If king is not in check, it's a legal move - legal_moves.push(mv); - } - - // Undo the move to restore the original board state - self.undo_move(); - } - - legal_moves - } - - pub fn make_move(&mut self, move_: Move) { - let fifty_move_counter_before = self.fifty_move_counter; - let castle_permission_before = self.castle_permission; - let mut en_passant_square_before = self.en_passant_square; - let position_key_before = self.position_key; - - // Update if capture made - if move_.captured_piece != Piece::None { - self.clear_square(move_.to); - } - - // Move piece from -> to, update piece list and the pieces array - self.transfer_piece(move_.from, move_.to); - - // Update 50 move rule - if move_.captured_piece != Piece::None - || move_.current_piece == Piece::WP - || move_.current_piece == Piece::BP - { - self.fifty_move_counter = 0; - } else { - self.fifty_move_counter += 1; - } - - // Promotions - if move_.promoted_piece != Piece::None { - self.clear_square(move_.to); - self.set_square(move_.to, move_.promoted_piece); - } - - // En passant, delete the en passanted pawn, update hash - if move_.is_enpassant { - let en_passant_pawn; - - if self.side_to_move == Color::White { - en_passant_pawn = move_.to.new_square(0, -1); - } else { - en_passant_pawn = move_.to.new_square(0, 1); - }; - - self.clear_square(en_passant_pawn); - en_passant_square_before = Square::None; - self.update_hash_given_en_passant_square(en_passant_square_before); - } - - // Castling, move the king - match move_.castle { - CastleType::KingSide => { - if self.side_to_move == Color::White { - self.transfer_piece(Square::H1, Square::F1); - self.castle_permission &= !(Castling::WK as u32); - } else { - self.transfer_piece(Square::H8, Square::F8); - self.castle_permission &= !(Castling::BK as u32); - } - } - CastleType::QueenSide => { - if self.side_to_move == Color::White { - self.transfer_piece(Square::A1, Square::D1); - self.castle_permission &= !(Castling::WQ as u32); - } else { - self.transfer_piece(Square::A8, Square::D8); - self.castle_permission &= !(Castling::BQ as u32); - } - } - CastleType::None => {} - } - self.update_hash_given_castle_permission(self.castle_permission); - - // Change side - self.side_to_move = if self.side_to_move == Color::White { - Color::Black - } else { - Color::White - }; - self.update_hash_given_playing_side(self.side_to_move); - - // Update castle permission - if move_.current_piece == Piece::WK { - self.castle_permission &= !(Castling::WK as u32); - self.castle_permission &= !(Castling::WQ as u32); - } else if move_.current_piece == Piece::BK { - self.castle_permission &= !(Castling::BK as u32); - self.castle_permission &= !(Castling::BQ as u32); - } else if move_.current_piece == Piece::WR { - if move_.from == Square::A1 { - self.castle_permission &= !(Castling::WQ as u32); - } else if move_.from == Square::H1 { - self.castle_permission &= !(Castling::WK as u32); - } - } else if move_.current_piece == Piece::BR { - if move_.from == Square::A8 { - self.castle_permission &= !(Castling::BQ as u32); - } else if move_.from == Square::H8 { - self.castle_permission &= !(Castling::BK as u32); - } - } - self.update_hash_given_castle_permission(self.castle_permission); - - // Increase ply, history_ply - self.ply += 1; - self.history_ply += 1; - - self.history.push(Undo { - move_, - fifty_move_counter: fifty_move_counter_before, - castle_permission: castle_permission_before, - en_passant_square: en_passant_square_before, - position_key: position_key_before, - }); - } - - pub fn undo_move(&mut self) { - let undo = self.history.pop().unwrap(); - let move_ = undo.move_; - - // Move piece to original position - self.transfer_piece(move_.to, move_.from); - - // Update if capture made - if move_.captured_piece != Piece::None && move_.is_enpassant == false { - self.set_square(move_.to, move_.captured_piece); - } - - // Promotions - if move_.promoted_piece != Piece::None { - self.clear_square(move_.from); - self.set_square(move_.from, move_.current_piece); // Restore original piece - } - - // En passant, restore the en passanted pawn - if move_.is_enpassant { - // Reverse the side_to_move first (the one who just moved) - let en_passant_square = move_.to; // the target square of the en passant move - let en_passant_pawn_square; - - if self.side_to_move == Color::White { - // Black captured, so the white pawn was captured - en_passant_pawn_square = move_.to.new_square(0, 1); - } else { - // White captured, so the black pawn was captured - en_passant_pawn_square = move_.to.new_square(0, -1); - } - self.set_square(en_passant_pawn_square, move_.captured_piece); - - // Clear the en passant square - self.clear_square(en_passant_square); - - // Update the hash with the en passant square removal - self.update_hash_given_en_passant_square(en_passant_square); - } - - // Change side - self.side_to_move = if self.side_to_move == Color::White { - Color::Black - } else { - Color::White - }; - self.update_hash_given_playing_side(self.side_to_move); - - // Castling, move the king back - match move_.castle { - CastleType::KingSide => { - if self.side_to_move == Color::White { - self.transfer_piece(Square::G1, Square::E1); - } else { - self.transfer_piece(Square::G8, Square::E8); - } - } - CastleType::QueenSide => { - if self.side_to_move == Color::White { - self.transfer_piece(Square::C1, Square::E1); - } else { - self.transfer_piece(Square::C8, Square::E8); - } - } - CastleType::None => {} - } - self.update_hash_given_castle_permission(self.castle_permission); - - // Update castle permission - self.castle_permission = undo.castle_permission; - - // Update en passant square - self.en_passant_square = undo.en_passant_square; - - // Decrease ply, history_ply - self.ply -= 1; - self.history_ply -= 1; - - self.fifty_move_counter = undo.fifty_move_counter; - self.position_key = undo.position_key; - } - - pub fn is_square_empty(&self, square: Square) -> bool { - self.get_piece_at_index(square as usize) == Piece::None - } - - pub fn perft(&mut self, depth: u32) -> u64 { - if depth == 0 { - return 1; // Base case: one leaf node reached - } - - let mut nodes = 0; - - // Generate all legal moves - let moves = self.get_legal_moves(); - - // Iterate over all moves - for mv in &moves { - self.make_move(*mv); // Make the move - let child_nodes = self.perft(depth - 1); // Recurse to calculate the number of nodes for the remaining depth - nodes += child_nodes; // Accumulate the total nodes for this depth - - // Print divide results for the first depth (depth-1 for initial call) - // if depth == 1 { - // println!("Move {}: {} nodes", mv, child_nodes); - // } - - self.undo_move(); // Undo the move to backtrack - } - - nodes - } - - pub fn perft_test(&mut self, max_depth: u32) -> u64 { - // let start_time = Instant::now(); - let mut total_nodes = 0; - for current_depth in 0..=max_depth { - let nodes = self.perft(current_depth); - // println!("Depth {}: {} nodes", current_depth, nodes); - total_nodes = nodes; - } - - // let elapsed_time = start_time.elapsed(); - // println!( - // "Total nodes: {}, total time elapsed: {:.2?}, time per node: {:.2?}, NPS: {:.0} n/s", - // total_nodes, - // elapsed_time, - // Duration::from_secs_f64((elapsed_time.as_secs_f64() / (total_nodes as f64))), - // (total_nodes as f64) / (elapsed_time.as_secs_f64()) - // ); - - total_nodes - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - #[rustfmt::skip] - fn test_perft_illegal_en_passant() { - // avoid illegal en passant capture - assert_eq!(Board::new("8/5bk1/8/2Pp4/8/1K6/8/8 w - d6 0 1").perft_test(6), 824064); - assert_eq!(Board::new("8/8/1k6/8/2pP4/8/5BK1/8 b - d3 0 1").perft_test(6), 824064); - } - - #[test] - #[rustfmt::skip] - fn test_perft_en_passant_capture_checks_opponent() { - //en passant capture checks opponent - assert_eq!(Board::new("8/8/1k6/2b5/2pP4/8/5K2/8 b - d3 0 1").perft_test(6), 1440467); - assert_eq!(Board::new("8/5k2/8/2Pp4/2B5/1K6/8/8 w - d6 0 1").perft_test(6), 1440467); - } - - #[test] - #[rustfmt::skip] - fn test_perft_short_castling_gives_check() { - // short castling gives check: - assert_eq!(Board::new("5k2/8/8/8/8/8/8/4K2R w K - 0 1").perft_test(6), 661072); - assert_eq!(Board::new("4k2r/8/8/8/8/8/8/5K2 b k - 0 1").perft_test(6), 661072); - } - - #[test] - #[rustfmt::skip] - fn test_perft_long_castling_gives_check() { - // long castling gives check - assert_eq!(Board::new("3k4/8/8/8/8/8/8/R3K3 w Q - 0 1").perft_test(6), 803711); - assert_eq!(Board::new("r3k3/8/8/8/8/8/8/3K4 b q - 0 1").perft_test(6), 803711); - } - - #[test] - #[rustfmt::skip] - fn test_perft_castling_including_rook_capture() { - // castling (including losing cr due to rook capture) - assert_eq!(Board::new("r3k2r/1b4bq/8/8/8/8/7B/R3K2R w KQkq - 0 1").perft_test(4), 1274206); - assert_eq!(Board::new("r3k2r/7b/8/8/8/8/1B4BQ/R3K2R b KQkq - 0 1").perft_test(4), 1274206); - } - - #[test] - #[rustfmt::skip] - fn test_perft_castling_promoted() { - // castling prevented - assert_eq!(Board::new("r3k2r/8/3Q4/8/8/5q2/8/R3K2R b KQkq - 0 1").perft_test(4), 1720476); - assert_eq!(Board::new("r3k2r/8/5Q2/8/8/3q4/8/R3K2R w KQkq - 0 1").perft_test(4), 1720476); - } - - #[test] - #[rustfmt::skip] - fn test_perft_promote_out_of_check() { - // promote out of check: - assert_eq!(Board::new("2K2r2/4P3/8/8/8/8/8/3k4 w - - 0 1").perft_test(6),3821001); - assert_eq!(Board::new("3K4/8/8/8/8/8/4p3/2k2R2 b - - 0 1").perft_test(6),3821001); - } - - #[test] - #[rustfmt::skip] - fn test_perft_discovered_check() { - // discovered check: - assert_eq!(Board::new("8/8/1P2K3/8/2n5/1q6/8/5k2 b - - 0 1").perft_test(5),1004658); - assert_eq!(Board::new("5K2/8/1Q6/2N5/8/1p2k3/8/8 w - - 0 1").perft_test(5),1004658); - } - - #[test] - #[rustfmt::skip] - fn test_perft_promote_to_give_check() { - // promote to give check: - assert_eq!(Board::new("4k3/1P6/8/8/8/8/K7/8 w - - 0 1").perft_test(6),217342); - assert_eq!(Board::new("8/k7/8/8/8/8/1p6/4K3 b - - 0 1").perft_test(6),217342); - } - - #[test] - #[rustfmt::skip] - fn test_perft_underpromote_to_check() { - // underpromote to check: - assert_eq!(Board::new("8/P1k5/K7/8/8/8/8/8 w - - 0 1").perft_test(6),92683); - assert_eq!(Board::new("8/8/8/8/8/k7/p1K5/8 b - - 0 1").perft_test(6),92683); - } - - #[test] - #[rustfmt::skip] - fn test_perft_self_stalemate() { - // self stalemate: - assert_eq!(Board::new("K1k5/8/P7/8/8/8/8/8 w - - 0 1").perft_test(6),2217); - assert_eq!(Board::new("8/8/8/8/8/p7/8/k1K5 b - - 0 1").perft_test(6),2217); - } - - #[test] - #[rustfmt::skip] - fn test_perft_stalemate_checkmate() { - // stalemate/checkmate: - assert_eq!(Board::new("8/k1P5/8/1K6/8/8/8/8 w - - 0 1").perft_test(7),567584); - assert_eq!(Board::new("8/8/8/8/1k6/8/K1p5/8 b - - 0 1").perft_test(7),567584); - } - - #[test] - #[rustfmt::skip] - fn test_perft_double_check() { - // double check: - assert_eq!(Board::new("8/8/2k5/5q2/5n2/8/5K2/8 b - - 0 1").perft_test(4),23527); - assert_eq!(Board::new("8/5k2/8/5N2/5Q2/2K5/8/8 w - - 0 1").perft_test(4),23527); - } -} diff --git a/src/definitions/castling.rs b/src/definitions/castling.rs deleted file mode 100644 index f68d291..0000000 --- a/src/definitions/castling.rs +++ /dev/null @@ -1,14 +0,0 @@ -#[rustfmt::skip] -pub enum Castling { - WK = 0b0001, - WQ = 0b0010, - BK = 0b0100, - BQ = 0b1000, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum CastleType { - KingSide, - QueenSide, - None, -} diff --git a/src/definitions/color.rs b/src/definitions/color.rs deleted file mode 100644 index 84654cb..0000000 --- a/src/definitions/color.rs +++ /dev/null @@ -1,28 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum Color { - White = 0, - Black, - Both, -} - -impl Display for Color { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Color::White => write!(f, "White"), - Color::Black => write!(f, "Black"), - Color::Both => write!(f, "Both"), - } - } -} - -impl Color { - pub fn flip(&self) -> Self { - match self { - Color::White => Color::Black, - Color::Black => Color::White, - Color::Both => Color::Both, - } - } -} diff --git a/src/definitions/file.rs b/src/definitions/file.rs deleted file mode 100644 index ca38ab1..0000000 --- a/src/definitions/file.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -#[rustfmt::skip] -pub enum File { - A = 0, B, C, D, E, F, G, H, None, -} - -impl Display for File { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - File::A => write!(f, "a"), - File::B => write!(f, "b"), - File::C => write!(f, "c"), - File::D => write!(f, "d"), - File::E => write!(f, "e"), - File::F => write!(f, "f"), - File::G => write!(f, "g"), - File::H => write!(f, "h"), - File::None => write!(f, "None"), - } - } -} diff --git a/src/definitions/mod.rs b/src/definitions/mod.rs deleted file mode 100644 index 0f58e27..0000000 --- a/src/definitions/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub mod bitboards; -pub mod board; -pub mod castling; -pub mod color; -pub mod file; -pub mod move_; -pub mod piece; -pub mod rank; -pub mod square; -pub mod undo; diff --git a/src/definitions/move_.rs b/src/definitions/move_.rs deleted file mode 100644 index 48f8ba4..0000000 --- a/src/definitions/move_.rs +++ /dev/null @@ -1,108 +0,0 @@ -use crate::definitions::castling::CastleType; -use crate::definitions::piece::Piece; -use crate::definitions::square::Square; -use std::fmt::{write, Display, Formatter}; - -#[derive(Debug, Clone, Copy)] -pub struct Move { - pub from: Square, - pub to: Square, - pub captured_piece: Piece, - pub is_enpassant: bool, - pub is_pawn_start: bool, - pub promoted_piece: Piece, - pub castle: CastleType, - pub current_piece: Piece, - pub score: i32, -} - -impl Move { - pub fn new( - from: Square, - to: Square, - captured_piece: Piece, // Piece which got captured - is_enpassant: bool, - is_pawn_start: bool, - promoted_piece: Piece, - castle: CastleType, - current_piece: Piece, - ) -> Self { - Self { - from, - to, - captured_piece, - is_enpassant, - is_pawn_start, - promoted_piece, - castle, - current_piece, - score: 0, - } - } -} - -impl Display for Move { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - // Return UCI notation - let mut notation = String::new(); - notation.push_str(&self.from.to_string()); - notation.push_str(&self.to.to_string()); - if self.promoted_piece != Piece::None { - notation.push_str(&self.promoted_piece.to_string().to_lowercase()); - } - write!(f, "{}", notation) - - // let mut notation = String::new(); - - // match self.castle { - // CastleType::KingSide => { - // // Handle king-side castling - // notation.push_str("O-O"); - // } - // CastleType::QueenSide => { - // // Handle queen-side castling - // notation.push_str("O-O-O"); - // } - // CastleType::None => { - // // Handle normal moves and captures - // if self.current_piece != Piece::WP && self.current_piece != Piece::BP { - // // For non-pawn pieces, add the piece type - // notation.push(format!("{}{}", self.current_piece, self.from.get_file())); - // } - - // // Handle captures - // if self.captured_piece != Piece::None { - // if self.current_piece == Piece::WP || self.current_piece == Piece::BP { - // // Pawn captures should include the file of the pawn - // notation.push( - // format!("{}", self.from.get_file()) - // .to_lowercase() - // .chars() - // .next() - // .unwrap(), - // ); - // } - // notation.push('x'); - // } - - // // Add the destination square - // notation.push_str(&self.to.to_string()); - - // // Handle promotion - // if self.promoted_piece != Piece::None { - // notation.push('='); - // notation.push( - // format!("{}", self.promoted_piece) - // .to_uppercase() - // .chars() - // .next() - // .unwrap(), - // ); - // } - // } - // } - - // // Write the constructed notation - // write!(f, "{}", notation) - } -} diff --git a/src/definitions/piece.rs b/src/definitions/piece.rs deleted file mode 100644 index 3ebba9a..0000000 --- a/src/definitions/piece.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -#[rustfmt::skip] -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum Piece { - None = 0, WP, WN, WB, WR, WQ, WK, BP, BN, BB, BR, BQ, BK, -} - -impl Display for Piece { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Piece::None => write!(f, "."), - Piece::WP => write!(f, "P"), - Piece::WN => write!(f, "N"), - Piece::WB => write!(f, "B"), - Piece::WR => write!(f, "R"), - Piece::WQ => write!(f, "Q"), - Piece::WK => write!(f, "K"), - Piece::BP => write!(f, "p"), - Piece::BN => write!(f, "n"), - Piece::BB => write!(f, "b"), - Piece::BR => write!(f, "r"), - Piece::BQ => write!(f, "q"), - Piece::BK => write!(f, "k"), - } - } -} - -#[rustfmt::skip] -macro_rules! impl_type_for_piece { - ($type:ty) => { - impl From<$type> for Piece { - fn from(value: $type) -> Self { - match value { - 0..=12 => unsafe { std::mem::transmute::(value as u8) }, - _ => Piece::None, - } - } - } - }; -} - -impl_type_for_piece!(u32); diff --git a/src/definitions/rank.rs b/src/definitions/rank.rs deleted file mode 100644 index 2618529..0000000 --- a/src/definitions/rank.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[rustfmt::skip] -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum Rank { - R1 = 0, R2, R3, R4, R5, R6, R7, R8, None, -} diff --git a/src/definitions/square.rs b/src/definitions/square.rs deleted file mode 100644 index c459b08..0000000 --- a/src/definitions/square.rs +++ /dev/null @@ -1,132 +0,0 @@ -use crate::definitions::file::File; -use crate::definitions::rank::Rank; -use std::fmt::{self, Display, Formatter}; -use std::ops::{Add, Sub}; - -#[rustfmt::skip] -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] -pub enum Square { - A1 = 0, B1, C1, D1, E1, F1, G1, H1, - A2, B2, C2, D2, E2, F2, G2, H2, - A3, B3, C3, D3, E3, F3, G3, H3, - A4, B4, C4, D4, E4, F4, G4, H4, - A5, B5, C5, D5, E5, F5, G5, H5, - A6, B6, C6, D6, E6, F6, G6, H6, - A7, B7, C7, D7, E7, F7, G7, H7, - A8, B8, C8, D8, E8, F8, G8, H8, None, -} - -impl Display for Square { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let files = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; - let ranks = ['1', '2', '3', '4', '5', '6', '7', '8']; - - match self { - Square::None => write!(f, "None"), - square => write!( - f, - "{}{}", - files[*square as usize % 8], - ranks[*square as usize / 8] - ), - } - } -} - -impl Square { - pub fn new_coor(&self, x_offset: isize, y_offset: isize) -> Option { - // Extract current row and column from the square index - let current_row = *self as usize / 8; - let current_col = *self as usize % 8; - - // Calculate new row and column based on offsets - let new_row = current_row as isize + y_offset; - let new_col = current_col as isize + x_offset; - - // Check if the new row and column are within bounds - if (0..8).contains(&new_row) && (0..8).contains(&new_col) { - Some((new_row * 8 + new_col) as usize) - } else { - None - } - } - - pub fn new_square(&self, x_offset: isize, y_offset: isize) -> Square { - match self.new_coor(x_offset, y_offset) { - Some(index) => unsafe { std::mem::transmute::(index as u8) }, - None => Square::None, - } - } - - #[rustfmt::skip] - pub fn get_rank(&self) -> Rank { - match self { - Square::A1 | Square::B1 | Square::C1 | Square::D1 | Square::E1 | Square::F1 | Square::G1 | Square::H1 => Rank::R1, - Square::A2 | Square::B2 | Square::C2 | Square::D2 | Square::E2 | Square::F2 | Square::G2 | Square::H2 => Rank::R2, - Square::A3 | Square::B3 | Square::C3 | Square::D3 | Square::E3 | Square::F3 | Square::G3 | Square::H3 => Rank::R3, - Square::A4 | Square::B4 | Square::C4 | Square::D4 | Square::E4 | Square::F4 | Square::G4 | Square::H4 => Rank::R4, - Square::A5 | Square::B5 | Square::C5 | Square::D5 | Square::E5 | Square::F5 | Square::G5 | Square::H5 => Rank::R5, - Square::A6 | Square::B6 | Square::C6 | Square::D6 | Square::E6 | Square::F6 | Square::G6 | Square::H6 => Rank::R6, - Square::A7 | Square::B7 | Square::C7 | Square::D7 | Square::E7 | Square::F7 | Square::G7 | Square::H7 => Rank::R7, - Square::A8 | Square::B8 | Square::C8 | Square::D8 | Square::E8 | Square::F8 | Square::G8 | Square::H8 => Rank::R8, - _ => Rank::None, - } - } - - #[rustfmt::skip] - pub fn get_file(&self) -> File { - match self { - Square::A1 | Square::A2 | Square::A3 | Square::A4 | Square::A5 | Square::A6 | Square::A7 | Square::A8 => File::A, - Square::B1 | Square::B2 | Square::B3 | Square::B4 | Square::B5 | Square::B6 | Square::B7 | Square::B8 => File::B, - Square::C1 | Square::C2 | Square::C3 | Square::C4 | Square::C5 | Square::C6 | Square::C7 | Square::C8 => File::C, - Square::D1 | Square::D2 | Square::D3 | Square::D4 | Square::D5 | Square::D6 | Square::D7 | Square::D8 => File::D, - Square::E1 | Square::E2 | Square::E3 | Square::E4 | Square::E5 | Square::E6 | Square::E7 | Square::E8 => File::E, - Square::F1 | Square::F2 | Square::F3 | Square::F4 | Square::F5 | Square::F6 | Square::F7 | Square::F8 => File::F, - Square::G1 | Square::G2 | Square::G3 | Square::G4 | Square::G5 | Square::G6 | Square::G7 | Square::G8 => File::G, - Square::H1 | Square::H2 | Square::H3 | Square::H4 | Square::H5 | Square::H6 | Square::H7 | Square::H8 => File::H, - _ => File::None, - } - } -} - -#[rustfmt::skip] -macro_rules! impl_type_for_square { - ($type:ty) => { - impl From<$type> for Square { - fn from(value: $type) -> Self { - match value { - 0..=63 => unsafe { std::mem::transmute::(value as u8) }, - _ => Square::None, - } - } - } - - impl Add<$type> for Square { - type Output = Square; - - fn add(self, rhs: $type) -> Square { - let result: $type = self as $type + rhs; - match result { - 0..=63 => unsafe { std::mem::transmute::(result as u8) }, - _ => Square::None, - } - } - } - - impl Sub<$type> for Square { - type Output = Square; - - fn sub(self, rhs: $type) -> Square { - let result: $type = self as $type - rhs; - match result { - 0..=63 => unsafe { std::mem::transmute::(result as u8) }, - _ => Square::None, - } - } - } - }; -} - -impl_type_for_square!(u8); -impl_type_for_square!(usize); -impl_type_for_square!(u64); diff --git a/src/definitions/undo.rs b/src/definitions/undo.rs deleted file mode 100644 index 4cf3e62..0000000 --- a/src/definitions/undo.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::definitions::move_::Move; -use crate::definitions::square::Square; - -#[derive(Debug)] -pub struct Undo { - pub move_: Move, - pub en_passant_square: Square, - pub fifty_move_counter: u32, - pub castle_permission: u32, - pub position_key: u64, -} diff --git a/src/main.rs b/src/main.rs index 5eb9aec..fcfed61 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,46 +1,436 @@ -mod definitions; -mod utils; -use clap::Parser; -use definitions::board::Board; -use definitions::castling::CastleType; -use definitions::move_::Move; -use definitions::piece::Piece; -use definitions::square::Square; +use chess::{Board, ChessMove, Color, Game, MoveGen, Piece, Square}; +use std::collections::HashMap; +use std::io::{self, BufRead, Write}; +use std::str::FromStr; +use std::time::{Duration, Instant}; -#[derive(Parser)] -#[command(version, about, long_about = None)] -struct Args { - fen: String, +const SAFETY_MARGIN: u64 = 50; +const DEFAULT_INCREMENT: u64 = 0; + +fn calculate_time_per_move( + is_white: bool, + wtime: u64, + btime: u64, + movestogo: Option, + increment: u64, + safety_margin: u64, +) -> Duration { + let remaining_time = if is_white { wtime } else { btime }; + let expected_moves = movestogo.unwrap_or(40); // Default to 40 moves if movestogo is not provided + + // Calculate time per move + let time_per_move = (remaining_time / expected_moves as u64) + increment - safety_margin; + + // Ensure time per move does not exceed remaining time + Duration::from_millis(time_per_move.min(remaining_time)) } fn main() { - let args = Args::parse(); - // Fixing: - // - en passant capture checks opponent: - // - castling (including losing cr due to rook capture): - // - castling prevented - - // let mut board = Board::default(); - // let mut board = Board::new("rnbqkbnr/ppp1p1pp/3p4/4Pp2/8/8/PPPP1PPP/RNBQKBNR w KQkq f6 0 3"); // En passant - // let mut board = Board::new("6k1/p5pp/b4r2/5p2/1Pq1p3/1Qpp1PP1/P5BP/3RR1K1 b - - 0 30"); - // let mut board = Board::new("rnbqkbnr/ppp1p1pp/8/4P3/2p2p2/5P2/PPPPN1PP/RNBQK2R w KQkq - 0 6"); // Castling - // let mut board = Board::new("8/P3k3/8/8/4p3/8/3K4/8 w - - 0 1"); // Endgame - // let mut board = Board::new("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 "); - // let mut board = Board::new("8/8/1k6/8/2pP4/8/5BK1/8 b - d3 0 1"); - let mut board = Board::new(args.fen); - // println!("{}", board); - let mut count = 0; - let mut moves = String::new(); - for move_ in board.get_legal_moves() { - board.make_move(move_); - // println!("{}\n{}", move_, board); - moves.push_str(&format!("{} ", move_)); - board.undo_move(); - count += 1; + let stdin = io::stdin(); + let mut game = Game::new(); + let mut quit = false; + let mut transposition_table = HashMap::new(); + + for line in stdin.lock().lines() { + let input = match line { + Ok(line) => line, + Err(_) => continue, + }; + let tokens: Vec<&str> = input.split_whitespace().collect(); + if tokens.is_empty() { + continue; + } + + match tokens[0] { + "uci" => { + println!("id name Shard"); + println!("id author Saphereye"); + println!("uciok"); + } + "isready" => { + println!("readyok"); + } + "position" => { + if tokens[1] == "startpos" { + game = Game::new(); + } else if tokens[1] == "fen" { + let fen = tokens[2..].join(" "); + game = Game::from_str(&fen).expect("Valid position"); + } + + if let Some(moves_index) = tokens.iter().position(|&x| x == "moves") { + for mv in &tokens[moves_index + 1..] { + let chess_move = uci_to_move(mv).expect("Valid move"); + game.make_move(chess_move); + } + } + } + "go" => { + let mut time_limit = Duration::from_millis(50); // Default time limit + let mut wtime = None; + let mut btime = None; + let mut movestogo = None; + let mut increment = DEFAULT_INCREMENT; + + // Parse time parameters from the "go" command + for i in 0..tokens.len() { + match tokens[i] { + "wtime" => { + wtime = tokens.get(i + 1).and_then(|&t| t.parse::().ok()); + } + "btime" => { + btime = tokens.get(i + 1).and_then(|&t| t.parse::().ok()); + } + "movestogo" => { + movestogo = tokens.get(i + 1).and_then(|&t| t.parse::().ok()); + } + "movetime" => { + if let Some(ms) = tokens.get(i + 1).and_then(|&t| t.parse::().ok()) + { + time_limit = Duration::from_millis(ms); + } + } + "inc" => { + increment = tokens + .get(i + 1) + .and_then(|&t| t.parse::().ok()) + .unwrap_or(DEFAULT_INCREMENT); + } + _ => {} + } + } + + // Calculate the time limit based on remaining time and moves to go + if let (Some(wtime), Some(btime)) = (wtime, btime) { + let is_white = game.side_to_move() == Color::White; + time_limit = calculate_time_per_move( + is_white, + wtime, + btime, + movestogo, + increment, + SAFETY_MARGIN, + ); + + println!("Time limit: {:?}", time_limit); + } + + // Find and make the best move + if let Some(best_move) = + find_best_move(&mut game, time_limit, &mut transposition_table) + { + game.make_move(best_move); + println!("bestmove {}", best_move); + } + } + "quit" => { + quit = true; + } + _ => {} + } + + io::stdout().flush().unwrap(); + if quit { + break; + } + } +} + +fn uci_to_move(uci: &str) -> Result { + let from = Square::from_str(&uci[0..2]).map_err(|_| "Invalid from square")?; + let to = Square::from_str(&uci[2..4]).map_err(|_| "Invalid to square")?; + + let promotion = if uci.len() == 5 { + Some(match uci.chars().nth(4).unwrap() { + 'q' => Piece::Queen, + 'r' => Piece::Rook, + 'b' => Piece::Bishop, + 'n' => Piece::Knight, + _ => return Err("Invalid promotion piece".to_string()), + }) + } else { + None + }; + + Ok(ChessMove::new(from, to, promotion)) +} + +fn find_best_move( + game: &mut Game, + time_limit: Duration, + transposition_table: &mut HashMap, +) -> Option { + let start_time = Instant::now(); + let mut best_move = None; + let mut best_score = i32::MIN; + let mut nodes_searched = 0; + + let board = game.current_position(); + let mut nodes_in_previous_layer = 1.0; // Starting node for depth 1 + let mut ebf = 1.0; // Exponential branching factor + + let mut pv_line = vec![]; // PV line for the best sequence of moves + + for depth in 1.. { + let legal_moves = MoveGen::new_legal(&board); + let mut nodes_in_current_layer = 0.0; + + for mv in legal_moves { + let new_board = board.make_move_new(mv); + + // Search the position recursively, retrieving the score + let score = search( + &new_board, + depth - 1, + i32::MIN, + i32::MAX, + transposition_table, + &mut pv_line, + ); + + nodes_searched += 1; + nodes_in_current_layer += 1.0; + + if score > best_score { + best_score = score; + best_move = Some(mv); + + // Update the PV line with the current move + pv_line.insert(0, mv); + } + } + + // Calculate elapsed time and nodes per second + let total_elapsed_time = start_time.elapsed().as_millis(); + let nps = if total_elapsed_time > 0 { + (nodes_searched as u128 * 1000) / total_elapsed_time + } else { + 1 // To avoid division by zero if elapsed time is very small + }; + + // Print the info line with the updated PV + println!( + "info depth {} seldepth {} multipv 1 score cp {} nodes {} nps {} hashfull 209 tbhits 0 time {} pv {}", + depth, + 0, // seldepth can be modified based on your search logic + best_score, + nodes_searched, + nps, + total_elapsed_time, + pv_line + .iter() + .map(|mv| format!("{}", mv)) + .collect::>() + .join(" ") + ); + + // Calculate EBF and estimate nodes for next depth + ebf = nodes_in_current_layer / nodes_in_previous_layer; + nodes_in_previous_layer = nodes_in_current_layer; + + let estimated_next_nodes = nodes_in_current_layer * ebf; + let estimated_time_for_next_depth = (estimated_next_nodes as u128 * 1000) / nps; + + // Break the loop if estimated time exceeds remaining time + if total_elapsed_time + estimated_time_for_next_depth >= time_limit.as_millis() { + break; + } } - moves.push_str("\n"); - // println!("{}", count); - println!("{}", moves); - // println!("{}", board.perft_test(6)); + best_move +} + +fn search( + board: &Board, + depth: i32, + mut alpha: i32, + beta: i32, + transposition_table: &mut HashMap, + pv_line: &mut Vec, // Mutable reference to the PV line +) -> i32 { + // Check if this position is already in the transposition table + if let Some(entry) = transposition_table.get(board) { + if entry.depth >= depth { + // Use the stored evaluation if it was at least as deep as the current search + return entry.score; + } + } + + if depth == 0 { + let score = evaluate(board); + + // Store the position in the transposition table + transposition_table.insert( + *board, + TranspositionTableEntry { + score, + depth, + best_move: None, + }, + ); + return score; + } + + let mut best_score = i32::MIN; + let mut best_move = None; + let legal_moves = MoveGen::new_legal(board); + + for mv in legal_moves { + let new_board = board.make_move_new(mv); + let score = -search( + &new_board, + depth - 1, + -beta, + -alpha, + transposition_table, + pv_line, + ); + + if score > best_score { + best_score = score; + best_move = Some(mv); + alpha = score.max(alpha); + } + + if alpha >= beta { + break; // Beta cut-off + } + } + + // Store the best move and score in the transposition table + transposition_table.insert( + *board, + TranspositionTableEntry { + score: best_score, + depth, + best_move, + }, + ); + + best_score +} + +#[derive(Clone, Copy)] +struct TranspositionTableEntry { + score: i32, + depth: i32, + best_move: Option, // Store the best move for PV reconstruction +} + +const PAWN_VALUE: i32 = 100; +const KNIGHT_VALUE: i32 = 320; +const BISHOP_VALUE: i32 = 330; +const ROOK_VALUE: i32 = 500; +const QUEEN_VALUE: i32 = 900; +const KING_VALUE: i32 = 20000; + +#[rustfmt::skip] +const PAWN_TABLE: [i32; 64] = [ + 0, 0, 0, 0, 0, 0, 0, 0, + 5, 10, 10, -20, -20, 10, 10, 5, + 5, -5, -10, 0, 0, -10, -5, 5, + 0, 0, 0, 20, 20, 0, 0, 0, + 5, 5, 10, 25, 25, 10, 5, 5, + 10, 10, 20, 30, 30, 20, 10, 10, + 50, 50, 50, 50, 50, 50, 50, 50, + 0, 0, 0, 0, 0, 0, 0, 0 +]; + +#[rustfmt::skip] +const KNIGHT_TABLE: [i32; 64] = [ + -50, -40, -30, -30, -30, -30, -40, -50, + -40, -20, 0, 0, 0, 0, -20, -40, + -30, 0, 10, 15, 15, 10, 0, -30, + -30, 5, 15, 20, 20, 15, 5, -30, + -30, 0, 15, 20, 20, 15, 0, -30, + -30, 5, 10, 15, 15, 10, 5, -30, + -40, -20, 0, 5, 5, 0, -20, -40, + -50, -40, -30, -30, -30, -30, -40, -50 +]; + +#[rustfmt::skip] +const BISHOP_TABLE: [i32; 64] = [ + -20, -10, -10, -10, -10, -10, -10, -20, + -10, 5, 0, 0, 0, 0, 5, -10, + -10, 10, 10, 10, 10, 10, 10, -10, + -10, 0, 10, 10, 10, 10, 0, -10, + -10, 5, 5, 10, 10, 5, 5, -10, + -10, 0, 5, 10, 10, 5, 0, -10, + -10, 0, 0, 0, 0, 0, 0, -10, + -20, -10, -10, -10, -10, -10, -10, -20 +]; + +#[rustfmt::skip] +const ROOK_TABLE: [i32; 64] = [ + 0, 0, 0, 5, 5, 0, 0, 0, + -5, 0, 0, 0, 0, 0, 0, -5, + -5, 0, 0, 0, 0, 0, 0, -5, + -5, 0, 0, 0, 0, 0, 0, -5, + -5, 0, 0, 0, 0, 0, 0, -5, + -5, 0, 0, 0, 0, 0, 0, -5, + 5, 10, 10, 10, 10, 10, 10, 5, + 0, 0, 0, 0, 0, 0, 0, 0 +]; + +#[rustfmt::skip] +const QUEEN_TABLE: [i32; 64] = [ + -20, -10, -10, -5, -5, -10, -10, -20, + -10, 0, 0, 0, 0, 0, 0, -10, + -10, 0, 5, 5, 5, 5, 0, -10, + -5, 0, 5, 5, 5, 5, 0, -5, + 0, 0, 5, 5, 5, 5, 0, -5, + -10, 5, 5, 5, 5, 5, 0, -10, + -10, 0, 5, 0, 0, 0, 0, -10, + -20, -10, -10, -5, -5, -10, -10, -20 +]; + +#[rustfmt::skip] +const KING_TABLE: [i32; 64] = [ + 20, 30, 10, 0, 0, 10, 30, 20, + 20, 20, 0, 0, 0, 0, 20, 20, + -10, -20, -20, -20, -20, -20, -20, -10, + 20, -30, -30, -40, -40, -30, -30, -20, + -30, -40, -40, -50, -50, -40, -40, -30, + -30, -40, -40, -50, -50, -40, -40, -30, + -30, -40, -40, -50, -50, -40, -40, -30, + -30, -40, -40, -50, -50, -40, -40, -30 +]; + +fn evaluate(board: &Board) -> i32 { + let mut score = 0; + + for sq in 0..64 { + let sq: Square = unsafe { Square::new(sq) }; + if let Some(piece) = board.piece_on(sq) { + let value = match piece { + Piece::Pawn => PAWN_VALUE + positional_value(piece, sq), + Piece::Knight => KNIGHT_VALUE + positional_value(piece, sq), + Piece::Bishop => BISHOP_VALUE + positional_value(piece, sq), + Piece::Rook => ROOK_VALUE + positional_value(piece, sq), + Piece::Queen => QUEEN_VALUE + positional_value(piece, sq), + Piece::King => KING_VALUE, + }; + + if board.color_on(sq) == Some(Color::White) { + score += value; + } else { + score -= value; + } + } + } + + score +} + +fn positional_value(piece: Piece, square: Square) -> i32 { + let index = square.to_index(); + + match piece { + Piece::Pawn => PAWN_TABLE[index], + Piece::Knight => KNIGHT_TABLE[index], + Piece::Bishop => BISHOP_TABLE[index], + Piece::Rook => ROOK_TABLE[index], + Piece::Queen => QUEEN_TABLE[index], + Piece::King => KING_TABLE[index], + } } diff --git a/src/utils/identification.rs b/src/utils/identification.rs deleted file mode 100644 index 678785a..0000000 --- a/src/utils/identification.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::definitions::color::Color; -use crate::definitions::piece::Piece; - -#[rustfmt::skip] -pub fn is_piece_big(piece: &Piece) -> bool {[false, false, true, true, true, true, true, false, true, true, true, true, true,][*piece as usize]} -#[rustfmt::skip] -pub fn is_piece_major(piece: &Piece) -> bool {[false, false, false, false, true, true, true, false, false, false, true, true, true,][*piece as usize]} -#[rustfmt::skip] -pub fn is_piece_minor(piece: &Piece) -> bool {[false, false, true, true, false, false, false, false, true, true, false, false, false,][*piece as usize]} -#[rustfmt::skip] -pub fn piece_value(piece: &Piece) -> u32 {[0, 100, 320, 330, 500, 900, 20000, 100, 320, 330, 500, 900, 20000,][*piece as usize]} - -// 0, WP, WN, WB, WR, WQ, WK, BP, BN, BB, BR, BQ, BK, -#[rustfmt::skip] -pub fn is_piece_knight(piece: &Piece) -> bool {[false, false, true, false, false, false, false, false, true, false, false, false, false,][*piece as usize]} -#[rustfmt::skip] -pub fn is_piece_king(piece: &Piece) -> bool {[false, false, false, false, false, false, true, false, false, false, false, false, true,][*piece as usize]} -#[rustfmt::skip] -pub fn is_piece_rook_or_queen(piece: &Piece) -> bool {[false, false, false, false, true, true, false, false, false, false, true, true, false,][*piece as usize]} -#[rustfmt::skip] -pub fn is_piece_bishop_or_queen(piece: &Piece) -> bool {[false, false, false, true, false, true, false, false, false, true, false, true, false,][*piece as usize]} -#[rustfmt::skip] -pub fn piece_color(piece: &Piece) -> Color {[Color::Both, Color::White, Color::White, Color::White, Color::White, Color::White, Color::White, Color::Black, Color::Black, Color::Black, Color::Black, Color::Black, Color::Black,][*piece as usize]} diff --git a/src/utils/mod.rs b/src/utils/mod.rs deleted file mode 100644 index 60e5a8a..0000000 --- a/src/utils/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod identification; -pub mod offsets; -pub mod zobrist; diff --git a/src/utils/offsets.rs b/src/utils/offsets.rs deleted file mode 100644 index 6e5b14a..0000000 --- a/src/utils/offsets.rs +++ /dev/null @@ -1,16 +0,0 @@ -#[rustfmt::skip] -pub const KING_OFFSETS: [(isize, isize); 8] = [ - (-1, -1), (0, -1), (1, -1), - (-1, 0), (1, 0), - (-1, 1), (0, 1), (1, 1), -]; - -#[rustfmt::skip] -pub const KNIGHT_OFFSETS: [(isize, isize); 8] = [ - (-1, -2), (1, -2), (-2, -1), (2, -1), - (-2, 1), (2, 1), (-1, 2), (1, 2), -]; - -pub const ROOK_OFFSETS: [(isize, isize); 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)]; - -pub const BISHOP_OFFSETS: [(isize, isize); 4] = [(-1, -1), (1, -1), (-1, 1), (1, 1)]; diff --git a/src/utils/zobrist.rs b/src/utils/zobrist.rs deleted file mode 100644 index 71d2018..0000000 --- a/src/utils/zobrist.rs +++ /dev/null @@ -1,16 +0,0 @@ -use rand::Rng; -use std::sync::LazyLock; - -pub static ZOBRIST_TABLE: LazyLock<[[u64; 64]; 14]> = std::sync::LazyLock::new(|| { - let mut rng = rand::thread_rng(); - let mut table = [[0u64; 64]; 14]; - - for piece_table in &mut table { - for square_hash in piece_table.iter_mut().take(64) { - // Generate a random u64 value for each side - *square_hash ^= rng.gen::(); - } - } - - table -});