Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
official-stockfish
GitHub Repository: official-stockfish/stockfish
Path: blob/master/src/nnue/features/full_threats.cpp
512 views
1
/*
2
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
4
5
Stockfish is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
9
10
Stockfish is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
14
15
You should have received a copy of the GNU General Public License
16
along with this program. If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
//Definition of input features FullThreats of NNUE evaluation function
20
21
#include "full_threats.h"
22
23
#include <array>
24
#include <cstdint>
25
#include <initializer_list>
26
#include <utility>
27
28
#include "../../bitboard.h"
29
#include "../../misc.h"
30
#include "../../position.h"
31
#include "../../types.h"
32
#include "../nnue_common.h"
33
34
namespace Stockfish::Eval::NNUE::Features {
35
36
struct HelperOffsets {
37
int cumulativePieceOffset, cumulativeOffset;
38
};
39
40
constexpr std::array<Piece, 12> AllPieces = {
41
W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
42
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
43
};
44
45
template<PieceType PT>
46
constexpr auto make_piece_indices_type() {
47
static_assert(PT != PieceType::PAWN);
48
49
std::array<std::array<uint8_t, SQUARE_NB>, SQUARE_NB> out{};
50
51
for (Square from = SQ_A1; from <= SQ_H8; ++from)
52
{
53
Bitboard attacks = PseudoAttacks[PT][from];
54
55
for (Square to = SQ_A1; to <= SQ_H8; ++to)
56
{
57
out[from][to] = constexpr_popcount(((1ULL << to) - 1) & attacks);
58
}
59
}
60
61
return out;
62
}
63
64
template<Piece P>
65
constexpr auto make_piece_indices_piece() {
66
static_assert(type_of(P) == PieceType::PAWN);
67
68
std::array<std::array<uint8_t, SQUARE_NB>, SQUARE_NB> out{};
69
70
constexpr Color C = color_of(P);
71
72
for (Square from = SQ_A1; from <= SQ_H8; ++from)
73
{
74
Bitboard attacks = PseudoAttacks[C][from];
75
76
for (Square to = SQ_A1; to <= SQ_H8; ++to)
77
{
78
out[from][to] = constexpr_popcount(((1ULL << to) - 1) & attacks);
79
}
80
}
81
82
return out;
83
}
84
85
constexpr auto index_lut2_array() {
86
constexpr auto KNIGHT_ATTACKS = make_piece_indices_type<PieceType::KNIGHT>();
87
constexpr auto BISHOP_ATTACKS = make_piece_indices_type<PieceType::BISHOP>();
88
constexpr auto ROOK_ATTACKS = make_piece_indices_type<PieceType::ROOK>();
89
constexpr auto QUEEN_ATTACKS = make_piece_indices_type<PieceType::QUEEN>();
90
constexpr auto KING_ATTACKS = make_piece_indices_type<PieceType::KING>();
91
92
std::array<std::array<std::array<uint8_t, SQUARE_NB>, SQUARE_NB>, PIECE_NB> indices{};
93
94
indices[W_PAWN] = make_piece_indices_piece<W_PAWN>();
95
indices[B_PAWN] = make_piece_indices_piece<B_PAWN>();
96
97
indices[W_KNIGHT] = KNIGHT_ATTACKS;
98
indices[B_KNIGHT] = KNIGHT_ATTACKS;
99
100
indices[W_BISHOP] = BISHOP_ATTACKS;
101
indices[B_BISHOP] = BISHOP_ATTACKS;
102
103
indices[W_ROOK] = ROOK_ATTACKS;
104
indices[B_ROOK] = ROOK_ATTACKS;
105
106
indices[W_QUEEN] = QUEEN_ATTACKS;
107
indices[B_QUEEN] = QUEEN_ATTACKS;
108
109
indices[W_KING] = KING_ATTACKS;
110
indices[B_KING] = KING_ATTACKS;
111
112
return indices;
113
}
114
115
constexpr auto init_threat_offsets() {
116
std::array<HelperOffsets, PIECE_NB> indices{};
117
std::array<std::array<IndexType, SQUARE_NB>, PIECE_NB> offsets{};
118
119
int cumulativeOffset = 0;
120
for (Piece piece : AllPieces)
121
{
122
int pieceIdx = piece;
123
int cumulativePieceOffset = 0;
124
125
for (Square from = SQ_A1; from <= SQ_H8; ++from)
126
{
127
offsets[pieceIdx][from] = cumulativePieceOffset;
128
129
if (type_of(piece) != PAWN)
130
{
131
Bitboard attacks = PseudoAttacks[type_of(piece)][from];
132
cumulativePieceOffset += constexpr_popcount(attacks);
133
}
134
135
else if (from >= SQ_A2 && from <= SQ_H7)
136
{
137
Bitboard attacks = (pieceIdx < 8) ? pawn_attacks_bb<WHITE>(square_bb(from))
138
: pawn_attacks_bb<BLACK>(square_bb(from));
139
cumulativePieceOffset += constexpr_popcount(attacks);
140
}
141
}
142
143
indices[pieceIdx] = {cumulativePieceOffset, cumulativeOffset};
144
145
cumulativeOffset += numValidTargets[pieceIdx] * cumulativePieceOffset;
146
}
147
148
return std::pair{indices, offsets};
149
}
150
151
constexpr auto helper_offsets = init_threat_offsets().first;
152
// Lookup array for indexing threats
153
constexpr auto offsets = init_threat_offsets().second;
154
155
constexpr auto init_index_luts() {
156
std::array<std::array<std::array<uint32_t, 2>, PIECE_NB>, PIECE_NB> indices{};
157
158
for (Piece attacker : AllPieces)
159
{
160
for (Piece attacked : AllPieces)
161
{
162
bool enemy = (attacker ^ attacked) == 8;
163
PieceType attackerType = type_of(attacker);
164
PieceType attackedType = type_of(attacked);
165
166
int map = FullThreats::map[attackerType - 1][attackedType - 1];
167
bool semi_excluded = attackerType == attackedType && (enemy || attackerType != PAWN);
168
IndexType feature = helper_offsets[attacker].cumulativeOffset
169
+ (color_of(attacked) * (numValidTargets[attacker] / 2) + map)
170
* helper_offsets[attacker].cumulativePieceOffset;
171
172
bool excluded = map < 0;
173
indices[attacker][attacked][0] = excluded ? FullThreats::Dimensions : feature;
174
indices[attacker][attacked][1] =
175
excluded || semi_excluded ? FullThreats::Dimensions : feature;
176
}
177
}
178
179
return indices;
180
}
181
182
// The final index is calculated from summing data found in these two LUTs, as well
183
// as offsets[attacker][from]
184
185
// [attacker][attacked][from < to]
186
constexpr auto index_lut1 = init_index_luts();
187
// [attacker][from][to]
188
constexpr auto index_lut2 = index_lut2_array();
189
190
// Index of a feature for a given king position and another piece on some square
191
inline sf_always_inline IndexType FullThreats::make_index(
192
Color perspective, Piece attacker, Square from, Square to, Piece attacked, Square ksq) {
193
const std::int8_t orientation = OrientTBL[ksq] ^ (56 * perspective);
194
unsigned from_oriented = uint8_t(from) ^ orientation;
195
unsigned to_oriented = uint8_t(to) ^ orientation;
196
197
std::int8_t swap = 8 * perspective;
198
unsigned attacker_oriented = attacker ^ swap;
199
unsigned attacked_oriented = attacked ^ swap;
200
201
return index_lut1[attacker_oriented][attacked_oriented][from_oriented < to_oriented]
202
+ offsets[attacker_oriented][from_oriented]
203
+ index_lut2[attacker_oriented][from_oriented][to_oriented];
204
}
205
206
// Get a list of indices for active features in ascending order
207
208
void FullThreats::append_active_indices(Color perspective, const Position& pos, IndexList& active) {
209
Square ksq = pos.square<KING>(perspective);
210
Bitboard occupied = pos.pieces();
211
212
for (Color color : {WHITE, BLACK})
213
{
214
for (PieceType pt = PAWN; pt <= KING; ++pt)
215
{
216
Color c = Color(perspective ^ color);
217
Piece attacker = make_piece(c, pt);
218
Bitboard bb = pos.pieces(c, pt);
219
220
if (pt == PAWN)
221
{
222
auto right = (c == WHITE) ? NORTH_EAST : SOUTH_WEST;
223
auto left = (c == WHITE) ? NORTH_WEST : SOUTH_EAST;
224
auto attacks_left =
225
((c == WHITE) ? shift<NORTH_EAST>(bb) : shift<SOUTH_WEST>(bb)) & occupied;
226
auto attacks_right =
227
((c == WHITE) ? shift<NORTH_WEST>(bb) : shift<SOUTH_EAST>(bb)) & occupied;
228
229
while (attacks_left)
230
{
231
Square to = pop_lsb(attacks_left);
232
Square from = to - right;
233
Piece attacked = pos.piece_on(to);
234
IndexType index = make_index(perspective, attacker, from, to, attacked, ksq);
235
236
if (index < Dimensions)
237
active.push_back(index);
238
}
239
240
while (attacks_right)
241
{
242
Square to = pop_lsb(attacks_right);
243
Square from = to - left;
244
Piece attacked = pos.piece_on(to);
245
IndexType index = make_index(perspective, attacker, from, to, attacked, ksq);
246
247
if (index < Dimensions)
248
active.push_back(index);
249
}
250
}
251
else
252
{
253
while (bb)
254
{
255
Square from = pop_lsb(bb);
256
Bitboard attacks = (attacks_bb(pt, from, occupied)) & occupied;
257
258
while (attacks)
259
{
260
Square to = pop_lsb(attacks);
261
Piece attacked = pos.piece_on(to);
262
IndexType index =
263
make_index(perspective, attacker, from, to, attacked, ksq);
264
265
if (index < Dimensions)
266
active.push_back(index);
267
}
268
}
269
}
270
}
271
}
272
}
273
274
// Get a list of indices for recently changed features
275
276
void FullThreats::append_changed_indices(Color perspective,
277
Square ksq,
278
const DiffType& diff,
279
IndexList& removed,
280
IndexList& added,
281
FusedUpdateData* fusedData,
282
bool first) {
283
284
for (const auto& dirty : diff.list)
285
{
286
auto attacker = dirty.pc();
287
auto attacked = dirty.threatened_pc();
288
auto from = dirty.pc_sq();
289
auto to = dirty.threatened_sq();
290
auto add = dirty.add();
291
292
if (fusedData)
293
{
294
if (from == fusedData->dp2removed)
295
{
296
if (add)
297
{
298
if (first)
299
{
300
fusedData->dp2removedOriginBoard |= to;
301
continue;
302
}
303
}
304
else if (fusedData->dp2removedOriginBoard & to)
305
continue;
306
}
307
308
if (to != SQ_NONE && to == fusedData->dp2removed)
309
{
310
if (add)
311
{
312
if (first)
313
{
314
fusedData->dp2removedTargetBoard |= from;
315
continue;
316
}
317
}
318
else if (fusedData->dp2removedTargetBoard & from)
319
continue;
320
}
321
}
322
323
auto& insert = add ? added : removed;
324
const IndexType index = make_index(perspective, attacker, from, to, attacked, ksq);
325
326
if (index < Dimensions)
327
insert.push_back(index);
328
}
329
}
330
331
bool FullThreats::requires_refresh(const DiffType& diff, Color perspective) {
332
return perspective == diff.us && (int8_t(diff.ksq) & 0b100) != (int8_t(diff.prevKsq) & 0b100);
333
}
334
335
} // namespace Stockfish::Eval::NNUE::Features
336
337