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
use crate::state::*;
use crate::utils::panic_fast;
use pst::*;
use std::ops;

#[cfg(feature = "dev")]
use crate::tuning::tuner::TunerParameter;

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

pub const INITIAL_GAME_PHASE: u8 = 24;
pub const PIECE_VALUES: [i16; 6] = [100, 337, 338, 521, 1050, 10000];
pub const PIECE_PHASE_VALUES: [u8; 6] = [0, 1, 1, 2, 4, 0];

macro_rules! s {
    ($opening_score: expr, $ending_score: expr) => {
        PackedEval::new($opening_score, $ending_score)
    };
}
pub(crate) use s;

#[derive(Copy, Clone, Default)]
pub struct PackedEval {
    pub data: i32,
}

impl PackedEval {
    /// Constructs a new instance of [PackedEval] with `opening` and `ending` scores.
    pub const fn new(opening: i16, ending: i16) -> Self {
        Self { data: ((ending as i32) << 16) + opening as i32 }
    }

    /// Constructs a new instance of [PackedEval] with raw `data`.
    pub const fn new_raw(data: i32) -> Self {
        Self { data }
    }

    /// Gets opening score from the internal data.
    pub fn get_opening(&self) -> i16 {
        self.data as i16
    }

    /// Gets ending score from the internal data.
    pub fn get_ending(&self) -> i16 {
        ((self.data + 0x8000) >> 16) as i16
    }

    /// 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) -> i16 {
        let opening_score = (self.get_opening() as i32) * (game_phase as i32);
        let ending_score = (self.get_ending() as i32) * ((INITIAL_GAME_PHASE as i32) - (game_phase as i32));

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

    /// Gets tuner coefficients for opening and ending score, constrained by `min`, `min_init`, `max_init` and `max`. Additionally, `offset` is added to each score.
    #[cfg(feature = "dev")]
    pub fn to_tuner_params(&self, min: i16, min_init: i16, max_init: i16, max: i16, offset: i16) -> [TunerParameter; 2] {
        use crate::utils::assert_fast;

        assert_fast!(min <= max);
        assert_fast!(min_init <= max_init);
        assert_fast!(min_init >= min && min_init <= max);
        assert_fast!(max_init >= min && max_init <= max);

        [
            TunerParameter::new(self.get_opening() + offset, min, min_init, max_init, max),
            TunerParameter::new(self.get_ending() + offset, min, min_init, max_init, max),
        ]
    }
}

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

    /// Implements `+` operator for [PackedEval].
    fn add(self, rhs: PackedEval) -> PackedEval {
        PackedEval::new_raw(self.data + rhs.data)
    }
}

impl ops::AddAssign<PackedEval> for PackedEval {
    /// Implements `+=` operator for [PackedEval].
    fn add_assign(&mut self, rhs: PackedEval) {
        self.data += rhs.data;
    }
}

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

    /// Implements `-` operator for [PackedEval].
    fn sub(self, rhs: PackedEval) -> PackedEval {
        PackedEval::new_raw(self.data - rhs.data)
    }
}

impl ops::SubAssign<PackedEval> for PackedEval {
    /// Implements `-=` operator for [PackedEval].
    fn sub_assign(&mut self, rhs: PackedEval) {
        self.data -= rhs.data;
    }
}

impl ops::Mul<PackedEval> for i8 {
    type Output = PackedEval;

    /// Implements `*` operator for [PackedEval].
    fn mul(self, rhs: PackedEval) -> PackedEval {
        PackedEval::new_raw(self as i32 * rhs.data)
    }
}

impl ops::Mul<PackedEval> for i16 {
    type Output = PackedEval;

    /// Implements `*` operator for [PackedEval].
    fn mul(self, rhs: PackedEval) -> PackedEval {
        PackedEval::new_raw(self as i32 * rhs.data)
    }
}

impl ops::Mul<PackedEval> for i32 {
    type Output = PackedEval;

    /// Implements `*` operator for [PackedEval].
    fn mul(self, rhs: PackedEval) -> PackedEval {
        PackedEval::new_raw(self * rhs.data)
    }
}