use super::*;
use crate::cache::pawns::PHTable;
use crate::engine::stats::SearchStats;
use crate::state::representation::Board;
use crate::utils::assert_fast;
use crate::utils::bithelpers::BitHelpers;
use crate::utils::dev;
use std::cmp;
#[cfg(feature = "dev")]
use crate::tuning::tuner::TunerCoeff;
pub struct PawnsData {
doubled_pawns: u8,
isolated_pawns: u8,
chained_pawns: u8,
passed_pawns: u8,
backward_pawns_open_file: u8,
backward_pawns_closed_file: u8,
opened_files: u8,
pawn_shield: u8,
}
pub fn evaluate(board: &Board, phtable: &PHTable, stats: &mut SearchStats) -> PackedEval {
match phtable.get(board.state.pawn_hash) {
Some(entry) => {
dev!(stats.phtable_hits += 1);
return PackedEval::new(entry.score_opening, entry.score_ending);
}
None => {
dev!(stats.phtable_misses += 1);
}
}
let white_eval = evaluate_color(board, WHITE);
let black_eval = evaluate_color(board, BLACK);
let eval = white_eval - black_eval;
phtable.add(board.state.pawn_hash, eval.get_opening(), eval.get_ending());
dev!(stats.phtable_added += 1);
eval
}
pub fn evaluate_without_cache(board: &Board) -> PackedEval {
evaluate_color(board, WHITE) - evaluate_color(board, BLACK)
}
fn evaluate_color(board: &Board, color: usize) -> PackedEval {
assert_fast!(color < 2);
let mut result = PackedEval::default();
let pawns_data = get_pawns_data(board, color);
result += params::DOUBLED_PAWN[pawns_data.doubled_pawns.min(7) as usize];
result += params::ISOLATED_PAWN[pawns_data.isolated_pawns.min(7) as usize];
result += params::CHAINED_PAWN[pawns_data.chained_pawns.min(7) as usize];
result += params::PASSED_PAWN[pawns_data.passed_pawns.min(7) as usize];
result += params::BACKWARD_PAWN_OPEN_FILE[pawns_data.backward_pawns_open_file.min(7) as usize];
result += params::BACKWARD_PAWN_CLOSED_FILE[pawns_data.backward_pawns_closed_file.min(7) as usize];
result += params::PAWN_SHIELD[pawns_data.pawn_shield.min(7) as usize];
result += params::PAWN_SHIELD_OPEN_FILE[pawns_data.opened_files.min(7) as usize];
result
}
fn get_pawns_data(board: &Board, color: usize) -> PawnsData {
assert_fast!(color < 2);
let mut doubled_pawns = 0;
let mut isolated_pawns = 0;
let mut chained_pawns = 0;
let mut passed_pawns = 0;
let mut backward_pawns_open_file = 0;
let mut backward_pawns_closed_file = 0;
let mut pawn_shield = 0;
let mut opened_files = 0;
for file in ALL_FILES {
let pawns_on_file = patterns::get_file(file) & board.pieces[color][PAWN];
if pawns_on_file != 0 {
let pawns_on_file_count = pawns_on_file.bit_count() as u8;
if pawns_on_file_count > 1 {
doubled_pawns += pawns_on_file_count - 1;
}
if (patterns::get_rail(file) & board.pieces[color][PAWN]) == 0 {
isolated_pawns += 1;
}
}
}
let mut pawns_bb = board.pieces[color][PAWN];
while pawns_bb != 0 {
let square_bb = pawns_bb.get_lsb();
let square = square_bb.bit_scan();
pawns_bb = pawns_bb.pop_lsb();
chained_pawns += ((patterns::get_front(color ^ 1, square) & patterns::get_diagonals(square) & board.pieces[color][PAWN]) != 0) as u8;
passed_pawns += ((patterns::get_front(color, square) & board.pieces[color ^ 1][PAWN]) == 0) as u8;
let offset = if color == WHITE { 8 } else { -8 };
let stop_square_bb = if color == WHITE { square_bb << 8 } else { square_bb >> 8 };
let front = patterns::get_front(color, square) & !patterns::get_file(square);
let front_backward = patterns::get_front(color ^ 1, (square as i8 + offset) as usize) & !patterns::get_file((square as i8 + offset) as usize);
let not_isolated = (front & board.pieces[color][PAWN]) != 0;
let no_pawns_behind = (front_backward & board.pieces[color][PAWN]) == 0;
let stop_square_attacked = (stop_square_bb & board.pawn_attacks[color ^ 1]) != 0;
let open_file = (patterns::get_file(square) & board.pieces[color ^ 1][PAWN]) == 0;
if not_isolated && no_pawns_behind && stop_square_attacked {
if open_file {
backward_pawns_open_file += 1;
} else {
backward_pawns_closed_file += 1;
}
}
}
let king_bb = board.pieces[color][KING];
let king_square = king_bb.bit_scan();
let king_square_file = (king_square & 7) as i8;
pawn_shield = (patterns::get_box(king_square) & board.pieces[color][PAWN]).bit_count() as u8;
for file in cmp::max(0, king_square_file - 1)..=(cmp::min(7, king_square_file + 1)) {
if (patterns::get_file(file as usize) & board.pieces[color][PAWN]) == 0 {
opened_files += 1;
}
}
PawnsData { doubled_pawns, isolated_pawns, chained_pawns, passed_pawns, backward_pawns_open_file, backward_pawns_closed_file, pawn_shield, opened_files }
}
#[cfg(feature = "dev")]
pub fn get_coeffs(board: &Board, index: &mut u16, coeffs: &mut Vec<TunerCoeff>, indices: &mut Vec<u16>) {
let white_pawns_data = get_pawns_data(board, WHITE);
let black_pawns_data = get_pawns_data(board, BLACK);
get_array_coeffs(white_pawns_data.doubled_pawns, black_pawns_data.doubled_pawns, 8, index, coeffs, indices);
get_array_coeffs(white_pawns_data.isolated_pawns, black_pawns_data.isolated_pawns, 8, index, coeffs, indices);
get_array_coeffs(white_pawns_data.chained_pawns, black_pawns_data.chained_pawns, 8, index, coeffs, indices);
get_array_coeffs(white_pawns_data.passed_pawns, black_pawns_data.passed_pawns, 8, index, coeffs, indices);
get_array_coeffs(white_pawns_data.backward_pawns_open_file, black_pawns_data.backward_pawns_open_file, 8, index, coeffs, indices);
get_array_coeffs(white_pawns_data.backward_pawns_closed_file, black_pawns_data.backward_pawns_closed_file, 8, index, coeffs, indices);
get_array_coeffs(white_pawns_data.pawn_shield, black_pawns_data.pawn_shield, 8, index, coeffs, indices);
get_array_coeffs(white_pawns_data.opened_files, black_pawns_data.opened_files, 8, index, coeffs, indices);
}