From 5c12f5e403ad2c8b20f29fb3287f7548c4ac3a7c Mon Sep 17 00:00:00 2001 From: andrea <63297263+dede1751@users.noreply.github.com> Date: Sun, 12 Feb 2023 14:43:03 +0100 Subject: [PATCH] improved draw detection --- Cargo.lock | 2 +- Cargo.toml | 2 +- readme.MD | 14 +++++++------- src/position.rs | 25 ++++++++++++++++++------- src/search.rs | 21 +++++---------------- src/square.rs | 4 ---- 6 files changed, 32 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30924ef..353bef5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,4 +4,4 @@ version = 3 [[package]] name = "carp" -version = "1.2.2" +version = "1.3.0" diff --git a/Cargo.toml b/Cargo.toml index 60c6f8b..4a72ed4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "carp" -version = "1.2.2" +version = "1.3.0" edition = "2021" authors = ["Andrea Sgobbi"] diff --git a/readme.MD b/readme.MD index c0eef5f..f7a773d 100644 --- a/readme.MD +++ b/readme.MD @@ -17,18 +17,16 @@ and most importantly the [chess programming wiki](https://www.chessprogramming.o Move generation is fully legal, inspired by [this article](https://www.codeproject.com/Articles/5313417/Worlds-Fastest-Bitboard-Chess-Movegenerator) and perft(7) on startpos will achieve ~130 MNodes/s locally (a roughly 4x speedup compared to pseudolegal generation). -Version 1.2 adds much more aggressive pruning techniques, which have brought a noticeable speedup in -time to depth and reduced the branching factor greatly. Now Carp can easily push to depths 16-17 in +Newer versions added much more aggressive pruning techniques, which have brought a noticeable speedup in +time to depth and reduced the branching factor greatly. Now Carp can easily push to depths 18-20 in blitz midgames, hitting much softer depth walls. +Version 1.3 includes many search optimizations discussed in the chess programming wiki, leading to +a massive elo increase. Currently I would estimate Carp to be ~2750 CCRL40/15 elo on 4CPU, though I +will wait until done with evaluation to submit it to competitions. To benchmark the engine I've also added a test directory with a simple shell script running cutechess cli. The script allows to run gauntlet tests against other engines or SPRT tests against older versions of Carp. Engines must be manually added to the shell script. -Version 1.2.2 implemented many search optimizations described in the Chess Programming wiki which -still need proper testing and parameter tuning, although Carp seems to have gained at least ~120 -elo compared to the previous version. -To accomodate for all this testing, I'm looking to make Carp OpenBench compliant and eventually -host my own instance. The engine is quite fun to play against when limiting the depth to reasonable values, since it is relatively dumb positionally: provided it does not see some crazy computer tactic a good @@ -61,3 +59,5 @@ Of course it is still lacking many optimizations, most notably: * Endgame tablebases * Various other heuristics (probcut, razoring, search extensions...) * Further improvement to evaluation/NNUE + +I also want to work on OpenBench compliance to simplify testing. diff --git a/src/position.rs b/src/position.rs index cf44ba0..fa938b2 100644 --- a/src/position.rs +++ b/src/position.rs @@ -180,17 +180,28 @@ impl Position { } /// Draw by insufficient material (strictly for when it is impossible to mate): - /// - King vs King - /// - King vs King + Bishop - /// - King vs King + Knight - /// - King + Bishop vs King + Bishop + /// Some of the logic is taken from Tantabus fn insufficient_material(&self) -> bool { + const WHITE_SQUARES: BitBoard = BitBoard(12273903644374837845); + const CORNERS: BitBoard = BitBoard(9295429630892703873); + const EDGES: BitBoard = BitBoard(18411139144890810879); + let kings = self.board.kings(); + let knights = self.board.knights(); + let bishops = self.board.bishops(); + match self.board.occupancy.count_bits() { 2 => true, - 3 => self.board.knights() | self.board.bishops() != EMPTY_BB, // 1 knight or 1 bishop + 3 => knights | bishops != EMPTY_BB, // 1 knight or 1 bishop 4 => { - self.board.bishops().count_bits() == 2 && // opposite color bishops - (self.board.bishops() & WHITE_SQUARES).count_bits() == 1 + let one_each = self.board.side_occupancy[0].count_bits() == 2; + let knight_count = knights.count_bits(); + let bishop_count = bishops.count_bits(); + + (knight_count == 2 && kings & EDGES != EMPTY_BB) // two knights, king not on edge + || (bishop_count == 2 + && ((bishops & WHITE_SQUARES).count_bits() != 1 // same color bishops + || (one_each && kings & CORNERS != EMPTY_BB))) // opposite color, king not in corner + || (one_each && kings & CORNERS != EMPTY_BB) // bishop and knight, king not in corner } _ => false, } diff --git a/src/search.rs b/src/search.rs index 6263f79..e616b2d 100644 --- a/src/search.rs +++ b/src/search.rs @@ -340,21 +340,10 @@ impl<'a> Search<'a> { // reduce depth for all moves beyond first let reduced_depth = if move_count >= LMR_THRESHOLD && depth >= LMR_LOWER_LIMIT && is_quiet { - let lmr_reduction = lmr_reduction(depth, move_count); - let lmr_extension = - is_check as usize + in_check as usize + pv_node as usize; - - if lmr_extension >= lmr_reduction { - depth - } else { - let lmr = lmr_reduction - lmr_extension; - - if depth >= lmr + 1 { - depth - lmr - } else { - 1 - } - } + let lmr_red = lmr_reduction(depth, move_count) as i32; + let lmr_ext = is_check as i32 + in_check as i32 + pv_node as i32; + + (depth as i32 + lmr_ext - lmr_red).clamp(1, depth as i32) as usize } else { depth }; @@ -420,7 +409,7 @@ impl<'a> Search<'a> { let in_check = self.position.king_in_check(); - // when in check, avoid aggressive pruning + // Stand pat and delta pruning avoided when in check let mut stand_pat = 0; if !in_check { stand_pat = self.position.evaluate(); diff --git a/src/square.rs b/src/square.rs index 9da2f75..bbaf1da 100644 --- a/src/square.rs +++ b/src/square.rs @@ -74,10 +74,6 @@ pub const DOUBLE_PUSH: [[Square; FILE_COUNT]; 2] = [ [ A5, B5, C5, D5, E5, F5, G5, H5 ] ]; -/// Black/White square bitboards -pub const WHITE_SQUARES: BitBoard = BitBoard(12273903644374837845); -pub const BLACK_SQUARES: BitBoard = BitBoard(6172840429334713770); - /// Print fen formatted square. impl fmt::Display for Square { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {