Path: blob/master/src/nnue/features/full_threats.cpp
512 views
/*1Stockfish, a UCI chess playing engine derived from Glaurung 2.12Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)34Stockfish is free software: you can redistribute it and/or modify5it under the terms of the GNU General Public License as published by6the Free Software Foundation, either version 3 of the License, or7(at your option) any later version.89Stockfish is distributed in the hope that it will be useful,10but WITHOUT ANY WARRANTY; without even the implied warranty of11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12GNU General Public License for more details.1314You should have received a copy of the GNU General Public License15along with this program. If not, see <http://www.gnu.org/licenses/>.16*/1718//Definition of input features FullThreats of NNUE evaluation function1920#include "full_threats.h"2122#include <array>23#include <cstdint>24#include <initializer_list>25#include <utility>2627#include "../../bitboard.h"28#include "../../misc.h"29#include "../../position.h"30#include "../../types.h"31#include "../nnue_common.h"3233namespace Stockfish::Eval::NNUE::Features {3435struct HelperOffsets {36int cumulativePieceOffset, cumulativeOffset;37};3839constexpr std::array<Piece, 12> AllPieces = {40W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,41B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,42};4344template<PieceType PT>45constexpr auto make_piece_indices_type() {46static_assert(PT != PieceType::PAWN);4748std::array<std::array<uint8_t, SQUARE_NB>, SQUARE_NB> out{};4950for (Square from = SQ_A1; from <= SQ_H8; ++from)51{52Bitboard attacks = PseudoAttacks[PT][from];5354for (Square to = SQ_A1; to <= SQ_H8; ++to)55{56out[from][to] = constexpr_popcount(((1ULL << to) - 1) & attacks);57}58}5960return out;61}6263template<Piece P>64constexpr auto make_piece_indices_piece() {65static_assert(type_of(P) == PieceType::PAWN);6667std::array<std::array<uint8_t, SQUARE_NB>, SQUARE_NB> out{};6869constexpr Color C = color_of(P);7071for (Square from = SQ_A1; from <= SQ_H8; ++from)72{73Bitboard attacks = PseudoAttacks[C][from];7475for (Square to = SQ_A1; to <= SQ_H8; ++to)76{77out[from][to] = constexpr_popcount(((1ULL << to) - 1) & attacks);78}79}8081return out;82}8384constexpr auto index_lut2_array() {85constexpr auto KNIGHT_ATTACKS = make_piece_indices_type<PieceType::KNIGHT>();86constexpr auto BISHOP_ATTACKS = make_piece_indices_type<PieceType::BISHOP>();87constexpr auto ROOK_ATTACKS = make_piece_indices_type<PieceType::ROOK>();88constexpr auto QUEEN_ATTACKS = make_piece_indices_type<PieceType::QUEEN>();89constexpr auto KING_ATTACKS = make_piece_indices_type<PieceType::KING>();9091std::array<std::array<std::array<uint8_t, SQUARE_NB>, SQUARE_NB>, PIECE_NB> indices{};9293indices[W_PAWN] = make_piece_indices_piece<W_PAWN>();94indices[B_PAWN] = make_piece_indices_piece<B_PAWN>();9596indices[W_KNIGHT] = KNIGHT_ATTACKS;97indices[B_KNIGHT] = KNIGHT_ATTACKS;9899indices[W_BISHOP] = BISHOP_ATTACKS;100indices[B_BISHOP] = BISHOP_ATTACKS;101102indices[W_ROOK] = ROOK_ATTACKS;103indices[B_ROOK] = ROOK_ATTACKS;104105indices[W_QUEEN] = QUEEN_ATTACKS;106indices[B_QUEEN] = QUEEN_ATTACKS;107108indices[W_KING] = KING_ATTACKS;109indices[B_KING] = KING_ATTACKS;110111return indices;112}113114constexpr auto init_threat_offsets() {115std::array<HelperOffsets, PIECE_NB> indices{};116std::array<std::array<IndexType, SQUARE_NB>, PIECE_NB> offsets{};117118int cumulativeOffset = 0;119for (Piece piece : AllPieces)120{121int pieceIdx = piece;122int cumulativePieceOffset = 0;123124for (Square from = SQ_A1; from <= SQ_H8; ++from)125{126offsets[pieceIdx][from] = cumulativePieceOffset;127128if (type_of(piece) != PAWN)129{130Bitboard attacks = PseudoAttacks[type_of(piece)][from];131cumulativePieceOffset += constexpr_popcount(attacks);132}133134else if (from >= SQ_A2 && from <= SQ_H7)135{136Bitboard attacks = (pieceIdx < 8) ? pawn_attacks_bb<WHITE>(square_bb(from))137: pawn_attacks_bb<BLACK>(square_bb(from));138cumulativePieceOffset += constexpr_popcount(attacks);139}140}141142indices[pieceIdx] = {cumulativePieceOffset, cumulativeOffset};143144cumulativeOffset += numValidTargets[pieceIdx] * cumulativePieceOffset;145}146147return std::pair{indices, offsets};148}149150constexpr auto helper_offsets = init_threat_offsets().first;151// Lookup array for indexing threats152constexpr auto offsets = init_threat_offsets().second;153154constexpr auto init_index_luts() {155std::array<std::array<std::array<uint32_t, 2>, PIECE_NB>, PIECE_NB> indices{};156157for (Piece attacker : AllPieces)158{159for (Piece attacked : AllPieces)160{161bool enemy = (attacker ^ attacked) == 8;162PieceType attackerType = type_of(attacker);163PieceType attackedType = type_of(attacked);164165int map = FullThreats::map[attackerType - 1][attackedType - 1];166bool semi_excluded = attackerType == attackedType && (enemy || attackerType != PAWN);167IndexType feature = helper_offsets[attacker].cumulativeOffset168+ (color_of(attacked) * (numValidTargets[attacker] / 2) + map)169* helper_offsets[attacker].cumulativePieceOffset;170171bool excluded = map < 0;172indices[attacker][attacked][0] = excluded ? FullThreats::Dimensions : feature;173indices[attacker][attacked][1] =174excluded || semi_excluded ? FullThreats::Dimensions : feature;175}176}177178return indices;179}180181// The final index is calculated from summing data found in these two LUTs, as well182// as offsets[attacker][from]183184// [attacker][attacked][from < to]185constexpr auto index_lut1 = init_index_luts();186// [attacker][from][to]187constexpr auto index_lut2 = index_lut2_array();188189// Index of a feature for a given king position and another piece on some square190inline sf_always_inline IndexType FullThreats::make_index(191Color perspective, Piece attacker, Square from, Square to, Piece attacked, Square ksq) {192const std::int8_t orientation = OrientTBL[ksq] ^ (56 * perspective);193unsigned from_oriented = uint8_t(from) ^ orientation;194unsigned to_oriented = uint8_t(to) ^ orientation;195196std::int8_t swap = 8 * perspective;197unsigned attacker_oriented = attacker ^ swap;198unsigned attacked_oriented = attacked ^ swap;199200return index_lut1[attacker_oriented][attacked_oriented][from_oriented < to_oriented]201+ offsets[attacker_oriented][from_oriented]202+ index_lut2[attacker_oriented][from_oriented][to_oriented];203}204205// Get a list of indices for active features in ascending order206207void FullThreats::append_active_indices(Color perspective, const Position& pos, IndexList& active) {208Square ksq = pos.square<KING>(perspective);209Bitboard occupied = pos.pieces();210211for (Color color : {WHITE, BLACK})212{213for (PieceType pt = PAWN; pt <= KING; ++pt)214{215Color c = Color(perspective ^ color);216Piece attacker = make_piece(c, pt);217Bitboard bb = pos.pieces(c, pt);218219if (pt == PAWN)220{221auto right = (c == WHITE) ? NORTH_EAST : SOUTH_WEST;222auto left = (c == WHITE) ? NORTH_WEST : SOUTH_EAST;223auto attacks_left =224((c == WHITE) ? shift<NORTH_EAST>(bb) : shift<SOUTH_WEST>(bb)) & occupied;225auto attacks_right =226((c == WHITE) ? shift<NORTH_WEST>(bb) : shift<SOUTH_EAST>(bb)) & occupied;227228while (attacks_left)229{230Square to = pop_lsb(attacks_left);231Square from = to - right;232Piece attacked = pos.piece_on(to);233IndexType index = make_index(perspective, attacker, from, to, attacked, ksq);234235if (index < Dimensions)236active.push_back(index);237}238239while (attacks_right)240{241Square to = pop_lsb(attacks_right);242Square from = to - left;243Piece attacked = pos.piece_on(to);244IndexType index = make_index(perspective, attacker, from, to, attacked, ksq);245246if (index < Dimensions)247active.push_back(index);248}249}250else251{252while (bb)253{254Square from = pop_lsb(bb);255Bitboard attacks = (attacks_bb(pt, from, occupied)) & occupied;256257while (attacks)258{259Square to = pop_lsb(attacks);260Piece attacked = pos.piece_on(to);261IndexType index =262make_index(perspective, attacker, from, to, attacked, ksq);263264if (index < Dimensions)265active.push_back(index);266}267}268}269}270}271}272273// Get a list of indices for recently changed features274275void FullThreats::append_changed_indices(Color perspective,276Square ksq,277const DiffType& diff,278IndexList& removed,279IndexList& added,280FusedUpdateData* fusedData,281bool first) {282283for (const auto& dirty : diff.list)284{285auto attacker = dirty.pc();286auto attacked = dirty.threatened_pc();287auto from = dirty.pc_sq();288auto to = dirty.threatened_sq();289auto add = dirty.add();290291if (fusedData)292{293if (from == fusedData->dp2removed)294{295if (add)296{297if (first)298{299fusedData->dp2removedOriginBoard |= to;300continue;301}302}303else if (fusedData->dp2removedOriginBoard & to)304continue;305}306307if (to != SQ_NONE && to == fusedData->dp2removed)308{309if (add)310{311if (first)312{313fusedData->dp2removedTargetBoard |= from;314continue;315}316}317else if (fusedData->dp2removedTargetBoard & from)318continue;319}320}321322auto& insert = add ? added : removed;323const IndexType index = make_index(perspective, attacker, from, to, attacked, ksq);324325if (index < Dimensions)326insert.push_back(index);327}328}329330bool FullThreats::requires_refresh(const DiffType& diff, Color perspective) {331return perspective == diff.us && (int8_t(diff.ksq) & 0b100) != (int8_t(diff.prevKsq) & 0b100);332}333334} // namespace Stockfish::Eval::NNUE::Features335336337