diff --git a/eval/src/complex.rs b/eval/src/complex.rs index 5ffa9eae..9b460d92 100644 --- a/eval/src/complex.rs +++ b/eval/src/complex.rs @@ -36,8 +36,15 @@ impl Eval for Complex { let pawn_scores = self.pawn_structure.scores(); let mobility_scores = self.mobility.scores(pos); let bishop_pair_scores = Self::bishop_pair_factor(pos) * params::BISHOP_PAIR; - let scores = - self.pst_scores + tempo_scores + pawn_scores + mobility_scores + bishop_pair_scores; + let (open_file_factor, semi_open_file_factor) = Self::rook_on_open_file_factor(pos); + let rook_on_open_file_scores = open_file_factor * params::ROOK_ON_OPEN_FILE + + semi_open_file_factor * params::ROOK_ON_SEMI_OPEN_FILE; + let scores = self.pst_scores + + tempo_scores + + pawn_scores + + mobility_scores + + bishop_pair_scores + + rook_on_open_file_scores; let game_phase = self.game_phase.game_phase_clamped(); let tapered_score = ((game_phase as i64 * scores.0 as i64 + (GamePhase::MAX - game_phase) as i64 * scores.1 as i64) @@ -158,11 +165,47 @@ impl Complex { pub fn bishop_pair_factor(pos: &Position) -> Score { pos.has_bishop_pair(Side::White) as Score - pos.has_bishop_pair(Side::Black) as Score } + + pub fn rook_on_open_file_factor(pos: &Position) -> (Score, Score) { + let mut open_file_factor = 0; + let mut semi_open_file_factor = 0; + + let white_pawns = pos.piece_occupancy(Side::White, piece::Type::Pawn); + let black_pawns = pos.piece_occupancy(Side::Black, piece::Type::Pawn); + + let mut white_rooks = pos.piece_occupancy(Side::White, piece::Type::Rook); + while white_rooks != Bitboard::EMPTY { + let white_rook = white_rooks.square_scan_forward_reset(); + let white_rook_file = Bitboard::from_square(white_rook).file_fill(); + if white_rook_file & white_pawns == Bitboard::EMPTY { + if white_rook_file & black_pawns == Bitboard::EMPTY { + open_file_factor += 1; + } else { + semi_open_file_factor += 1; + } + } + } + + let mut black_rooks = pos.piece_occupancy(Side::Black, piece::Type::Rook); + while black_rooks != Bitboard::EMPTY { + let black_rook = black_rooks.square_scan_forward_reset(); + let black_rook_file = Bitboard::from_square(black_rook).file_fill(); + if black_rook_file & black_pawns == Bitboard::EMPTY { + if black_rook_file & white_pawns == Bitboard::EMPTY { + open_file_factor -= 1; + } else { + semi_open_file_factor -= 1; + } + } + } + + (open_file_factor, semi_open_file_factor) + } } #[cfg(test)] mod tests { - use movegen::fen::Fen; + use movegen::{fen::Fen, piece, position::Position, square::Square}; use crate::{Eval, EQ_POSITION}; @@ -210,4 +253,35 @@ mod tests { ); } } + + #[test] + fn rook_on_open_file_factor() { + let pos = Position::initial(); + assert_eq!((0, 0), Complex::rook_on_open_file_factor(&pos)); + + let mut pos = Position::empty(); + pos.set_piece_at(Square::E1, Some(piece::Piece::WHITE_KING)); + pos.set_piece_at(Square::E8, Some(piece::Piece::BLACK_KING)); + assert_eq!((0, 0), Complex::rook_on_open_file_factor(&pos)); + + pos.set_piece_at(Square::A1, Some(piece::Piece::WHITE_ROOK)); + assert_eq!((1, 0), Complex::rook_on_open_file_factor(&pos)); + pos.set_piece_at(Square::H1, Some(piece::Piece::WHITE_ROOK)); + assert_eq!((2, 0), Complex::rook_on_open_file_factor(&pos)); + + pos.set_piece_at(Square::A8, Some(piece::Piece::BLACK_ROOK)); + assert_eq!((1, 0), Complex::rook_on_open_file_factor(&pos)); + pos.set_piece_at(Square::H8, Some(piece::Piece::BLACK_ROOK)); + assert_eq!((0, 0), Complex::rook_on_open_file_factor(&pos)); + + pos.set_piece_at(Square::A2, Some(piece::Piece::WHITE_PAWN)); + assert_eq!((0, -1), Complex::rook_on_open_file_factor(&pos)); + pos.set_piece_at(Square::H2, Some(piece::Piece::WHITE_PAWN)); + assert_eq!((0, -2), Complex::rook_on_open_file_factor(&pos)); + + pos.set_piece_at(Square::A7, Some(piece::Piece::BLACK_PAWN)); + assert_eq!((0, -1), Complex::rook_on_open_file_factor(&pos)); + pos.set_piece_at(Square::H7, Some(piece::Piece::BLACK_PAWN)); + assert_eq!((0, 0), Complex::rook_on_open_file_factor(&pos)); + } } diff --git a/eval/src/params.rs b/eval/src/params.rs index 534acfe9..8841916b 100644 --- a/eval/src/params.rs +++ b/eval/src/params.rs @@ -13,14 +13,16 @@ const MATERIAL_KNIGHT: ScorePair = ScorePair(0, 0); const MATERIAL_PAWN: ScorePair = ScorePair(0, 0); // The side to move gets a small bonus -pub const TEMPO: ScorePair = ScorePair(26, 21); +pub const TEMPO: ScorePair = ScorePair(31, 22); -pub const PASSED_PAWN: ScorePair = ScorePair(-10, 30); -pub const ISOLATED_PAWN: ScorePair = ScorePair(-14, -6); -pub const BACKWARD_PAWN: ScorePair = ScorePair(-10, -2); -pub const DOUBLED_PAWN: ScorePair = ScorePair(-6, -5); +pub const PASSED_PAWN: ScorePair = ScorePair(-12, 32); +pub const ISOLATED_PAWN: ScorePair = ScorePair(-16, -6); +pub const BACKWARD_PAWN: ScorePair = ScorePair(-13, -2); +pub const DOUBLED_PAWN: ScorePair = ScorePair(-13, -4); -pub const BISHOP_PAIR: ScorePair = ScorePair(32, 28); +pub const BISHOP_PAIR: ScorePair = ScorePair(44, 22); +pub const ROOK_ON_OPEN_FILE: ScorePair = ScorePair(41, -5); +pub const ROOK_ON_SEMI_OPEN_FILE: ScorePair = ScorePair(13, 1); pub const KNIGHT_MOB_LEN: usize = 9; pub const BISHOP_MOB_LEN: usize = 14; @@ -29,31 +31,29 @@ pub const QUEEN_MOB_LEN: usize = 28; pub const MOB_LEN: usize = KNIGHT_MOB_LEN + BISHOP_MOB_LEN + ROOK_MOB_LEN + QUEEN_MOB_LEN; const MOBILITY_KNIGHT_MG_EG: ([Score; KNIGHT_MOB_LEN], [Score; KNIGHT_MOB_LEN]) = ( - [-16, 26, 35, 41, 49, 50, 52, 48, 50], - [0, -37, -42, -40, -38, -39, -36, -34, -40], + [-14, 69, 84, 90, 105, 100, 96, 95, 98], + [4, -20, -35, -42, -40, -39, -30, -31, -35], ); const MOBILITY_BISHOP_MG_EG: ([Score; BISHOP_MOB_LEN], [Score; BISHOP_MOB_LEN]) = ( - [15, 26, 37, 37, 44, 48, 47, 52, 63, 58, 73, 74, 53, 32], + [53, 63, 75, 77, 84, 92, 92, 94, 108, 106, 108, 116, 97, 63], [ - -54, -57, -41, -41, -40, -25, -21, -16, -22, -13, -13, -14, -13, 6, + -40, -43, -41, -37, -35, -28, -19, -12, -17, -13, -14, -3, 0, 2, ], ); const MOBILITY_ROOK_MG_EG: ([Score; ROOK_MOB_LEN], [Score; ROOK_MOB_LEN]) = ( + [30, 38, 36, 39, 39, 45, 49, 57, 51, 56, 47, 51, 61, 69, 48], [ - -32, -26, -22, -19, -17, -13, -4, -4, 0, 9, 16, 15, 33, 28, 26, - ], - [ - -42, -37, -22, -28, -20, -18, -23, -20, -12, -8, -15, -8, -8, -4, -1, + -40, -36, -17, -25, -12, -10, -12, -17, -4, -8, -5, -3, 3, 8, 7, ], ); const MOBILITY_QUEEN_MG_EG: ([Score; QUEEN_MOB_LEN], [Score; QUEEN_MOB_LEN]) = ( [ - -19, -28, -15, -12, -4, 5, 11, 17, 13, 11, 26, 16, 25, 26, 23, 30, 35, 28, 38, 39, 33, 60, - 51, 67, 37, 38, 12, 10, + -2, -4, 24, 19, 33, 37, 43, 49, 48, 49, 64, 55, 58, 63, 61, 68, 63, 66, 64, 70, 66, 104, + 92, 126, 68, 70, 35, 23, ], [ - -5, -4, -13, -4, -50, -10, -37, -32, -9, 14, 19, 34, 23, 30, 41, 47, 47, 63, 75, 70, 57, - 55, 39, 54, 44, 48, 17, 11, + -6, -12, -3, 19, -44, -16, -10, 0, 34, 58, 44, 78, 71, 72, 81, 80, 92, 117, 116, 107, 104, + 80, 64, 69, 72, 82, 50, 37, ], ); @@ -64,138 +64,138 @@ const MOBILITY_QUEEN_MG_EG: ([Score; QUEEN_MOB_LEN], [Score; QUEEN_MOB_LEN]) = ( const PST_PAWN_MG_EG: ([Score; 32], [Score; 32]) = ( [ 0, 0, 0, 0, - 115, 147, 137, 175, - 77, 95, 128, 107, - 90, 101, 99, 124, - 68, 83, 100, 118, - 84, 96, 104, 112, - 79, 104, 103, 106, + 143, 156, 167, 215, + 99, 120, 162, 145, + 116, 117, 118, 150, + 85, 99, 122, 138, + 106, 114, 123, 135, + 96, 123, 122, 126, 0, 0, 0, 0, ], [ 0, 0, 0, 0, - 266, 249, 244, 191, - 165, 149, 121, 125, - 91, 79, 77, 60, - 75, 69, 58, 54, - 70, 62, 58, 57, - 71, 63, 65, 65, + 277, 269, 245, 205, + 178, 147, 124, 133, + 91, 79, 79, 52, + 75, 68, 61, 52, + 68, 62, 56, 55, + 68, 58, 66, 58, 0, 0, 0, 0, ], ); #[rustfmt::skip] const PST_KNIGHT_MG_EG: ([Score; 32], [Score; 32]) = ( [ - 197, 285, 238, 264, - 282, 277, 369, 351, - 293, 323, 364, 378, - 319, 317, 335, 340, - 299, 322, 336, 334, - 299, 323, 328, 340, - 304, 299, 307, 333, - 278, 307, 300, 289, + 177, 274, 225, 271, + 289, 287, 405, 375, + 307, 341, 394, 407, + 331, 331, 352, 356, + 299, 338, 358, 351, + 307, 335, 341, 362, + 327, 300, 329, 343, + 285, 311, 308, 302, ], [ - 210, 281, 305, 294, - 278, 298, 277, 288, - 277, 293, 318, 300, - 293, 313, 326, 319, - 298, 289, 309, 310, - 281, 285, 284, 301, - 265, 281, 289, 289, - 278, 268, 295, 303, + 220, 283, 310, 296, + 282, 291, 265, 281, + 273, 294, 321, 295, + 301, 314, 332, 320, + 304, 298, 310, 317, + 280, 287, 290, 302, + 272, 285, 286, 290, + 275, 269, 289, 299, ], ); #[rustfmt::skip] const PST_BISHOP_MG_EG: ([Score; 32], [Score; 32]) = ( [ - 276, 300, 271, 246, - 320, 313, 303, 306, - 345, 336, 337, 351, - 309, 313, 303, 346, - 320, 312, 314, 343, - 334, 340, 346, 331, - 330, 352, 332, 335, - 328, 318, 323, 329, + 289, 290, 268, 233, + 361, 325, 325, 315, + 370, 375, 361, 376, + 327, 334, 312, 358, + 331, 343, 335, 360, + 359, 361, 370, 353, + 356, 377, 355, 353, + 345, 330, 335, 347, ], [ - 297, 302, 256, 276, - 270, 294, 289, 287, - 280, 281, 292, 288, - 279, 299, 300, 310, - 278, 297, 297, 302, - 281, 284, 286, 292, - 282, 287, 288, 291, - 275, 305, 290, 301, + 294, 297, 276, 291, + 267, 298, 303, 289, + 285, 285, 288, 291, + 289, 292, 311, 309, + 293, 285, 300, 303, + 286, 283, 282, 292, + 275, 279, 280, 287, + 287, 307, 292, 303, ], ); #[rustfmt::skip] const PST_ROOK_MG_EG: ([Score; 32], [Score; 32]) = ( [ - 485, 489, 523, 513, - 495, 483, 512, 527, - 519, 528, 506, 523, - 477, 506, 486, 504, - 476, 487, 465, 492, - 472, 504, 500, 496, - 456, 506, 498, 510, - 502, 505, 519, 527, + 507, 502, 542, 528, + 524, 504, 548, 552, + 541, 546, 519, 528, + 517, 522, 528, 528, + 499, 516, 499, 515, + 502, 513, 512, 531, + 464, 531, 518, 536, + 528, 530, 542, 546, ], [ - 514, 519, 493, 493, - 503, 514, 515, 500, - 494, 498, 494, 490, - 496, 483, 498, 492, - 482, 495, 507, 491, - 474, 476, 481, 487, - 503, 485, 476, 491, - 469, 476, 473, 471, + 516, 525, 490, 496, + 503, 516, 516, 499, + 502, 503, 494, 498, + 486, 491, 493, 500, + 485, 500, 511, 494, + 481, 479, 478, 481, + 517, 489, 491, 489, + 467, 482, 479, 478, ], ); #[rustfmt::skip] const PST_QUEEN_MG_EG: ([Score; 32], [Score; 32]) = ( [ - 935, 914, 937, 918, - 908, 879, 926, 868, - 936, 931, 931, 941, - 919, 886, 897, 900, - 905, 909, 917, 904, - 914, 919, 918, 914, - 922, 936, 940, 931, - 913, 928, 936, 941, + 951, 925, 964, 950, + 941, 905, 954, 881, + 985, 977, 966, 960, + 960, 929, 920, 924, + 924, 934, 947, 928, + 952, 949, 943, 954, + 956, 974, 970, 962, + 943, 964, 954, 966, ], [ - 921, 933, 938, 924, - 925, 939, 948, 965, - 912, 920, 936, 953, - 900, 935, 935, 960, - 941, 911, 917, 961, - 874, 894, 923, 912, - 913, 902, 884, 917, - 891, 879, 885, 875, + 935, 941, 967, 958, + 942, 972, 979, 1016, + 929, 914, 965, 996, + 918, 952, 978, 993, + 949, 960, 952, 1004, + 884, 925, 954, 945, + 938, 906, 908, 940, + 911, 892, 908, 904, ], ); #[rustfmt::skip] const PST_KING_MG_EG: ([Score; 32], [Score; 32]) = ( [ - 9, -4, 7, 6, - 5, 1, 30, 23, - 8, 14, 9, 19, - 3, 5, -11, -10, - -23, 6, -24, -43, - -7, -13, -50, -69, - 28, 23, -25, -48, - 43, 60, 11, 15, + 20, 6, 17, 17, + 3, 2, 42, 32, + 26, 30, 16, 39, + 7, 22, -11, 4, + -37, -20, -41, -75, + -8, -16, -52, -85, + 27, 18, -30, -60, + 39, 59, -1, 5, ], [ - -30, -2, -17, -1, - 0, 10, 6, 17, - -1, 25, 30, 12, - -12, 7, 18, 24, - -15, 0, 24, 28, - -20, 5, 24, 32, - -24, -4, 10, 21, - -71, -44, -18, -33, + -29, -15, -12, -15, + -15, 8, 7, 25, + -3, 25, 16, 14, + -7, 14, 24, 22, + -19, 5, 29, 36, + -16, 6, 23, 34, + -28, -4, 8, 25, + -68, -45, -14, -29, ], ); diff --git a/tuner/src/eval_params.rs b/tuner/src/eval_params.rs index d40257bb..78fd16ca 100644 --- a/tuner/src/eval_params.rs +++ b/tuner/src/eval_params.rs @@ -11,7 +11,7 @@ use crate::{ position_features::{ PST_SIZE, START_IDX_BACKWARD_PAWN, START_IDX_BISHOP_PAIR, START_IDX_DOUBLED_PAWN, START_IDX_ISOLATED_PAWN, START_IDX_MOBILITY, START_IDX_PASSED_PAWN, START_IDX_PST, - START_IDX_TEMPO, + START_IDX_ROOK_ON_OPEN_FILE, START_IDX_TEMPO, }, }; @@ -30,6 +30,7 @@ pub struct EvalParams { doubled_pawn: ScorePair, mobility: [ScorePair; MOB_LEN], bishop_pair: ScorePair, + rook_on_open_file: [ScorePair; 2], } impl Default for EvalParams { @@ -48,6 +49,7 @@ impl Default for EvalParams { doubled_pawn: ScorePair(0, 0), mobility: [ScorePair(0, 0); MOB_LEN], bishop_pair: ScorePair(0, 0), + rook_on_open_file: [ScorePair(0, 0); 2], } } } @@ -93,6 +95,14 @@ impl From<&WeightVector> for EvalParams { eval_params.bishop_pair.0 = weights[START_IDX_BISHOP_PAIR].round() as Score; eval_params.bishop_pair.1 = weights[START_IDX_BISHOP_PAIR + 1].round() as Score; + eval_params.rook_on_open_file[0].0 = weights[START_IDX_ROOK_ON_OPEN_FILE].round() as Score; + eval_params.rook_on_open_file[0].1 = + weights[START_IDX_ROOK_ON_OPEN_FILE + 1].round() as Score; + eval_params.rook_on_open_file[1].0 = + weights[START_IDX_ROOK_ON_OPEN_FILE + 2].round() as Score; + eval_params.rook_on_open_file[1].1 = + weights[START_IDX_ROOK_ON_OPEN_FILE + 3].round() as Score; + eval_params } } @@ -130,6 +140,16 @@ impl Display for EvalParams { "pub const BISHOP_PAIR: ScorePair = ScorePair({}, {});", self.bishop_pair.0, self.bishop_pair.1 )?; + writeln!( + f, + "pub const ROOK_ON_OPEN_FILE: ScorePair = ScorePair({}, {});", + self.rook_on_open_file[0].0, self.rook_on_open_file[0].1 + )?; + writeln!( + f, + "pub const ROOK_ON_SEMI_OPEN_FILE: ScorePair = ScorePair({}, {});", + self.rook_on_open_file[1].0, self.rook_on_open_file[1].1 + )?; self.fmt_mob(f)?; diff --git a/tuner/src/feature_evaluator.rs b/tuner/src/feature_evaluator.rs index 00e2e053..078e570d 100644 --- a/tuner/src/feature_evaluator.rs +++ b/tuner/src/feature_evaluator.rs @@ -4,7 +4,7 @@ use nalgebra::SVector; use crate::position_features::{ EvalType, PositionFeatures, NUM_FEATURES, PST_SIZE, START_IDX_BACKWARD_PAWN, START_IDX_BISHOP_PAIR, START_IDX_DOUBLED_PAWN, START_IDX_ISOLATED_PAWN, START_IDX_MOBILITY, - START_IDX_PASSED_PAWN, START_IDX_PST, START_IDX_TEMPO, + START_IDX_PASSED_PAWN, START_IDX_PST, START_IDX_ROOK_ON_OPEN_FILE, START_IDX_TEMPO, }; type Weight = f64; @@ -81,6 +81,11 @@ pub fn initialize_weights() -> WeightVector { weights[START_IDX_BISHOP_PAIR] = params::BISHOP_PAIR.0.into(); weights[START_IDX_BISHOP_PAIR + 1] = params::BISHOP_PAIR.1.into(); + weights[START_IDX_ROOK_ON_OPEN_FILE] = params::ROOK_ON_OPEN_FILE.0.into(); + weights[START_IDX_ROOK_ON_OPEN_FILE + 1] = params::ROOK_ON_OPEN_FILE.1.into(); + weights[START_IDX_ROOK_ON_OPEN_FILE + 2] = params::ROOK_ON_SEMI_OPEN_FILE.0.into(); + weights[START_IDX_ROOK_ON_OPEN_FILE + 3] = params::ROOK_ON_SEMI_OPEN_FILE.1.into(); + weights } diff --git a/tuner/src/position_features.rs b/tuner/src/position_features.rs index a7c2075b..6b032bc6 100644 --- a/tuner/src/position_features.rs +++ b/tuner/src/position_features.rs @@ -18,6 +18,7 @@ const NUM_BACKWARD_PAWN_FEATURES: usize = 2; const NUM_DOUBLED_PAWN_FEATURES: usize = 2; const NUM_MOBILITY_FEATURES: usize = 2 * MOB_LEN; const NUM_BISHOP_PAIR_FEATURES: usize = 2; +const NUM_ROOK_ON_OPEN_FILE_FEATURES: usize = 4; pub const NUM_FEATURES: usize = NUM_PST_FEATURES + NUM_TEMPO_FEATURES + NUM_PASSED_PAWN_FEATURES @@ -25,7 +26,8 @@ pub const NUM_FEATURES: usize = NUM_PST_FEATURES + NUM_BACKWARD_PAWN_FEATURES + NUM_DOUBLED_PAWN_FEATURES + NUM_MOBILITY_FEATURES - + NUM_BISHOP_PAIR_FEATURES; + + NUM_BISHOP_PAIR_FEATURES + + NUM_ROOK_ON_OPEN_FILE_FEATURES; pub const START_IDX_PST: usize = 0; pub const START_IDX_TEMPO: usize = START_IDX_PST + NUM_PST_FEATURES; @@ -35,6 +37,7 @@ pub const START_IDX_BACKWARD_PAWN: usize = START_IDX_ISOLATED_PAWN + NUM_ISOLATE pub const START_IDX_DOUBLED_PAWN: usize = START_IDX_BACKWARD_PAWN + NUM_BACKWARD_PAWN_FEATURES; pub const START_IDX_MOBILITY: usize = START_IDX_DOUBLED_PAWN + NUM_DOUBLED_PAWN_FEATURES; pub const START_IDX_BISHOP_PAIR: usize = START_IDX_MOBILITY + NUM_MOBILITY_FEATURES; +pub const START_IDX_ROOK_ON_OPEN_FILE: usize = START_IDX_BISHOP_PAIR + NUM_BISHOP_PAIR_FEATURES; #[derive(Debug, Clone)] pub struct PositionFeatures { @@ -63,6 +66,7 @@ impl From<&Position> for PositionFeatures { extract_pawn_structure(&mut features, pos); extract_mobility(&mut features, pos); extract_bishop_pair(&mut features, pos); + extract_rook_on_open_file(&mut features, pos); let mg_phase = 1.0 - game_phase; let eg_phase = game_phase; @@ -180,3 +184,19 @@ fn extract_bishop_pair(features: &mut CooMatrix, pos: &Position) { features.push(0, START_IDX_BISHOP_PAIR, multiplier); features.push(0, START_IDX_BISHOP_PAIR + 1, multiplier); } + +fn extract_rook_on_open_file(features: &mut CooMatrix, pos: &Position) { + let (open_file_mul, semi_open_file_mul) = Complex::rook_on_open_file_factor(pos); + features.push(0, START_IDX_ROOK_ON_OPEN_FILE, open_file_mul.into()); + features.push(0, START_IDX_ROOK_ON_OPEN_FILE + 1, open_file_mul.into()); + features.push( + 0, + START_IDX_ROOK_ON_OPEN_FILE + 2, + semi_open_file_mul.into(), + ); + features.push( + 0, + START_IDX_ROOK_ON_OPEN_FILE + 3, + semi_open_file_mul.into(), + ); +}