Skip to main content

sisyphus32/
eval_move.rs

1use crate::{BitMove, HistoryHeuristic, PIECE_TYPE_COUNT, KillerMoves, Position, Score, TTNodeType, TranspositionTable};
2
3#[allow(unused_imports)]
4use crate::{Color, MoveMasks, Piece, MoveFlag};
5
6// Most valuable victim - least valuable attacker [attacker][victim]
7const MVV_LVA: [[i16; PIECE_TYPE_COUNT]; PIECE_TYPE_COUNT] = [
8    [105, 205, 305, 405, 505, 605, 105, 205, 305, 405, 505, 605],
9    [104, 204, 304, 404, 504, 604, 104, 204, 304, 404, 504, 604],
10    [103, 203, 303, 403, 503, 603, 103, 203, 303, 403, 503, 603],
11    [102, 202, 302, 402, 502, 602, 102, 202, 302, 402, 502, 602],
12    [101, 201, 301, 401, 501, 601, 101, 201, 301, 401, 501, 601],
13    [100, 200, 300, 400, 500, 600, 100, 200, 300, 400, 500, 600],
14    [105, 205, 305, 405, 505, 605, 105, 205, 305, 405, 505, 605],
15    [104, 204, 304, 404, 504, 604, 104, 204, 304, 404, 504, 604],
16    [103, 203, 303, 403, 503, 603, 103, 203, 303, 403, 503, 603],
17    [102, 202, 302, 402, 502, 602, 102, 202, 302, 402, 502, 602],
18    [101, 201, 301, 401, 501, 601, 101, 201, 301, 401, 501, 601],
19    [100, 200, 300, 400, 500, 600, 100, 200, 300, 400, 500, 600],
20];
21
22pub struct EvalMove;
23
24impl EvalMove {
25    #[inline(always)]
26    pub fn eval(position: &Position, bit_move: BitMove) -> Score {
27        let mut score = Score::ZERO;
28        let source = bit_move.source();
29        let target = bit_move.target();
30        let piece = position.get_piece(source);
31        let capture_option = position.get_piece_option(target);
32
33        #[cfg(feature = "move_flag_eval")]
34        {
35            score += match bit_move.flag_option() {
36                None | Some(MoveFlag::WDoublePawn) | Some(MoveFlag::BDoublePawn) => 0,
37                Some(MoveFlag::WKCastle) | Some(MoveFlag::BKCastle) => 200, 
38                Some(MoveFlag::WQCastle) | Some(MoveFlag::BQCastle) => 50,
39                Some(MoveFlag::WEnPassant) | Some(MoveFlag::BEnPassant) => 150,
40                Some(MoveFlag::PromoQ) => 500,
41                Some(MoveFlag::PromoR) | Some(MoveFlag::PromoB) | Some(MoveFlag::PromoN) => -100,
42            };
43        }
44
45        // NOTE: Although the following idea seems logical, it yields 10-20% worse performance!
46        // score += EvalPosition::get_base_piece_position_score(piece, target, position.side) - EvalPosition::get_base_piece_position_score(piece, source, position.side); 
47
48        if let Some(capture) = capture_option {
49            score += MVV_LVA[piece][capture];
50
51            #[cfg(feature = "capture_with_check_eval")]
52            {
53                let enemy_king_bb = match position.side {
54                    Color::White => position.bitboards[Piece::BK],
55                    Color::Black => position.bitboards[Piece::WK],
56                };
57                if (MoveMasks::get_piece_mask(piece, target, position.all_occupancy) & enemy_king_bb).is_not_empty() {
58                    score += 300
59                }
60            }
61        };
62
63        #[cfg(feature = "eval_tt")]
64        {
65            if let Some(tt_data) = TranspositionTable::probe(position.zobrist_key) {
66                if tt_data.best_move.bit_move == bit_move {
67                    match tt_data.node_type {
68                        TTNodeType::Exact => score += 10000,
69                        TTNodeType::LowerBound => score += 4000,
70                        TTNodeType::UpperBound => score += 3000,
71                    }
72                }
73            }
74        }
75
76        #[cfg(feature = "killer_heuristic")]
77        {
78            if KillerMoves::get_primary(position.ply) == Some(bit_move) {
79                score += 2000;
80            } else if KillerMoves::get_secondary(position.ply) == Some(bit_move) {
81                score += 1000;
82            }
83        }
84
85        #[cfg(feature = "history_heuristic")]
86        {
87            score += HistoryHeuristic::get(position.side, source, target);
88        }
89
90        score
91    }
92}