1 | /* |
2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 |
3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) |
4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad |
5 | Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad |
6 | |
7 | Stockfish is free software: you can redistribute it and/or modify |
8 | it under the terms of the GNU General Public License as published by |
9 | the Free Software Foundation, either version 3 of the License, or |
10 | (at your option) any later version. |
11 | |
12 | Stockfish is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | GNU General Public License for more details. |
16 | |
17 | You should have received a copy of the GNU General Public License |
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | #include <algorithm> |
22 | #include <cassert> |
23 | |
24 | #include "bitboard.h" |
25 | #include "pawns.h" |
26 | #include "position.h" |
27 | #include "thread.h" |
28 | |
29 | namespace { |
30 | |
31 | #define V Value |
32 | #define S(mg, eg) make_score(mg, eg) |
33 | |
34 | // Pawn penalties |
35 | constexpr Score Backward = S( 9, 24); |
36 | constexpr Score Doubled = S(11, 56); |
37 | constexpr Score Isolated = S( 5, 15); |
38 | constexpr Score WeakLever = S( 0, 56); |
39 | constexpr Score WeakUnopposed = S(13, 27); |
40 | |
41 | // Connected pawn bonus |
42 | constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 }; |
43 | |
44 | // Strength of pawn shelter for our king by [distance from edge][rank]. |
45 | // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. |
46 | constexpr Value ShelterStrength[int(FILE_NB) / 2][RANK_NB] = { |
47 | { V( -6), V( 81), V( 93), V( 58), V( 39), V( 18), V( 25) }, |
48 | { V(-43), V( 61), V( 35), V(-49), V(-29), V(-11), V( -63) }, |
49 | { V(-10), V( 75), V( 23), V( -2), V( 32), V( 3), V( -45) }, |
50 | { V(-39), V(-13), V(-29), V(-52), V(-48), V(-67), V(-166) } |
51 | }; |
52 | |
53 | // Danger of enemy pawns moving toward our king by [distance from edge][rank]. |
54 | // RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn |
55 | // is behind our king. Note that UnblockedStorm[0][1-2] accommodate opponent pawn |
56 | // on edge, likely blocked by our king. |
57 | constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = { |
58 | { V( 89), V(-285), V(-185), V(93), V(57), V( 45), V( 51) }, |
59 | { V( 44), V( -18), V( 123), V(46), V(39), V( -7), V( 23) }, |
60 | { V( 4), V( 52), V( 162), V(37), V( 7), V(-14), V( -2) }, |
61 | { V(-10), V( -14), V( 90), V(15), V( 2), V( -7), V(-16) } |
62 | }; |
63 | |
64 | #undef S |
65 | #undef V |
66 | |
67 | template<Color Us> |
68 | Score evaluate(const Position& pos, Pawns::Entry* e) { |
69 | |
70 | constexpr Color Them = (Us == WHITE ? BLACK : WHITE); |
71 | constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); |
72 | |
73 | Bitboard neighbours, stoppers, doubled, support, phalanx; |
74 | Bitboard lever, leverPush; |
75 | Square s; |
76 | bool opposed, backward, passed; |
77 | Score score = SCORE_ZERO; |
78 | const Square* pl = pos.squares<PAWN>(Us); |
79 | |
80 | Bitboard ourPawns = pos.pieces( Us, PAWN); |
81 | Bitboard theirPawns = pos.pieces(Them, PAWN); |
82 | |
83 | Bitboard doubleAttackThem = pawn_double_attacks_bb<Them>(theirPawns); |
84 | |
85 | e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0; |
86 | e->kingSquares[Us] = SQ_NONE; |
87 | e->pawnAttacks[Us] = pawn_attacks_bb<Us>(ourPawns); |
88 | |
89 | // Loop through all pawns of the current color and score each pawn |
90 | while ((s = *pl++) != SQ_NONE) |
91 | { |
92 | assert(pos.piece_on(s) == make_piece(Us, PAWN)); |
93 | |
94 | Rank r = relative_rank(Us, s); |
95 | |
96 | e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s); |
97 | |
98 | // Flag the pawn |
99 | opposed = theirPawns & forward_file_bb(Us, s); |
100 | stoppers = theirPawns & passed_pawn_span(Us, s); |
101 | lever = theirPawns & PawnAttacks[Us][s]; |
102 | leverPush = theirPawns & PawnAttacks[Us][s + Up]; |
103 | doubled = ourPawns & (s - Up); |
104 | neighbours = ourPawns & adjacent_files_bb(s); |
105 | phalanx = neighbours & rank_bb(s); |
106 | support = neighbours & rank_bb(s - Up); |
107 | |
108 | // A pawn is backward when it is behind all pawns of the same color on |
109 | // the adjacent files and cannot safely advance. Phalanx and isolated |
110 | // pawns will be excluded when the pawn is scored. |
111 | backward = !(neighbours & forward_ranks_bb(Them, s)) |
112 | && (stoppers & (leverPush | (s + Up))); |
113 | |
114 | // A pawn is passed if one of the three following conditions is true: |
115 | // (a) there is no stoppers except some levers |
116 | // (b) the only stoppers are the leverPush, but we outnumber them |
117 | // (c) there is only one front stopper which can be levered. |
118 | passed = !(stoppers ^ lever) |
119 | || ( !(stoppers ^ leverPush) |
120 | && popcount(phalanx) >= popcount(leverPush)) |
121 | || ( stoppers == square_bb(s + Up) && r >= RANK_5 |
122 | && (shift<Up>(support) & ~(theirPawns | doubleAttackThem))); |
123 | |
124 | // Passed pawns will be properly scored later in evaluation when we have |
125 | // full attack info. |
126 | if (passed) |
127 | e->passedPawns[Us] |= s; |
128 | |
129 | // Score this pawn |
130 | if (support | phalanx) |
131 | { |
132 | int v = Connected[r] * (phalanx ? 3 : 2) / (opposed ? 2 : 1) |
133 | + 17 * popcount(support); |
134 | |
135 | score += make_score(v, v * (r - 2) / 4); |
136 | } |
137 | |
138 | else if (!neighbours) |
139 | score -= Isolated + WeakUnopposed * int(!opposed); |
140 | |
141 | else if (backward) |
142 | score -= Backward + WeakUnopposed * int(!opposed); |
143 | |
144 | if (doubled && !support) |
145 | score -= Doubled; |
146 | } |
147 | |
148 | // Penalize our unsupported pawns attacked twice by enemy pawns |
149 | score -= WeakLever * popcount( ourPawns |
150 | & doubleAttackThem |
151 | & ~e->pawnAttacks[Us]); |
152 | |
153 | return score; |
154 | } |
155 | |
156 | } // namespace |
157 | |
158 | namespace Pawns { |
159 | |
160 | /// Pawns::probe() looks up the current position's pawns configuration in |
161 | /// the pawns hash table. It returns a pointer to the Entry if the position |
162 | /// is found. Otherwise a new Entry is computed and stored there, so we don't |
163 | /// have to recompute all when the same pawns configuration occurs again. |
164 | |
165 | Entry* probe(const Position& pos) { |
166 | |
167 | Key key = pos.pawn_key(); |
168 | Entry* e = pos.this_thread()->pawnsTable[key]; |
169 | |
170 | if (e->key == key) |
171 | return e; |
172 | |
173 | e->key = key; |
174 | e->scores[WHITE] = evaluate<WHITE>(pos, e); |
175 | e->scores[BLACK] = evaluate<BLACK>(pos, e); |
176 | |
177 | return e; |
178 | } |
179 | |
180 | |
181 | /// Entry::evaluate_shelter() calculates the shelter bonus and the storm |
182 | /// penalty for a king, looking at the king file and the two closest files. |
183 | |
184 | template<Color Us> |
185 | void Entry::evaluate_shelter(const Position& pos, Square ksq, Score& shelter) { |
186 | |
187 | constexpr Color Them = (Us == WHITE ? BLACK : WHITE); |
188 | |
189 | Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq); |
190 | Bitboard ourPawns = b & pos.pieces(Us); |
191 | Bitboard theirPawns = b & pos.pieces(Them); |
192 | |
193 | Score bonus = make_score(5, 5); |
194 | |
195 | File center = clamp(file_of(ksq), FILE_B, FILE_G); |
196 | for (File f = File(center - 1); f <= File(center + 1); ++f) |
197 | { |
198 | b = ourPawns & file_bb(f); |
199 | int ourRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; |
200 | |
201 | b = theirPawns & file_bb(f); |
202 | int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; |
203 | |
204 | int d = std::min(f, ~f); |
205 | bonus += make_score(ShelterStrength[d][ourRank], 0); |
206 | |
207 | if (ourRank && (ourRank == theirRank - 1)) |
208 | bonus -= make_score(82 * (theirRank == RANK_3), 82 * (theirRank == RANK_3)); |
209 | else |
210 | bonus -= make_score(UnblockedStorm[d][theirRank], 0); |
211 | } |
212 | |
213 | if (mg_value(bonus) > mg_value(shelter)) |
214 | shelter = bonus; |
215 | } |
216 | |
217 | |
218 | /// Entry::do_king_safety() calculates a bonus for king safety. It is called only |
219 | /// when king square changes, which is about 20% of total king_safety() calls. |
220 | |
221 | template<Color Us> |
222 | Score Entry::do_king_safety(const Position& pos) { |
223 | |
224 | Square ksq = pos.square<KING>(Us); |
225 | kingSquares[Us] = ksq; |
226 | castlingRights[Us] = pos.castling_rights(Us); |
227 | |
228 | Bitboard pawns = pos.pieces(Us, PAWN); |
229 | int minPawnDist = pawns ? 8 : 0; |
230 | |
231 | if (pawns & PseudoAttacks[KING][ksq]) |
232 | minPawnDist = 1; |
233 | |
234 | else while (pawns) |
235 | minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns))); |
236 | |
237 | Score shelter = make_score(-VALUE_INFINITE, 0); |
238 | evaluate_shelter<Us>(pos, ksq, shelter); |
239 | |
240 | // If we can castle use the bonus after the castling if it is bigger |
241 | if (pos.can_castle(Us | KING_SIDE)) |
242 | evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1), shelter); |
243 | |
244 | if (pos.can_castle(Us | QUEEN_SIDE)) |
245 | evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1), shelter); |
246 | |
247 | return shelter - make_score(0, 16 * minPawnDist); |
248 | } |
249 | |
250 | // Explicit template instantiation |
251 | template Score Entry::do_king_safety<WHITE>(const Position& pos); |
252 | template Score Entry::do_king_safety<BLACK>(const Position& pos); |
253 | |
254 | } // namespace Pawns |
255 | |