1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use crate::state::*;
use std::ops;

pub mod material;
pub mod mobility;
pub mod parameters;
pub mod pawns;
pub mod pst;
pub mod safety;

#[derive(Clone)]
pub struct EvaluationParameters {
    pub piece_value: [i16; 6],
    pub piece_phase_value: [u8; 6],
    pub initial_game_phase: u8,

    pub mobility_opening: [i16; 6],
    pub mobility_ending: [i16; 6],
    pub mobility_center_multiplier: [i16; 6],

    pub doubled_pawn_opening: i16,
    pub doubled_pawn_ending: i16,

    pub isolated_pawn_opening: i16,
    pub isolated_pawn_ending: i16,

    pub chained_pawn_opening: i16,
    pub chained_pawn_ending: i16,

    pub passed_pawn_opening: i16,
    pub passed_pawn_ending: i16,

    pub pawn_shield_opening: i16,
    pub pawn_shield_ending: i16,

    pub pawn_shield_open_file_opening: i16,
    pub pawn_shield_open_file_ending: i16,

    pub king_attacked_squares_opening: i16,
    pub king_attacked_squares_ending: i16,

    pub pst: [[[[i16; 64]; 2]; 6]; 2],
    pub pst_patterns: [[[i16; 64]; 2]; 6],
}

pub struct EvaluationResult {
    pub opening_score: i16,
    pub ending_score: i16,
}

impl EvaluationParameters {
    /// Initializes PST patterns with used by default during search.
    fn set_default_pst_patterns(&mut self) {
        self.pst_patterns[PAWN] = self.get_pawn_pst_pattern();
        self.pst_patterns[KNIGHT] = self.get_knight_pst_pattern();
        self.pst_patterns[BISHOP] = self.get_bishop_pst_pattern();
        self.pst_patterns[ROOK] = self.get_rook_pst_pattern();
        self.pst_patterns[QUEEN] = self.get_queen_pst_pattern();
        self.pst_patterns[KING] = self.get_king_pst_pattern();
    }

    /// Recalculates initial material and PST tables.
    pub fn recalculate(&mut self) {
        for color in ALL_COLORS {
            for piece in ALL_PIECES {
                for phase in ALL_PHASES {
                    self.pst[color][piece][phase] = self.calculate_pst(color, &self.pst_patterns[piece][phase]);
                }
            }
        }
    }

    /// Calculates PST table for the specified `color` and `pattern`.
    fn calculate_pst(&self, color: usize, pattern: &[i16; 64]) -> [i16; 64] {
        let mut array = [0; 64];

        match color {
            WHITE => {
                for square in ALL_SQUARES {
                    array[square] = pattern[63 - square];
                }
            }
            BLACK => {
                for file in ALL_FILES {
                    for rank in ALL_RANKS {
                        array[file + rank * 8] = pattern[(7 - file) + rank * 8];
                    }
                }
            }
            _ => panic!("Invalid parameter: color={}", color),
        }

        array
    }

    /// Gets a PST value for the specified `color`, `piece`, `phase` and `square`.
    pub fn get_pst_value(&self, color: usize, piece: usize, phase: usize, square: usize) -> i16 {
        self.pst[color][piece][phase][square]
    }
}

impl EvaluationResult {
    pub fn new(opening_score: i16, ending_score: i16) -> EvaluationResult {
        EvaluationResult { opening_score, ending_score }
    }

    /// Blends `opening_score` and `ending_score` with the ratio passed in `game_phase`. The ratio is a number from 0 to `max_game_phase`, where:
    ///  - `max_game_phase` represents a board with the initial state set (opening phase)
    ///  - 0 represents a board without any piece (ending phase)
    ///  - every value between them represents a board state somewhere in the middle game
    pub fn taper_score(&self, game_phase: u8, max_game_phase: u8) -> i16 {
        let opening_score = (self.opening_score as i32) * (game_phase as i32);
        let ending_score = (self.ending_score as i32) * ((max_game_phase as i32) - (game_phase as i32));

        ((opening_score + ending_score) / (max_game_phase as i32)) as i16
    }
}

impl ops::Add<i16> for EvaluationResult {
    type Output = EvaluationResult;

    fn add(self, rhs: i16) -> EvaluationResult {
        EvaluationResult::new(self.opening_score + rhs, self.ending_score + rhs)
    }
}

impl ops::Add<EvaluationResult> for i16 {
    type Output = EvaluationResult;

    fn add(self, rhs: EvaluationResult) -> EvaluationResult {
        EvaluationResult::new(self + rhs.opening_score, self + rhs.ending_score)
    }
}

impl ops::Add<EvaluationResult> for EvaluationResult {
    type Output = EvaluationResult;

    fn add(self, rhs: EvaluationResult) -> EvaluationResult {
        EvaluationResult::new(self.opening_score + rhs.opening_score, self.ending_score + rhs.ending_score)
    }
}

impl ops::Sub<EvaluationResult> for EvaluationResult {
    type Output = EvaluationResult;

    fn sub(self, rhs: EvaluationResult) -> EvaluationResult {
        EvaluationResult::new(self.opening_score - rhs.opening_score, self.ending_score - rhs.ending_score)
    }
}