mirror of
https://github.com/official-stockfish/Stockfish.git
synced 2026-07-22 12:47:08 +00:00
Speedup movegen with VBMI2
Passed STC LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 166720 W: 43191 L: 42701 D: 80828 Ptnml(0-2): 348, 18567, 45069, 18999, 377 https://tests.stockfishchess.org/tests/view/686ae98dfe0f2fe354c0c867 Refactor movegen to emit to a vector with 16-bit elements, which enables a speedup with AVX512-VBMI2 when writing moves to the move list. Very crude timing measurements of perft via timing ./stockfish "go perft 7" demonstrates approximately 17% perft speedup: Summary ./Stockfish-dev/src/stockfish 'go perft 7' ran 1.17 ± 0.04 times faster than ./Stockfish-base/src/stockfish 'go perft 7' Estimated overall nps increase of 0.4% via speedtest: 33605229 -> 33749825 (many thanks JonathanHallstrom). The corresponding arch is avx512icl as it is a good baseline for consumer avx-512 feature set support; Intel Ice Lake was the first consumer AVX-512 CPU and it is a decent subset of what AMD Zen 4 supports. closes https://github.com/official-stockfish/Stockfish/pull/6153 No functional change
This commit is contained in:
@@ -10,6 +10,7 @@ Motohiro Isozaki (yaneurao)
|
||||
Hisayori Noda (nodchip)
|
||||
|
||||
# All other authors of Stockfish code (in alphabetical order)
|
||||
87flowers
|
||||
Aditya (absimaldata)
|
||||
Adrian Petrescu (apetresc)
|
||||
Ahmed Kerimov (wcdbmv)
|
||||
|
||||
@@ -39,7 +39,9 @@ set_arch_loongarch64() {
|
||||
|
||||
# Set the file CPU x86_64 architecture
|
||||
set_arch_x86_64() {
|
||||
if check_flags 'avx512vnni' 'avx512dq' 'avx512f' 'avx512bw' 'avx512vl'; then
|
||||
if check_flags 'avx512f' 'avx512cd' 'avx512vl' 'avx512dq' 'avx512bw' 'avx512ifma' 'avx512vbmi' 'avx512vbmi2' 'avx512vpopcntdq' 'avx512bitalg' 'avx512vnni' 'vpclmulqdq' 'gfni' 'vaes'; then
|
||||
true_arch='x86-64-avx512icl'
|
||||
elif check_flags 'avx512vnni' 'avx512dq' 'avx512f' 'avx512bw' 'avx512vl'; then
|
||||
true_arch='x86-64-vnni256'
|
||||
elif check_flags 'avx512f' 'avx512bw'; then
|
||||
true_arch='x86-64-avx512'
|
||||
|
||||
+28
-3
@@ -99,6 +99,7 @@ VPATH = syzygy:nnue:nnue/features
|
||||
# avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512
|
||||
# vnni256 = yes/no --- -mavx256vnni --- Use Intel Vector Neural Network Instructions 512 with 256bit operands
|
||||
# vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512
|
||||
# avx512icl = yes/no --- ... multiple ... --- Use All AVX-512 features available on both Intel Ice Lake and AMD Zen 4
|
||||
# altivec = yes/no --- -maltivec --- Use PowerPC Altivec SIMD extension
|
||||
# vsx = yes/no --- -mvsx --- Use POWER VSX SIMD extension
|
||||
# neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture
|
||||
@@ -125,10 +126,10 @@ ifeq ($(ARCH), native)
|
||||
endif
|
||||
|
||||
# explicitly check for the list of supported architectures (as listed with make help),
|
||||
# the user can override with `make ARCH=x86-32-vnni256 SUPPORTED_ARCH=true`
|
||||
# the user can override with `make ARCH=x86-64-avx512icl SUPPORTED_ARCH=true`
|
||||
ifeq ($(ARCH), $(filter $(ARCH), \
|
||||
x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \
|
||||
x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \
|
||||
x86-64-avx512icl x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni \
|
||||
x86-64-bmi2 x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \
|
||||
x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-64-altivec ppc-64-vsx ppc-32 e2k \
|
||||
armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64 \
|
||||
loongarch64 loongarch64-lsx loongarch64-lasx))
|
||||
@@ -154,6 +155,7 @@ avxvnni = no
|
||||
avx512 = no
|
||||
vnni256 = no
|
||||
vnni512 = no
|
||||
avx512icl = no
|
||||
altivec = no
|
||||
vsx = no
|
||||
neon = no
|
||||
@@ -290,6 +292,19 @@ ifeq ($(findstring -vnni512,$(ARCH)),-vnni512)
|
||||
vnni512 = yes
|
||||
endif
|
||||
|
||||
ifeq ($(findstring -avx512icl,$(ARCH)),-avx512icl)
|
||||
popcnt = yes
|
||||
sse = yes
|
||||
sse2 = yes
|
||||
ssse3 = yes
|
||||
sse41 = yes
|
||||
avx2 = yes
|
||||
pext = yes
|
||||
avx512 = yes
|
||||
vnni512 = yes
|
||||
avx512icl = yes
|
||||
endif
|
||||
|
||||
ifeq ($(sse),yes)
|
||||
prefetch = yes
|
||||
endif
|
||||
@@ -719,6 +734,13 @@ ifeq ($(vnni512),yes)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(avx512icl),yes)
|
||||
CXXFLAGS += -DUSE_AVX512 -DUSE_VNNI -DUSE_AVX512ICL
|
||||
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
|
||||
CXXFLAGS += -mavx512f -mavx512cd -mavx512vl -mavx512dq -mavx512bw -mavx512ifma -mavx512vbmi -mavx512vbmi2 -mavx512vpopcntdq -mavx512bitalg -mavx512vnni -mvpclmulqdq -mgfni -mvaes
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(sse41),yes)
|
||||
CXXFLAGS += -DUSE_SSE41
|
||||
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
|
||||
@@ -877,6 +899,7 @@ help:
|
||||
echo "Supported archs:" && \
|
||||
echo "" && \
|
||||
echo "native > select the best architecture for the host processor (default)" && \
|
||||
echo "x86-64-avx512icl > x86 64-bit with minimum avx512 support of Intel Ice Lane or AMD Zen 4" && \
|
||||
echo "x86-64-vnni512 > x86 64-bit with vnni 512bit support" && \
|
||||
echo "x86-64-vnni256 > x86 64-bit with vnni 512bit support, limit operands to 256bit wide" && \
|
||||
echo "x86-64-avx512 > x86 64-bit with avx512 support" && \
|
||||
@@ -1025,6 +1048,7 @@ config-sanity: net
|
||||
echo "avx512: '$(avx512)'" && \
|
||||
echo "vnni256: '$(vnni256)'" && \
|
||||
echo "vnni512: '$(vnni512)'" && \
|
||||
echo "avx512icl: '$(avx512icl)'" && \
|
||||
echo "altivec: '$(altivec)'" && \
|
||||
echo "vsx: '$(vsx)'" && \
|
||||
echo "neon: '$(neon)'" && \
|
||||
@@ -1061,6 +1085,7 @@ config-sanity: net
|
||||
(test "$(avx512)" = "yes" || test "$(avx512)" = "no") && \
|
||||
(test "$(vnni256)" = "yes" || test "$(vnni256)" = "no") && \
|
||||
(test "$(vnni512)" = "yes" || test "$(vnni512)" = "no") && \
|
||||
(test "$(avx512icl)" = "yes" || test "$(avx512icl)" = "no") && \
|
||||
(test "$(altivec)" = "yes" || test "$(altivec)" = "no") && \
|
||||
(test "$(vsx)" = "yes" || test "$(vsx)" = "no") && \
|
||||
(test "$(neon)" = "yes" || test "$(neon)" = "no") && \
|
||||
|
||||
@@ -237,6 +237,9 @@ std::string compiler_info() {
|
||||
|
||||
compiler += "\nCompilation settings : ";
|
||||
compiler += (Is64Bit ? "64bit" : "32bit");
|
||||
#if defined(USE_AVX512ICL)
|
||||
compiler += " AVX512ICL";
|
||||
#endif
|
||||
#if defined(USE_VNNI)
|
||||
compiler += " VNNI";
|
||||
#endif
|
||||
|
||||
+93
-38
@@ -24,12 +24,88 @@
|
||||
#include "bitboard.h"
|
||||
#include "position.h"
|
||||
|
||||
#if defined(USE_AVX512ICL)
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <immintrin.h>
|
||||
#endif
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(USE_AVX512ICL)
|
||||
|
||||
inline Move* write_moves(Move* moveList, uint32_t mask, __m512i vector) {
|
||||
_mm512_storeu_si512(reinterpret_cast<__m512i*>(moveList),
|
||||
_mm512_maskz_compress_epi16(mask, vector));
|
||||
return moveList + popcount(mask);
|
||||
}
|
||||
|
||||
template<Direction offset>
|
||||
inline Move* splat_pawn_moves(Move* moveList, Bitboard to_bb) {
|
||||
alignas(64) static constexpr auto SPLAT_TABLE = [] {
|
||||
std::array<Move, 64> table{};
|
||||
for (int8_t i = 0; i < 64; i++)
|
||||
{
|
||||
Square from{std::clamp<int8_t>(i - offset, 0, 63)};
|
||||
table[i] = {Move(from, Square{i})};
|
||||
}
|
||||
return table;
|
||||
}();
|
||||
|
||||
auto table = reinterpret_cast<const __m512i*>(SPLAT_TABLE.data());
|
||||
|
||||
moveList =
|
||||
write_moves(moveList, static_cast<uint32_t>(to_bb >> 0), _mm512_load_si512(table + 0));
|
||||
moveList =
|
||||
write_moves(moveList, static_cast<uint32_t>(to_bb >> 32), _mm512_load_si512(table + 1));
|
||||
|
||||
return moveList;
|
||||
}
|
||||
|
||||
inline Move* splat_moves(Move* moveList, Square from, Bitboard to_bb) {
|
||||
alignas(64) static constexpr auto SPLAT_TABLE = [] {
|
||||
std::array<Move, 64> table{};
|
||||
for (int8_t i = 0; i < 64; i++)
|
||||
table[i] = {Move(SQUARE_ZERO, Square{i})};
|
||||
return table;
|
||||
}();
|
||||
|
||||
__m512i fromVec = _mm512_set1_epi16(Move(from, SQUARE_ZERO).raw());
|
||||
|
||||
auto table = reinterpret_cast<const __m512i*>(SPLAT_TABLE.data());
|
||||
|
||||
moveList = write_moves(moveList, static_cast<uint32_t>(to_bb >> 0),
|
||||
_mm512_or_si512(_mm512_load_si512(table + 0), fromVec));
|
||||
moveList = write_moves(moveList, static_cast<uint32_t>(to_bb >> 32),
|
||||
_mm512_or_si512(_mm512_load_si512(table + 1), fromVec));
|
||||
|
||||
return moveList;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template<Direction offset>
|
||||
inline Move* splat_pawn_moves(Move* moveList, Bitboard to_bb) {
|
||||
while (to_bb)
|
||||
{
|
||||
Square to = pop_lsb(to_bb);
|
||||
*moveList++ = Move(to - offset, to);
|
||||
}
|
||||
return moveList;
|
||||
}
|
||||
|
||||
inline Move* splat_moves(Move* moveList, Square from, Bitboard to_bb) {
|
||||
while (to_bb)
|
||||
*moveList++ = Move(from, pop_lsb(to_bb));
|
||||
return moveList;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
template<GenType Type, Direction D, bool Enemy>
|
||||
ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) {
|
||||
Move* make_promotions(Move* moveList, [[maybe_unused]] Square to) {
|
||||
|
||||
constexpr bool all = Type == EVASIONS || Type == NON_EVASIONS;
|
||||
|
||||
@@ -48,7 +124,7 @@ ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) {
|
||||
|
||||
|
||||
template<Color Us, GenType Type>
|
||||
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
|
||||
Move* generate_pawn_moves(const Position& pos, Move* moveList, Bitboard target) {
|
||||
|
||||
constexpr Color Them = ~Us;
|
||||
constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
|
||||
@@ -75,17 +151,8 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta
|
||||
b2 &= target;
|
||||
}
|
||||
|
||||
while (b1)
|
||||
{
|
||||
Square to = pop_lsb(b1);
|
||||
*moveList++ = Move(to - Up, to);
|
||||
}
|
||||
|
||||
while (b2)
|
||||
{
|
||||
Square to = pop_lsb(b2);
|
||||
*moveList++ = Move(to - Up - Up, to);
|
||||
}
|
||||
moveList = splat_pawn_moves<Up>(moveList, b1);
|
||||
moveList = splat_pawn_moves<Up + Up>(moveList, b2);
|
||||
}
|
||||
|
||||
// Promotions and underpromotions
|
||||
@@ -114,17 +181,8 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta
|
||||
Bitboard b1 = shift<UpRight>(pawnsNotOn7) & enemies;
|
||||
Bitboard b2 = shift<UpLeft>(pawnsNotOn7) & enemies;
|
||||
|
||||
while (b1)
|
||||
{
|
||||
Square to = pop_lsb(b1);
|
||||
*moveList++ = Move(to - UpRight, to);
|
||||
}
|
||||
|
||||
while (b2)
|
||||
{
|
||||
Square to = pop_lsb(b2);
|
||||
*moveList++ = Move(to - UpLeft, to);
|
||||
}
|
||||
moveList = splat_pawn_moves<UpRight>(moveList, b1);
|
||||
moveList = splat_pawn_moves<UpLeft>(moveList, b2);
|
||||
|
||||
if (pos.ep_square() != SQ_NONE)
|
||||
{
|
||||
@@ -148,7 +206,7 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta
|
||||
|
||||
|
||||
template<Color Us, PieceType Pt>
|
||||
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
|
||||
Move* generate_moves(const Position& pos, Move* moveList, Bitboard target) {
|
||||
|
||||
static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()");
|
||||
|
||||
@@ -159,8 +217,7 @@ ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target)
|
||||
Square from = pop_lsb(bb);
|
||||
Bitboard b = attacks_bb<Pt>(from, pos.pieces()) & target;
|
||||
|
||||
while (b)
|
||||
*moveList++ = Move(from, pop_lsb(b));
|
||||
moveList = splat_moves(moveList, from, b);
|
||||
}
|
||||
|
||||
return moveList;
|
||||
@@ -168,7 +225,7 @@ ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target)
|
||||
|
||||
|
||||
template<Color Us, GenType Type>
|
||||
ExtMove* generate_all(const Position& pos, ExtMove* moveList) {
|
||||
Move* generate_all(const Position& pos, Move* moveList) {
|
||||
|
||||
static_assert(Type != LEGAL, "Unsupported type in generate_all()");
|
||||
|
||||
@@ -192,8 +249,7 @@ ExtMove* generate_all(const Position& pos, ExtMove* moveList) {
|
||||
|
||||
Bitboard b = attacks_bb<KING>(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target);
|
||||
|
||||
while (b)
|
||||
*moveList++ = Move(ksq, pop_lsb(b));
|
||||
moveList = splat_moves(moveList, ksq, b);
|
||||
|
||||
if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING))
|
||||
for (CastlingRights cr : {Us & KING_SIDE, Us & QUEEN_SIDE})
|
||||
@@ -213,7 +269,7 @@ ExtMove* generate_all(const Position& pos, ExtMove* moveList) {
|
||||
//
|
||||
// Returns a pointer to the end of the move list.
|
||||
template<GenType Type>
|
||||
ExtMove* generate(const Position& pos, ExtMove* moveList) {
|
||||
Move* generate(const Position& pos, Move* moveList) {
|
||||
|
||||
static_assert(Type != LEGAL, "Unsupported type in generate()");
|
||||
assert((Type == EVASIONS) == bool(pos.checkers()));
|
||||
@@ -225,21 +281,20 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) {
|
||||
}
|
||||
|
||||
// Explicit template instantiations
|
||||
template ExtMove* generate<CAPTURES>(const Position&, ExtMove*);
|
||||
template ExtMove* generate<QUIETS>(const Position&, ExtMove*);
|
||||
template ExtMove* generate<EVASIONS>(const Position&, ExtMove*);
|
||||
template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
|
||||
|
||||
template Move* generate<CAPTURES>(const Position&, Move*);
|
||||
template Move* generate<QUIETS>(const Position&, Move*);
|
||||
template Move* generate<EVASIONS>(const Position&, Move*);
|
||||
template Move* generate<NON_EVASIONS>(const Position&, Move*);
|
||||
|
||||
// generate<LEGAL> generates all the legal moves in the given position
|
||||
|
||||
template<>
|
||||
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
|
||||
Move* generate<LEGAL>(const Position& pos, Move* moveList) {
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
Bitboard pinned = pos.blockers_for_king(us) & pos.pieces(us);
|
||||
Square ksq = pos.square<KING>(us);
|
||||
ExtMove* cur = moveList;
|
||||
Move* cur = moveList;
|
||||
|
||||
moveList =
|
||||
pos.checkers() ? generate<EVASIONS>(pos, moveList) : generate<NON_EVASIONS>(pos, moveList);
|
||||
|
||||
+4
-4
@@ -49,7 +49,7 @@ struct ExtMove: public Move {
|
||||
inline bool operator<(const ExtMove& f, const ExtMove& s) { return f.value < s.value; }
|
||||
|
||||
template<GenType>
|
||||
ExtMove* generate(const Position& pos, ExtMove* moveList);
|
||||
Move* generate(const Position& pos, Move* moveList);
|
||||
|
||||
// The MoveList struct wraps the generate() function and returns a convenient
|
||||
// list of moves. Using MoveList is sometimes preferable to directly calling
|
||||
@@ -59,13 +59,13 @@ struct MoveList {
|
||||
|
||||
explicit MoveList(const Position& pos) :
|
||||
last(generate<T>(pos, moveList)) {}
|
||||
const ExtMove* begin() const { return moveList; }
|
||||
const ExtMove* end() const { return last; }
|
||||
const Move* begin() const { return moveList; }
|
||||
const Move* end() const { return last; }
|
||||
size_t size() const { return last - moveList; }
|
||||
bool contains(Move move) const { return std::find(begin(), end(), move) != end(); }
|
||||
|
||||
private:
|
||||
ExtMove moveList[MAX_MOVES], *last;
|
||||
Move moveList[MAX_MOVES], *last;
|
||||
};
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+20
-10
@@ -123,7 +123,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, int th, const CapturePieceTo
|
||||
// Captures are ordered by Most Valuable Victim (MVV), preferring captures
|
||||
// with a good history. Quiets moves are ordered using the history tables.
|
||||
template<GenType Type>
|
||||
void MovePicker::score() {
|
||||
ExtMove* MovePicker::score(MoveList<Type>& ml) {
|
||||
|
||||
static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
|
||||
|
||||
@@ -138,8 +138,12 @@ void MovePicker::score() {
|
||||
threatByLesser[QUEEN] = pos.attacks_by<ROOK>(~us) | threatByLesser[ROOK];
|
||||
}
|
||||
|
||||
for (auto& m : *this)
|
||||
ExtMove* it = cur;
|
||||
for (auto move : ml)
|
||||
{
|
||||
ExtMove& m = *it++;
|
||||
m = move;
|
||||
|
||||
const Square from = m.from_sq();
|
||||
const Square to = m.to_sq();
|
||||
const Piece pc = pos.moved_piece(m);
|
||||
@@ -189,6 +193,7 @@ void MovePicker::score() {
|
||||
}
|
||||
}
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
// Returns the next move satisfying a predicate function.
|
||||
@@ -222,14 +227,16 @@ top:
|
||||
|
||||
case CAPTURE_INIT :
|
||||
case PROBCUT_INIT :
|
||||
case QCAPTURE_INIT :
|
||||
case QCAPTURE_INIT : {
|
||||
MoveList<CAPTURES> ml(pos);
|
||||
|
||||
cur = endBadCaptures = moves;
|
||||
endCur = endCaptures = generate<CAPTURES>(pos, cur);
|
||||
endCur = endCaptures = score<CAPTURES>(ml);
|
||||
|
||||
score<CAPTURES>();
|
||||
partial_insertion_sort(cur, endCur, std::numeric_limits<int>::min());
|
||||
++stage;
|
||||
goto top;
|
||||
}
|
||||
|
||||
case GOOD_CAPTURE :
|
||||
if (select([&]() {
|
||||
@@ -246,9 +253,10 @@ top:
|
||||
case QUIET_INIT :
|
||||
if (!skipQuiets)
|
||||
{
|
||||
endCur = endGenerated = generate<QUIETS>(pos, cur);
|
||||
MoveList<QUIETS> ml(pos);
|
||||
|
||||
endCur = endGenerated = score<QUIETS>(ml);
|
||||
|
||||
score<QUIETS>();
|
||||
partial_insertion_sort(cur, endCur, -3560 * depth);
|
||||
}
|
||||
|
||||
@@ -283,14 +291,16 @@ top:
|
||||
|
||||
return Move::none();
|
||||
|
||||
case EVASION_INIT :
|
||||
case EVASION_INIT : {
|
||||
MoveList<EVASIONS> ml(pos);
|
||||
|
||||
cur = moves;
|
||||
endCur = endGenerated = generate<EVASIONS>(pos, cur);
|
||||
endCur = endGenerated = score<EVASIONS>(ml);
|
||||
|
||||
score<EVASIONS>();
|
||||
partial_insertion_sort(cur, endCur, std::numeric_limits<int>::min());
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
}
|
||||
|
||||
case EVASION :
|
||||
case QCAPTURE :
|
||||
|
||||
+2
-2
@@ -55,8 +55,8 @@ class MovePicker {
|
||||
private:
|
||||
template<typename Pred>
|
||||
Move select(Pred);
|
||||
template<GenType>
|
||||
void score();
|
||||
template<GenType T>
|
||||
ExtMove* score(MoveList<T>&);
|
||||
ExtMove* begin() { return cur; }
|
||||
ExtMove* end() { return endCur; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user