diff --git a/Cargo.lock b/Cargo.lock index 77b0d42..103ea89 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,24 +9,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] -name = "atomic-polyfill" -version = "0.1.11" +name = "anstyle" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" -dependencies = [ - "critical-section", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "autocfg" @@ -36,9 +22,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" -version = "1.3.2" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bumpalo" @@ -93,40 +79,44 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.23" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" dependencies = [ - "bitflags", - "clap_lex", - "indexmap", - "textwrap", + "clap_builder", ] [[package]] -name = "clap_lex" -version = "0.2.4" +name = "clap_builder" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" dependencies = [ - "os_str_bytes", + "anstyle", + "clap_lex", ] +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + [[package]] name = "criterion" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" dependencies = [ "anes", - "atty", "cast", "ciborium", "clap", "criterion-plot", + "is-terminal", "itertools", - "lazy_static", "num-traits", + "once_cell", "oorandom", "plotters", "rayon", @@ -148,12 +138,6 @@ dependencies = [ "itertools", ] -[[package]] -name = "critical-section" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" - [[package]] name = "crossbeam-channel" version = "0.5.7" @@ -204,14 +188,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] -name = "fastrand" -version = "1.9.0" +name = "errno" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ - "instant", + "libc", + "windows-sys 0.52.0", ] +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "half" version = "1.8.2" @@ -220,41 +211,23 @@ checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hash32" -version = "0.2.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" dependencies = [ "byteorder", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "heapless" -version = "0.7.16" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ - "atomic-polyfill", "hash32", - "rustc_version", - "spin", "stable_deref_trait", ] -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.2.6" @@ -265,22 +238,20 @@ dependencies = [ ] [[package]] -name = "indexmap" -version = "1.9.2" +name = "hermit-abi" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" -dependencies = [ - "autocfg", - "hashbrown", -] +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] -name = "instant" -version = "0.1.12" +name = "is-terminal" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "cfg-if", + "hermit-abi 0.3.3", + "rustix", + "windows-sys 0.48.0", ] [[package]] @@ -307,27 +278,17 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" -version = "0.2.140" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] -name = "lock_api" -version = "0.4.9" +name = "linux-raw-sys" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "log" @@ -349,7 +310,7 @@ dependencies = [ [[package]] name = "monster_chess" -version = "0.0.13" +version = "0.0.24" dependencies = [ "criterion", "fastrand", @@ -388,12 +349,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - [[package]] name = "plotters" version = "0.3.4" @@ -478,12 +433,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] -name = "rustc_version" -version = "0.4.0" +name = "rustix" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ - "semver", + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", ] [[package]] @@ -507,12 +466,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "semver" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" - [[package]] name = "serde" version = "1.0.154" @@ -550,15 +503,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" -[[package]] -name = "spin" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5d6e0250b93c8427a177b849d144a96d5acc57006149479403d7861ab721e34" -dependencies = [ - "lock_api", -] - [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -576,12 +520,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - [[package]] name = "tinytemplate" version = "1.2.1" @@ -703,3 +641,135 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/Cargo.toml b/Cargo.toml index 38533e1..82cc820 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "monster_chess" -version = "0.0.13" +version = "0.0.24" edition = "2021" license = "MIT" description = "A fairy chess movegen library that can be easily extended to new chess-adjacent games." @@ -13,13 +13,13 @@ keywords = ["chess", "ataxx", "movegen"] debug = true [dependencies] -fastrand = "1.9.0" -heapless = "0.7.16" +fastrand = "2.0.0" +heapless = "0.8.0" shell-words = "1.1.0" [dev-dependencies] -criterion = "0.4" +criterion = "0.5.1" [[bench]] -name = "chess-perft" +name = "perft" harness = false diff --git a/benches/chess-perft.rs b/benches/chess-perft.rs deleted file mode 100644 index 5c18feb..0000000 --- a/benches/chess-perft.rs +++ /dev/null @@ -1,18 +0,0 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use monster_chess::{board::Board, games::chess::Chess}; - -fn startpos(depth: u32) { - let chess = Chess::create(); - let mut board = chess.from_fen( - "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", - ); - - board.perft(depth, true); -} - -fn criterion_benchmark(c: &mut Criterion) { - c.bench_function("startpos", |b| b.iter(|| startpos(4))); -} - -criterion_group!(benches, criterion_benchmark); -criterion_main!(benches); diff --git a/benches/perft.rs b/benches/perft.rs new file mode 100644 index 0000000..66abd58 --- /dev/null +++ b/benches/perft.rs @@ -0,0 +1,15 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use monster_chess::{games::{chess::Chess, ataxx::Ataxx}, board::game::Game}; + +fn startpos(game: Game<1>, depth: u32) { + let mut board = game.default(); + board.perft(depth, true); +} + +fn criterion_benchmark(c: &mut Criterion) { + c.bench_function("chess", |b| b.iter(|| startpos(Chess::create(), 4))); + c.bench_function("ataxx", |b| b.iter(|| startpos(Ataxx::create(), 4))); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/src/bitboard/bitscan.rs b/src/bitboard/bitscan.rs index c2afb2a..b6d8009 100644 --- a/src/bitboard/bitscan.rs +++ b/src/bitboard/bitscan.rs @@ -1,5 +1,6 @@ use super::BitBoard; +/// A direction of either `LEFT` or `RIGHT`, created primarily for bitscans of an arbitrary direction. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Direction { LEFT, @@ -64,6 +65,7 @@ impl BitBoard { } } + /// A bitscan from either given direction, left or right. pub fn bitscan(&self, direction: Direction) -> u16 { match direction { Direction::LEFT => self.bitscan_forward(), diff --git a/src/bitboard/shifts.rs b/src/bitboard/shifts.rs index e7a90cd..a8e68de 100644 --- a/src/bitboard/shifts.rs +++ b/src/bitboard/shifts.rs @@ -3,34 +3,42 @@ use crate::board::Cols; use super::BitBoard; impl BitBoard { + /// Shifts the `BitBoard` upwards by a given amount. pub fn up(&self, shift: u16, cols: Cols) -> BitBoard { *self >> shift * (cols) } + /// Shifts the `BitBoard` downwards by a given amount. pub fn down(&self, shift: u16, cols: Cols) -> BitBoard { *self << shift * (cols) } + /// Shifts the `BitBoard` right by a given amount. pub fn right(&self, shift: u16) -> BitBoard { *self << shift } + /// Shifts the `BitBoard` left by a given amount. pub fn left(&self, shift: u16) -> BitBoard { *self >> shift } + /// Shifts the given `BitBoard` upwards by a given amount, mutating it. pub fn up_mut(&mut self, shift: u16, cols: Cols) { *self >>= shift * (cols); } + /// Shifts the given `BitBoard` upwards by a given amount, mutating it. pub fn down_mut(&mut self, shift: u16, cols: Cols) { *self <<= shift * (cols); } + /// Shifts the given `BitBoard` right by a given amount, mutating it. pub fn right_mut(&mut self, shift: u16) { *self <<= shift; } + /// Shifts the given `BitBoard` left by a given amount, mutating it. pub fn left_mut(&mut self, shift: u16) { *self >>= shift; } diff --git a/src/bitboard/transforms.rs b/src/bitboard/transforms.rs index 43700d1..352d3c7 100644 --- a/src/bitboard/transforms.rs +++ b/src/bitboard/transforms.rs @@ -3,6 +3,7 @@ use std::cmp::Ordering; use super::BitBoard; +/// Generate `BitBoard`s for each rank of the board. pub fn generate_ranks(cols: Cols, rows: Rows) -> Vec> { let mut ranks: Vec> = Vec::with_capacity(rows as usize); let mut rank = BitBoard::::starting_at_lsb(0, cols); @@ -15,6 +16,7 @@ pub fn generate_ranks(cols: Cols, rows: Rows) -> Vec ranks } +/// Generate `BitBoard`s for each file of the board. pub fn generate_files(cols: Cols, rows: Rows) -> Vec> { let mut files: Vec> = Vec::with_capacity(rows as usize); let mut file = BitBoard::::from_lsb(0); @@ -32,7 +34,8 @@ pub fn generate_files(cols: Cols, rows: Rows) -> Vec } impl BitBoard { - pub fn flip_vertically(self, ranks: &Vec>, cols: Cols, rows: Rows) -> BitBoard { + /// Flip the `BitBoard` vertically. + pub fn flip_vertically(self, ranks: &[BitBoard], cols: Cols, rows: Rows) -> BitBoard { let mut new_board = BitBoard::::new(); let max_rank = rows - 1; @@ -57,7 +60,8 @@ impl BitBoard { new_board } - pub fn flip_horizontally(self, files: &Vec>, cols: Cols) -> BitBoard { + /// Flip the `BitBoard` horizontally. + pub fn flip_horizontally(self, files: &[BitBoard], cols: Cols) -> BitBoard { let mut new_board = BitBoard::::new(); let max_file = cols - 1; diff --git a/src/bitboard/util.rs b/src/bitboard/util.rs index dc46dbd..99761d8 100644 --- a/src/bitboard/util.rs +++ b/src/bitboard/util.rs @@ -1,38 +1,47 @@ -/// I've chosen to use this little utility because of its performance in benchmarks being the best, and because it makes it the easiest to specialize to the needs of this project (in terms of both optimizations and code structure.) -/// In this case, those needs being a way to have bigger integer sizes that are compatible with bit operations at high speeds. - +/// `BitBoard` is essentially a wrapper around a `[u128; T]`. +/// It has multiple `u128`s so it can serve as essentially a bigger unsigned integer for `T > 1`. +/// It handles bit operations and even some mathematical operations to operate essentially as if it was any other integer type. +/// It also has methods specifically related to its use as a BitBoard, like moving it up, down, right, left, etc. +/// For `T = 1` (a BitBoard with only one `u128`), it should compile down to esssentially a single `u128`, and should be very fast. #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord)] pub struct BitBoard { pub bits: [u128; T], } impl BitBoard { + /// Create a `BitBoard` from a backing array. pub fn from_data(data: [u128; T]) -> BitBoard { BitBoard { bits: data } } + /// Create a `BitBoard` from a single `u128`. pub fn from_element(el: u128) -> BitBoard { let mut arr = [0; T]; arr[T - 1] = el; BitBoard { bits: arr } } + /// Create a `BitBoard` with a single bit from the index of that bit from the LSB. pub fn from_lsb(bit: u16) -> BitBoard { BitBoard::::from_element(1) << bit } + /// Create a `BitBoard` with a single bit from the index of that bit from the MSB. pub fn from_msb(bit: u16) -> BitBoard { !(BitBoard::::max() >> 1) >> bit } + /// Create a `BitBoard` starting at a given bit, and continuing for a given length. pub fn starting_at_lsb(bit: u16, length: u16) -> BitBoard { (BitBoard::::from_lsb(length) - BitBoard::::from_element(1)) << bit } + /// Check if a `BitBoard` has a given bit. pub fn has_bit(self, bit: u16) -> bool { (self & (BitBoard::::from_element(1) << bit)).is_set() } + /// Check if a `BitBoard` is empty (if that `BitBoard` is `0`.) pub fn is_empty(&self) -> bool { if T == 1 { return self.bits[0] == 0; @@ -41,6 +50,7 @@ impl BitBoard { self.bits.iter().all(|el| *el == 0) } + /// Check if a `BitBoard` is set (if that `BitBoard` isn't `0`, if it has at least one set bit.) pub fn is_set(&self) -> bool { if T == 1 { return self.bits[0] != 0; @@ -49,14 +59,19 @@ impl BitBoard { self.bits.iter().any(|el| *el != 0) } + /// Get the highest possible value for a given `BitBoard`. pub fn max() -> BitBoard { BitBoard::::from_data([u128::MAX; T]) } + /// Initialize a new, empty `BitBoard`. pub fn new() -> BitBoard { BitBoard::::from_data([0; T]) } + /// A utility method primarily for internally handling bit operations. + /// Apply a function to each of the `u128`s of two `BitBoards`. + /// For instance, `bitboard.apply(rhs, |el| el.0 | el.1)` would get a `BitBoard` for the `or` bit operation. #[inline(always)] pub fn apply(self, rhs: BitBoard, apply: impl Fn((&u128, u128)) -> u128) -> Self { if T == 1 { @@ -77,6 +92,9 @@ impl BitBoard { } } + /// A utility method primarily for internally handling bit operations. + /// Apply a function to each of the `u128`s of two `BitBoard`s, mutating the first of those `BitBoard`s. + /// For instance, `bitboard.effect(rhs, |el| el.0 | el.1)` would apply the `or` operation to this `BitBoard`. #[inline(always)] pub fn effect(&mut self, rhs: BitBoard, apply: impl Fn((&u128, u128)) -> u128) { if T == 1 { @@ -94,6 +112,7 @@ impl BitBoard { .expect(&format!("Could not convert BitBoard data vector into an array when applying operation with `effect`.")); } + /// Count the number of zero bits in a given `BitBoard`. pub fn count_zeros(&self) -> u32 { if T == 1 { self.bits[0].count_zeros() @@ -102,6 +121,7 @@ impl BitBoard { } } + /// Count the number of one bits in a given `BitBoard`. pub fn count_ones(&self) -> u32 { if T == 1 { self.bits[0].count_ones() @@ -110,6 +130,7 @@ impl BitBoard { } } + /// Outputs an array of all bits, with `0` if they're off, and `1` otherwise. /// Not a well optimized method; avoid using in hot loops. pub fn get_bits(&self) -> Vec { let mut bits: Vec = Vec::with_capacity(128 * T); @@ -121,10 +142,13 @@ impl BitBoard { bits } - pub fn iter_set_bits(mut self, end: u16) -> BitIterator { + /// Iterates over all set bits in the `BitBoard`. + pub fn iter_set_bits(self, end: u16) -> BitIterator { BitIterator(self, end) } + /// Displays a `BitBoard`, showing ⬛ if the bit is off, and ⬜ if it's on. + /// Consider using this if you need to debug a `BitBoard`. pub fn display(&self, rows: usize, cols: usize) -> String { let mut chunks = Vec::::with_capacity(rows); for (ind, row) in self.get_bits().chunks(cols).enumerate() { @@ -144,6 +168,7 @@ impl BitBoard { } } +/// `BitIterator` is used to efficiently iterate over all of the bits in a `BitBoard` with `BitBoard.iter_set_bits()`. pub struct BitIterator(pub BitBoard, u16); impl Iterator for BitIterator { diff --git a/src/board/actions.rs b/src/board/actions.rs index 9cdfdf1..ad013d2 100644 --- a/src/board/actions.rs +++ b/src/board/actions.rs @@ -25,7 +25,7 @@ pub struct Action { pub move_type: ActionInfo } -/// This is a theoretically possible action. It doesn't even have to be actually possible. +/// A theoretically possible action. It doesn't even have to be actually possible. /// It's mainly there for Neural Networks to be able to index moves. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct TheoreticalAction { diff --git a/src/board/edges.rs b/src/board/edges.rs index 222378c..159d6d1 100644 --- a/src/board/edges.rs +++ b/src/board/edges.rs @@ -1,10 +1,7 @@ use crate::bitboard::BitBoard; use super::{ - actions::{Action, HistoryMove, UndoMoveError}, - game::Game, - pieces::Piece, - Board, Cols, Rows, + Cols, Rows, }; pub type EdgeBuffer = u16; @@ -24,7 +21,7 @@ pub fn generate_edges(buffer: EdgeBuffer, rows: Rows, cols: Cols let mut left = BitBoard::max() & (!(BitBoard::max() << (buffer))); for _ in 1..rows { - left |= (left << (cols)); + left |= left << (cols); } let right = left << (cols - buffer); diff --git a/src/board/fen/args.rs b/src/board/fen/args.rs index cc7f7cc..5d3a83a 100644 --- a/src/board/fen/args.rs +++ b/src/board/fen/args.rs @@ -1,12 +1,10 @@ use std::fmt::Debug; use super::super::{ - actions::{Action, HistoryMove, UndoMoveError}, - game::Game, - pieces::Piece, - Board, Cols, Rows, + Board, }; +/// `FenTeamArgument` represents which team is going to move next in your FEN. #[derive(Clone, Debug)] pub enum FenTeamArgument { Number, @@ -51,6 +49,9 @@ impl FenArgument for FenTeamArgument { } } +/// `FenTurns` represents how many turns have been played in your game. +/// This counter might not be a counter of all turns in the entire game, but how many since the last of a certain type of move, for instance. +/// Games can manage what counters represent by handling `update` in `MoveController`. #[derive(Debug)] pub struct FenTurns; @@ -69,6 +70,9 @@ impl FenArgument for FenTurns { } } +/// `FenSubMoves` represents how many sub moves (or in the case of two players, half moves) have been played in your game. +/// This counter might not be a counter of all sub moves in the entire game, but how many since the last of a certain type of move, for instance. +/// Games can manage what counters represent by handling `update` in `MoveController`. #[derive(Debug)] pub struct FenSubMoves; @@ -87,6 +91,9 @@ impl FenArgument for FenSubMoves { } } +/// `FenFullMoves` represents how many sub moves (or in the case of two players, half moves) have been played in your game. +/// This counter might not be a counter of all full moves in the entire game, but how many since the last of a certain type of move, for instance. +/// Games can manage what counters represent by handling `update` in `MoveController`. #[derive(Debug)] pub struct FenFullMoves; @@ -105,11 +112,15 @@ impl FenArgument for FenFullMoves { } } +/// This is the error that is shown if a given `FenArgument` has an error when being parsed. #[derive(Debug, Clone)] pub enum FenDecodeError { InvalidArgument(String), } +/// `FenArgument` represents a given argument in your FEN. +/// All FENs will show the board state, but after that, games can specify a variety of arguments. +/// In chess, the FEN arguments are `FenTeamArgument`, `ChessCastlingRights`, `ChessEnPassant`, `FebSubMoves` and `FenFullMoves`. pub trait FenArgument : Debug + Send + Sync { /// `encode` takes in a board, and outputs what this FEN argument's encoded result would be (eg. for a team argument, it could be `"b"`) fn encode(&self, board: &Board) -> String; diff --git a/src/board/fen/init.rs b/src/board/fen/init.rs index 0428faf..4015b96 100644 --- a/src/board/fen/init.rs +++ b/src/board/fen/init.rs @@ -1,13 +1,12 @@ use shell_words::split; use super::super::{ - actions::{Action, HistoryMove, UndoMoveError}, game::Game, - pieces::Piece, - Board, Cols, Rows, + Board, }; impl Game { + /// Initialize a `Board` from a given FEN. pub fn from_fen(&self, fen: &str) -> Board { let args = split(fen).expect(&format!("{fen} cannot be split into arguments.")); let mut board = Board::from_fen_state(self, (self.rows, self.cols), &args[0]); @@ -44,6 +43,7 @@ impl Game { } impl<'a, const T: usize> Board<'a, T> { + /// Convert this `Board` to a FEN. pub fn to_fen(&self) -> String { let mut fen = self.to_fen_state(); if fen.contains(" ") { diff --git a/src/board/fen/options.rs b/src/board/fen/options.rs index 4b12c2f..2fe2430 100644 --- a/src/board/fen/options.rs +++ b/src/board/fen/options.rs @@ -2,32 +2,39 @@ use std::fmt::Debug; use super::{ super::{ - actions::{Action, HistoryMove, UndoMoveError}, - game::Game, - pieces::Piece, - Board, Cols, Rows, + Board, }, FenArgument, }; -pub enum FenStateTeams { - Number, - TeamNames(Vec), -} - +/// `FenState` represents all of the information regarding your FEN implementation's representation of the board state itself. #[derive(Debug)] pub struct FenState { - pub first_moves: bool, pub gaps: char + /// This represents whether or not the first move of a piece should be shown in your game's FEN. + /// If `true`, then `!` will be shown after the piece type in a FEN if that piece has moved once before. + /// Otherwise, all pieces will be assumed to be on their first move (but you can modify this behavior with `PostProcess`) + pub first_moves: bool, + /// This is what character gaps will have in your FENs. + /// Gaps are 'holes' in the board that can't be moved to, but can be moved through depending on the piece. + pub gaps: char } +/// `PostProcess` allows you to apply additional changes to the board after you've parsed the FEN state and all FEN arguments. pub trait PostProcess : Debug + Send + Sync { + /// Apply a change to the `Board` after all FEN parsing is done. + /// For instance, `Chess` uses this to handle whether or not pawns have had their first move. fn apply(&self, board: &mut Board); } +/// `FenOptions` represents all of the information regarding how your FEN representation should be encoded and decoded. #[derive(Debug)] pub struct FenOptions { + /// `state` represents how the board state should be encoded itself. pub state: FenState, + /// `args` contains all of your `FenArgument`s which handle any additional information the board state doesn't specify. pub args: Vec<(String, Box>)>, + /// `post_process` applies any additional processing after your FEN parsing is done. pub post_process: Box>, + /// `default_fen` is the default FEN position of your game. pub default_fen: String } diff --git a/src/board/fen/state.rs b/src/board/fen/state.rs index 7359007..6e0f28e 100644 --- a/src/board/fen/state.rs +++ b/src/board/fen/state.rs @@ -1,9 +1,7 @@ use crate::{board::pieces::PieceSymbol, bitboard::BitBoard}; use super::super::{ - actions::{Action, HistoryMove, UndoMoveError}, game::Game, - pieces::Piece, Board, Cols, Rows, }; diff --git a/src/board/game.rs b/src/board/game.rs index e4a7065..34b09a0 100644 --- a/src/board/game.rs +++ b/src/board/game.rs @@ -36,13 +36,18 @@ pub fn get_theoretical_moves_bound(board: &Board, max_info: A theoretical_moves } +pub struct MoveLegalResponse { + pub is_legal: bool, + pub made_move: Option>> +} + pub trait MoveController : Debug + Send + Sync { fn transform_moves(&self, board: &mut Board, mode: u16, actions: Vec) -> Vec; - fn is_legal(&self, board: &mut Board, action: &Move) -> bool; + fn is_legal(&self, board: &mut Board, action: &Move, unmake_move: bool) -> MoveLegalResponse; fn use_pseudolegal(&self) -> bool; - fn add_moves(&self, board: &Board, actions: &mut Vec) {} - fn make_drop_move(&self, board: &mut Board, action: &Action) -> Option> { + fn add_moves(&self, _board: &Board, _actions: &mut Vec) {} + fn make_drop_move(&self, _board: &mut Board, _action: &Action) -> Option> { panic!("Drop moves aren't supported. Make sure to override `make_drop_move` in your game's MoveController to support them."); } @@ -54,7 +59,7 @@ pub trait MoveController : Debug + Send + Sync { .map(|el| el.clone()) } - /// This is fetches all theoretically possible moves. These moves might not even be actually possible, they're just used for indexing. + /// Is fetches all theoretically possible moves. These moves might not even be actually possible, they're just used for indexing. /// Ideally, this should be a list of all actually possible moves, but an upper bound is fine. fn get_theoretical_moves(&self, board: &Board) -> Vec; @@ -92,7 +97,7 @@ pub trait MoveController : Debug + Send + Sync { } } - fn update(&self, action: &Move, state: &BoardState) -> TurnUpdate { + fn update(&self, _action: &Move, _state: &BoardState) -> TurnUpdate { TurnUpdate { turns: CounterUpdate::Next, sub_moves: CounterUpdate::Next, @@ -100,7 +105,7 @@ pub trait MoveController : Debug + Send + Sync { } } - /// This is an upper-bound of all max available moves from any given position. + /// An upper-bound of all max available moves from any given position. fn get_max_available_moves(&self) -> u32; } @@ -112,12 +117,12 @@ pub enum GameResults { } pub trait Resolution : Debug + Send + Sync { - fn resolve(&self, board: &mut Board, legal_moves: &Vec) -> GameResults; + fn resolve(&self, board: &mut Board, legal_moves: &[Move]) -> GameResults; } pub trait ZobristController : Debug + Send + Sync { fn get_extra_hashes(&self) -> usize { 0 } - fn apply(&self, hash: &mut u64, zobrist: &mut ZobristHashTable, board: &mut Board) {} + fn apply(&self, _hash: &mut u64, _zobrist: &mut ZobristHashTable, _board: &mut Board) {} } #[derive(Debug)] diff --git a/src/board/lookups.rs b/src/board/lookups.rs index a819351..470727f 100644 --- a/src/board/lookups.rs +++ b/src/board/lookups.rs @@ -5,8 +5,8 @@ use super::{pieces::Piece, AttackLookup, Board, Cols, Rows}; pub fn generate_lookups( board: &Board, piece: &&'static dyn Piece, - rows: Rows, - cols: Cols, + _rows: Rows, + _cols: Cols, ) -> AttackLookup { let mut lookups = Vec::with_capacity(board.state.squares as usize); diff --git a/src/board/perft.rs b/src/board/perft.rs index 6eae2f2..6f2ef32 100644 --- a/src/board/perft.rs +++ b/src/board/perft.rs @@ -1,9 +1,6 @@ use super::{ - actions::{Action, HistoryMove, UndoMoveError}, - edges::Edges, - game::Game, - pieces::Piece, - Board, + game::{MoveLegalResponse, NORMAL_MODE}, + Board, actions::HistoryMove, }; pub type PerftBranch = (String, PerftResults); @@ -32,13 +29,42 @@ impl<'a, const T: usize> Board<'a, T> { } let mut nodes = 0; - let moves = if legality { - self.generate_legal_moves(0) + + let psuedolegal = self.game.controller.use_pseudolegal(); + let psuedolegal_check = legality && psuedolegal; + + let moves = if psuedolegal { + self.generate_moves(NORMAL_MODE) } else { - self.generate_moves(0) + self.generate_legal_moves(NORMAL_MODE) }; + + if depth == 1 && !psuedolegal_check { + return moves.len() as u64; + } + for node in moves { - let undo = self.make_move(&node); + let mut undo: Option> = None; + + if psuedolegal_check { + let MoveLegalResponse { is_legal, made_move } = self.game.controller.is_legal(self, &node, false); + + if !is_legal { + if let Some(made_move) = made_move { + self.undo_move(made_move); + } + continue; + } + + if let Some(made_move) = made_move { + undo = made_move; + } + } + + if undo.is_none() { + undo = self.make_move(&node); + } + nodes += self.perft(depth - 1, legality); self.undo_move(undo); } @@ -76,7 +102,7 @@ impl<'a, const T: usize> Board<'a, T> { let mut nodes = 0; let mut branches: Vec = vec![]; - for node in self.generate_legal_moves(0) { + for node in self.generate_legal_moves(NORMAL_MODE) { let undo = self.make_move(&node); let results = self.branch_perft(depth - 1); nodes += results.nodes; diff --git a/src/board/pieces/mod.rs b/src/board/pieces/mod.rs index 45deee2..be61e3f 100644 --- a/src/board/pieces/mod.rs +++ b/src/board/pieces/mod.rs @@ -4,32 +4,62 @@ use crate::bitboard::BitBoard; use super::{ actions::{ - Action, HistoryMove, HistoryState, HistoryUpdate, IndexedPreviousBoard, PreviousBoard, - UndoMoveError, Move, ActionInfo, TurnInfo, + Action, HistoryMove, HistoryState, HistoryUpdate, IndexedPreviousBoard, PreviousBoard, Move, ActionInfo, TurnInfo, }, game::Game, - AttackDirections, AttackLookup, Board, BoardState, Cols, PieceType, Rows, update_turns, reverse_turns, + AttackDirections, AttackLookup, Board, BoardState, PieceType, update_turns, reverse_turns, }; +/// `PieceSymbol` shows how a piece will be displayed in FEN. +/// +/// If it's `Char`, it'll be displayed based on what char it is, and uppercase or lowercase depending on the team. +/// For teams of three or more, you'd have to specify the team number after the piece. +/// +/// If it's `TeamSymbol`, the piece is displayed with a different char depending on the team. +/// This is more useful for games with very few pieces, like Ataxx with only one piece type. pub enum PieceSymbol { Char(char), TeamSymbol(Vec), } +/// This is a concept for the base value of `move_type` or `info`, representing a move with normal info. const NORMAL_MOVE: u16 = 0; +/// `Piece` is the essential trait that all of `monster-chess` is built on top of. +/// +/// It defines the following: +/// - How a piece will be displayed in the FEN. +/// - What `info` means for a given piece (`info` describing extra information that `from` and `to` cannot express.) +/// - If the piece has an attack lookup, and how to generate that lookup table. +/// - What BitBoard moves are generated by the piece. +/// - What should happen when the move is made, and when it is undone. pub trait Piece : Debug + Send + Sync { + /// Gets the `PieceSymbol`, which describes how the piece is represented in the board state of FENs. fn get_piece_symbol(&self) -> PieceSymbol; - fn format_info(&self, board: &Board, info: ActionInfo) -> String { + /// Formats the move information. + /// + /// For instance, take pawns in chess. + /// If it's `0`, it'll be represented as nothing. + /// However, for `1..5`, it'll show that piece is being promoted to (eg. `"q"`.) + fn format_info(&self, _board: &Board, _info: ActionInfo) -> String { "".to_string() } - fn parse_info(&self, board: &Board, info: String) -> u32 { + + /// Parses the information from the move info. + /// + /// For instance, take pawns in chess. + /// If it's nothing, it knows it'll be `0`. + /// However, for say, `"q"`, it'll know that's the 4th promotion piece and output `4`. + fn parse_info(&self, _board: &Board, _info: String) -> u32 { 0 } + /// Whether or not this `Piece` takes advantage of lookup tables. fn can_lookup(&self) -> bool; + /// This gets the lookup table for the given piece type, if one exists. + /// It's best not to override this method, as `monster-chess` will handle it for you. fn get_attack_lookup<'a>( &self, board: &'a Board, @@ -38,6 +68,7 @@ pub trait Piece : Debug + Send + Sync { board.attack_lookup.get(piece_type as usize) } + /// This gives you a `BitBoard` of possible move squares. fn get_moves( &self, board: &Board, @@ -46,24 +77,34 @@ pub trait Piece : Debug + Send + Sync { team: u16, mode: u16, ) -> BitBoard; + + /// This gives you a mask of all possible move squares. + /// Unless you'll frequently need to check for if a piece can move to a given square, it's best not to reimplement this. fn can_move_mask( &self, board: &Board, from: BitBoard, - from_bit: u16, + _from_bit: u16, piece_type: PieceType, team: u16, mode: u16, - to: BitBoard, + _to: BitBoard, ) -> BitBoard { self.get_moves(board, from, piece_type, team, mode) } + /// This gives you all of the lookup squares from a given position. + /// It returns an `AttackDirections`, because you may have multiple lookup boards for any given position. + /// Take slider pieces in chess. + /// You have to apply move generation to every individual ray, so you can see if a piece is in the way. + /// For those pieces then, there's a lookup board for every ray. #[allow(unused_variables)] fn generate_lookup_moves(&self, board: &Board, from: BitBoard) -> AttackDirections { Vec::new() } + /// This makes a capture move, assuming that captures work just like they do in chess. + /// If you aren't overriding `make_move` and you need custom capture logic, make sure to override this. fn make_capture_move( &self, board: &mut Board, @@ -132,6 +173,8 @@ pub trait Piece : Debug + Send + Sync { Some(history_move) } + /// This makes a normal (non-capturing) move, assuming that normal moves work just like they do in chess. + /// If you aren't overriding `make_move` and you need custom normal move logic, make sure to override this. fn make_normal_move( &self, board: &mut Board, @@ -170,6 +213,10 @@ pub trait Piece : Debug + Send + Sync { Some(history_move) } + /// An implementation of `make_move`, provided move-making follows the same logic as chess. + /// + /// If the distinction between normal moves and capture moves is the same as chess, consider keeping `make_move` untouched, and overriding `make_normal_move` and `make_capture_move`. + /// Otherwise, it may be best to implement `make_move` yourself. fn make_move(&self, board: &mut Board, action: &Action) -> Option> { if let Some(from) = action.from { let from = BitBoard::from_lsb(from); @@ -190,6 +237,8 @@ pub trait Piece : Debug + Send + Sync { } } + /// An implementation of `undo_move`, resetting the board to its previous state. + /// It's not recommended to override `undo_move`, as it generalizes to all games and all types of move-making. fn undo_move(&self, state: &mut BoardState, game: &Game, history_move: &HistoryMove) { reverse_turns(state, game, &history_move); @@ -227,6 +276,9 @@ pub trait Piece : Debug + Send + Sync { } } + /// An implementation of `add_actions`, mapping every bit in `get_moves` to a new action. + /// + /// It may be best to override `add_actions` if you need to handle custom types of moves with their own `info`. fn add_actions( &self, actions: &mut Vec, diff --git a/src/board/positions.rs b/src/board/positions.rs index d6ea4c6..8a14cd8 100644 --- a/src/board/positions.rs +++ b/src/board/positions.rs @@ -1,8 +1,5 @@ use super::{ - actions::{Action, HistoryMove, UndoMoveError, Move}, - edges::Edges, - game::Game, - pieces::Piece, + actions::{Move}, Board, }; diff --git a/src/board/tests.rs b/src/board/tests.rs index 691c229..b9cfecb 100644 --- a/src/board/tests.rs +++ b/src/board/tests.rs @@ -2,7 +2,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use super::game::Game; -fn get_time_ms() -> u128 { +pub fn get_time_ms() -> u128 { let start = SystemTime::now(); let since_the_epoch = start .duration_since(UNIX_EPOCH) diff --git a/src/board/util.rs b/src/board/util.rs index 96f3b59..35ac1d7 100644 --- a/src/board/util.rs +++ b/src/board/util.rs @@ -4,17 +4,16 @@ use std::fmt::Error; use std::fmt::Formatter; use heapless::{Vec as HeapVec, Deque as HeapDeque}; -use std::collections::VecDeque; -use fastrand; + + use crate::bitboard::{BitBoard, generate_ranks, generate_files}; use super::actions::{Move, TurnInfo, CounterUpdate}; use super::{ - actions::{Action, HistoryMove, UndoMoveError, HistoryState}, + actions::{HistoryMove, HistoryState}, edges::{generate_edge_list, Edges}, - game::Game, - pieces::Piece, zobrist::ZobristHashTable, + game::Game }; pub type PieceType = u16; @@ -150,7 +149,7 @@ fn generate_forward_lookup(count: u16) -> HeapVec { if new_val >= count { new_val = 0; } - lookup.push(new_val); + lookup.push(new_val).expect("Can't exceed allocated space."); } lookup } @@ -163,7 +162,7 @@ fn generate_reverse_lookup(count: u16) -> HeapVec { if new_val < 0 { new_val = (count - 1) as i16; } - lookup.push(new_val as u16); + lookup.push(new_val as u16).expect("Can't exceed allocated space."); } lookup } @@ -233,8 +232,6 @@ impl<'a, const T: usize> Board<'a, T> { pub fn can_move(&self, team: u16, target: BitBoard, mode: u16) -> bool { let board_len = self.state.squares; - - let team = self.state.moving_team; let mut mask = BitBoard::new(); for (ind, board) in self.state.pieces.iter().enumerate() { @@ -276,8 +273,8 @@ impl<'a, const T: usize> Board<'a, T> { vec![] } - pub fn generate_drop_moves(&self, mode: u16) -> Vec { - let team = self.state.moving_team; + pub fn generate_drop_moves(&self, _mode: u16) -> Vec { + let _team = self.state.moving_team; let mut actions: Vec = Vec::with_capacity(self.state.squares as usize); self.game.controller.add_moves(self, &mut actions); @@ -338,7 +335,7 @@ impl<'a, const T: usize> Board<'a, T> { return None; } - self.history.push_back(action); + self.history.push_back(action).ok()?; if self.history.len() > self.game.saved_last_moves.into() { self.history.pop_front() @@ -354,7 +351,7 @@ impl<'a, const T: usize> Board<'a, T> { if let Some(first_history_move) = first_history_move { self.history.pop_back(); - self.history.push_front(first_history_move); + self.history.push_front(first_history_move).expect("Won't fully fill history allocation."); } } @@ -407,7 +404,7 @@ impl<'a, const T: usize> Board<'a, T> { }; } None => { - // We panic instead of making it an error because this is an incredible unlikely error that almost + // We panic instead of making it an error because this is an incredibly unlikely error that almost // certainly won't happen in monster-chess's code, and consumers would easily be able // to come across and handle this. // It isn't worth the effort having to propagate the error through so many functions. diff --git a/src/board/zobrist.rs b/src/board/zobrist.rs index 067771f..31fc059 100644 --- a/src/board/zobrist.rs +++ b/src/board/zobrist.rs @@ -1,4 +1,4 @@ -use super::{Board, game::Game}; +use super::{Board}; #[derive(Debug, Clone)] pub struct ZobristHashTable { @@ -70,11 +70,11 @@ impl ZobristHashTable { #[cfg(test)] mod tests { - use fastrand::u64; + use crate::games::chess::Chess; - use super::ZobristHashTable; + #[test] fn chess_zobrist_test() { diff --git a/src/games/ataxx/controller.rs b/src/games/ataxx/controller.rs index d2bb3dd..42e916d 100644 --- a/src/games/ataxx/controller.rs +++ b/src/games/ataxx/controller.rs @@ -1,25 +1,28 @@ use std::collections::HashSet; -use crate::{board::{game::{MoveController, NORMAL_MODE, get_theoretical_moves_bound}, Board, actions::{Action, TheoreticalAction, Move, TheoreticalMove, TurnUpdate, CounterUpdate}, BoardState}, bitboard::BitBoard}; +use crate::{board::{game::{MoveController, NORMAL_MODE, get_theoretical_moves_bound, MoveLegalResponse}, Board, actions::{Move, TheoreticalMove, TurnUpdate, CounterUpdate}, BoardState}, bitboard::BitBoard}; -use super::is_single_move; +use super::{is_single_move, pieces::STONE}; #[derive(Debug)] pub struct AtaxxMoveController; impl MoveController for AtaxxMoveController { - fn is_legal(&self, board: &mut Board, action: &Move) -> bool { - return true; + fn is_legal(&self, _board: &mut Board, _action: &Move, _make_moves: bool) -> MoveLegalResponse { + MoveLegalResponse { + is_legal: true, + made_move: None + } } fn use_pseudolegal(&self) -> bool { return false; } - fn transform_moves(&self, board: &mut Board, mode: u16, actions: Vec) -> Vec { + fn transform_moves(&self, board: &mut Board, _mode: u16, actions: Vec) -> Vec { // No Legal Moves if actions.len() == 0 { let board_mask = BitBoard::starting_at_lsb(0, 49); - let filled_squares = board.state.pieces[0]; + let filled_squares = board.state.pieces[STONE]; let empty_squares = board_mask & !filled_squares; let team_squares = board.state.teams[board.state.moving_team as usize]; @@ -32,7 +35,6 @@ impl MoveController for AtaxxMoveController { } else { actions } - } else { let mut set = HashSet::::with_capacity(actions.len()); let mut new_actions = Vec::with_capacity(actions.len()); @@ -85,7 +87,7 @@ impl MoveController for AtaxxMoveController { ] } - fn update(&self, action: &Move, state: &BoardState) -> TurnUpdate { + fn update(&self, action: &Move, _state: &BoardState) -> TurnUpdate { TurnUpdate { turns: CounterUpdate::Next, sub_moves: match action { diff --git a/src/games/ataxx/pieces/mod.rs b/src/games/ataxx/pieces/mod.rs index e9e1599..2d94010 100644 --- a/src/games/ataxx/pieces/mod.rs +++ b/src/games/ataxx/pieces/mod.rs @@ -1,6 +1,6 @@ use std::usize; -use crate::{board::{pieces::{Piece, PieceSymbol}, Board, AttackLookup, AttackDirections, actions::{Action, PreviousBoard, HistoryMove, HistoryState, HistoryUpdate, IndexedPreviousBoard, Move}, edges::Edges, Cols, update_turns, PieceType}, bitboard::BitBoard}; +use crate::{board::{pieces::{Piece, PieceSymbol}, Board, AttackDirections, actions::{Action, PreviousBoard, HistoryMove, HistoryState, HistoryUpdate, IndexedPreviousBoard, Move}, edges::Edges, Cols, update_turns, PieceType}, bitboard::BitBoard}; use super::is_single_move; @@ -39,7 +39,7 @@ impl Piece for StonePiece { true } - fn generate_lookup_moves(&self, board: &Board, mut from: BitBoard) -> AttackDirections { + fn generate_lookup_moves(&self, board: &Board, from: BitBoard) -> AttackDirections { let cols = board.state.cols; let edges = &board.state.edges[0]; @@ -54,8 +54,8 @@ impl Piece for StonePiece { board: &Board, from: BitBoard, piece_type: PieceType, - team: u16, - mode: u16, + _team: u16, + _mode: u16, ) -> BitBoard { let lookup = self.get_attack_lookup(board, piece_type); let base_moves = match lookup { @@ -136,4 +136,6 @@ impl Piece for StonePiece { None } } -} \ No newline at end of file +} + +pub const STONE: usize = 0; \ No newline at end of file diff --git a/src/games/ataxx/process.rs b/src/games/ataxx/process.rs index c8c7f76..c590cae 100644 --- a/src/games/ataxx/process.rs +++ b/src/games/ataxx/process.rs @@ -1,7 +1,7 @@ -use crate::{board::{fen::PostProcess, Board, actions::Action}, bitboard::BitBoard}; +use crate::{board::{fen::PostProcess, Board}}; #[derive(Debug)] pub struct AtaxxPostProcess; impl PostProcess for AtaxxPostProcess { - fn apply(&self, board: &mut Board) {} + fn apply(&self, _board: &mut Board) {} } \ No newline at end of file diff --git a/src/games/ataxx/resolution.rs b/src/games/ataxx/resolution.rs index a846018..c62804c 100644 --- a/src/games/ataxx/resolution.rs +++ b/src/games/ataxx/resolution.rs @@ -1,12 +1,12 @@ use std::cmp::Ordering; -use crate::board::{game::{Resolution, GameResults}, Board, actions::{Action, Move}}; +use crate::board::{game::{Resolution, GameResults}, Board, actions::{Move}}; #[derive(Debug)] pub struct AtaxxResolution; impl Resolution for AtaxxResolution { - fn resolve(&self, board: &mut Board, legal_moves: &Vec) -> GameResults { + fn resolve(&self, board: &mut Board, legal_moves: &[Move]) -> GameResults { if legal_moves.len() > 0 { if board.state.sub_moves == 100 { return GameResults::Draw; diff --git a/src/games/ataxx/suite.rs b/src/games/ataxx/suite.rs index cef4c7a..6c7dc20 100644 --- a/src/games/ataxx/suite.rs +++ b/src/games/ataxx/suite.rs @@ -1,30 +1,28 @@ -use std::time::{SystemTime, UNIX_EPOCH}; - -use crate::board::{tests::run_tests}; -use super::Ataxx; - -const TEST_STR: &str = r#"7/7/7/7/7/7/7 x 0 1; D1 0; D2 0; D3 0; D4 0 -7/7/7/7/7/7/7 o 0 1; D1 0; D2 0; D3 0; D4 0 -x5o/7/7/7/7/7/o5x x 0 1; D1 16; D2 256; D3 6460; D4 155888; D5 4752668 -x5o/7/7/7/7/7/o5x o 0 1; D1 16; D2 256; D3 6460; D4 155888; D5 4752668 -7/7/7/7/ooooooo/ooooooo/xxxxxxx x 0 1; D1 1; D2 75; D3 249; D4 14270; D5 452980 -7/7/7/7/ooooooo/ooooooo/xxxxxxx o 0 1; D1 75; D2 249; D3 14270; D4 452980 -7/7/7/7/xxxxxxx/xxxxxxx/ooooooo x 0 1; D1 75; D2 249; D3 14270; D4 452980 -7/7/7/7/xxxxxxx/xxxxxxx/ooooooo o 0 1; D1 1; D2 75; D3 249; D4 14270; D5 452980 -7/7/7/2x1o2/7/7/7 x 0 1; D1 23; D2 419; D3 7887; D4 168317; D5 4266992 -7/7/7/2x1o2/7/7/7 o 0 1; D1 23; D2 419; D3 7887; D4 168317; D5 4266992"#; - -const GAPPED_TESTS: &str = r#"7/7/7/7/-------/-------/x5o x 0 1; D1 2; D2 4; D3 13; D4 30; D5 73; D6 174 -7/7/7/7/-------/-------/x5o o 0 1; D1 2; D2 4; D3 13; D4 30; D5 73; D6 174 -x5o/7/2-1-2/7/2-1-2/7/o5x x 0 1; D1 14; D2 196; D3 4184; D4 86528; D5 2266352 -x5o/7/2-1-2/7/2-1-2/7/o5x o 0 1; D1 14; D2 196; D3 4184; D4 86528; D5 2266352 -x5o/7/2-1-2/3-3/2-1-2/7/o5x x 0 1; D1 14; D2 196; D3 4100; D4 83104; D5 2114588 -x5o/7/2-1-2/3-3/2-1-2/7/o5x o 0 1; D1 14; D2 196; D3 4100; D4 83104; D5 2114588 -x5o/7/3-3/2-1-2/3-3/7/o5x x 0 1; D1 16; D2 256; D3 5948; D4 133264; D5 3639856 -x5o/7/3-3/2-1-2/3-3/7/o5x o 0 1; D1 16; D2 256; D3 5948; D4 133264; D5 3639856"#; - #[test] fn ataxx_perft_suite() { + use crate::board::tests::run_tests; + use super::Ataxx; + + const TEST_STR: &str = r#"7/7/7/7/7/7/7 x 0 1; D1 0; D2 0; D3 0; D4 0 + 7/7/7/7/7/7/7 o 0 1; D1 0; D2 0; D3 0; D4 0 + x5o/7/7/7/7/7/o5x x 0 1; D1 16; D2 256; D3 6460; D4 155888; D5 4752668 + x5o/7/7/7/7/7/o5x o 0 1; D1 16; D2 256; D3 6460; D4 155888; D5 4752668 + 7/7/7/7/ooooooo/ooooooo/xxxxxxx x 0 1; D1 1; D2 75; D3 249; D4 14270; D5 452980 + 7/7/7/7/ooooooo/ooooooo/xxxxxxx o 0 1; D1 75; D2 249; D3 14270; D4 452980 + 7/7/7/7/xxxxxxx/xxxxxxx/ooooooo x 0 1; D1 75; D2 249; D3 14270; D4 452980 + 7/7/7/7/xxxxxxx/xxxxxxx/ooooooo o 0 1; D1 1; D2 75; D3 249; D4 14270; D5 452980 + 7/7/7/2x1o2/7/7/7 x 0 1; D1 23; D2 419; D3 7887; D4 168317; D5 4266992 + 7/7/7/2x1o2/7/7/7 o 0 1; D1 23; D2 419; D3 7887; D4 168317; D5 4266992"#; + + const GAPPED_TESTS: &str = r#"7/7/7/7/-------/-------/x5o x 0 1; D1 2; D2 4; D3 13; D4 30; D5 73; D6 174 + 7/7/7/7/-------/-------/x5o o 0 1; D1 2; D2 4; D3 13; D4 30; D5 73; D6 174 + x5o/7/2-1-2/7/2-1-2/7/o5x x 0 1; D1 14; D2 196; D3 4184; D4 86528; D5 2266352 + x5o/7/2-1-2/7/2-1-2/7/o5x o 0 1; D1 14; D2 196; D3 4184; D4 86528; D5 2266352 + x5o/7/2-1-2/3-3/2-1-2/7/o5x x 0 1; D1 14; D2 196; D3 4100; D4 83104; D5 2114588 + x5o/7/2-1-2/3-3/2-1-2/7/o5x o 0 1; D1 14; D2 196; D3 4100; D4 83104; D5 2114588 + x5o/7/3-3/2-1-2/3-3/7/o5x x 0 1; D1 16; D2 256; D3 5948; D4 133264; D5 3639856 + x5o/7/3-3/2-1-2/3-3/7/o5x o 0 1; D1 16; D2 256; D3 5948; D4 133264; D5 3639856"#; + run_tests("Ataxx Gapless", Ataxx::create(), TEST_STR); run_tests("Ataxx Gapped", Ataxx::create(), GAPPED_TESTS); } diff --git a/src/games/chess/args.rs b/src/games/chess/args.rs index 7fd0b65..1bf4263 100644 --- a/src/games/chess/args.rs +++ b/src/games/chess/args.rs @@ -1,19 +1,14 @@ use crate::{ bitboard::{Direction, BitBoard}, board::{ - actions::{Action, HistoryMove, HistoryState, IndexedPreviousBoard, PreviousBoard, Move}, - edges::Edges, + actions::{Action, Move}, fen::{ - FenArgument, FenDecodeError, FenFullMoves, FenOptions, FenState, FenSubMoves, - FenTeamArgument, PostProcess, - }, - game::{Game, MoveController}, - pieces::{Piece, PieceSymbol}, - AttackDirections, Board, Cols, PieceType, + FenArgument, FenDecodeError, + }, Board, }, }; -use super::pieces::{down, up}; +use super::pieces::{down, up, KING, ROOK}; #[derive(Debug)] @@ -21,7 +16,7 @@ pub struct ChessCastlingRights; impl FenArgument for ChessCastlingRights { fn decode(&self, board: &mut Board, arg: &str) -> Result<(), FenDecodeError> { if arg == "-" { - board.state.first_move &= !board.state.pieces[3]; + board.state.first_move &= !board.state.pieces[ROOK]; Ok(()) } else { let mut lost_castling_rights = vec!['Q', 'K', 'q', 'k']; @@ -43,6 +38,20 @@ impl FenArgument for ChessCastlingRights { lost_castling_rights.retain(|el| el != &char); } + let team_pairs = vec![ + ( [ 'Q', 'K' ], 0 ), + ( [ 'q', 'k' ], 1 ) + ]; + + for (letters, team) in team_pairs { + if !letters.iter().all(|el| lost_castling_rights.contains(el)) { continue; } + + let king_board = board.state.pieces[KING] & board.state.teams[team]; + board.state.first_move &= !king_board; + + lost_castling_rights.retain(|el| !letters.contains(&el)); + } + for char in lost_castling_rights { let (team, scan_dir) = match char { 'Q' => (0, Direction::LEFT), @@ -56,7 +65,7 @@ impl FenArgument for ChessCastlingRights { } }; - let rook_board = board.state.pieces[3] & board.state.teams[team]; + let rook_board = board.state.pieces[ROOK] & board.state.teams[team]; if rook_board.is_empty() { continue; } @@ -64,12 +73,11 @@ impl FenArgument for ChessCastlingRights { let rook = rook_board.bitscan(scan_dir); let rook_board = BitBoard::from_lsb(rook); - let king_board = board.state.pieces[5] & board.state.teams[team]; + let king_board = board.state.pieces[KING] & board.state.teams[team]; let king = king_board.bitscan_forward(); if (rook < king && scan_dir == Direction::LEFT) || (rook > king && scan_dir == Direction::RIGHT) { board.state.first_move &= !rook_board; - } } Ok(()) @@ -79,12 +87,12 @@ impl FenArgument for ChessCastlingRights { fn encode(&self, board: &Board) -> String { let mut castling_rights: Vec = Vec::with_capacity(4); for team in 0..board.state.teams.len() { - let king = board.state.pieces[5] & board.state.teams[team] & board.state.first_move; + let king = board.state.pieces[KING] & board.state.teams[team] & board.state.first_move; if king.is_empty() { continue; } - let rooks = board.state.pieces[3] & board.state.teams[team] & board.state.first_move; + let rooks = board.state.pieces[ROOK] & board.state.teams[team] & board.state.first_move; let mut one_bits = rooks .iter_set_bits(board.state.rows * board.state.cols) .collect::>(); @@ -154,7 +162,8 @@ impl FenArgument for ChessEnPassant { move_type: 0 }; - board.history.push_back(Move::Action(action)); + board.history.push_back(Move::Action(action)) + .expect("No reason why adding en passant move to history will error, right?"); Ok(()) } diff --git a/src/games/chess/controller.rs b/src/games/chess/controller.rs index 465fec7..694eb91 100644 --- a/src/games/chess/controller.rs +++ b/src/games/chess/controller.rs @@ -1,45 +1,60 @@ -use crate::{board::{game::{MoveController, get_theoretical_moves_bound}, Board, actions::{Action, TheoreticalAction, Move, TheoreticalMove, CounterUpdate, TurnUpdate}, BoardState}, bitboard::BitBoard}; - -use super::ATTACKS_MODE; +use crate::{board::{game::{MoveController, get_theoretical_moves_bound, MoveLegalResponse}, Board, actions::{Move, TheoreticalMove, CounterUpdate, TurnUpdate}, BoardState}, bitboard::BitBoard, games::chess::pieces::CASTLING_MOVE}; +use super::{ATTACKS_MODE, pieces::KING}; #[derive(Debug)] pub struct ChessMoveController; impl MoveController for ChessMoveController { - fn transform_moves(&self, board: &mut Board, mode: u16, actions: Vec) -> Vec { + fn transform_moves(&self, board: &mut Board, mode: u16, _actions: Vec) -> Vec { let moves = board.generate_moves(mode); let mut legal_moves = Vec::with_capacity(moves.len()); for action in moves { - if self.is_legal(board, &action) { + if self.is_legal(board, &action, true).is_legal { legal_moves.push(action); } } legal_moves } - fn is_legal(&self, board: &mut Board, action: &Move) -> bool { + fn is_legal(&self, board: &mut Board, action: &Move, unmake_move: bool) -> MoveLegalResponse { match action { Move::Action(action) => { let to_board = BitBoard::from_lsb(action.to); - let kings = board.state.pieces[5]; + let kings = board.state.pieces[KING]; if (to_board & kings).is_set() { - return false; + return MoveLegalResponse { + made_move: None, + is_legal: false + }; } let current_team = board.state.moving_team; let undo = board.make_move(&Move::Action(*action)); - let kings = board.state.pieces[5]; + let kings = board.state.pieces[KING]; let king_board = board.state.teams[current_team as usize] & kings; let in_check = board.can_move(board.state.moving_team, king_board, ATTACKS_MODE); - board.undo_move(undo); - !in_check + if unmake_move { + board.undo_move(undo); + MoveLegalResponse { + made_move: None, + is_legal: !in_check + } + } else { + MoveLegalResponse { + made_move: Some(undo), + is_legal: !in_check + } + } } Move::Pass => { // Null Moves are not legal in chess. // They wouldn't be generated anyways, but I think it would be best to show them as illegal here anyways. // Consumers of the library can still make null moves, it just won't be generated by the movegen. - false + MoveLegalResponse { + made_move: None, + is_legal: false + } } } } @@ -49,22 +64,38 @@ impl MoveController for ChessMoveController { } fn encode_action(&self, board: &Board, action: &Move) -> Vec { - vec![ - match action { - Move::Action(action) => { - match action.from { - Some(from) => format!( - "{}{}{}", - board.encode_position(from), - board.encode_position(action.to), - board.game.pieces[action.piece_type as usize].format_info(board, action.info) - ), - None => "----".to_string() - } - }, - Move::Pass => "0000".to_string() - } - ] + match action { + Move::Action(action) => { + match action.from { + Some(from) => { + let mut moves = vec![ + format!( + "{}{}{}", + board.encode_position(from), + board.encode_position(action.to), + board.game.pieces[action.piece_type as usize].format_info(board, action.info) + ) + ]; + + if action.piece_type == KING as u16 && action.move_type == CASTLING_MOVE { + let direction = ((action.to as i32) - (from as i32)).signum(); + let new_pos = ((from as i32) + (direction * 2)) as u16; + + moves.push(format!( + "{}{}{}", + board.encode_position(from), + board.encode_position(new_pos), + board.game.pieces[action.piece_type as usize].format_info(board, action.info) + )) + } + + moves + }, + None => vec![ "----".to_string() ] + } + }, + Move::Pass => vec! [ "0000".to_string() ] + } } fn update(&self, action: &Move, state: &BoardState) -> TurnUpdate { diff --git a/src/games/chess/game.rs b/src/games/chess/game.rs index 371d75a..10dc098 100644 --- a/src/games/chess/game.rs +++ b/src/games/chess/game.rs @@ -1,20 +1,16 @@ use crate::{ - bitboard::{Direction, BitBoard}, board::{ - actions::{Action, HistoryMove, HistoryState, IndexedPreviousBoard, PreviousBoard}, - edges::Edges, fen::{ - FenArgument, FenDecodeError, FenFullMoves, FenOptions, FenState, FenSubMoves, - FenTeamArgument, PostProcess, + FenFullMoves, FenOptions, FenState, FenSubMoves, + FenTeamArgument, }, - game::{Game, MoveController, DefaultZobristController}, - pieces::{Piece, PieceSymbol}, - AttackDirections, Board, Cols, PieceType, zobrist::ZobristHashTable, + game::{Game, DefaultZobristController}, + pieces::{Piece}, zobrist::ZobristHashTable, }, }; use super::{pieces::{ - down, up, BishopPiece, KingPiece, KnightPiece, PawnPiece, QueenPiece, RookPiece, + BishopPiece, KingPiece, KnightPiece, PawnPiece, QueenPiece, RookPiece, }, ChessMoveController, ChessPostProcess, ChessCastlingRights, ChessEnPassant, ChessResolution}; pub const ATTACKS_MODE: u16 = 1; diff --git a/src/games/chess/pieces/king.rs b/src/games/chess/pieces/king.rs index e145ec6..69f2440 100644 --- a/src/games/chess/pieces/king.rs +++ b/src/games/chess/pieces/king.rs @@ -11,10 +11,10 @@ use crate::{ games::chess::game::ATTACKS_MODE, }; -const NORMAL_KING_MOVE: u16 = 0; -const CASTLING_MOVE: u16 = 1; +use super::{ROOK, KING}; -const ROOK_PIECE_TYPE: PieceType = 3; +pub const NORMAL_KING_MOVE: u16 = 0; +pub const CASTLING_MOVE: u16 = 1; #[derive(Debug)] pub struct KingPiece; @@ -59,7 +59,6 @@ impl KingPiece { }; let color: usize = action.team as usize; - let piece_type = action.piece_type as usize; let history_move = HistoryMove { action: Move::Action(*action), @@ -71,12 +70,12 @@ impl KingPiece { updates: vec![ HistoryUpdate::Team(IndexedPreviousBoard(color, board.state.teams[color])), HistoryUpdate::Piece(IndexedPreviousBoard( - piece_type, - board.state.pieces[piece_type], + KING, + board.state.pieces[KING], )), HistoryUpdate::Piece(IndexedPreviousBoard( - ROOK_PIECE_TYPE as usize, - board.state.pieces[ROOK_PIECE_TYPE as usize], + ROOK, + board.state.pieces[ROOK], )), ], }, @@ -85,16 +84,16 @@ impl KingPiece { board.state.teams[color] ^= from; board.state.teams[color] ^= to; - board.state.pieces[piece_type] ^= from; - board.state.pieces[ROOK_PIECE_TYPE as usize] ^= to; + board.state.pieces[KING] ^= from; + board.state.pieces[ROOK] ^= to; board.state.all_pieces ^= from; board.state.all_pieces ^= to; match dir { Direction::LEFT => { - board.state.pieces[piece_type] |= castle_left_king; - board.state.pieces[ROOK_PIECE_TYPE as usize] |= castle_left_rook; + board.state.pieces[KING] |= castle_left_king; + board.state.pieces[ROOK] |= castle_left_rook; board.state.teams[color] |= castle_left_king; board.state.teams[color] |= castle_left_rook; @@ -103,8 +102,8 @@ impl KingPiece { board.state.all_pieces |= castle_left_rook; } Direction::RIGHT => { - board.state.pieces[piece_type] |= castle_right_king; - board.state.pieces[ROOK_PIECE_TYPE as usize] |= castle_right_rook; + board.state.pieces[KING] |= castle_right_king; + board.state.pieces[ROOK] |= castle_right_rook; board.state.teams[color] |= castle_right_king; board.state.teams[color] |= castle_right_rook; @@ -143,12 +142,12 @@ impl Piece for KingPiece { fn can_move_mask( &self, board: &Board, - from: BitBoard, + _from: BitBoard, from_bit: u16, piece_type: PieceType, - team: u16, - mode: u16, - to: BitBoard, + _team: u16, + _mode: u16, + _to: BitBoard, ) -> BitBoard { self.get_attack_lookup(board, piece_type).expect("Could not find move lookup table for king")[from_bit as usize][0] } @@ -173,7 +172,7 @@ impl Piece for KingPiece { &self, board: &mut Board, action: &Action, - piece_type: PieceType, + _piece_type: PieceType, from: BitBoard, to: BitBoard, turn_info: TurnInfo @@ -183,7 +182,6 @@ impl Piece for KingPiece { } let color: usize = action.team as usize; - let piece_type = piece_type as usize; let captured_color: usize = if (to & board.state.teams[0]).is_set() { 0 } else { @@ -211,8 +209,8 @@ impl Piece for KingPiece { board.state.teams[captured_color], )), HistoryUpdate::Piece(IndexedPreviousBoard( - piece_type, - board.state.pieces[piece_type], + KING, + board.state.pieces[KING], )), HistoryUpdate::Piece(IndexedPreviousBoard( captured_piece_type, @@ -227,8 +225,8 @@ impl Piece for KingPiece { board.state.teams[color] |= to; board.state.pieces[captured_piece_type] ^= to; - board.state.pieces[piece_type] ^= from; - board.state.pieces[piece_type] |= to; + board.state.pieces[KING] ^= from; + board.state.pieces[KING] |= to; board.state.all_pieces ^= from; @@ -248,7 +246,7 @@ impl Piece for KingPiece { team: u16, mode: u16, ) { - let rows = board.state.rows; + let _rows = board.state.rows; let cols = board.state.cols; let board_len = board.state.squares; @@ -267,7 +265,7 @@ impl Piece for KingPiece { team, info: NORMAL_KING_MOVE, move_type: NORMAL_KING_MOVE, - piece_type, + piece_type })); } @@ -291,7 +289,7 @@ impl Piece for KingPiece { return; } - let rooks = board.state.pieces[ROOK_PIECE_TYPE as usize] & team_board & first_move & bottom_row; + let rooks = board.state.pieces[ROOK] & team_board & first_move & bottom_row; /* FRC Castling brings us to the same positions that traditional chess castling would. diff --git a/src/games/chess/pieces/knight.rs b/src/games/chess/pieces/knight.rs index ead58d7..36da34d 100644 --- a/src/games/chess/pieces/knight.rs +++ b/src/games/chess/pieces/knight.rs @@ -1,12 +1,10 @@ use crate::{ - bitboard::{Direction, BitBoard}, + bitboard::{BitBoard}, board::{ - actions::{Action, HistoryMove, HistoryState, IndexedPreviousBoard, PreviousBoard}, edges::Edges, pieces::{Piece, PieceSymbol}, AttackDirections, Board, Cols, PieceType, }, - games::chess::game::ATTACKS_MODE, }; #[derive(Debug)] pub struct KnightPiece; @@ -76,12 +74,12 @@ impl Piece for KnightPiece { fn can_move_mask( &self, board: &Board, - from: BitBoard, + _from: BitBoard, from_bit: u16, piece_type: PieceType, - team: u16, - mode: u16, - to: BitBoard, + _team: u16, + _mode: u16, + _to: BitBoard, ) -> BitBoard { self.get_attack_lookup(board, piece_type).expect("Could not find move lookup table for Knight")[from_bit as usize][0] } diff --git a/src/games/chess/pieces/mod.rs b/src/games/chess/pieces/mod.rs index 9b1573d..2c48386 100644 --- a/src/games/chess/pieces/mod.rs +++ b/src/games/chess/pieces/mod.rs @@ -3,6 +3,13 @@ mod knight; mod pawn; mod sliders; +pub const PAWN: usize = 0; +pub const KNIGHT: usize = 1; +pub const BISHOP: usize = 2; +pub const ROOK: usize = 3; +pub const QUEEN: usize = 4; +pub const KING: usize = 5; + pub use king::*; pub use knight::*; pub use pawn::*; diff --git a/src/games/chess/pieces/pawn.rs b/src/games/chess/pieces/pawn.rs index 0ca41d1..f48a66f 100644 --- a/src/games/chess/pieces/pawn.rs +++ b/src/games/chess/pieces/pawn.rs @@ -1,10 +1,9 @@ use crate::{ - bitboard::{Direction, BitBoard}, + bitboard::{BitBoard}, board::{ actions::{ Action, HistoryMove, HistoryState, HistoryUpdate, IndexedPreviousBoard, PreviousBoard, Move, ActionInfo, TurnInfo, }, - edges::Edges, pieces::{Piece, PieceSymbol}, AttackDirections, Board, Cols, PieceType, }, @@ -19,7 +18,7 @@ fn promotion_move(piece_type: PieceType) -> u16 { #[derive(Debug)] pub struct PawnPiece; -pub fn up(bitboard: &BitBoard, shift: u16, cols: Cols, team: u16) -> BitBoard { +pub(crate) fn up(bitboard: &BitBoard, shift: u16, cols: Cols, team: u16) -> BitBoard { match team { 0 => bitboard.up(shift, cols), 1 => bitboard.down(shift, cols), @@ -27,7 +26,7 @@ pub fn up(bitboard: &BitBoard, shift: u16, cols: Cols, team: } } -pub fn down(bitboard: &BitBoard, shift: u16, cols: Cols, team: u16) -> BitBoard { +pub(crate) fn down(bitboard: &BitBoard, shift: u16, cols: Cols, team: u16) -> BitBoard { match team { 0 => bitboard.down(shift, cols), 1 => bitboard.up(shift, cols), @@ -103,7 +102,7 @@ impl Piece for PawnPiece { true } - fn generate_lookup_moves(&self, board: &Board, mut from: BitBoard) -> AttackDirections { + fn generate_lookup_moves(&self, board: &Board, from: BitBoard) -> AttackDirections { let mut attack_dirs: AttackDirections = vec![]; let edges = board.state.edges[0]; for team in 0..board.game.teams { @@ -166,12 +165,12 @@ impl Piece for PawnPiece { fn can_move_mask( &self, board: &Board, - from: BitBoard, + _from: BitBoard, from_bit: u16, piece_type: PieceType, team: u16, - mode: u16, - to: BitBoard, + _mode: u16, + _to: BitBoard, ) -> BitBoard { self.get_attack_lookup(board, piece_type).expect("Could not find pawn attack lookup")[from_bit as usize][team as usize] } @@ -185,7 +184,7 @@ impl Piece for PawnPiece { mode: u16, ) -> BitBoard { let cols = board.state.cols; - let edges = &board.state.edges[0]; + let _edges = &board.state.edges[0]; if mode == ATTACKS_MODE { return self.get_attack_lookup(board, piece_type).expect("Could not find pawn attack lookup") diff --git a/src/games/chess/pieces/sliders/bishop.rs b/src/games/chess/pieces/sliders/bishop.rs index e16cf58..98839d0 100644 --- a/src/games/chess/pieces/sliders/bishop.rs +++ b/src/games/chess/pieces/sliders/bishop.rs @@ -55,8 +55,8 @@ impl Piece for BishopPiece { from: BitBoard, from_bit: u16, piece_type: PieceType, - team: u16, - mode: u16, + _team: u16, + _mode: u16, to: BitBoard, ) -> BitBoard { let lookup = self diff --git a/src/games/chess/pieces/sliders/queen.rs b/src/games/chess/pieces/sliders/queen.rs index b02f7bf..0cec4f0 100644 --- a/src/games/chess/pieces/sliders/queen.rs +++ b/src/games/chess/pieces/sliders/queen.rs @@ -75,8 +75,8 @@ impl Piece for QueenPiece { from: BitBoard, from_bit: u16, piece_type: PieceType, - team: u16, - mode: u16, + _team: u16, + _mode: u16, to: BitBoard, ) -> BitBoard { let lookup = self diff --git a/src/games/chess/pieces/sliders/rook.rs b/src/games/chess/pieces/sliders/rook.rs index 73e9981..381c4c4 100644 --- a/src/games/chess/pieces/sliders/rook.rs +++ b/src/games/chess/pieces/sliders/rook.rs @@ -51,8 +51,8 @@ impl Piece for RookPiece { from: BitBoard, from_bit: u16, piece_type: PieceType, - team: u16, - mode: u16, + _team: u16, + _mode: u16, to: BitBoard, ) -> BitBoard { let lookup = self diff --git a/src/games/chess/pieces/sliders/util.rs b/src/games/chess/pieces/sliders/util.rs index 83fa0db..484e519 100644 --- a/src/games/chess/pieces/sliders/util.rs +++ b/src/games/chess/pieces/sliders/util.rs @@ -1,6 +1,5 @@ use crate::{board::{ - pieces::{Piece, PieceSymbol}, - AttackDirections, AttackLookup, Board, PieceType, + AttackLookup, Board, }, bitboard::BitBoard}; pub fn get_moves_ray( diff --git a/src/games/chess/process.rs b/src/games/chess/process.rs index 757d37a..be3d1ac 100644 --- a/src/games/chess/process.rs +++ b/src/games/chess/process.rs @@ -1,4 +1,5 @@ -use crate::{board::{fen::PostProcess, Board, actions::Action}, bitboard::BitBoard}; +use crate::board::{fen::PostProcess, Board}; +use super::pieces::PAWN; #[derive(Debug)] pub struct ChessPostProcess; @@ -12,9 +13,9 @@ impl PostProcess for ChessPostProcess { bottom |= bottom.up(1, cols); top |= top.down(1, cols); - let first_move = (board.state.pieces[0] & board.state.teams[0] & bottom) - | (board.state.pieces[0] & board.state.teams[1] & top) - | (board.state.all_pieces ^ board.state.pieces[0]); + let first_move = (board.state.pieces[PAWN] & board.state.teams[0] & bottom) + | (board.state.pieces[PAWN] & board.state.teams[1] & top) + | (board.state.all_pieces ^ board.state.pieces[PAWN]); board.state.first_move &= first_move; } } \ No newline at end of file diff --git a/src/games/chess/resolution.rs b/src/games/chess/resolution.rs index 4b2fd69..4a8d317 100644 --- a/src/games/chess/resolution.rs +++ b/src/games/chess/resolution.rs @@ -1,16 +1,16 @@ -use std::cmp::Ordering; -use crate::board::{game::{Resolution, GameResults}, Board, actions::{Action, Move}}; -use super::ATTACKS_MODE; +use crate::board::{game::{Resolution, GameResults}, Board, actions::Move}; + +use super::{ATTACKS_MODE, pieces::KING}; #[derive(Debug)] pub struct ChessResolution; impl Resolution for ChessResolution { - fn resolve(&self, board: &mut Board, legal_moves: &Vec) -> GameResults { + fn resolve(&self, board: &mut Board, legal_moves: &[Move]) -> GameResults { if legal_moves.len() == 0 { - let kings = board.state.pieces[5]; + let kings = board.state.pieces[KING]; let king_board = board.state.teams[board.state.moving_team as usize] & kings; let next_team = board.state.team_lookup[board.state.moving_team as usize]; diff --git a/src/games/chess/suite.rs b/src/games/chess/suite.rs index 12bb39f..fd4bff9 100644 --- a/src/games/chess/suite.rs +++ b/src/games/chess/suite.rs @@ -1,136 +1,134 @@ -use std::time::{SystemTime, UNIX_EPOCH}; +#[test] +fn chess_perft_suite() { + use crate::board::tests::run_tests; -use crate::board::{tests::run_tests}; use super::Chess; + const TEST_STR: &str = r#"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 ;D1 20 ;D2 400 ;D3 8902 ;D4 197281 ;D5 4865609 ;D6 119060324 + r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1 ;D1 48 ;D2 2039 ;D3 97862 ;D4 4085603 ;D5 193690690 ;D6 8031647685 + 4k3/8/8/8/8/8/8/4K2R w K - 0 1 ;D1 15 ;D2 66 ;D3 1197 ;D4 7059 ;D5 133987 ;D6 764643 + 4k3/8/8/8/8/8/8/R3K3 w Q - 0 1 ;D1 16 ;D2 71 ;D3 1287 ;D4 7626 ;D5 145232 ;D6 846648 + 4k2r/8/8/8/8/8/8/4K3 w k - 0 1 ;D1 5 ;D2 75 ;D3 459 ;D4 8290 ;D5 47635 ;D6 899442 + r3k3/8/8/8/8/8/8/4K3 w q - 0 1 ;D1 5 ;D2 80 ;D3 493 ;D4 8897 ;D5 52710 ;D6 1001523 + 4k3/8/8/8/8/8/8/R3K2R w KQ - 0 1 ;D1 26 ;D2 112 ;D3 3189 ;D4 17945 ;D5 532933 ;D6 2788982 + r3k2r/8/8/8/8/8/8/4K3 w kq - 0 1 ;D1 5 ;D2 130 ;D3 782 ;D4 22180 ;D5 118882 ;D6 3517770 + 8/8/8/8/8/8/6k1/4K2R w K - 0 1 ;D1 12 ;D2 38 ;D3 564 ;D4 2219 ;D5 37735 ;D6 185867 + 8/8/8/8/8/8/1k6/R3K3 w Q - 0 1 ;D1 15 ;D2 65 ;D3 1018 ;D4 4573 ;D5 80619 ;D6 413018 + 4k2r/6K1/8/8/8/8/8/8 w k - 0 1 ;D1 3 ;D2 32 ;D3 134 ;D4 2073 ;D5 10485 ;D6 179869 + r3k3/1K6/8/8/8/8/8/8 w q - 0 1 ;D1 4 ;D2 49 ;D3 243 ;D4 3991 ;D5 20780 ;D6 367724 + r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1 ;D1 26 ;D2 568 ;D3 13744 ;D4 314346 ;D5 7594526 ;D6 179862938 + r3k2r/8/8/8/8/8/8/1R2K2R w Kkq - 0 1 ;D1 25 ;D2 567 ;D3 14095 ;D4 328965 ;D5 8153719 ;D6 195629489 + r3k2r/8/8/8/8/8/8/2R1K2R w Kkq - 0 1 ;D1 25 ;D2 548 ;D3 13502 ;D4 312835 ;D5 7736373 ;D6 184411439 + r3k2r/8/8/8/8/8/8/R3K1R1 w Qkq - 0 1 ;D1 25 ;D2 547 ;D3 13579 ;D4 316214 ;D5 7878456 ;D6 189224276 + 1r2k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1 ;D1 26 ;D2 583 ;D3 14252 ;D4 334705 ;D5 8198901 ;D6 198328929 + 2r1k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1 ;D1 25 ;D2 560 ;D3 13592 ;D4 317324 ;D5 7710115 ;D6 185959088 + r3k1r1/8/8/8/8/8/8/R3K2R w KQq - 0 1 ;D1 25 ;D2 560 ;D3 13607 ;D4 320792 ;D5 7848606 ;D6 190755813 + 4k3/8/8/8/8/8/8/4K2R b K - 0 1 ;D1 5 ;D2 75 ;D3 459 ;D4 8290 ;D5 47635 ;D6 899442 + 4k3/8/8/8/8/8/8/R3K3 b Q - 0 1 ;D1 5 ;D2 80 ;D3 493 ;D4 8897 ;D5 52710 ;D6 1001523 + 4k2r/8/8/8/8/8/8/4K3 b k - 0 1 ;D1 15 ;D2 66 ;D3 1197 ;D4 7059 ;D5 133987 ;D6 764643 + r3k3/8/8/8/8/8/8/4K3 b q - 0 1 ;D1 16 ;D2 71 ;D3 1287 ;D4 7626 ;D5 145232 ;D6 846648 + 4k3/8/8/8/8/8/8/R3K2R b KQ - 0 1 ;D1 5 ;D2 130 ;D3 782 ;D4 22180 ;D5 118882 ;D6 3517770 + r3k2r/8/8/8/8/8/8/4K3 b kq - 0 1 ;D1 26 ;D2 112 ;D3 3189 ;D4 17945 ;D5 532933 ;D6 2788982 + 8/8/8/8/8/8/6k1/4K2R b K - 0 1 ;D1 3 ;D2 32 ;D3 134 ;D4 2073 ;D5 10485 ;D6 179869 + 8/8/8/8/8/8/1k6/R3K3 b Q - 0 1 ;D1 4 ;D2 49 ;D3 243 ;D4 3991 ;D5 20780 ;D6 367724 + 4k2r/6K1/8/8/8/8/8/8 b k - 0 1 ;D1 12 ;D2 38 ;D3 564 ;D4 2219 ;D5 37735 ;D6 185867 + r3k3/1K6/8/8/8/8/8/8 b q - 0 1 ;D1 15 ;D2 65 ;D3 1018 ;D4 4573 ;D5 80619 ;D6 413018 + r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1 ;D1 26 ;D2 568 ;D3 13744 ;D4 314346 ;D5 7594526 ;D6 179862938 + r3k2r/8/8/8/8/8/8/1R2K2R b Kkq - 0 1 ;D1 26 ;D2 583 ;D3 14252 ;D4 334705 ;D5 8198901 ;D6 198328929 + r3k2r/8/8/8/8/8/8/2R1K2R b Kkq - 0 1 ;D1 25 ;D2 560 ;D3 13592 ;D4 317324 ;D5 7710115 ;D6 185959088 + r3k2r/8/8/8/8/8/8/R3K1R1 b Qkq - 0 1 ;D1 25 ;D2 560 ;D3 13607 ;D4 320792 ;D5 7848606 ;D6 190755813 + 1r2k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1 ;D1 25 ;D2 567 ;D3 14095 ;D4 328965 ;D5 8153719 ;D6 195629489 + 2r1k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1 ;D1 25 ;D2 548 ;D3 13502 ;D4 312835 ;D5 7736373 ;D6 184411439 + r3k1r1/8/8/8/8/8/8/R3K2R b KQq - 0 1 ;D1 25 ;D2 547 ;D3 13579 ;D4 316214 ;D5 7878456 ;D6 189224276 + 8/1n4N1/2k5/8/8/5K2/1N4n1/8 w - - 0 1 ;D1 14 ;D2 195 ;D3 2760 ;D4 38675 ;D5 570726 ;D6 8107539 + 8/1k6/8/5N2/8/4n3/8/2K5 w - - 0 1 ;D1 11 ;D2 156 ;D3 1636 ;D4 20534 ;D5 223507 ;D6 2594412 + 8/8/4k3/3Nn3/3nN3/4K3/8/8 w - - 0 1 ;D1 19 ;D2 289 ;D3 4442 ;D4 73584 ;D5 1198299 ;D6 19870403 + K7/8/2n5/1n6/8/8/8/k6N w - - 0 1 ;D1 3 ;D2 51 ;D3 345 ;D4 5301 ;D5 38348 ;D6 588695 + k7/8/2N5/1N6/8/8/8/K6n w - - 0 1 ;D1 17 ;D2 54 ;D3 835 ;D4 5910 ;D5 92250 ;D6 688780 + 8/1n4N1/2k5/8/8/5K2/1N4n1/8 b - - 0 1 ;D1 15 ;D2 193 ;D3 2816 ;D4 40039 ;D5 582642 ;D6 8503277 + 8/1k6/8/5N2/8/4n3/8/2K5 b - - 0 1 ;D1 16 ;D2 180 ;D3 2290 ;D4 24640 ;D5 288141 ;D6 3147566 + 8/8/3K4/3Nn3/3nN3/4k3/8/8 b - - 0 1 ;D1 4 ;D2 68 ;D3 1118 ;D4 16199 ;D5 281190 ;D6 4405103 + K7/8/2n5/1n6/8/8/8/k6N b - - 0 1 ;D1 17 ;D2 54 ;D3 835 ;D4 5910 ;D5 92250 ;D6 688780 + k7/8/2N5/1N6/8/8/8/K6n b - - 0 1 ;D1 3 ;D2 51 ;D3 345 ;D4 5301 ;D5 38348 ;D6 588695 + B6b/8/8/8/2K5/4k3/8/b6B w - - 0 1 ;D1 17 ;D2 278 ;D3 4607 ;D4 76778 ;D5 1320507 ;D6 22823890 + 8/8/1B6/7b/7k/8/2B1b3/7K w - - 0 1 ;D1 21 ;D2 316 ;D3 5744 ;D4 93338 ;D5 1713368 ;D6 28861171 + k7/B7/1B6/1B6/8/8/8/K6b w - - 0 1 ;D1 21 ;D2 144 ;D3 3242 ;D4 32955 ;D5 787524 ;D6 7881673 + K7/b7/1b6/1b6/8/8/8/k6B w - - 0 1 ;D1 7 ;D2 143 ;D3 1416 ;D4 31787 ;D5 310862 ;D6 7382896 + B6b/8/8/8/2K5/5k2/8/b6B b - - 0 1 ;D1 6 ;D2 106 ;D3 1829 ;D4 31151 ;D5 530585 ;D6 9250746 + 8/8/1B6/7b/7k/8/2B1b3/7K b - - 0 1 ;D1 17 ;D2 309 ;D3 5133 ;D4 93603 ;D5 1591064 ;D6 29027891 + k7/B7/1B6/1B6/8/8/8/K6b b - - 0 1 ;D1 7 ;D2 143 ;D3 1416 ;D4 31787 ;D5 310862 ;D6 7382896 + K7/b7/1b6/1b6/8/8/8/k6B b - - 0 1 ;D1 21 ;D2 144 ;D3 3242 ;D4 32955 ;D5 787524 ;D6 7881673 + 7k/RR6/8/8/8/8/rr6/7K w - - 0 1 ;D1 19 ;D2 275 ;D3 5300 ;D4 104342 ;D5 2161211 ;D6 44956585 + R6r/8/8/2K5/5k2/8/8/r6R w - - 0 1 ;D1 36 ;D2 1027 ;D3 29215 ;D4 771461 ;D5 20506480 ;D6 525169084 + 7k/RR6/8/8/8/8/rr6/7K b - - 0 1 ;D1 19 ;D2 275 ;D3 5300 ;D4 104342 ;D5 2161211 ;D6 44956585 + R6r/8/8/2K5/5k2/8/8/r6R b - - 0 1 ;D1 36 ;D2 1027 ;D3 29227 ;D4 771368 ;D5 20521342 ;D6 524966748 + 6kq/8/8/8/8/8/8/7K w - - 0 1 ;D1 2 ;D2 36 ;D3 143 ;D4 3637 ;D5 14893 ;D6 391507 + 6KQ/8/8/8/8/8/8/7k b - - 0 1 ;D1 2 ;D2 36 ;D3 143 ;D4 3637 ;D5 14893 ;D6 391507 + K7/8/8/3Q4/4q3/8/8/7k w - - 0 1 ;D1 6 ;D2 35 ;D3 495 ;D4 8349 ;D5 166741 ;D6 3370175 + 6qk/8/8/8/8/8/8/7K b - - 0 1 ;D1 22 ;D2 43 ;D3 1015 ;D4 4167 ;D5 105749 ;D6 419369 + 6KQ/8/8/8/8/8/8/7k b - - 0 1 ;D1 2 ;D2 36 ;D3 143 ;D4 3637 ;D5 14893 ;D6 391507 + K7/8/8/3Q4/4q3/8/8/7k b - - 0 1 ;D1 6 ;D2 35 ;D3 495 ;D4 8349 ;D5 166741 ;D6 3370175 + 8/8/8/8/8/K7/P7/k7 w - - 0 1 ;D1 3 ;D2 7 ;D3 43 ;D4 199 ;D5 1347 ;D6 6249 + 8/8/8/8/8/7K/7P/7k w - - 0 1 ;D1 3 ;D2 7 ;D3 43 ;D4 199 ;D5 1347 ;D6 6249 + K7/p7/k7/8/8/8/8/8 w - - 0 1 ;D1 1 ;D2 3 ;D3 12 ;D4 80 ;D5 342 ;D6 2343 + 7K/7p/7k/8/8/8/8/8 w - - 0 1 ;D1 1 ;D2 3 ;D3 12 ;D4 80 ;D5 342 ;D6 2343 + 8/2k1p3/3pP3/3P2K1/8/8/8/8 w - - 0 1 ;D1 7 ;D2 35 ;D3 210 ;D4 1091 ;D5 7028 ;D6 34834 + 8/8/8/8/8/K7/P7/k7 b - - 0 1 ;D1 1 ;D2 3 ;D3 12 ;D4 80 ;D5 342 ;D6 2343 + 8/8/8/8/8/7K/7P/7k b - - 0 1 ;D1 1 ;D2 3 ;D3 12 ;D4 80 ;D5 342 ;D6 2343 + K7/p7/k7/8/8/8/8/8 b - - 0 1 ;D1 3 ;D2 7 ;D3 43 ;D4 199 ;D5 1347 ;D6 6249 + 7K/7p/7k/8/8/8/8/8 b - - 0 1 ;D1 3 ;D2 7 ;D3 43 ;D4 199 ;D5 1347 ;D6 6249 + 8/2k1p3/3pP3/3P2K1/8/8/8/8 b - - 0 1 ;D1 5 ;D2 35 ;D3 182 ;D4 1091 ;D5 5408 ;D6 34822 + 8/8/8/8/8/4k3/4P3/4K3 w - - 0 1 ;D1 2 ;D2 8 ;D3 44 ;D4 282 ;D5 1814 ;D6 11848 + 4k3/4p3/4K3/8/8/8/8/8 b - - 0 1 ;D1 2 ;D2 8 ;D3 44 ;D4 282 ;D5 1814 ;D6 11848 + 8/8/7k/7p/7P/7K/8/8 w - - 0 1 ;D1 3 ;D2 9 ;D3 57 ;D4 360 ;D5 1969 ;D6 10724 + 8/8/k7/p7/P7/K7/8/8 w - - 0 1 ;D1 3 ;D2 9 ;D3 57 ;D4 360 ;D5 1969 ;D6 10724 + 8/8/3k4/3p4/3P4/3K4/8/8 w - - 0 1 ;D1 5 ;D2 25 ;D3 180 ;D4 1294 ;D5 8296 ;D6 53138 + 8/3k4/3p4/8/3P4/3K4/8/8 w - - 0 1 ;D1 8 ;D2 61 ;D3 483 ;D4 3213 ;D5 23599 ;D6 157093 + 8/8/3k4/3p4/8/3P4/3K4/8 w - - 0 1 ;D1 8 ;D2 61 ;D3 411 ;D4 3213 ;D5 21637 ;D6 158065 + k7/8/3p4/8/3P4/8/8/7K w - - 0 1 ;D1 4 ;D2 15 ;D3 90 ;D4 534 ;D5 3450 ;D6 20960 + 8/8/7k/7p/7P/7K/8/8 b - - 0 1 ;D1 3 ;D2 9 ;D3 57 ;D4 360 ;D5 1969 ;D6 10724 + 8/8/k7/p7/P7/K7/8/8 b - - 0 1 ;D1 3 ;D2 9 ;D3 57 ;D4 360 ;D5 1969 ;D6 10724 + 8/8/3k4/3p4/3P4/3K4/8/8 b - - 0 1 ;D1 5 ;D2 25 ;D3 180 ;D4 1294 ;D5 8296 ;D6 53138 + 8/3k4/3p4/8/3P4/3K4/8/8 b - - 0 1 ;D1 8 ;D2 61 ;D3 411 ;D4 3213 ;D5 21637 ;D6 158065 + 8/8/3k4/3p4/8/3P4/3K4/8 b - - 0 1 ;D1 8 ;D2 61 ;D3 483 ;D4 3213 ;D5 23599 ;D6 157093 + k7/8/3p4/8/3P4/8/8/7K b - - 0 1 ;D1 4 ;D2 15 ;D3 89 ;D4 537 ;D5 3309 ;D6 21104 + 7k/3p4/8/8/3P4/8/8/K7 w - - 0 1 ;D1 4 ;D2 19 ;D3 117 ;D4 720 ;D5 4661 ;D6 32191 + 7k/8/8/3p4/8/8/3P4/K7 w - - 0 1 ;D1 5 ;D2 19 ;D3 116 ;D4 716 ;D5 4786 ;D6 30980 + k7/8/8/7p/6P1/8/8/K7 w - - 0 1 ;D1 5 ;D2 22 ;D3 139 ;D4 877 ;D5 6112 ;D6 41874 + k7/8/7p/8/8/6P1/8/K7 w - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4354 ;D6 29679 + k7/8/8/6p1/7P/8/8/K7 w - - 0 1 ;D1 5 ;D2 22 ;D3 139 ;D4 877 ;D5 6112 ;D6 41874 + k7/8/6p1/8/8/7P/8/K7 w - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4354 ;D6 29679 + k7/8/8/3p4/4p3/8/8/7K w - - 0 1 ;D1 3 ;D2 15 ;D3 84 ;D4 573 ;D5 3013 ;D6 22886 + k7/8/3p4/8/8/4P3/8/7K w - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4271 ;D6 28662 + 7k/3p4/8/8/3P4/8/8/K7 b - - 0 1 ;D1 5 ;D2 19 ;D3 117 ;D4 720 ;D5 5014 ;D6 32167 + 7k/8/8/3p4/8/8/3P4/K7 b - - 0 1 ;D1 4 ;D2 19 ;D3 117 ;D4 712 ;D5 4658 ;D6 30749 + k7/8/8/7p/6P1/8/8/K7 b - - 0 1 ;D1 5 ;D2 22 ;D3 139 ;D4 877 ;D5 6112 ;D6 41874 + k7/8/7p/8/8/6P1/8/K7 b - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4354 ;D6 29679 + k7/8/8/6p1/7P/8/8/K7 b - - 0 1 ;D1 5 ;D2 22 ;D3 139 ;D4 877 ;D5 6112 ;D6 41874 + k7/8/6p1/8/8/7P/8/K7 b - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4354 ;D6 29679 + k7/8/8/3p4/4p3/8/8/7K b - - 0 1 ;D1 5 ;D2 15 ;D3 102 ;D4 569 ;D5 4337 ;D6 22579 + k7/8/3p4/8/8/4P3/8/7K b - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4271 ;D6 28662 + 7k/8/8/p7/1P6/8/8/7K w - - 0 1 ;D1 5 ;D2 22 ;D3 139 ;D4 877 ;D5 6112 ;D6 41874 + 7k/8/p7/8/8/1P6/8/7K w - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4354 ;D6 29679 + 7k/8/8/1p6/P7/8/8/7K w - - 0 1 ;D1 5 ;D2 22 ;D3 139 ;D4 877 ;D5 6112 ;D6 41874 + 7k/8/1p6/8/8/P7/8/7K w - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4354 ;D6 29679 + k7/7p/8/8/8/8/6P1/K7 w - - 0 1 ;D1 5 ;D2 25 ;D3 161 ;D4 1035 ;D5 7574 ;D6 55338 + k7/6p1/8/8/8/8/7P/K7 w - - 0 1 ;D1 5 ;D2 25 ;D3 161 ;D4 1035 ;D5 7574 ;D6 55338 + 3k4/3pp3/8/8/8/8/3PP3/3K4 w - - 0 1 ;D1 7 ;D2 49 ;D3 378 ;D4 2902 ;D5 24122 ;D6 199002 + 7k/8/8/p7/1P6/8/8/7K b - - 0 1 ;D1 5 ;D2 22 ;D3 139 ;D4 877 ;D5 6112 ;D6 41874 + 7k/8/p7/8/8/1P6/8/7K b - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4354 ;D6 29679 + 7k/8/8/1p6/P7/8/8/7K b - - 0 1 ;D1 5 ;D2 22 ;D3 139 ;D4 877 ;D5 6112 ;D6 41874 + 7k/8/1p6/8/8/P7/8/7K b - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4354 ;D6 29679 + k7/7p/8/8/8/8/6P1/K7 b - - 0 1 ;D1 5 ;D2 25 ;D3 161 ;D4 1035 ;D5 7574 ;D6 55338 + k7/6p1/8/8/8/8/7P/K7 b - - 0 1 ;D1 5 ;D2 25 ;D3 161 ;D4 1035 ;D5 7574 ;D6 55338 + 3k4/3pp3/8/8/8/8/3PP3/3K4 b - - 0 1 ;D1 7 ;D2 49 ;D3 378 ;D4 2902 ;D5 24122 ;D6 199002 + 8/Pk6/8/8/8/8/6Kp/8 w - - 0 1 ;D1 11 ;D2 97 ;D3 887 ;D4 8048 ;D5 90606 ;D6 1030499 + n1n5/1Pk5/8/8/8/8/5Kp1/5N1N w - - 0 1 ;D1 24 ;D2 421 ;D3 7421 ;D4 124608 ;D5 2193768 ;D6 37665329 + 8/PPPk4/8/8/8/8/4Kppp/8 w - - 0 1 ;D1 18 ;D2 270 ;D3 4699 ;D4 79355 ;D5 1533145 ;D6 28859283 + n1n5/PPPk4/8/8/8/8/4Kppp/5N1N w - - 0 1 ;D1 24 ;D2 496 ;D3 9483 ;D4 182838 ;D5 3605103 ;D6 71179139 + 8/Pk6/8/8/8/8/6Kp/8 b - - 0 1 ;D1 11 ;D2 97 ;D3 887 ;D4 8048 ;D5 90606 ;D6 1030499 + n1n5/1Pk5/8/8/8/8/5Kp1/5N1N b - - 0 1 ;D1 24 ;D2 421 ;D3 7421 ;D4 124608 ;D5 2193768 ;D6 37665329 + 8/PPPk4/8/8/8/8/4Kppp/8 b - - 0 1 ;D1 18 ;D2 270 ;D3 4699 ;D4 79355 ;D5 1533145 ;D6 28859283 + n1n5/PPPk4/8/8/8/8/4Kppp/5N1N b - - 0 1 ;D1 24 ;D2 496 ;D3 9483 ;D4 182838 ;D5 3605103 ;D6 71179139"#; -const TEST_STR: &str = r#"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 ;D1 20 ;D2 400 ;D3 8902 ;D4 197281 ;D5 4865609 ;D6 119060324 -r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1 ;D1 48 ;D2 2039 ;D3 97862 ;D4 4085603 ;D5 193690690 ;D6 8031647685 -4k3/8/8/8/8/8/8/4K2R w K - 0 1 ;D1 15 ;D2 66 ;D3 1197 ;D4 7059 ;D5 133987 ;D6 764643 -4k3/8/8/8/8/8/8/R3K3 w Q - 0 1 ;D1 16 ;D2 71 ;D3 1287 ;D4 7626 ;D5 145232 ;D6 846648 -4k2r/8/8/8/8/8/8/4K3 w k - 0 1 ;D1 5 ;D2 75 ;D3 459 ;D4 8290 ;D5 47635 ;D6 899442 -r3k3/8/8/8/8/8/8/4K3 w q - 0 1 ;D1 5 ;D2 80 ;D3 493 ;D4 8897 ;D5 52710 ;D6 1001523 -4k3/8/8/8/8/8/8/R3K2R w KQ - 0 1 ;D1 26 ;D2 112 ;D3 3189 ;D4 17945 ;D5 532933 ;D6 2788982 -r3k2r/8/8/8/8/8/8/4K3 w kq - 0 1 ;D1 5 ;D2 130 ;D3 782 ;D4 22180 ;D5 118882 ;D6 3517770 -8/8/8/8/8/8/6k1/4K2R w K - 0 1 ;D1 12 ;D2 38 ;D3 564 ;D4 2219 ;D5 37735 ;D6 185867 -8/8/8/8/8/8/1k6/R3K3 w Q - 0 1 ;D1 15 ;D2 65 ;D3 1018 ;D4 4573 ;D5 80619 ;D6 413018 -4k2r/6K1/8/8/8/8/8/8 w k - 0 1 ;D1 3 ;D2 32 ;D3 134 ;D4 2073 ;D5 10485 ;D6 179869 -r3k3/1K6/8/8/8/8/8/8 w q - 0 1 ;D1 4 ;D2 49 ;D3 243 ;D4 3991 ;D5 20780 ;D6 367724 -r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1 ;D1 26 ;D2 568 ;D3 13744 ;D4 314346 ;D5 7594526 ;D6 179862938 -r3k2r/8/8/8/8/8/8/1R2K2R w Kkq - 0 1 ;D1 25 ;D2 567 ;D3 14095 ;D4 328965 ;D5 8153719 ;D6 195629489 -r3k2r/8/8/8/8/8/8/2R1K2R w Kkq - 0 1 ;D1 25 ;D2 548 ;D3 13502 ;D4 312835 ;D5 7736373 ;D6 184411439 -r3k2r/8/8/8/8/8/8/R3K1R1 w Qkq - 0 1 ;D1 25 ;D2 547 ;D3 13579 ;D4 316214 ;D5 7878456 ;D6 189224276 -1r2k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1 ;D1 26 ;D2 583 ;D3 14252 ;D4 334705 ;D5 8198901 ;D6 198328929 -2r1k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1 ;D1 25 ;D2 560 ;D3 13592 ;D4 317324 ;D5 7710115 ;D6 185959088 -r3k1r1/8/8/8/8/8/8/R3K2R w KQq - 0 1 ;D1 25 ;D2 560 ;D3 13607 ;D4 320792 ;D5 7848606 ;D6 190755813 -4k3/8/8/8/8/8/8/4K2R b K - 0 1 ;D1 5 ;D2 75 ;D3 459 ;D4 8290 ;D5 47635 ;D6 899442 -4k3/8/8/8/8/8/8/R3K3 b Q - 0 1 ;D1 5 ;D2 80 ;D3 493 ;D4 8897 ;D5 52710 ;D6 1001523 -4k2r/8/8/8/8/8/8/4K3 b k - 0 1 ;D1 15 ;D2 66 ;D3 1197 ;D4 7059 ;D5 133987 ;D6 764643 -r3k3/8/8/8/8/8/8/4K3 b q - 0 1 ;D1 16 ;D2 71 ;D3 1287 ;D4 7626 ;D5 145232 ;D6 846648 -4k3/8/8/8/8/8/8/R3K2R b KQ - 0 1 ;D1 5 ;D2 130 ;D3 782 ;D4 22180 ;D5 118882 ;D6 3517770 -r3k2r/8/8/8/8/8/8/4K3 b kq - 0 1 ;D1 26 ;D2 112 ;D3 3189 ;D4 17945 ;D5 532933 ;D6 2788982 -8/8/8/8/8/8/6k1/4K2R b K - 0 1 ;D1 3 ;D2 32 ;D3 134 ;D4 2073 ;D5 10485 ;D6 179869 -8/8/8/8/8/8/1k6/R3K3 b Q - 0 1 ;D1 4 ;D2 49 ;D3 243 ;D4 3991 ;D5 20780 ;D6 367724 -4k2r/6K1/8/8/8/8/8/8 b k - 0 1 ;D1 12 ;D2 38 ;D3 564 ;D4 2219 ;D5 37735 ;D6 185867 -r3k3/1K6/8/8/8/8/8/8 b q - 0 1 ;D1 15 ;D2 65 ;D3 1018 ;D4 4573 ;D5 80619 ;D6 413018 -r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1 ;D1 26 ;D2 568 ;D3 13744 ;D4 314346 ;D5 7594526 ;D6 179862938 -r3k2r/8/8/8/8/8/8/1R2K2R b Kkq - 0 1 ;D1 26 ;D2 583 ;D3 14252 ;D4 334705 ;D5 8198901 ;D6 198328929 -r3k2r/8/8/8/8/8/8/2R1K2R b Kkq - 0 1 ;D1 25 ;D2 560 ;D3 13592 ;D4 317324 ;D5 7710115 ;D6 185959088 -r3k2r/8/8/8/8/8/8/R3K1R1 b Qkq - 0 1 ;D1 25 ;D2 560 ;D3 13607 ;D4 320792 ;D5 7848606 ;D6 190755813 -1r2k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1 ;D1 25 ;D2 567 ;D3 14095 ;D4 328965 ;D5 8153719 ;D6 195629489 -2r1k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1 ;D1 25 ;D2 548 ;D3 13502 ;D4 312835 ;D5 7736373 ;D6 184411439 -r3k1r1/8/8/8/8/8/8/R3K2R b KQq - 0 1 ;D1 25 ;D2 547 ;D3 13579 ;D4 316214 ;D5 7878456 ;D6 189224276 -8/1n4N1/2k5/8/8/5K2/1N4n1/8 w - - 0 1 ;D1 14 ;D2 195 ;D3 2760 ;D4 38675 ;D5 570726 ;D6 8107539 -8/1k6/8/5N2/8/4n3/8/2K5 w - - 0 1 ;D1 11 ;D2 156 ;D3 1636 ;D4 20534 ;D5 223507 ;D6 2594412 -8/8/4k3/3Nn3/3nN3/4K3/8/8 w - - 0 1 ;D1 19 ;D2 289 ;D3 4442 ;D4 73584 ;D5 1198299 ;D6 19870403 -K7/8/2n5/1n6/8/8/8/k6N w - - 0 1 ;D1 3 ;D2 51 ;D3 345 ;D4 5301 ;D5 38348 ;D6 588695 -k7/8/2N5/1N6/8/8/8/K6n w - - 0 1 ;D1 17 ;D2 54 ;D3 835 ;D4 5910 ;D5 92250 ;D6 688780 -8/1n4N1/2k5/8/8/5K2/1N4n1/8 b - - 0 1 ;D1 15 ;D2 193 ;D3 2816 ;D4 40039 ;D5 582642 ;D6 8503277 -8/1k6/8/5N2/8/4n3/8/2K5 b - - 0 1 ;D1 16 ;D2 180 ;D3 2290 ;D4 24640 ;D5 288141 ;D6 3147566 -8/8/3K4/3Nn3/3nN3/4k3/8/8 b - - 0 1 ;D1 4 ;D2 68 ;D3 1118 ;D4 16199 ;D5 281190 ;D6 4405103 -K7/8/2n5/1n6/8/8/8/k6N b - - 0 1 ;D1 17 ;D2 54 ;D3 835 ;D4 5910 ;D5 92250 ;D6 688780 -k7/8/2N5/1N6/8/8/8/K6n b - - 0 1 ;D1 3 ;D2 51 ;D3 345 ;D4 5301 ;D5 38348 ;D6 588695 -B6b/8/8/8/2K5/4k3/8/b6B w - - 0 1 ;D1 17 ;D2 278 ;D3 4607 ;D4 76778 ;D5 1320507 ;D6 22823890 -8/8/1B6/7b/7k/8/2B1b3/7K w - - 0 1 ;D1 21 ;D2 316 ;D3 5744 ;D4 93338 ;D5 1713368 ;D6 28861171 -k7/B7/1B6/1B6/8/8/8/K6b w - - 0 1 ;D1 21 ;D2 144 ;D3 3242 ;D4 32955 ;D5 787524 ;D6 7881673 -K7/b7/1b6/1b6/8/8/8/k6B w - - 0 1 ;D1 7 ;D2 143 ;D3 1416 ;D4 31787 ;D5 310862 ;D6 7382896 -B6b/8/8/8/2K5/5k2/8/b6B b - - 0 1 ;D1 6 ;D2 106 ;D3 1829 ;D4 31151 ;D5 530585 ;D6 9250746 -8/8/1B6/7b/7k/8/2B1b3/7K b - - 0 1 ;D1 17 ;D2 309 ;D3 5133 ;D4 93603 ;D5 1591064 ;D6 29027891 -k7/B7/1B6/1B6/8/8/8/K6b b - - 0 1 ;D1 7 ;D2 143 ;D3 1416 ;D4 31787 ;D5 310862 ;D6 7382896 -K7/b7/1b6/1b6/8/8/8/k6B b - - 0 1 ;D1 21 ;D2 144 ;D3 3242 ;D4 32955 ;D5 787524 ;D6 7881673 -7k/RR6/8/8/8/8/rr6/7K w - - 0 1 ;D1 19 ;D2 275 ;D3 5300 ;D4 104342 ;D5 2161211 ;D6 44956585 -R6r/8/8/2K5/5k2/8/8/r6R w - - 0 1 ;D1 36 ;D2 1027 ;D3 29215 ;D4 771461 ;D5 20506480 ;D6 525169084 -7k/RR6/8/8/8/8/rr6/7K b - - 0 1 ;D1 19 ;D2 275 ;D3 5300 ;D4 104342 ;D5 2161211 ;D6 44956585 -R6r/8/8/2K5/5k2/8/8/r6R b - - 0 1 ;D1 36 ;D2 1027 ;D3 29227 ;D4 771368 ;D5 20521342 ;D6 524966748 -6kq/8/8/8/8/8/8/7K w - - 0 1 ;D1 2 ;D2 36 ;D3 143 ;D4 3637 ;D5 14893 ;D6 391507 -6KQ/8/8/8/8/8/8/7k b - - 0 1 ;D1 2 ;D2 36 ;D3 143 ;D4 3637 ;D5 14893 ;D6 391507 -K7/8/8/3Q4/4q3/8/8/7k w - - 0 1 ;D1 6 ;D2 35 ;D3 495 ;D4 8349 ;D5 166741 ;D6 3370175 -6qk/8/8/8/8/8/8/7K b - - 0 1 ;D1 22 ;D2 43 ;D3 1015 ;D4 4167 ;D5 105749 ;D6 419369 -6KQ/8/8/8/8/8/8/7k b - - 0 1 ;D1 2 ;D2 36 ;D3 143 ;D4 3637 ;D5 14893 ;D6 391507 -K7/8/8/3Q4/4q3/8/8/7k b - - 0 1 ;D1 6 ;D2 35 ;D3 495 ;D4 8349 ;D5 166741 ;D6 3370175 -8/8/8/8/8/K7/P7/k7 w - - 0 1 ;D1 3 ;D2 7 ;D3 43 ;D4 199 ;D5 1347 ;D6 6249 -8/8/8/8/8/7K/7P/7k w - - 0 1 ;D1 3 ;D2 7 ;D3 43 ;D4 199 ;D5 1347 ;D6 6249 -K7/p7/k7/8/8/8/8/8 w - - 0 1 ;D1 1 ;D2 3 ;D3 12 ;D4 80 ;D5 342 ;D6 2343 -7K/7p/7k/8/8/8/8/8 w - - 0 1 ;D1 1 ;D2 3 ;D3 12 ;D4 80 ;D5 342 ;D6 2343 -8/2k1p3/3pP3/3P2K1/8/8/8/8 w - - 0 1 ;D1 7 ;D2 35 ;D3 210 ;D4 1091 ;D5 7028 ;D6 34834 -8/8/8/8/8/K7/P7/k7 b - - 0 1 ;D1 1 ;D2 3 ;D3 12 ;D4 80 ;D5 342 ;D6 2343 -8/8/8/8/8/7K/7P/7k b - - 0 1 ;D1 1 ;D2 3 ;D3 12 ;D4 80 ;D5 342 ;D6 2343 -K7/p7/k7/8/8/8/8/8 b - - 0 1 ;D1 3 ;D2 7 ;D3 43 ;D4 199 ;D5 1347 ;D6 6249 -7K/7p/7k/8/8/8/8/8 b - - 0 1 ;D1 3 ;D2 7 ;D3 43 ;D4 199 ;D5 1347 ;D6 6249 -8/2k1p3/3pP3/3P2K1/8/8/8/8 b - - 0 1 ;D1 5 ;D2 35 ;D3 182 ;D4 1091 ;D5 5408 ;D6 34822 -8/8/8/8/8/4k3/4P3/4K3 w - - 0 1 ;D1 2 ;D2 8 ;D3 44 ;D4 282 ;D5 1814 ;D6 11848 -4k3/4p3/4K3/8/8/8/8/8 b - - 0 1 ;D1 2 ;D2 8 ;D3 44 ;D4 282 ;D5 1814 ;D6 11848 -8/8/7k/7p/7P/7K/8/8 w - - 0 1 ;D1 3 ;D2 9 ;D3 57 ;D4 360 ;D5 1969 ;D6 10724 -8/8/k7/p7/P7/K7/8/8 w - - 0 1 ;D1 3 ;D2 9 ;D3 57 ;D4 360 ;D5 1969 ;D6 10724 -8/8/3k4/3p4/3P4/3K4/8/8 w - - 0 1 ;D1 5 ;D2 25 ;D3 180 ;D4 1294 ;D5 8296 ;D6 53138 -8/3k4/3p4/8/3P4/3K4/8/8 w - - 0 1 ;D1 8 ;D2 61 ;D3 483 ;D4 3213 ;D5 23599 ;D6 157093 -8/8/3k4/3p4/8/3P4/3K4/8 w - - 0 1 ;D1 8 ;D2 61 ;D3 411 ;D4 3213 ;D5 21637 ;D6 158065 -k7/8/3p4/8/3P4/8/8/7K w - - 0 1 ;D1 4 ;D2 15 ;D3 90 ;D4 534 ;D5 3450 ;D6 20960 -8/8/7k/7p/7P/7K/8/8 b - - 0 1 ;D1 3 ;D2 9 ;D3 57 ;D4 360 ;D5 1969 ;D6 10724 -8/8/k7/p7/P7/K7/8/8 b - - 0 1 ;D1 3 ;D2 9 ;D3 57 ;D4 360 ;D5 1969 ;D6 10724 -8/8/3k4/3p4/3P4/3K4/8/8 b - - 0 1 ;D1 5 ;D2 25 ;D3 180 ;D4 1294 ;D5 8296 ;D6 53138 -8/3k4/3p4/8/3P4/3K4/8/8 b - - 0 1 ;D1 8 ;D2 61 ;D3 411 ;D4 3213 ;D5 21637 ;D6 158065 -8/8/3k4/3p4/8/3P4/3K4/8 b - - 0 1 ;D1 8 ;D2 61 ;D3 483 ;D4 3213 ;D5 23599 ;D6 157093 -k7/8/3p4/8/3P4/8/8/7K b - - 0 1 ;D1 4 ;D2 15 ;D3 89 ;D4 537 ;D5 3309 ;D6 21104 -7k/3p4/8/8/3P4/8/8/K7 w - - 0 1 ;D1 4 ;D2 19 ;D3 117 ;D4 720 ;D5 4661 ;D6 32191 -7k/8/8/3p4/8/8/3P4/K7 w - - 0 1 ;D1 5 ;D2 19 ;D3 116 ;D4 716 ;D5 4786 ;D6 30980 -k7/8/8/7p/6P1/8/8/K7 w - - 0 1 ;D1 5 ;D2 22 ;D3 139 ;D4 877 ;D5 6112 ;D6 41874 -k7/8/7p/8/8/6P1/8/K7 w - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4354 ;D6 29679 -k7/8/8/6p1/7P/8/8/K7 w - - 0 1 ;D1 5 ;D2 22 ;D3 139 ;D4 877 ;D5 6112 ;D6 41874 -k7/8/6p1/8/8/7P/8/K7 w - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4354 ;D6 29679 -k7/8/8/3p4/4p3/8/8/7K w - - 0 1 ;D1 3 ;D2 15 ;D3 84 ;D4 573 ;D5 3013 ;D6 22886 -k7/8/3p4/8/8/4P3/8/7K w - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4271 ;D6 28662 -7k/3p4/8/8/3P4/8/8/K7 b - - 0 1 ;D1 5 ;D2 19 ;D3 117 ;D4 720 ;D5 5014 ;D6 32167 -7k/8/8/3p4/8/8/3P4/K7 b - - 0 1 ;D1 4 ;D2 19 ;D3 117 ;D4 712 ;D5 4658 ;D6 30749 -k7/8/8/7p/6P1/8/8/K7 b - - 0 1 ;D1 5 ;D2 22 ;D3 139 ;D4 877 ;D5 6112 ;D6 41874 -k7/8/7p/8/8/6P1/8/K7 b - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4354 ;D6 29679 -k7/8/8/6p1/7P/8/8/K7 b - - 0 1 ;D1 5 ;D2 22 ;D3 139 ;D4 877 ;D5 6112 ;D6 41874 -k7/8/6p1/8/8/7P/8/K7 b - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4354 ;D6 29679 -k7/8/8/3p4/4p3/8/8/7K b - - 0 1 ;D1 5 ;D2 15 ;D3 102 ;D4 569 ;D5 4337 ;D6 22579 -k7/8/3p4/8/8/4P3/8/7K b - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4271 ;D6 28662 -7k/8/8/p7/1P6/8/8/7K w - - 0 1 ;D1 5 ;D2 22 ;D3 139 ;D4 877 ;D5 6112 ;D6 41874 -7k/8/p7/8/8/1P6/8/7K w - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4354 ;D6 29679 -7k/8/8/1p6/P7/8/8/7K w - - 0 1 ;D1 5 ;D2 22 ;D3 139 ;D4 877 ;D5 6112 ;D6 41874 -7k/8/1p6/8/8/P7/8/7K w - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4354 ;D6 29679 -k7/7p/8/8/8/8/6P1/K7 w - - 0 1 ;D1 5 ;D2 25 ;D3 161 ;D4 1035 ;D5 7574 ;D6 55338 -k7/6p1/8/8/8/8/7P/K7 w - - 0 1 ;D1 5 ;D2 25 ;D3 161 ;D4 1035 ;D5 7574 ;D6 55338 -3k4/3pp3/8/8/8/8/3PP3/3K4 w - - 0 1 ;D1 7 ;D2 49 ;D3 378 ;D4 2902 ;D5 24122 ;D6 199002 -7k/8/8/p7/1P6/8/8/7K b - - 0 1 ;D1 5 ;D2 22 ;D3 139 ;D4 877 ;D5 6112 ;D6 41874 -7k/8/p7/8/8/1P6/8/7K b - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4354 ;D6 29679 -7k/8/8/1p6/P7/8/8/7K b - - 0 1 ;D1 5 ;D2 22 ;D3 139 ;D4 877 ;D5 6112 ;D6 41874 -7k/8/1p6/8/8/P7/8/7K b - - 0 1 ;D1 4 ;D2 16 ;D3 101 ;D4 637 ;D5 4354 ;D6 29679 -k7/7p/8/8/8/8/6P1/K7 b - - 0 1 ;D1 5 ;D2 25 ;D3 161 ;D4 1035 ;D5 7574 ;D6 55338 -k7/6p1/8/8/8/8/7P/K7 b - - 0 1 ;D1 5 ;D2 25 ;D3 161 ;D4 1035 ;D5 7574 ;D6 55338 -3k4/3pp3/8/8/8/8/3PP3/3K4 b - - 0 1 ;D1 7 ;D2 49 ;D3 378 ;D4 2902 ;D5 24122 ;D6 199002 -8/Pk6/8/8/8/8/6Kp/8 w - - 0 1 ;D1 11 ;D2 97 ;D3 887 ;D4 8048 ;D5 90606 ;D6 1030499 -n1n5/1Pk5/8/8/8/8/5Kp1/5N1N w - - 0 1 ;D1 24 ;D2 421 ;D3 7421 ;D4 124608 ;D5 2193768 ;D6 37665329 -8/PPPk4/8/8/8/8/4Kppp/8 w - - 0 1 ;D1 18 ;D2 270 ;D3 4699 ;D4 79355 ;D5 1533145 ;D6 28859283 -n1n5/PPPk4/8/8/8/8/4Kppp/5N1N w - - 0 1 ;D1 24 ;D2 496 ;D3 9483 ;D4 182838 ;D5 3605103 ;D6 71179139 -8/Pk6/8/8/8/8/6Kp/8 b - - 0 1 ;D1 11 ;D2 97 ;D3 887 ;D4 8048 ;D5 90606 ;D6 1030499 -n1n5/1Pk5/8/8/8/8/5Kp1/5N1N b - - 0 1 ;D1 24 ;D2 421 ;D3 7421 ;D4 124608 ;D5 2193768 ;D6 37665329 -8/PPPk4/8/8/8/8/4Kppp/8 b - - 0 1 ;D1 18 ;D2 270 ;D3 4699 ;D4 79355 ;D5 1533145 ;D6 28859283 -n1n5/PPPk4/8/8/8/8/4Kppp/5N1N b - - 0 1 ;D1 24 ;D2 496 ;D3 9483 ;D4 182838 ;D5 3605103 ;D6 71179139"#; - -#[test] -fn chess_perft_suite() { run_tests("Chess", Chess::create(), TEST_STR); } diff --git a/src/lib.rs b/src/lib.rs index 2b08789..104f01b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![doc = include_str!("../README.md")] + pub mod bitboard; pub mod board; pub mod games; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..b329c52 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,8 @@ +use monster_chess::{games::{chess::Chess}, board::tests::get_time_ms}; + +pub fn main() { + let game = Chess::create(); + let board = game.from_fen("r1bq1rk1/ppp2ppp/5n2/2bp4/2NPP3/2P5/PP3PPP/RNBQK2R w KQ - 0 1"); + + println!("{}", board.to_fen()); +} \ No newline at end of file