1use crate::{BitMove, HistoryHeuristic, PIECE_TYPE_COUNT, KillerMoves, Position, Score, TTNodeType, TranspositionTable};
2
3#[allow(unused_imports)]
4use crate::{Color, MoveMasks, Piece, MoveFlag};
5
6const 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 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}