DroidFish: Updated stockfish engine to development version 2017-09-06.

This commit is contained in:
Peter Osterlund
2017-09-10 10:30:09 +02:00
parent c0b69b6bf8
commit 3536c6290a
42 changed files with 3431 additions and 4042 deletions

View File

@@ -6,8 +6,6 @@
<copy file="${basedir}/libs/armeabi/stockfish-nopie" tofile="${basedir}/assets/stockfish-armeabi-nopie"/> <copy file="${basedir}/libs/armeabi/stockfish-nopie" tofile="${basedir}/assets/stockfish-armeabi-nopie"/>
<copy file="${basedir}/libs/armeabi-v7a/stockfish" tofile="${basedir}/assets/stockfish-armeabi-v7a"/> <copy file="${basedir}/libs/armeabi-v7a/stockfish" tofile="${basedir}/assets/stockfish-armeabi-v7a"/>
<copy file="${basedir}/libs/armeabi-v7a/stockfish-nopie" tofile="${basedir}/assets/stockfish-armeabi-v7a-nopie"/> <copy file="${basedir}/libs/armeabi-v7a/stockfish-nopie" tofile="${basedir}/assets/stockfish-armeabi-v7a-nopie"/>
<copy file="${basedir}/libs/mips/stockfish" tofile="${basedir}/assets/stockfish-mips"/>
<copy file="${basedir}/libs/mips/stockfish-nopie" tofile="${basedir}/assets/stockfish-mips-nopie"/>
<copy file="${basedir}/libs/mips64/stockfish" tofile="${basedir}/assets/stockfish-mips64"/> <copy file="${basedir}/libs/mips64/stockfish" tofile="${basedir}/assets/stockfish-mips64"/>
<copy file="${basedir}/libs/x86/stockfish" tofile="${basedir}/assets/stockfish-x86"/> <copy file="${basedir}/libs/x86/stockfish" tofile="${basedir}/assets/stockfish-x86"/>
<copy file="${basedir}/libs/x86/stockfish-nopie" tofile="${basedir}/assets/stockfish-x86-nopie"/> <copy file="${basedir}/libs/x86/stockfish-nopie" tofile="${basedir}/assets/stockfish-x86-nopie"/>

View File

@@ -1,5 +1,5 @@
APP_PLATFORM := 11 APP_PLATFORM := 11
APP_ABI := all APP_ABI := arm64-v8a armeabi-v7a armeabi mips64 x86 x86_64
APP_STL := gnustl_static APP_STL := gnustl_static
APP_OPTIM := release APP_OPTIM := release
NDK_TOOLCHAIN_VERSION := 4.9 NDK_TOOLCHAIN_VERSION := 4.9

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -23,23 +23,20 @@
#include <istream> #include <istream>
#include <vector> #include <vector>
#include "misc.h"
#include "position.h" #include "position.h"
#include "search.h"
#include "thread.h"
#include "uci.h"
using namespace std; using namespace std;
namespace { namespace {
const vector<string> Defaults = { const vector<string> Defaults = {
"setoption name UCI_Chess960 value false",
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10", "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10",
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11", "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11",
"4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19", "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
"rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14", "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14 moves d4e6",
"r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14", "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14 moves g2g4",
"r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15", "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15",
"r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13", "r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13",
"r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16", "r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16",
@@ -52,7 +49,7 @@ const vector<string> Defaults = {
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26", "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26",
"6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b - - 0 1", "6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b - - 0 1",
"3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w - - 0 1", "3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w - - 0 1",
"2K5/p7/7P/5pR1/8/5k2/r7/8 w - - 0 1", "2K5/p7/7P/5pR1/8/5k2/r7/8 w - - 0 1 moves g5g6 f3e3 g6g5 e3f3",
"8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w - - 0 1", "8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w - - 0 1",
"7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w - - 0 1", "7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w - - 0 1",
"8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w - - 0 1", "8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w - - 0 1",
@@ -79,28 +76,35 @@ const vector<string> Defaults = {
"8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw "8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw
// Mate and stalemate positions // Mate and stalemate positions
"6k1/3b3r/1p1p4/p1n2p2/1PPNpP1q/P3Q1p1/1R1RB1P1/5K2 b - - 0 1",
"r2r1n2/pp2bk2/2p1p2p/3q4/3PN1QP/2P3R1/P4PP1/5RK1 w - - 0 1",
"8/8/8/8/8/6k1/6p1/6K1 w - -", "8/8/8/8/8/6k1/6p1/6K1 w - -",
"5k2/5P2/5K2/8/8/8/8/8 b - -", "7k/7P/6K1/8/3B4/8/8/8 b - -",
"8/8/8/8/8/4k3/4p3/4K3 w - -",
"8/8/8/8/8/5K2/8/3Q1k2 b - -", // Chess 960
"7k/7P/6K1/8/3B4/8/8/8 b - -" "setoption name UCI_Chess960 value true",
"bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w KQkq - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6",
"setoption name UCI_Chess960 value false"
}; };
} // namespace } // namespace
/// benchmark() runs a simple benchmark by letting Stockfish analyze a set /// setup_bench() builds a list of UCI commands to be run by bench. There
/// of positions for a given limit each. There are five parameters: the /// are five parameters: TT size in MB, number of search threads that
/// transposition table size, the number of search threads that should /// should be used, the limit value spent for each position, a file name
/// be used, the limit value spent for each position (optional, default is /// where to look for positions in FEN format and the type of the limit:
/// depth 13), an optional file name where to look for positions in FEN /// depth, perft, nodes and movetime (in millisecs).
/// format (defaults are the positions defined above) and the type of the ///
/// limit value: depth (default), time in millisecs or number of nodes. /// bench -> search default positions up to depth 13
/// bench 64 1 15 -> search default positions up to depth 15 (TT = 64MB)
/// bench 64 4 5000 current movetime -> search current position with 4 threads for 5 sec
/// bench 64 1 100000 default nodes -> search default positions for 100K nodes each
/// bench 16 1 5 default perft -> run a perft 5 on default positions
void benchmark(const Position& current, istream& is) { vector<string> setup_bench(const Position& current, istream& is) {
string token; vector<string> fens, list;
vector<string> fens; string go, token;
Search::LimitsType limits;
// Assign default values to missing arguments // Assign default values to missing arguments
string ttSize = (is >> token) ? token : "16"; string ttSize = (is >> token) ? token : "16";
@@ -109,21 +113,7 @@ void benchmark(const Position& current, istream& is) {
string fenFile = (is >> token) ? token : "default"; string fenFile = (is >> token) ? token : "default";
string limitType = (is >> token) ? token : "depth"; string limitType = (is >> token) ? token : "depth";
Options["Hash"] = ttSize; go = "go " + limitType + " " + limit;
Options["Threads"] = threads;
Search::clear();
if (limitType == "time")
limits.movetime = stoi(limit); // movetime is in millisecs
else if (limitType == "nodes")
limits.nodes = stoi(limit);
else if (limitType == "mate")
limits.mate = stoi(limit);
else
limits.depth = stoi(limit);
if (fenFile == "default") if (fenFile == "default")
fens = Defaults; fens = Defaults;
@@ -139,7 +129,7 @@ void benchmark(const Position& current, istream& is) {
if (!file.is_open()) if (!file.is_open())
{ {
cerr << "Unable to open file " << fenFile << endl; cerr << "Unable to open file " << fenFile << endl;
return; exit(EXIT_FAILURE);
} }
while (getline(file, fen)) while (getline(file, fen))
@@ -149,35 +139,18 @@ void benchmark(const Position& current, istream& is) {
file.close(); file.close();
} }
uint64_t nodes = 0; list.emplace_back("ucinewgame");
TimePoint elapsed = now(); list.emplace_back("setoption name Threads value " + threads);
Position pos; list.emplace_back("setoption name Hash value " + ttSize);
for (size_t i = 0; i < fens.size(); ++i)
{
StateListPtr states(new std::deque<StateInfo>(1));
pos.set(fens[i], Options["UCI_Chess960"], &states->back(), Threads.main());
cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl;
if (limitType == "perft")
nodes += Search::perft(pos, limits.depth * ONE_PLY);
for (const string& fen : fens)
if (fen.find("setoption") != string::npos)
list.emplace_back(fen);
else else
{ {
limits.startTime = now(); list.emplace_back("position fen " + fen);
Threads.start_thinking(pos, states, limits); list.emplace_back(go);
Threads.main()->wait_for_search_finished();
nodes += Threads.nodes_searched();
}
} }
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero' return list;
dbg_print(); // Just before exiting
cerr << "\n==========================="
<< "\nTotal time (ms) : " << elapsed
<< "\nNodes searched : " << nodes
<< "\nNodes/second : " << 1000 * nodes / elapsed << endl;
} }

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -117,7 +117,7 @@ namespace {
if ( distance(ksq[WHITE], ksq[BLACK]) <= 1 if ( distance(ksq[WHITE], ksq[BLACK]) <= 1
|| ksq[WHITE] == psq || ksq[WHITE] == psq
|| ksq[BLACK] == psq || ksq[BLACK] == psq
|| (us == WHITE && (StepAttacksBB[PAWN][psq] & ksq[BLACK]))) || (us == WHITE && (PawnAttacks[WHITE][psq] & ksq[BLACK])))
result = INVALID; result = INVALID;
// Immediate win if a pawn can be promoted without getting captured // Immediate win if a pawn can be promoted without getting captured
@@ -125,13 +125,13 @@ namespace {
&& rank_of(psq) == RANK_7 && rank_of(psq) == RANK_7
&& ksq[us] != psq + NORTH && ksq[us] != psq + NORTH
&& ( distance(ksq[~us], psq + NORTH) > 1 && ( distance(ksq[~us], psq + NORTH) > 1
|| (StepAttacksBB[KING][ksq[us]] & (psq + NORTH)))) || (PseudoAttacks[KING][ksq[us]] & (psq + NORTH))))
result = WIN; result = WIN;
// Immediate draw if it is a stalemate or a king captures undefended pawn // Immediate draw if it is a stalemate or a king captures undefended pawn
else if ( us == BLACK else if ( us == BLACK
&& ( !(StepAttacksBB[KING][ksq[us]] & ~(StepAttacksBB[KING][ksq[~us]] | StepAttacksBB[PAWN][psq])) && ( !(PseudoAttacks[KING][ksq[us]] & ~(PseudoAttacks[KING][ksq[~us]] | PawnAttacks[~us][psq]))
|| (StepAttacksBB[KING][ksq[us]] & psq & ~StepAttacksBB[KING][ksq[~us]]))) || (PseudoAttacks[KING][ksq[us]] & psq & ~PseudoAttacks[KING][ksq[~us]])))
result = DRAW; result = DRAW;
// Position will be classified later // Position will be classified later
@@ -157,7 +157,7 @@ namespace {
const Result Bad = (Us == WHITE ? DRAW : WIN); const Result Bad = (Us == WHITE ? DRAW : WIN);
Result r = INVALID; Result r = INVALID;
Bitboard b = StepAttacksBB[KING][ksq[Us]]; Bitboard b = PseudoAttacks[KING][ksq[Us]];
while (b) while (b)
r |= Us == WHITE ? db[index(Them, ksq[Them] , pop_lsb(&b), psq)] r |= Us == WHITE ? db[index(Them, ksq[Them] , pop_lsb(&b), psq)]

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -26,29 +26,22 @@
uint8_t PopCnt16[1 << 16]; uint8_t PopCnt16[1 << 16];
int SquareDistance[SQUARE_NB][SQUARE_NB]; int SquareDistance[SQUARE_NB][SQUARE_NB];
Bitboard RookMasks [SQUARE_NB];
Bitboard RookMagics [SQUARE_NB];
Bitboard* RookAttacks[SQUARE_NB];
unsigned RookShifts [SQUARE_NB];
Bitboard BishopMasks [SQUARE_NB];
Bitboard BishopMagics [SQUARE_NB];
Bitboard* BishopAttacks[SQUARE_NB];
unsigned BishopShifts [SQUARE_NB];
Bitboard SquareBB[SQUARE_NB]; Bitboard SquareBB[SQUARE_NB];
Bitboard FileBB[FILE_NB]; Bitboard FileBB[FILE_NB];
Bitboard RankBB[RANK_NB]; Bitboard RankBB[RANK_NB];
Bitboard AdjacentFilesBB[FILE_NB]; Bitboard AdjacentFilesBB[FILE_NB];
Bitboard InFrontBB[COLOR_NB][RANK_NB]; Bitboard ForwardRanksBB[COLOR_NB][RANK_NB];
Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard LineBB[SQUARE_NB][SQUARE_NB]; Bitboard LineBB[SQUARE_NB][SQUARE_NB];
Bitboard DistanceRingBB[SQUARE_NB][8]; Bitboard DistanceRingBB[SQUARE_NB][8];
Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; Bitboard ForwardFileBB[COLOR_NB][SQUARE_NB];
Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB]; Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
Magic RookMagics[SQUARE_NB];
Magic BishopMagics[SQUARE_NB];
namespace { namespace {
@@ -61,10 +54,7 @@ namespace {
Bitboard RookTable[0x19000]; // To store rook attacks Bitboard RookTable[0x19000]; // To store rook attacks
Bitboard BishopTable[0x1480]; // To store bishop attacks Bitboard BishopTable[0x1480]; // To store bishop attacks
typedef unsigned (Fn)(Square, Bitboard); void init_magics(Bitboard table[], Magic magics[], Square deltas[]);
void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[],
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index);
// bsf_index() returns the index into BSFTable[] to look up the bitscan. Uses // bsf_index() returns the index into BSFTable[] to look up the bitscan. Uses
// Matt Taylor's folding for 32 bit case, extended to 64 bit by Kim Walisch. // Matt Taylor's folding for 32 bit case, extended to 64 bit by Kim Walisch.
@@ -173,14 +163,14 @@ void Bitboards::init() {
AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0); AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
for (Rank r = RANK_1; r < RANK_8; ++r) for (Rank r = RANK_1; r < RANK_8; ++r)
InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]); ForwardRanksBB[WHITE][r] = ~(ForwardRanksBB[BLACK][r + 1] = ForwardRanksBB[BLACK][r] | RankBB[r]);
for (Color c = WHITE; c <= BLACK; ++c) for (Color c = WHITE; c <= BLACK; ++c)
for (Square s = SQ_A1; s <= SQ_H8; ++s) for (Square s = SQ_A1; s <= SQ_H8; ++s)
{ {
ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)]; ForwardFileBB [c][s] = ForwardRanksBB[c][rank_of(s)] & FileBB[file_of(s)];
PawnAttackSpan[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)]; PawnAttackSpan[c][s] = ForwardRanksBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)];
PassedPawnMask[c][s] = ForwardBB[c][s] | PawnAttackSpan[c][s]; PassedPawnMask[c][s] = ForwardFileBB [c][s] | PawnAttackSpan[c][s];
} }
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
@@ -191,39 +181,43 @@ void Bitboards::init() {
DistanceRingBB[s1][SquareDistance[s1][s2] - 1] |= s2; DistanceRingBB[s1][SquareDistance[s1][s2] - 1] |= s2;
} }
int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 }, int steps[][5] = { {}, { 7, 9 }, { 6, 10, 15, 17 }, {}, {}, {}, { 1, 7, 8, 9 } };
{}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } };
for (Color c = WHITE; c <= BLACK; ++c) for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt = PAWN; pt <= KING; ++pt) for (PieceType pt : { PAWN, KNIGHT, KING })
for (Square s = SQ_A1; s <= SQ_H8; ++s) for (Square s = SQ_A1; s <= SQ_H8; ++s)
for (int i = 0; steps[pt][i]; ++i) for (int i = 0; steps[pt][i]; ++i)
{ {
Square to = s + Square(c == WHITE ? steps[pt][i] : -steps[pt][i]); Square to = s + Square(c == WHITE ? steps[pt][i] : -steps[pt][i]);
if (is_ok(to) && distance(s, to) < 3) if (is_ok(to) && distance(s, to) < 3)
StepAttacksBB[make_piece(c, pt)][s] |= to; {
if (pt == PAWN)
PawnAttacks[c][s] |= to;
else
PseudoAttacks[pt][s] |= to;
}
} }
Square RookDeltas[] = { NORTH, EAST, SOUTH, WEST }; Square RookDeltas[] = { NORTH, EAST, SOUTH, WEST };
Square BishopDeltas[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }; Square BishopDeltas[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
init_magics(RookTable, RookAttacks, RookMagics, RookMasks, RookShifts, RookDeltas, magic_index<ROOK>); init_magics(RookTable, RookMagics, RookDeltas);
init_magics(BishopTable, BishopAttacks, BishopMagics, BishopMasks, BishopShifts, BishopDeltas, magic_index<BISHOP>); init_magics(BishopTable, BishopMagics, BishopDeltas);
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
{ {
PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb<BISHOP>(s1, 0); PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb<BISHOP>(s1, 0);
PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0); PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0);
for (Piece pc = W_BISHOP; pc <= W_ROOK; ++pc) for (PieceType pt : { BISHOP, ROOK })
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
{ {
if (!(PseudoAttacks[pc][s1] & s2)) if (!(PseudoAttacks[pt][s1] & s2))
continue; continue;
LineBB[s1][s2] = (attacks_bb(pc, s1, 0) & attacks_bb(pc, s2, 0)) | s1 | s2; LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
BetweenBB[s1][s2] = attacks_bb(pc, s1, SquareBB[s2]) & attacks_bb(pc, s2, SquareBB[s1]); BetweenBB[s1][s2] = attacks_bb(pt, s1, SquareBB[s2]) & attacks_bb(pt, s2, SquareBB[s1]);
} }
} }
} }
@@ -255,17 +249,14 @@ namespace {
// chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we // chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
// use the so called "fancy" approach. // use the so called "fancy" approach.
void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[], void init_magics(Bitboard table[], Magic magics[], Square deltas[]) {
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) {
// Optimal PRNG seeds to pick the correct magics in the shortest time
int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 }, int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 },
{ 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } }; { 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } };
Bitboard occupancy[4096], reference[4096], edges, b; Bitboard occupancy[4096], reference[4096], edges, b;
int age[4096] = {0}, current = 0, i, size; int epoch[4096] = {}, cnt = 0, size = 0;
// attacks[s] is a pointer to the beginning of the attacks table for square 's'
attacks[SQ_A1] = table;
for (Square s = SQ_A1; s <= SQ_H8; ++s) for (Square s = SQ_A1; s <= SQ_H8; ++s)
{ {
@@ -277,8 +268,13 @@ namespace {
// all the attacks for each possible subset of the mask and so is 2 power // all the attacks for each possible subset of the mask and so is 2 power
// the number of 1s of the mask. Hence we deduce the size of the shift to // the number of 1s of the mask. Hence we deduce the size of the shift to
// apply to the 64 or 32 bits word to get the index. // apply to the 64 or 32 bits word to get the index.
masks[s] = sliding_attack(deltas, s, 0) & ~edges; Magic& m = magics[s];
shifts[s] = (Is64Bit ? 64 : 32) - popcount(masks[s]); m.mask = sliding_attack(deltas, s, 0) & ~edges;
m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
// Set the offset for the attacks table of the square. We have individual
// table sizes for each square with "Fancy Magic Bitboards".
m.attacks = s == SQ_A1 ? table : magics[s - 1].attacks + size;
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and // Use Carry-Rippler trick to enumerate all subsets of masks[s] and
// store the corresponding sliding attack bitboard in reference[]. // store the corresponding sliding attack bitboard in reference[].
@@ -288,17 +284,12 @@ namespace {
reference[size] = sliding_attack(deltas, s, b); reference[size] = sliding_attack(deltas, s, b);
if (HasPext) if (HasPext)
attacks[s][pext(b, masks[s])] = reference[size]; m.attacks[pext(b, m.mask)] = reference[size];
size++; size++;
b = (b - masks[s]) & masks[s]; b = (b - m.mask) & m.mask;
} while (b); } while (b);
// Set the offset for the table of the next square. We have individual
// table sizes for each square with "Fancy Magic Bitboards".
if (s < SQ_H8)
attacks[s + 1] = attacks[s] + size;
if (HasPext) if (HasPext)
continue; continue;
@@ -306,28 +297,30 @@ namespace {
// Find a magic for square 's' picking up an (almost) random number // Find a magic for square 's' picking up an (almost) random number
// until we find the one that passes the verification test. // until we find the one that passes the verification test.
do { for (int i = 0; i < size; )
do {
magics[s] = rng.sparse_rand<Bitboard>(); for (m.magic = 0; popcount((m.magic * m.mask) >> 56) < 6; )
while (popcount((magics[s] * masks[s]) >> 56) < 6); m.magic = rng.sparse_rand<Bitboard>();
// A good magic must map every possible occupancy to an index that // A good magic must map every possible occupancy to an index that
// looks up the correct sliding attack in the attacks[s] database. // looks up the correct sliding attack in the attacks[s] database.
// Note that we build up the database for square 's' as a side // Note that we build up the database for square 's' as a side
// effect of verifying the magic. // effect of verifying the magic. Keep track of the attempt count
for (++current, i = 0; i < size; ++i) // and save it in epoch[], little speed-up trick to avoid resetting
// m.attacks[] after every failed attempt.
for (++cnt, i = 0; i < size; ++i)
{ {
unsigned idx = index(s, occupancy[i]); unsigned idx = m.index(occupancy[i]);
if (age[idx] < current) if (epoch[idx] < cnt)
{ {
age[idx] = current; epoch[idx] = cnt;
attacks[s][idx] = reference[i]; m.attacks[idx] = reference[i];
} }
else if (attacks[s][idx] != reference[i]) else if (m.attacks[idx] != reference[i])
break; break;
} }
} while (i < size); }
} }
} }
} }

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -39,6 +39,7 @@ const std::string pretty(Bitboard b);
} }
const Bitboard AllSquares = ~Bitboard(0);
const Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL; const Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
const Bitboard FileABB = 0x0101010101010101ULL; const Bitboard FileABB = 0x0101010101010101ULL;
@@ -65,15 +66,41 @@ extern Bitboard SquareBB[SQUARE_NB];
extern Bitboard FileBB[FILE_NB]; extern Bitboard FileBB[FILE_NB];
extern Bitboard RankBB[RANK_NB]; extern Bitboard RankBB[RANK_NB];
extern Bitboard AdjacentFilesBB[FILE_NB]; extern Bitboard AdjacentFilesBB[FILE_NB];
extern Bitboard InFrontBB[COLOR_NB][RANK_NB]; extern Bitboard ForwardRanksBB[COLOR_NB][RANK_NB];
extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
extern Bitboard DistanceRingBB[SQUARE_NB][8]; extern Bitboard DistanceRingBB[SQUARE_NB][8];
extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; extern Bitboard ForwardFileBB[COLOR_NB][SQUARE_NB];
extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
extern Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB]; extern Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
/// Magic holds all magic bitboards relevant data for a single square
struct Magic {
Bitboard mask;
Bitboard magic;
Bitboard* attacks;
unsigned shift;
// Compute the attack's index using the 'magic bitboards' approach
unsigned index(Bitboard occupied) const {
if (HasPext)
return unsigned(pext(occupied, mask));
if (Is64Bit)
return unsigned(((occupied & mask) * magic) >> shift);
unsigned lo = unsigned(occupied) & unsigned(mask);
unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32);
return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift;
}
};
extern Magic RookMagics[SQUARE_NB];
extern Magic BishopMagics[SQUARE_NB];
/// Overloads of bitwise operators between a Bitboard and a Square for testing /// Overloads of bitwise operators between a Bitboard and a Square for testing
@@ -153,28 +180,28 @@ inline Bitboard between_bb(Square s1, Square s2) {
} }
/// in_front_bb() returns a bitboard representing all the squares on all the ranks /// forward_ranks_bb() returns a bitboard representing all the squares on all the ranks
/// in front of the given one, from the point of view of the given color. For /// in front of the given one, from the point of view of the given color. For
/// instance, in_front_bb(BLACK, RANK_3) will return the squares on ranks 1 and 2. /// instance, forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2.
inline Bitboard in_front_bb(Color c, Rank r) { inline Bitboard forward_ranks_bb(Color c, Square s) {
return InFrontBB[c][r]; return ForwardRanksBB[c][rank_of(s)];
} }
/// forward_bb() returns a bitboard representing all the squares along the line /// forward_file_bb() returns a bitboard representing all the squares along the line
/// in front of the given one, from the point of view of the given color: /// in front of the given one, from the point of view of the given color:
/// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s) /// ForwardFileBB[c][s] = forward_ranks_bb(c, s) & file_bb(s)
inline Bitboard forward_bb(Color c, Square s) { inline Bitboard forward_file_bb(Color c, Square s) {
return ForwardBB[c][s]; return ForwardFileBB[c][s];
} }
/// pawn_attack_span() returns a bitboard representing all the squares that can be /// pawn_attack_span() returns a bitboard representing all the squares that can be
/// attacked by a pawn of the given color when it moves along its file, starting /// attacked by a pawn of the given color when it moves along its file, starting
/// from the given square: /// from the given square:
/// PawnAttackSpan[c][s] = in_front_bb(c, s) & adjacent_files_bb(s); /// PawnAttackSpan[c][s] = forward_ranks_bb(c, s) & adjacent_files_bb(file_of(s));
inline Bitboard pawn_attack_span(Color c, Square s) { inline Bitboard pawn_attack_span(Color c, Square s) {
return PawnAttackSpan[c][s]; return PawnAttackSpan[c][s];
@@ -183,7 +210,7 @@ inline Bitboard pawn_attack_span(Color c, Square s) {
/// passed_pawn_mask() returns a bitboard mask which can be used to test if a /// passed_pawn_mask() returns a bitboard mask which can be used to test if a
/// pawn of the given color and on the given square is a passed pawn: /// pawn of the given color and on the given square is a passed pawn:
/// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_bb(c, s) /// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_file_bb(c, s)
inline Bitboard passed_pawn_mask(Color c, Square s) { inline Bitboard passed_pawn_mask(Color c, Square s) {
return PassedPawnMask[c][s]; return PassedPawnMask[c][s];
@@ -210,50 +237,25 @@ template<> inline int distance<Rank>(Square x, Square y) { return distance(rank_
/// attacks_bb() returns a bitboard representing all the squares attacked by a /// attacks_bb() returns a bitboard representing all the squares attacked by a
/// piece of type Pt (bishop or rook) placed on 's'. The helper magic_index() /// piece of type Pt (bishop or rook) placed on 's'.
/// looks up the index using the 'magic bitboards' approach.
template<PieceType Pt>
inline unsigned magic_index(Square s, Bitboard occupied) {
extern Bitboard RookMasks[SQUARE_NB];
extern Bitboard RookMagics[SQUARE_NB];
extern unsigned RookShifts[SQUARE_NB];
extern Bitboard BishopMasks[SQUARE_NB];
extern Bitboard BishopMagics[SQUARE_NB];
extern unsigned BishopShifts[SQUARE_NB];
Bitboard* const Masks = Pt == ROOK ? RookMasks : BishopMasks;
Bitboard* const Magics = Pt == ROOK ? RookMagics : BishopMagics;
unsigned* const Shifts = Pt == ROOK ? RookShifts : BishopShifts;
if (HasPext)
return unsigned(pext(occupied, Masks[s]));
if (Is64Bit)
return unsigned(((occupied & Masks[s]) * Magics[s]) >> Shifts[s]);
unsigned lo = unsigned(occupied) & unsigned(Masks[s]);
unsigned hi = unsigned(occupied >> 32) & unsigned(Masks[s] >> 32);
return (lo * unsigned(Magics[s]) ^ hi * unsigned(Magics[s] >> 32)) >> Shifts[s];
}
template<PieceType Pt> template<PieceType Pt>
inline Bitboard attacks_bb(Square s, Bitboard occupied) { inline Bitboard attacks_bb(Square s, Bitboard occupied) {
extern Bitboard* RookAttacks[SQUARE_NB]; const Magic& m = Pt == ROOK ? RookMagics[s] : BishopMagics[s];
extern Bitboard* BishopAttacks[SQUARE_NB]; return m.attacks[m.index(occupied)];
return (Pt == ROOK ? RookAttacks : BishopAttacks)[s][magic_index<Pt>(s, occupied)];
} }
inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) { inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
switch (type_of(pc)) assert(pt != PAWN);
switch (pt)
{ {
case BISHOP: return attacks_bb<BISHOP>(s, occupied); case BISHOP: return attacks_bb<BISHOP>(s, occupied);
case ROOK : return attacks_bb< ROOK>(s, occupied); case ROOK : return attacks_bb< ROOK>(s, occupied);
case QUEEN : return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied); case QUEEN : return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
default : return StepAttacksBB[pc][s]; default : return PseudoAttacks[pt][s];
} }
} }
@@ -291,7 +293,7 @@ inline Square lsb(Bitboard b) {
inline Square msb(Bitboard b) { inline Square msb(Bitboard b) {
assert(b); assert(b);
return Square(63 - __builtin_clzll(b)); return Square(63 ^ __builtin_clzll(b));
} }
#elif defined(_WIN64) && defined(_MSC_VER) #elif defined(_WIN64) && defined(_MSC_VER)

View File

@@ -1,105 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BITCOUNT_H_INCLUDED
#define BITCOUNT_H_INCLUDED
#include <cassert>
#include "types.h"
enum BitCountType {
CNT_64,
CNT_64_MAX15,
CNT_32,
CNT_32_MAX15,
CNT_HW_POPCNT
};
/// Determine at compile time the best popcount<> specialization according to
/// whether the platform is 32 or 64 bit, the maximum number of non-zero
/// bits to count and if the hardware popcnt instruction is available.
const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32;
const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15;
/// popcount() counts the number of non-zero bits in a bitboard
template<BitCountType> inline int popcount(Bitboard);
template<>
inline int popcount<CNT_64>(Bitboard b) {
b -= (b >> 1) & 0x5555555555555555ULL;
b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b = ((b >> 4) + b) & 0x0F0F0F0F0F0F0F0FULL;
return (b * 0x0101010101010101ULL) >> 56;
}
template<>
inline int popcount<CNT_64_MAX15>(Bitboard b) {
b -= (b >> 1) & 0x5555555555555555ULL;
b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
return (b * 0x1111111111111111ULL) >> 60;
}
template<>
inline int popcount<CNT_32>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555;
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
v = ((v >> 4) + v + (w >> 4) + w) & 0x0F0F0F0F;
return (v * 0x01010101) >> 24;
}
template<>
inline int popcount<CNT_32_MAX15>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555;
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
return ((v + w) * 0x11111111) >> 28;
}
template<>
inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#ifndef USE_POPCNT
assert(false);
return b != 0; // Avoid 'b not used' warning
#elif defined(_MSC_VER) && defined(__INTEL_COMPILER)
return _mm_popcnt_u64(b);
#elif defined(_MSC_VER)
return (int)__popcnt64(b);
#else // Assumed gcc or compatible compiler
return __builtin_popcountll(b);
#endif
}
#endif // #ifndef BITCOUNT_H_INCLUDED

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -83,26 +83,6 @@ namespace {
return sq; return sq;
} }
// Get the material key of Position out of the given endgame key code
// like "KBPKN". The trick here is to first forge an ad-hoc FEN string
// and then let a Position object do the work for us.
Key key(const string& code, Color c) {
assert(code.length() > 0 && code.length() < 8);
assert(code[0] == 'K');
string sides[] = { code.substr(code.find('K', 1)), // Weak
code.substr(0, code.find('K', 1)) }; // Strong
std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower);
string fen = sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/8/8/"
+ sides[1] + char(8 - sides[1].length() + '0') + " w - - 0 10";
StateInfo st;
return Position().set(fen, false, &st, nullptr).material_key();
}
} // namespace } // namespace
@@ -130,13 +110,6 @@ Endgames::Endgames() {
} }
template<EndgameType E, typename T>
void Endgames::add(const string& code) {
map<T>()[key(code, WHITE)] = std::unique_ptr<EndgameBase<T>>(new Endgame<E>(WHITE));
map<T>()[key(code, BLACK)] = std::unique_ptr<EndgameBase<T>>(new Endgame<E>(BLACK));
}
/// Mate with KX vs K. This function is used to evaluate positions with /// Mate with KX vs K. This function is used to evaluate positions with
/// king and plenty of material vs a lone king. It simply gives the /// king and plenty of material vs a lone king. It simply gives the
/// attacking side a bonus for driving the defending king towards the edge /// attacking side a bonus for driving the defending king towards the edge
@@ -162,8 +135,8 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
if ( pos.count<QUEEN>(strongSide) if ( pos.count<QUEEN>(strongSide)
|| pos.count<ROOK>(strongSide) || pos.count<ROOK>(strongSide)
||(pos.count<BISHOP>(strongSide) && pos.count<KNIGHT>(strongSide)) ||(pos.count<BISHOP>(strongSide) && pos.count<KNIGHT>(strongSide))
||(pos.count<BISHOP>(strongSide) > 1 && opposite_colors(pos.squares<BISHOP>(strongSide)[0], || ( (pos.pieces(strongSide, BISHOP) & ~DarkSquares)
pos.squares<BISHOP>(strongSide)[1]))) && (pos.pieces(strongSide, BISHOP) & DarkSquares)))
result = std::min(result + VALUE_KNOWN_WIN, VALUE_MATE_IN_MAX_PLY - 1); result = std::min(result + VALUE_KNOWN_WIN, VALUE_MATE_IN_MAX_PLY - 1);
return strongSide == pos.side_to_move() ? result : -result; return strongSide == pos.side_to_move() ? result : -result;
@@ -625,7 +598,7 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
// If all pawns are ahead of the king, on a single rook file and // If all pawns are ahead of the king, on a single rook file and
// the king is within one file of the pawns, it's a draw. // the king is within one file of the pawns, it's a draw.
if ( !(pawns & ~in_front_bb(weakSide, rank_of(ksq))) if ( !(pawns & ~forward_ranks_bb(weakSide, ksq))
&& !((pawns & ~FileABB) && (pawns & ~FileHBB)) && !((pawns & ~FileABB) && (pawns & ~FileHBB))
&& distance<File>(ksq, lsb(pawns)) <= 1) && distance<File>(ksq, lsb(pawns)) <= 1)
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
@@ -671,9 +644,8 @@ ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
if (relative_rank(strongSide, pawnSq) <= RANK_5) if (relative_rank(strongSide, pawnSq) <= RANK_5)
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
else
{ Bitboard path = forward_file_bb(strongSide, pawnSq);
Bitboard path = forward_bb(strongSide, pawnSq);
if (path & pos.pieces(weakSide, KING)) if (path & pos.pieces(weakSide, KING))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
@@ -682,7 +654,6 @@ ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
&& distance(weakBishopSq, pawnSq) >= 3) && distance(weakBishopSq, pawnSq) >= 3)
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
} }
}
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
@@ -809,7 +780,7 @@ ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
// King needs to get close to promoting pawn to prevent knight from blocking. // King needs to get close to promoting pawn to prevent knight from blocking.
// Rules for this are very tricky, so just approximate. // Rules for this are very tricky, so just approximate.
if (forward_bb(strongSide, pawnSq) & pos.attacks_from<BISHOP>(bishopSq)) if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from<BISHOP>(bishopSq))
return ScaleFactor(distance(weakKingSq, pawnSq)); return ScaleFactor(distance(weakKingSq, pawnSq));
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -31,12 +31,11 @@
#include "types.h" #include "types.h"
/// EndgameType lists all supported endgames /// EndgameCode lists all supported endgame functions by corresponding codes
enum EndgameType { enum EndgameCode {
// Evaluation functions
EVALUATION_FUNCTIONS,
KNNK, // KNN vs K KNNK, // KNN vs K
KXK, // Generic "mate lone king" eval KXK, // Generic "mate lone king" eval
KBNK, // KBN vs K KBNK, // KBN vs K
@@ -47,10 +46,7 @@ enum EndgameType {
KQKP, // KQ vs KP KQKP, // KQ vs KP
KQKR, // KQ vs KR KQKR, // KQ vs KR
// Scaling functions
SCALING_FUNCTIONS, SCALING_FUNCTIONS,
KBPsK, // KB and pawns vs K KBPsK, // KB and pawns vs K
KQKRPs, // KQ vs KR and pawns KQKRPs, // KQ vs KR and pawns
KRPKR, // KRP vs KR KRPKR, // KRP vs KR
@@ -68,30 +64,28 @@ enum EndgameType {
/// Endgame functions can be of two types depending on whether they return a /// Endgame functions can be of two types depending on whether they return a
/// Value or a ScaleFactor. /// Value or a ScaleFactor.
template<EndgameType E> using template<EndgameCode E> using
eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type; eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type;
/// Base and derived templates for endgame evaluation and scaling functions /// Base and derived functors for endgame evaluation and scaling functions
template<typename T> template<typename T>
struct EndgameBase { struct EndgameBase {
explicit EndgameBase(Color c) : strongSide(c), weakSide(~c) {}
virtual ~EndgameBase() = default; virtual ~EndgameBase() = default;
virtual Color strong_side() const = 0;
virtual T operator()(const Position&) const = 0; virtual T operator()(const Position&) const = 0;
const Color strongSide, weakSide;
}; };
template<EndgameType E, typename T = eg_type<E>> template<EndgameCode E, typename T = eg_type<E>>
struct Endgame : public EndgameBase<T> { struct Endgame : public EndgameBase<T> {
explicit Endgame(Color c) : strongSide(c), weakSide(~c) {} explicit Endgame(Color c) : EndgameBase<T>(c) {}
Color strong_side() const { return strongSide; } T operator()(const Position&) const override;
T operator()(const Position&) const;
private:
Color strongSide, weakSide;
}; };
@@ -101,16 +95,22 @@ private:
class Endgames { class Endgames {
template<typename T> using Map = std::map<Key, std::unique_ptr<EndgameBase<T>>>; template<typename T> using Ptr = std::unique_ptr<EndgameBase<T>>;
template<typename T> using Map = std::map<Key, Ptr<T>>;
template<EndgameType E, typename T = eg_type<E>>
void add(const std::string& code);
template<typename T> template<typename T>
Map<T>& map() { Map<T>& map() {
return std::get<std::is_same<T, ScaleFactor>::value>(maps); return std::get<std::is_same<T, ScaleFactor>::value>(maps);
} }
template<EndgameCode E, typename T = eg_type<E>, typename P = Ptr<T>>
void add(const std::string& code) {
StateInfo st;
map<T>()[Position().set(code, WHITE, &st).material_key()] = P(new Endgame<E>(WHITE));
map<T>()[Position().set(code, BLACK, &st).material_key()] = P(new Endgame<E>(BLACK));
}
std::pair<Map<Value>, Map<ScaleFactor>> maps; std::pair<Map<Value>, Map<ScaleFactor>> maps;
public: public:

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -33,7 +33,6 @@ const Value Tempo = Value(20); // Must be visible to search
std::string trace(const Position& pos); std::string trace(const Position& pos);
template<bool DoTrace = false>
Value evaluate(const Position& pos); Value evaluate(const Position& pos);
} }

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -43,9 +43,10 @@ int main(int argc, char* argv[]) {
Bitbases::init(); Bitbases::init();
Search::init(); Search::init();
Pawns::init(); Pawns::init();
Threads.init();
Tablebases::init(Options["SyzygyPath"]); Tablebases::init(Options["SyzygyPath"]);
TT.resize(Options["Hash"]); TT.resize(Options["Hash"]);
Threads.init(Options["Threads"]);
Search::clear(); // After threads are up
UCI::loop(argc, argv); UCI::loop(argc, argv);

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -35,7 +35,7 @@ namespace {
// OUR PIECES // OUR PIECES
// pair pawn knight bishop rook queen // pair pawn knight bishop rook queen
{1667 }, // Bishop pair {1667 }, // Bishop pair
{ 40, 2 }, // Pawn { 40, 0 }, // Pawn
{ 32, 255, -3 }, // Knight OUR PIECES { 32, 255, -3 }, // Knight OUR PIECES
{ 0, 104, 4, 0 }, // Bishop { 0, 104, 4, 0 }, // Bishop
{ -26, -2, 47, 105, -149 }, // Rook { -26, -2, 47, 105, -149 }, // Rook
@@ -53,6 +53,17 @@ namespace {
{ 101, 100, -37, 141, 268, 0 } // Queen { 101, 100, -37, 141, 268, 0 } // Queen
}; };
// PawnSet[pawn count] contains a bonus/malus indexed by number of pawns
const int PawnSet[] = {
24, -32, 107, -51, 117, -9, -126, -21, 31
};
// QueenMinorsImbalance[opp_minor_count] is applied when only one side has a queen.
// It contains a bonus/malus for the side with the queen.
const int QueenMinorsImbalance[13] = {
31, -8, -15, -25, -5
};
// Endgame evaluation and scaling functions are accessed directly and not through // Endgame evaluation and scaling functions are accessed directly and not through
// the function maps because they correspond to more than one material hash key. // the function maps because they correspond to more than one material hash key.
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) }; Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
@@ -89,7 +100,7 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
int bonus = 0; int bonus = PawnSet[pieceCount[Us][PAWN]];
// Second-degree polynomial material imbalance by Tord Romstad // Second-degree polynomial material imbalance by Tord Romstad
for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1) for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
@@ -106,6 +117,10 @@ namespace {
bonus += pieceCount[Us][pt1] * v; bonus += pieceCount[Us][pt1] * v;
} }
// Special handling of Queen vs. Minors
if (pieceCount[Us][QUEEN] == 1 && pieceCount[Them][QUEEN] == 0)
bonus += QueenMinorsImbalance[pieceCount[Them][KNIGHT] + pieceCount[Them][BISHOP]];
return bonus; return bonus;
} }
@@ -129,7 +144,13 @@ Entry* probe(const Position& pos) {
std::memset(e, 0, sizeof(Entry)); std::memset(e, 0, sizeof(Entry));
e->key = key; e->key = key;
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL; e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
e->gamePhase = pos.game_phase();
Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK);
Value npm = std::max(EndgameLimit, std::min(npm_w + npm_b, MidgameLimit));
// Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME]
e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
// Let's look if we have a specialized evaluation function for this particular // Let's look if we have a specialized evaluation function for this particular
// material configuration. Firstly we look for a fixed configuration one, then // material configuration. Firstly we look for a fixed configuration one, then
@@ -150,7 +171,7 @@ Entry* probe(const Position& pos) {
if ((sf = pos.this_thread()->endgames.probe<ScaleFactor>(key)) != nullptr) if ((sf = pos.this_thread()->endgames.probe<ScaleFactor>(key)) != nullptr)
{ {
e->scalingFunction[sf->strong_side()] = sf; // Only strong color assigned e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned
return e; return e;
} }
@@ -166,9 +187,6 @@ Entry* probe(const Position& pos) {
e->scalingFunction[c] = &ScaleKQKRPs[c]; e->scalingFunction[c] = &ScaleKQKRPs[c];
} }
Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK);
if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board
{ {
if (!pos.count<PAWN>(BLACK)) if (!pos.count<PAWN>(BLACK))

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -56,11 +56,11 @@ struct Entry {
} }
Key key; Key key;
int16_t value;
uint8_t factor[COLOR_NB];
EndgameBase<Value>* evaluationFunction; EndgameBase<Value>* evaluationFunction;
EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each
// side (e.g. KPKP, KBPsKs) // side (e.g. KPKP, KBPsKs)
int16_t value;
uint8_t factor[COLOR_NB];
Phase gamePhase; Phase gamePhase;
}; };

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,10 +18,29 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifdef _WIN32
#if _WIN32_WINNT < 0x0601
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0601 // Force to include needed API prototypes
#endif
#include <windows.h>
// The needed Windows API for processor groups could be missed from old Windows
// versions, so instead of calling them directly (forcing the linker to resolve
// the calls at compile time), try to load them at runtime. To do this we need
// first to define the corresponding function pointers.
extern "C" {
typedef bool(*fun1_t)(LOGICAL_PROCESSOR_RELATIONSHIP,
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD);
typedef bool(*fun2_t)(USHORT, PGROUP_AFFINITY);
typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
}
#endif
#include <fstream> #include <fstream>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <vector>
#include "misc.h" #include "misc.h"
#include "thread.h" #include "thread.h"
@@ -32,7 +51,7 @@ namespace {
/// Version number. If Version is left empty, then compile date in the format /// Version number. If Version is left empty, then compile date in the format
/// DD-MM-YY and show in engine_info. /// DD-MM-YY and show in engine_info.
const string Version = "8"; const string Version = "2017-09-06";
/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
@@ -44,10 +63,10 @@ struct Tie: public streambuf { // MSVC requires split streambuf for cin and cout
Tie(streambuf* b, streambuf* l) : buf(b), logBuf(l) {} Tie(streambuf* b, streambuf* l) : buf(b), logBuf(l) {}
int sync() { return logBuf->pubsync(), buf->pubsync(); } int sync() override { return logBuf->pubsync(), buf->pubsync(); }
int overflow(int c) { return log(buf->sputc((char)c), "<< "); } int overflow(int c) override { return log(buf->sputc((char)c), "<< "); }
int underflow() { return buf->sgetc(); } int underflow() override { return buf->sgetc(); }
int uflow() { return log(buf->sbumpc(), ">> "); } int uflow() override { return log(buf->sbumpc(), ">> "); }
streambuf *buf, *logBuf; streambuf *buf, *logBuf;
@@ -185,3 +204,122 @@ void prefetch(void* addr) {
} }
#endif #endif
void prefetch2(void* addr) {
prefetch(addr);
prefetch((uint8_t*)addr + 64);
}
namespace WinProcGroup {
#ifndef _WIN32
void bindThisThread(size_t) {}
#else
/// get_group() retrieves logical processor information using Windows specific
/// API and returns the best group id for the thread with index idx. Original
/// code from Texel by Peter Österlund.
int get_group(size_t idx) {
int threads = 0;
int nodes = 0;
int cores = 0;
DWORD returnLength = 0;
DWORD byteOffset = 0;
// Early exit if the needed API is not available at runtime
HMODULE k32 = GetModuleHandle("Kernel32.dll");
auto fun1 = (fun1_t)GetProcAddress(k32, "GetLogicalProcessorInformationEx");
if (!fun1)
return -1;
// First call to get returnLength. We expect it to fail due to null buffer
if (fun1(RelationAll, nullptr, &returnLength))
return -1;
// Once we know returnLength, allocate the buffer
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr;
ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(returnLength);
// Second call, now we expect to succeed
if (!fun1(RelationAll, buffer, &returnLength))
{
free(buffer);
return -1;
}
while (ptr->Size > 0 && byteOffset + ptr->Size <= returnLength)
{
if (ptr->Relationship == RelationNumaNode)
nodes++;
else if (ptr->Relationship == RelationProcessorCore)
{
cores++;
threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1;
}
byteOffset += ptr->Size;
ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size);
}
free(buffer);
std::vector<int> groups;
// Run as many threads as possible on the same node until core limit is
// reached, then move on filling the next node.
for (int n = 0; n < nodes; n++)
for (int i = 0; i < cores / nodes; i++)
groups.push_back(n);
// In case a core has more than one logical processor (we assume 2) and we
// have still threads to allocate, then spread them evenly across available
// nodes.
for (int t = 0; t < threads - cores; t++)
groups.push_back(t % nodes);
// If we still have more threads than the total number of logical processors
// then return -1 and let the OS to decide what to do.
return idx < groups.size() ? groups[idx] : -1;
}
/// bindThisThread() set the group affinity of the current thread
void bindThisThread(size_t idx) {
// If OS already scheduled us on a different group than 0 then don't overwrite
// the choice, eventually we are one of many one-threaded processes running on
// some Windows NUMA hardware, for instance in fishtest. To make it simple,
// just check if running threads are below a threshold, in this case all this
// NUMA machinery is not needed.
if (Threads.size() < 8)
return;
// Use only local variables to be thread-safe
int group = get_group(idx);
if (group == -1)
return;
// Early exit if the needed API are not available at runtime
HMODULE k32 = GetModuleHandle("Kernel32.dll");
auto fun2 = (fun2_t)GetProcAddress(k32, "GetNumaNodeProcessorMaskEx");
auto fun3 = (fun3_t)GetProcAddress(k32, "SetThreadGroupAffinity");
if (!fun2 || !fun3)
return;
GROUP_AFFINITY affinity;
if (fun2(group, &affinity))
fun3(GetCurrentThread(), &affinity, nullptr);
}
#endif
} // namespace WinProcGroup

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -32,6 +32,7 @@
const std::string engine_info(bool to_uci = false); const std::string engine_info(bool to_uci = false);
void prefetch(void* addr); void prefetch(void* addr);
void prefetch2(void* addr);
void start_logger(const std::string& fname); void start_logger(const std::string& fname);
void dbg_hit_on(bool b); void dbg_hit_on(bool b);
@@ -98,6 +99,17 @@ public:
{ return T(rand64() & rand64() & rand64()); } { return T(rand64() & rand64() & rand64()); }
}; };
/// Under Windows it is not possible for a process to run on more than one
/// logical processor group. This usually means to be limited to use max 64
/// cores. To overcome this, some special platform specific API should be
/// called to set group affinity for each thread. Original code from Texel by
/// Peter Österlund.
namespace WinProcGroup {
void bindThisThread(size_t idx);
}
inline int stoi(const std::string& s) { inline int stoi(const std::string& s) {
std::stringstream ss(s); std::stringstream ss(s);
int result = 0; int result = 0;

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -80,7 +80,7 @@ namespace {
// Knight promotion is the only promotion that can give a direct check // Knight promotion is the only promotion that can give a direct check
// that's not already included in the queen promotion. // that's not already included in the queen promotion.
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ksq)) if (Type == QUIET_CHECKS && (PseudoAttacks[KNIGHT][to] & ksq))
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT); *moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
else else
(void)ksq; // Silence a warning under MSVC (void)ksq; // Silence a warning under MSVC
@@ -346,7 +346,7 @@ ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
if (pt == PAWN) if (pt == PAWN)
continue; // Will be generated together with direct checks continue; // Will be generated together with direct checks
Bitboard b = pos.attacks_from(Piece(pt), from) & ~pos.pieces(); Bitboard b = pos.attacks_from(pt, from) & ~pos.pieces();
if (pt == KING) if (pt == KING)
b &= ~PseudoAttacks[QUEEN][pos.square<KING>(~us)]; b &= ~PseudoAttacks[QUEEN][pos.square<KING>(~us)];

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,6 +21,8 @@
#ifndef MOVEGEN_H_INCLUDED #ifndef MOVEGEN_H_INCLUDED
#define MOVEGEN_H_INCLUDED #define MOVEGEN_H_INCLUDED
#include <algorithm>
#include "types.h" #include "types.h"
class Position; class Position;
@@ -36,10 +38,14 @@ enum GenType {
struct ExtMove { struct ExtMove {
Move move; Move move;
Value value; int value;
operator Move() const { return move; } operator Move() const { return move; }
void operator=(Move m) { move = m; } void operator=(Move m) { move = m; }
// Inhibit unwanted implicit conversions to Move
// with an ambiguity that yields to a compile error.
operator float() const;
}; };
inline bool operator<(const ExtMove& f, const ExtMove& s) { inline bool operator<(const ExtMove& f, const ExtMove& s) {
@@ -59,8 +65,7 @@ struct MoveList {
const ExtMove* end() const { return last; } const ExtMove* end() const { return last; }
size_t size() const { return last - moveList; } size_t size() const { return last - moveList; }
bool contains(Move move) const { bool contains(Move move) const {
for (const auto& m : *this) if (m == move) return true; return std::find(begin(), end(), move) != end();
return false;
} }
private: private:

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,7 +21,6 @@
#include <cassert> #include <cassert>
#include "movepick.h" #include "movepick.h"
#include "thread.h"
namespace { namespace {
@@ -34,15 +33,16 @@ namespace {
QSEARCH_RECAPTURES, QRECAPTURES QSEARCH_RECAPTURES, QRECAPTURES
}; };
// Our insertion sort, which is guaranteed to be stable, as it should be // partial_insertion_sort() sorts moves in descending order up to and including
void insertion_sort(ExtMove* begin, ExtMove* end) // a given limit. The order of moves smaller than the limit is left unspecified.
{ void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) {
ExtMove tmp, *p, *q;
for (p = begin + 1; p < end; ++p) for (ExtMove *sortedEnd = begin, *p = begin + 1; p < end; ++p)
if (p->value >= limit)
{ {
tmp = *p; ExtMove tmp = *p, *q;
for (q = p; q != begin && *(q-1) < tmp; --q) *p = *++sortedEnd;
for (q = sortedEnd; q != begin && *(q - 1) < tmp; --q)
*q = *(q - 1); *q = *(q - 1);
*q = tmp; *q = tmp;
} }
@@ -51,8 +51,8 @@ namespace {
// pick_best() finds the best move in the range (begin, end) and moves it to // pick_best() finds the best move in the range (begin, end) and moves it to
// the front. It's faster than sorting all the moves in advance when there // the front. It's faster than sorting all the moves in advance when there
// are few moves, e.g., the possible captures. // are few moves, e.g., the possible captures.
Move pick_best(ExtMove* begin, ExtMove* end) Move pick_best(ExtMove* begin, ExtMove* end) {
{
std::swap(*begin, *std::max_element(begin, end)); std::swap(*begin, *std::max_element(begin, end));
return *begin; return *begin;
} }
@@ -66,21 +66,22 @@ namespace {
/// search captures, promotions, and some checks) and how important good move /// search captures, promotions, and some checks) and how important good move
/// ordering is at the current node. /// ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, Search::Stack* s) /// MovePicker constructor for the main search
: pos(p), ss(s), depth(d) { MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
const PieceToHistory** ch, Move cm, Move* killers_p)
: pos(p), mainHistory(mh), contHistory(ch), countermove(cm),
killers{killers_p[0], killers_p[1]}, depth(d){
assert(d > DEPTH_ZERO); assert(d > DEPTH_ZERO);
Square prevSq = to_sq((ss-1)->currentMove);
countermove = pos.this_thread()->counterMoves[pos.piece_on(prevSq)][prevSq];
stage = pos.checkers() ? EVASION : MAIN_SEARCH; stage = pos.checkers() ? EVASION : MAIN_SEARCH;
ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
stage += (ttMove == MOVE_NONE); stage += (ttMove == MOVE_NONE);
} }
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, Square s) /// MovePicker constructor for quiescence search
: pos(p) { MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, Square s)
: pos(p), mainHistory(mh) {
assert(d <= DEPTH_ZERO); assert(d <= DEPTH_ZERO);
@@ -104,81 +105,57 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, Square s)
stage += (ttMove == MOVE_NONE); stage += (ttMove == MOVE_NONE);
} }
/// MovePicker constructor for ProbCut: we generate captures with SEE higher
/// than or equal to the given threshold.
MovePicker::MovePicker(const Position& p, Move ttm, Value th) MovePicker::MovePicker(const Position& p, Move ttm, Value th)
: pos(p), threshold(th) { : pos(p), threshold(th) {
assert(!pos.checkers()); assert(!pos.checkers());
stage = PROBCUT; stage = PROBCUT;
// In ProbCut we generate captures with SEE higher than the given threshold
ttMove = ttm ttMove = ttm
&& pos.pseudo_legal(ttm) && pos.pseudo_legal(ttm)
&& pos.capture(ttm) && pos.capture(ttm)
&& pos.see_ge(ttm, threshold + 1)? ttm : MOVE_NONE; && pos.see_ge(ttm, threshold) ? ttm : MOVE_NONE;
stage += (ttMove == MOVE_NONE); stage += (ttMove == MOVE_NONE);
} }
/// score() assigns a numerical value to each move in a list, used for sorting.
/// Captures are ordered by Most Valuable Victim (MVV), preferring captures
/// near our home rank. Quiets are ordered using the histories.
template<GenType Type>
void MovePicker::score() {
static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
/// score() assigns a numerical value to each move in a move list. The moves with
/// highest values will be picked first.
template<>
void MovePicker::score<CAPTURES>() {
// Winning and equal captures in the main search are ordered by MVV, preferring
// captures near our home rank. Surprisingly, this appears to perform slightly
// better than SEE-based move ordering: exchanging big pieces before capturing
// a hanging piece probably helps to reduce the subtree size.
// In the main search we want to push captures with negative SEE values to the
// badCaptures[] array, but instead of doing it now we delay until the move
// has been picked up, saving some SEE calls in case we get a cutoff.
for (auto& m : *this) for (auto& m : *this)
if (Type == CAPTURES)
m.value = PieceValue[MG][pos.piece_on(to_sq(m))] m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
- Value(200 * relative_rank(pos.side_to_move(), to_sq(m))); - Value(200 * relative_rank(pos.side_to_move(), to_sq(m)));
}
template<> else if (Type == QUIETS)
void MovePicker::score<QUIETS>() { m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
+ (*contHistory[0])[pos.moved_piece(m)][to_sq(m)]
+ (*contHistory[1])[pos.moved_piece(m)][to_sq(m)]
+ (*contHistory[3])[pos.moved_piece(m)][to_sq(m)];
const HistoryStats& history = pos.this_thread()->history; else // Type == EVASIONS
const FromToStats& fromTo = pos.this_thread()->fromTo; {
const CounterMoveStats* cm = (ss-1)->counterMoves;
const CounterMoveStats* fm = (ss-2)->counterMoves;
const CounterMoveStats* f2 = (ss-4)->counterMoves;
Color c = pos.side_to_move();
for (auto& m : *this)
m.value = history[pos.moved_piece(m)][to_sq(m)]
+ (cm ? (*cm)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO)
+ (fm ? (*fm)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO)
+ (f2 ? (*f2)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO)
+ fromTo.get(c, m);
}
template<>
void MovePicker::score<EVASIONS>() {
// Try captures ordered by MVV/LVA, then non-captures ordered by history value
const HistoryStats& history = pos.this_thread()->history;
const FromToStats& fromTo = pos.this_thread()->fromTo;
Color c = pos.side_to_move();
for (auto& m : *this)
if (pos.capture(m)) if (pos.capture(m))
m.value = PieceValue[MG][pos.piece_on(to_sq(m))] m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
- Value(type_of(pos.moved_piece(m))) + HistoryStats::Max; - Value(type_of(pos.moved_piece(m)));
else else
m.value = history[pos.moved_piece(m)][to_sq(m)] + fromTo.get(c, m); m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] - (1 << 28);
}
} }
/// next_move() is the most important method of the MovePicker class. It returns /// next_move() is the most important method of the MovePicker class. It returns
/// a new pseudo legal move every time it is called, until there are no more moves /// a new pseudo legal move every time it is called, until there are no more moves
/// left. It picks the move with the biggest value from a list of generated moves /// left. It picks the move with the biggest value from a list of generated moves
/// taking care not to return the ttMove if it has already been searched. /// taking care not to return the ttMove if it has already been searched.
Move MovePicker::next_move() { Move MovePicker::next_move(bool skipQuiets) {
Move move; Move move;
@@ -194,6 +171,7 @@ Move MovePicker::next_move() {
endMoves = generate<CAPTURES>(pos, cur); endMoves = generate<CAPTURES>(pos, cur);
score<CAPTURES>(); score<CAPTURES>();
++stage; ++stage;
/* fallthrough */
case GOOD_CAPTURES: case GOOD_CAPTURES:
while (cur < endMoves) while (cur < endMoves)
@@ -201,7 +179,7 @@ Move MovePicker::next_move() {
move = pick_best(cur++, endMoves); move = pick_best(cur++, endMoves);
if (move != ttMove) if (move != ttMove)
{ {
if (pos.see_ge(move, VALUE_ZERO)) if (pos.see_ge(move))
return move; return move;
// Losing capture, move it to the beginning of the array // Losing capture, move it to the beginning of the array
@@ -210,58 +188,59 @@ Move MovePicker::next_move() {
} }
++stage; ++stage;
move = ss->killers[0]; // First killer move move = killers[0]; // First killer move
if ( move != MOVE_NONE if ( move != MOVE_NONE
&& move != ttMove && move != ttMove
&& pos.pseudo_legal(move) && pos.pseudo_legal(move)
&& !pos.capture(move)) && !pos.capture(move))
return move; return move;
/* fallthrough */
case KILLERS: case KILLERS:
++stage; ++stage;
move = ss->killers[1]; // Second killer move move = killers[1]; // Second killer move
if ( move != MOVE_NONE if ( move != MOVE_NONE
&& move != ttMove && move != ttMove
&& pos.pseudo_legal(move) && pos.pseudo_legal(move)
&& !pos.capture(move)) && !pos.capture(move))
return move; return move;
/* fallthrough */
case COUNTERMOVE: case COUNTERMOVE:
++stage; ++stage;
move = countermove; move = countermove;
if ( move != MOVE_NONE if ( move != MOVE_NONE
&& move != ttMove && move != ttMove
&& move != ss->killers[0] && move != killers[0]
&& move != ss->killers[1] && move != killers[1]
&& pos.pseudo_legal(move) && pos.pseudo_legal(move)
&& !pos.capture(move)) && !pos.capture(move))
return move; return move;
/* fallthrough */
case QUIET_INIT: case QUIET_INIT:
cur = endBadCaptures; cur = endBadCaptures;
endMoves = generate<QUIETS>(pos, cur); endMoves = generate<QUIETS>(pos, cur);
score<QUIETS>(); score<QUIETS>();
if (depth < 3 * ONE_PLY) partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY);
{
ExtMove* goodQuiet = std::partition(cur, endMoves, [](const ExtMove& m)
{ return m.value > VALUE_ZERO; });
insertion_sort(cur, goodQuiet);
} else
insertion_sort(cur, endMoves);
++stage; ++stage;
/* fallthrough */
case QUIET: case QUIET:
while (cur < endMoves) while ( cur < endMoves
&& (!skipQuiets || cur->value >= VALUE_ZERO))
{ {
move = *cur++; move = *cur++;
if ( move != ttMove if ( move != ttMove
&& move != ss->killers[0] && move != killers[0]
&& move != ss->killers[1] && move != killers[1]
&& move != countermove) && move != countermove)
return move; return move;
} }
++stage; ++stage;
cur = moves; // Point to beginning of bad captures cur = moves; // Point to beginning of bad captures
/* fallthrough */
case BAD_CAPTURES: case BAD_CAPTURES:
if (cur < endBadCaptures) if (cur < endBadCaptures)
@@ -273,6 +252,7 @@ Move MovePicker::next_move() {
endMoves = generate<EVASIONS>(pos, cur); endMoves = generate<EVASIONS>(pos, cur);
score<EVASIONS>(); score<EVASIONS>();
++stage; ++stage;
/* fallthrough */
case ALL_EVASIONS: case ALL_EVASIONS:
while (cur < endMoves) while (cur < endMoves)
@@ -288,13 +268,14 @@ Move MovePicker::next_move() {
endMoves = generate<CAPTURES>(pos, cur); endMoves = generate<CAPTURES>(pos, cur);
score<CAPTURES>(); score<CAPTURES>();
++stage; ++stage;
/* fallthrough */
case PROBCUT_CAPTURES: case PROBCUT_CAPTURES:
while (cur < endMoves) while (cur < endMoves)
{ {
move = pick_best(cur++, endMoves); move = pick_best(cur++, endMoves);
if ( move != ttMove if ( move != ttMove
&& pos.see_ge(move, threshold + 1)) && pos.see_ge(move, threshold))
return move; return move;
} }
break; break;
@@ -304,6 +285,7 @@ Move MovePicker::next_move() {
endMoves = generate<CAPTURES>(pos, cur); endMoves = generate<CAPTURES>(pos, cur);
score<CAPTURES>(); score<CAPTURES>();
++stage; ++stage;
/* fallthrough */
case QCAPTURES_1: case QCAPTURES_2: case QCAPTURES_1: case QCAPTURES_2:
while (cur < endMoves) while (cur < endMoves)
@@ -317,6 +299,7 @@ Move MovePicker::next_move() {
cur = moves; cur = moves;
endMoves = generate<QUIET_CHECKS>(pos, cur); endMoves = generate<QUIET_CHECKS>(pos, cur);
++stage; ++stage;
/* fallthrough */
case QCHECKS: case QCHECKS:
while (cur < endMoves) while (cur < endMoves)
@@ -332,6 +315,7 @@ Move MovePicker::next_move() {
endMoves = generate<CAPTURES>(pos, cur); endMoves = generate<CAPTURES>(pos, cur);
score<CAPTURES>(); score<CAPTURES>();
++stage; ++stage;
/* fallthrough */
case QRECAPTURES: case QRECAPTURES:
while (cur < endMoves) while (cur < endMoves)

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,68 +21,66 @@
#ifndef MOVEPICK_H_INCLUDED #ifndef MOVEPICK_H_INCLUDED
#define MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED
#include <algorithm> // For std::max #include <array>
#include <cstring> // For std::memset
#include "movegen.h" #include "movegen.h"
#include "position.h" #include "position.h"
#include "types.h" #include "types.h"
/// StatBoards is a generic 2-dimensional array used to store various statistics
template<int Size1, int Size2, typename T = int16_t>
struct StatBoards : public std::array<std::array<T, Size2>, Size1> {
/// The Stats struct stores moves statistics. According to the template parameter void fill(const T& v) {
/// the class can store History and Countermoves. History records how often T* p = &(*this)[0][0];
/// different moves have been successful or unsuccessful during the current search std::fill(p, p + sizeof(*this) / sizeof(*p), v);
/// and is used for reduction and move ordering decisions.
/// Countermoves store the move that refute a previous one. Entries are stored
/// using only the moving piece and destination square, hence two moves with
/// different origin but same destination and piece will be considered identical.
template<typename T, bool CM = false>
struct Stats {
static const Value Max = Value(1 << 28);
const T* operator[](Piece pc) const { return table[pc]; }
T* operator[](Piece pc) { return table[pc]; }
void clear() { std::memset(table, 0, sizeof(table)); }
void update(Piece pc, Square to, Move m) { table[pc][to] = m; }
void update(Piece pc, Square to, Value v) {
if (abs(int(v)) >= 324)
return;
table[pc][to] -= table[pc][to] * abs(int(v)) / (CM ? 936 : 324);
table[pc][to] += int(v) * 32;
} }
private: void update(T& entry, int bonus, const int D) {
T table[PIECE_NB][SQUARE_NB];
};
typedef Stats<Move> MoveStats; assert(abs(bonus) <= D); // Ensure range is [-32 * D, 32 * D]
typedef Stats<Value, false> HistoryStats; assert(abs(32 * D) < INT16_MAX); // Ensure we don't overflow
typedef Stats<Value, true> CounterMoveStats;
typedef Stats<CounterMoveStats> CounterMoveHistoryStats;
struct FromToStats { entry += bonus * 32 - entry * abs(bonus) / D;
Value get(Color c, Move m) const { return table[c][from_sq(m)][to_sq(m)]; } assert(abs(entry) <= 32 * D);
void clear() { std::memset(table, 0, sizeof(table)); }
void update(Color c, Move m, Value v) {
if (abs(int(v)) >= 324)
return;
Square from = from_sq(m);
Square to = to_sq(m);
table[c][from][to] -= table[c][from][to] * abs(int(v)) / 324;
table[c][from][to] += int(v) * 32;
} }
private:
Value table[COLOR_NB][SQUARE_NB][SQUARE_NB];
}; };
/// ButterflyBoards are 2 tables (one for each color) indexed by the move's from
/// and to squares, see chessprogramming.wikispaces.com/Butterfly+Boards
typedef StatBoards<COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyBoards;
/// PieceToBoards are addressed by a move's [piece][to] information
typedef StatBoards<PIECE_NB, SQUARE_NB> PieceToBoards;
/// ButterflyHistory records how often quiet moves have been successful or
/// unsuccessful during the current search, and is used for reduction and move
/// ordering decisions. It uses ButterflyBoards as backing store.
struct ButterflyHistory : public ButterflyBoards {
void update(Color c, Move m, int bonus) {
StatBoards::update((*this)[c][from_to(m)], bonus, 324);
}
};
/// PieceToHistory is like ButterflyHistory, but is based on PieceToBoards
struct PieceToHistory : public PieceToBoards {
void update(Piece pc, Square to, int bonus) {
StatBoards::update((*this)[pc][to], bonus, 936);
}
};
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
/// move, see chessprogramming.wikispaces.com/Countermove+Heuristic
typedef StatBoards<PIECE_NB, SQUARE_NB, Move> CounterMoveHistory;
/// ContinuationHistory is the history of a given pair of moves, usually the
/// current one given a previous one. History table is based on PieceToBoards
/// instead of ButterflyBoards.
typedef StatBoards<PIECE_NB, SQUARE_NB, PieceToHistory> ContinuationHistory;
/// MovePicker class is used to pick one pseudo legal move at a time from the /// MovePicker class is used to pick one pseudo legal move at a time from the
/// current position. The most important method is next_move(), which returns a /// current position. The most important method is next_move(), which returns a
@@ -90,18 +88,15 @@ private:
/// when MOVE_NONE is returned. In order to improve the efficiency of the alpha /// when MOVE_NONE is returned. In order to improve the efficiency of the alpha
/// beta algorithm, MovePicker attempts to return the moves which are most likely /// beta algorithm, MovePicker attempts to return the moves which are most likely
/// to get a cut-off first. /// to get a cut-off first.
namespace Search { struct Stack; }
class MovePicker { class MovePicker {
public: public:
MovePicker(const MovePicker&) = delete; MovePicker(const MovePicker&) = delete;
MovePicker& operator=(const MovePicker&) = delete; MovePicker& operator=(const MovePicker&) = delete;
MovePicker(const Position&, Move, Value); MovePicker(const Position&, Move, Value);
MovePicker(const Position&, Move, Depth, Square); MovePicker(const Position&, Move, Depth, const ButterflyHistory*, Square);
MovePicker(const Position&, Move, Depth, Search::Stack*); MovePicker(const Position&, Move, Depth, const ButterflyHistory*, const PieceToHistory**, Move, Move*);
Move next_move(bool skipQuiets = false);
Move next_move();
private: private:
template<GenType> void score(); template<GenType> void score();
@@ -109,14 +104,14 @@ private:
ExtMove* end() { return endMoves; } ExtMove* end() { return endMoves; }
const Position& pos; const Position& pos;
const Search::Stack* ss; const ButterflyHistory* mainHistory;
Move countermove; const PieceToHistory** contHistory;
Depth depth; Move ttMove, countermove, killers[2];
Move ttMove; ExtMove *cur, *endMoves, *endBadCaptures;
int stage;
Square recaptureSquare; Square recaptureSquare;
Value threshold; Value threshold;
int stage; Depth depth;
ExtMove *cur, *endMoves, *endBadCaptures;
ExtMove moves[MAX_MOVES]; ExtMove moves[MAX_MOVES];
}; };

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -32,16 +32,13 @@ namespace {
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
// Isolated pawn penalty by opposed flag // Isolated pawn penalty by opposed flag
const Score Isolated[2] = { S(45, 40), S(30, 27) }; const Score Isolated[] = { S(27, 30), S(13, 18) };
// Backward pawn penalty by opposed flag // Backward pawn penalty by opposed flag
const Score Backward[2] = { S(56, 33), S(41, 19) }; const Score Backward[] = { S(40, 26), S(24, 12) };
// Unsupported pawn penalty for pawns which are neither isolated or backward // Connected pawn bonus by opposed, phalanx, #support and rank
const Score Unsupported = S(17, 8); Score Connected[2][2][3][RANK_NB];
// Connected pawn bonus by opposed, phalanx, twice supported and rank
Score Connected[2][2][2][RANK_NB];
// Doubled pawn penalty // Doubled pawn penalty
const Score Doubled = S(18, 38); const Score Doubled = S(18, 38);
@@ -52,32 +49,39 @@ namespace {
S(17, 16), S(33, 32), S(0, 0), S(0, 0) S(17, 16), S(33, 32), S(0, 0), S(0, 0)
}; };
// Weakness of our pawn shelter in front of the king by [distance from edge][rank] // Weakness of our pawn shelter in front of the king by [isKingFile][distance from edge][rank].
const Value ShelterWeakness[][RANK_NB] = { // RANK_1 = 0 is used for files where we have no pawns or our pawn is behind our king.
{ V( 97), V(21), V(26), V(51), V(87), V( 89), V( 99) }, const Value ShelterWeakness[][int(FILE_NB) / 2][RANK_NB] = {
{ V(120), V( 0), V(28), V(76), V(88), V(103), V(104) }, { { V( 97), V(17), V( 9), V(44), V( 84), V( 87), V( 99) }, // Not On King file
{ V(101), V( 7), V(54), V(78), V(77), V( 92), V(101) }, { V(106), V( 6), V(33), V(86), V( 87), V(104), V(112) },
{ V( 80), V(11), V(44), V(68), V(87), V( 90), V(119) } { V(101), V( 2), V(65), V(98), V( 58), V( 89), V(115) },
{ V( 73), V( 7), V(54), V(73), V( 84), V( 83), V(111) } },
{ { V(104), V(20), V( 6), V(27), V( 86), V( 93), V( 82) }, // On King file
{ V(123), V( 9), V(34), V(96), V(112), V( 88), V( 75) },
{ V(120), V(25), V(65), V(91), V( 66), V( 78), V(117) },
{ V( 81), V( 2), V(47), V(63), V( 94), V( 93), V(104) } }
}; };
// Danger of enemy pawns moving toward our king by [type][distance from edge][rank] // Danger of enemy pawns moving toward our king by [type][distance from edge][rank].
// For the unopposed and unblocked cases, RANK_1 = 0 is used when opponent has
// no pawn on the given file, or their pawn is behind our king.
const Value StormDanger[][4][RANK_NB] = { const Value StormDanger[][4][RANK_NB] = {
{ { V( 0), V( 67), V( 134), V(38), V(32) }, { { V( 0), V(-290), V(-274), V(57), V(41) }, // BlockedByKing
{ V( 0), V( 57), V( 139), V(37), V(22) }, { V( 0), V( 60), V( 144), V(39), V(13) },
{ V( 0), V( 43), V( 115), V(43), V(27) }, { V( 0), V( 65), V( 141), V(41), V(34) },
{ V( 0), V( 68), V( 124), V(57), V(32) } }, { V( 0), V( 53), V( 127), V(56), V(14) } },
{ { V(20), V( 43), V( 100), V(56), V(20) }, { { V( 4), V( 73), V( 132), V(46), V(31) }, // Unopposed
{ V(23), V( 20), V( 98), V(40), V(15) }, { V( 1), V( 64), V( 143), V(26), V(13) },
{ V(23), V( 39), V( 103), V(36), V(18) }, { V( 1), V( 47), V( 110), V(44), V(24) },
{ V(28), V( 19), V( 108), V(42), V(26) } }, { V( 0), V( 72), V( 127), V(50), V(31) } },
{ { V( 0), V( 0), V( 75), V(14), V( 2) }, { { V( 0), V( 0), V( 79), V(23), V( 1) }, // BlockedByPawn
{ V( 0), V( 0), V( 150), V(30), V( 4) }, { V( 0), V( 0), V( 148), V(27), V( 2) },
{ V( 0), V( 0), V( 160), V(22), V( 5) }, { V( 0), V( 0), V( 161), V(16), V( 1) },
{ V( 0), V( 0), V( 166), V(24), V(13) } }, { V( 0), V( 0), V( 171), V(22), V(15) } },
{ { V( 0), V(-283), V(-281), V(57), V(31) }, { { V(22), V( 45), V( 104), V(62), V( 6) }, // Unblocked
{ V( 0), V( 58), V( 141), V(39), V(18) }, { V(31), V( 30), V( 99), V(39), V(19) },
{ V( 0), V( 65), V( 142), V(48), V(32) }, { V(23), V( 29), V( 96), V(41), V(15) },
{ V( 0), V( 60), V( 126), V(51), V(19) } } { V(21), V( 23), V( 116), V(41), V(15) } }
}; };
// Max bonus for king safety. Corresponds to start position with all the pawns // Max bonus for king safety. Corresponds to start position with all the pawns
@@ -96,11 +100,11 @@ namespace {
const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
Bitboard b, neighbours, stoppers, doubled, supported, phalanx; Bitboard b, neighbours, stoppers, doubled, supported, phalanx;
Bitboard lever, leverPush;
Square s; Square s;
bool opposed, lever, connected, backward; bool opposed, backward;
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
const Square* pl = pos.squares<PAWN>(Us); const Square* pl = pos.squares<PAWN>(Us);
const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)];
Bitboard ourPawns = pos.pieces( Us, PAWN); Bitboard ourPawns = pos.pieces( Us, PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN);
@@ -123,14 +127,14 @@ namespace {
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s); e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
// Flag the pawn // Flag the pawn
opposed = theirPawns & forward_bb(Us, s); opposed = theirPawns & forward_file_bb(Us, s);
stoppers = theirPawns & passed_pawn_mask(Us, s); stoppers = theirPawns & passed_pawn_mask(Us, s);
lever = theirPawns & pawnAttacksBB[s]; lever = theirPawns & PawnAttacks[Us][s];
doubled = ourPawns & (s + Up); leverPush = theirPawns & PawnAttacks[Us][s + Up];
doubled = ourPawns & (s - Up);
neighbours = ourPawns & adjacent_files_bb(f); neighbours = ourPawns & adjacent_files_bb(f);
phalanx = neighbours & rank_bb(s); phalanx = neighbours & rank_bb(s);
supported = neighbours & rank_bb(s - Up); supported = neighbours & rank_bb(s - Up);
connected = supported | phalanx;
// A pawn is backward when it is behind all pawns of the same color on the // A pawn is backward when it is behind all pawns of the same color on the
// adjacent files and cannot be safely advanced. // adjacent files and cannot be safely advanced.
@@ -146,28 +150,39 @@ namespace {
// stopper on adjacent file which controls the way to that rank. // stopper on adjacent file which controls the way to that rank.
backward = (b | shift<Up>(b & adjacent_files_bb(f))) & stoppers; backward = (b | shift<Up>(b & adjacent_files_bb(f))) & stoppers;
assert(!backward || !(pawn_attack_span(Them, s + Up) & neighbours)); assert(!(backward && (forward_ranks_bb(Them, s + Up) & neighbours)));
} }
// Passed pawns will be properly scored in evaluation because we need // Passed pawns will be properly scored in evaluation because we need
// full attack info to evaluate them. // full attack info to evaluate them. Include also not passed pawns
if (!stoppers && !(ourPawns & forward_bb(Us, s))) // which could become passed after one or two pawn pushes when are
// not attacked more times than defended.
if ( !(stoppers ^ lever ^ leverPush)
&& !(ourPawns & forward_file_bb(Us, s))
&& popcount(supported) >= popcount(lever)
&& popcount(phalanx) >= popcount(leverPush))
e->passedPawns[Us] |= s; e->passedPawns[Us] |= s;
else if ( stoppers == SquareBB[s + Up]
&& relative_rank(Us, s) >= RANK_5)
{
b = shift<Up>(supported) & ~theirPawns;
while (b)
if (!more_than_one(theirPawns & PawnAttacks[Us][pop_lsb(&b)]))
e->passedPawns[Us] |= s;
}
// Score this pawn // Score this pawn
if (!neighbours) if (supported | phalanx)
score += Connected[opposed][!!phalanx][popcount(supported)][relative_rank(Us, s)];
else if (!neighbours)
score -= Isolated[opposed]; score -= Isolated[opposed];
else if (backward) else if (backward)
score -= Backward[opposed]; score -= Backward[opposed];
else if (!supported) if (doubled && !supported)
score -= Unsupported;
if (connected)
score += Connected[opposed][!!phalanx][more_than_one(supported)][relative_rank(Us, s)];
if (doubled)
score -= Doubled; score -= Doubled;
if (lever) if (lever)
@@ -187,16 +202,17 @@ namespace Pawns {
void init() { void init() {
static const int Seed[RANK_NB] = { 0, 8, 19, 13, 71, 94, 169, 324 }; static const int Seed[RANK_NB] = { 0, 13, 24, 18, 76, 100, 175, 330 };
for (int opposed = 0; opposed <= 1; ++opposed) for (int opposed = 0; opposed <= 1; ++opposed)
for (int phalanx = 0; phalanx <= 1; ++phalanx) for (int phalanx = 0; phalanx <= 1; ++phalanx)
for (int apex = 0; apex <= 1; ++apex) for (int support = 0; support <= 2; ++support)
for (Rank r = RANK_2; r < RANK_8; ++r) for (Rank r = RANK_2; r < RANK_8; ++r)
{ {
int v = (Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0)) >> opposed; int v = 17 * support;
v += (apex ? v / 2 : 0); v += (Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0)) >> opposed;
Connected[opposed][phalanx][apex][r] = make_score(v, v * 5 / 8);
Connected[opposed][phalanx][support][r] = make_score(v, v * (r - 2) / 4);
} }
} }
@@ -223,16 +239,16 @@ Entry* probe(const Position& pos) {
/// Entry::shelter_storm() calculates shelter and storm penalties for the file /// Entry::shelter_storm() calculates shelter and storm penalties for the file
/// the king is on, as well as the two adjacent files. /// the king is on, as well as the two closest files.
template<Color Us> template<Color Us>
Value Entry::shelter_storm(const Position& pos, Square ksq) { Value Entry::shelter_storm(const Position& pos, Square ksq) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
enum { NoFriendlyPawn, Unblocked, BlockedByPawn, BlockedByKing }; enum { BlockedByKing, Unopposed, BlockedByPawn, Unblocked };
Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq)); Bitboard b = pos.pieces(PAWN) & (forward_ranks_bb(Us, ksq) | rank_bb(ksq));
Bitboard ourPawns = b & pos.pieces(Us); Bitboard ourPawns = b & pos.pieces(Us);
Bitboard theirPawns = b & pos.pieces(Them); Bitboard theirPawns = b & pos.pieces(Them);
Value safety = MaxSafetyBonus; Value safety = MaxSafetyBonus;
@@ -246,12 +262,13 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) {
b = theirPawns & file_bb(f); b = theirPawns & file_bb(f);
Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
safety -= ShelterWeakness[std::min(f, FILE_H - f)][rkUs] int d = std::min(f, FILE_H - f);
safety -= ShelterWeakness[f == file_of(ksq)][d][rkUs]
+ StormDanger + StormDanger
[f == file_of(ksq) && rkThem == relative_rank(Us, ksq) + 1 ? BlockedByKing : [f == file_of(ksq) && rkThem == relative_rank(Us, ksq) + 1 ? BlockedByKing :
rkUs == RANK_1 ? NoFriendlyPawn : rkUs == RANK_1 ? Unopposed :
rkThem == rkUs + 1 ? BlockedByPawn : Unblocked] rkThem == rkUs + 1 ? BlockedByPawn : Unblocked]
[std::min(f, FILE_H - f)][rkThem]; [d][rkThem];
} }
return safety; return safety;

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -32,6 +32,7 @@
#include "thread.h" #include "thread.h"
#include "tt.h" #include "tt.h"
#include "uci.h" #include "uci.h"
#include "syzygy/tbprobe.h"
using std::string; using std::string;
@@ -44,14 +45,17 @@ namespace Zobrist {
Key psq[PIECE_NB][SQUARE_NB]; Key psq[PIECE_NB][SQUARE_NB];
Key enpassant[FILE_NB]; Key enpassant[FILE_NB];
Key castling[CASTLING_RIGHT_NB]; Key castling[CASTLING_RIGHT_NB];
Key side; Key side, noPawns;
} }
namespace { namespace {
const string PieceToChar(" PNBRQK pnbrqk"); const string PieceToChar(" PNBRQK pnbrqk");
// min_attacker() is a helper function used by see() to locate the least const Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING };
// min_attacker() is a helper function used by see_ge() to locate the least
// valuable attacker for the side to move, remove the attacker we just found // valuable attacker for the side to move, remove the attacker we just found
// from the bitboards and scan for new X-ray attacks behind it. // from the bitboards and scan for new X-ray attacks behind it.
@@ -98,11 +102,25 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) {
} }
os << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase os << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase
<< std::setfill('0') << std::setw(16) << pos.key() << std::dec << "\nCheckers: "; << std::setfill('0') << std::setw(16) << pos.key()
<< std::setfill(' ') << std::dec << "\nCheckers: ";
for (Bitboard b = pos.checkers(); b; ) for (Bitboard b = pos.checkers(); b; )
os << UCI::square(pop_lsb(&b)) << " "; os << UCI::square(pop_lsb(&b)) << " ";
if ( int(Tablebases::MaxCardinality) >= popcount(pos.pieces())
&& !pos.can_castle(ANY_CASTLING))
{
StateInfo st;
Position p;
p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread());
Tablebases::ProbeState s1, s2;
Tablebases::WDLScore wdl = Tablebases::probe_wdl(p, &s1);
int dtz = Tablebases::probe_dtz(p, &s2);
os << "\nTablebases WDL: " << std::setw(4) << wdl << " (" << s1 << ")"
<< "\nTablebases DTZ: " << std::setw(4) << dtz << " (" << s2 << ")";
}
return os; return os;
} }
@@ -133,6 +151,7 @@ void Position::init() {
} }
Zobrist::side = rng.rand<Key>(); Zobrist::side = rng.rand<Key>();
Zobrist::noPawns = rng.rand<Key>();
} }
@@ -164,8 +183,9 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
4) En passant target square (in algebraic notation). If there's no en passant 4) En passant target square (in algebraic notation). If there's no en passant
target square, this is "-". If a pawn has just made a 2-square move, this target square, this is "-". If a pawn has just made a 2-square move, this
is the position "behind" the pawn. This is recorded regardless of whether is the position "behind" the pawn. This is recorded only if there is a pawn
there is a pawn in position to make an en passant capture. in position to make an en passant capture, and if there really is a pawn
that might have advanced two squares.
5) Halfmove clock. This is the number of halfmoves since the last pawn advance 5) Halfmove clock. This is the number of halfmoves since the last pawn advance
or capture. This is used to determine if a draw can be claimed under the or capture. This is used to determine if a draw can be claimed under the
@@ -242,7 +262,8 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
{ {
st->epSquare = make_square(File(col - 'a'), Rank(row - '1')); st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
if (!(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))) if ( !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))
|| !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))))
st->epSquare = SQ_NONE; st->epSquare = SQ_NONE;
} }
else else
@@ -317,7 +338,8 @@ void Position::set_check_info(StateInfo* si) const {
void Position::set_state(StateInfo* si) const { void Position::set_state(StateInfo* si) const {
si->key = si->pawnKey = si->materialKey = 0; si->key = si->materialKey = 0;
si->pawnKey = Zobrist::noPawns;
si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO; si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO;
si->psq = SCORE_ZERO; si->psq = SCORE_ZERO;
si->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove); si->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove);
@@ -357,6 +379,27 @@ void Position::set_state(StateInfo* si) const {
} }
/// Position::set() is an overload to initialize the position object with
/// the given endgame code string like "KBPKN". It is mainly a helper to
/// get the material key out of an endgame code.
Position& Position::set(const string& code, Color c, StateInfo* si) {
assert(code.length() > 0 && code.length() < 8);
assert(code[0] == 'K');
string sides[] = { code.substr(code.find('K', 1)), // Weak
code.substr(0, code.find('K', 1)) }; // Strong
std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower);
string fenStr = "8/" + sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/"
+ sides[1] + char(8 - sides[1].length() + '0') + "/8 w - - 0 10";
return set(fenStr, false, si, nullptr);
}
/// Position::fen() returns a FEN representation of the position. In case of /// Position::fen() returns a FEN representation of the position. In case of
/// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function. /// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function.
@@ -407,19 +450,6 @@ const string Position::fen() const {
} }
/// Position::game_phase() calculates the game phase interpolating total non-pawn
/// material between endgame and midgame limits.
Phase Position::game_phase() const {
Value npm = st->nonPawnMaterial[WHITE] + st->nonPawnMaterial[BLACK];
npm = std::max(EndgameLimit, std::min(npm, MidgameLimit));
return Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
}
/// Position::slider_blockers() returns a bitboard of all the pieces (both colors) /// Position::slider_blockers() returns a bitboard of all the pieces (both colors)
/// that are blocking attacks on the square 's' from 'sliders'. A piece blocks a /// that are blocking attacks on the square 's' from 'sliders'. A piece blocks a
/// slider if removing that piece from the board would result in a position where /// slider if removing that piece from the board would result in a position where
@@ -554,7 +584,7 @@ bool Position::pseudo_legal(const Move m) const {
&& empty(to - pawn_push(us)))) && empty(to - pawn_push(us))))
return false; return false;
} }
else if (!(attacks_from(pc, from) & to)) else if (!(attacks_from(type_of(pc), from) & to))
return false; return false;
// Evasions generator already takes care to avoid some kind of illegal moves // Evasions generator already takes care to avoid some kind of illegal moves
@@ -607,7 +637,7 @@ bool Position::gives_check(Move m) const {
return false; return false;
case PROMOTION: case PROMOTION:
return attacks_bb(Piece(promotion_type(m)), to, pieces() ^ from) & square<KING>(~sideToMove); return attacks_bb(promotion_type(m), to, pieces() ^ from) & square<KING>(~sideToMove);
// En passant capture with check? We have already handled the case // En passant capture with check? We have already handled the case
// of direct checks and ordinary discovered check, so the only case we // of direct checks and ordinary discovered check, so the only case we
@@ -647,7 +677,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
assert(is_ok(m)); assert(is_ok(m));
assert(&newSt != st); assert(&newSt != st);
++nodes; thisThread->nodes.fetch_add(1, std::memory_order_relaxed);
Key k = st->key ^ Zobrist::side; Key k = st->key ^ Zobrist::side;
// Copy some fields of the old state to our new StateInfo object except the // Copy some fields of the old state to our new StateInfo object except the
@@ -786,7 +816,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
// Update pawn hash key and prefetch access to pawnsTable // Update pawn hash key and prefetch access to pawnsTable
st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
prefetch(thisThread->pawnsTable[st->pawnKey]); prefetch2(thisThread->pawnsTable[st->pawnKey]);
// Reset rule 50 draw counter // Reset rule 50 draw counter
st->rule50 = 0; st->rule50 = 0;
@@ -956,18 +986,16 @@ Key Position::key_after(Move m) const {
/// Position::see_ge (Static Exchange Evaluation Greater or Equal) tests if the /// Position::see_ge (Static Exchange Evaluation Greater or Equal) tests if the
/// SEE value of move is greater or equal to the given value. We'll use an /// SEE value of move is greater or equal to the given threshold. We'll use an
/// algorithm similar to alpha-beta pruning with a null window. /// algorithm similar to alpha-beta pruning with a null window.
bool Position::see_ge(Move m, Value v) const { bool Position::see_ge(Move m, Value threshold) const {
assert(is_ok(m)); assert(is_ok(m));
// Castling moves are implemented as king capturing the rook so cannot be // Only deal with normal moves, assume others pass a simple see
// handled correctly. Simply assume the SEE value is VALUE_ZERO that is always if (type_of(m) != NORMAL)
// correct unless in the rare case the rook ends up under attack. return VALUE_ZERO >= threshold;
if (type_of(m) == CASTLING)
return VALUE_ZERO >= v;
Square from = from_sq(m), to = to_sq(m); Square from = from_sq(m), to = to_sq(m);
PieceType nextVictim = type_of(piece_on(from)); PieceType nextVictim = type_of(piece_on(from));
@@ -975,30 +1003,18 @@ bool Position::see_ge(Move m, Value v) const {
Value balance; // Values of the pieces taken by us minus opponent's ones Value balance; // Values of the pieces taken by us minus opponent's ones
Bitboard occupied, stmAttackers; Bitboard occupied, stmAttackers;
if (type_of(m) == ENPASSANT)
{
occupied = SquareBB[to - pawn_push(~stm)]; // Remove the captured pawn
balance = PieceValue[MG][PAWN];
}
else
{
balance = PieceValue[MG][piece_on(to)]; balance = PieceValue[MG][piece_on(to)];
occupied = 0;
}
if (balance < v) if (balance < threshold)
return false; return false;
if (nextVictim == KING)
return true;
balance -= PieceValue[MG][nextVictim]; balance -= PieceValue[MG][nextVictim];
if (balance >= v) if (balance >= threshold) // Always true if nextVictim == KING
return true; return true;
bool relativeStm = true; // True if the opponent is to move bool relativeStm = true; // True if the opponent is to move
occupied ^= pieces() ^ from ^ to; occupied = pieces() ^ from ^ to;
// Find all attackers to the destination square, with the moving piece removed, // Find all attackers to the destination square, with the moving piece removed,
// but possibly an X-ray attacker added behind it. // but possibly an X-ray attacker added behind it.
@@ -1027,7 +1043,7 @@ bool Position::see_ge(Move m, Value v) const {
relativeStm = !relativeStm; relativeStm = !relativeStm;
if (relativeStm == (balance >= v)) if (relativeStm == (balance >= threshold))
return relativeStm; return relativeStm;
stm = ~stm; stm = ~stm;
@@ -1038,18 +1054,29 @@ bool Position::see_ge(Move m, Value v) const {
/// Position::is_draw() tests whether the position is drawn by 50-move rule /// Position::is_draw() tests whether the position is drawn by 50-move rule
/// or by repetition. It does not detect stalemates. /// or by repetition. It does not detect stalemates.
bool Position::is_draw() const { bool Position::is_draw(int ply) const {
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size())) if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
return true; return true;
StateInfo* stp = st; int end = std::min(st->rule50, st->pliesFromNull);
for (int i = 2, e = std::min(st->rule50, st->pliesFromNull); i <= e; i += 2)
if (end < 4)
return false;
StateInfo* stp = st->previous->previous;
int cnt = 0;
for (int i = 4; i <= end; i += 2)
{ {
stp = stp->previous->previous; stp = stp->previous->previous;
if (stp->key == st->key) // At root position ply is 1, so return a draw score if a position
return true; // Draw at first repetition // repeats once earlier but strictly after the root, or repeats twice
// before or at the root.
if ( stp->key == st->key
&& ++cnt + (ply - 1 > i) == 2)
return true;
} }
return false; return false;
@@ -1091,66 +1118,61 @@ void Position::flip() {
} }
/// Position::pos_is_ok() performs some consistency checks for the position object. /// Position::pos_is_ok() performs some consistency checks for the
/// position object and raises an asserts if something wrong is detected.
/// This is meant to be helpful when debugging. /// This is meant to be helpful when debugging.
bool Position::pos_is_ok(int* failedStep) const { bool Position::pos_is_ok() const {
const bool Fast = true; // Quick (default) or full check? const bool Fast = true; // Quick (default) or full check?
enum { Default, King, Bitboards, State, Lists, Castling };
for (int step = Default; step <= (Fast ? Default : Castling); step++)
{
if (failedStep)
*failedStep = step;
if (step == Default)
if ( (sideToMove != WHITE && sideToMove != BLACK) if ( (sideToMove != WHITE && sideToMove != BLACK)
|| piece_on(square<KING>(WHITE)) != W_KING || piece_on(square<KING>(WHITE)) != W_KING
|| piece_on(square<KING>(BLACK)) != B_KING || piece_on(square<KING>(BLACK)) != B_KING
|| ( ep_square() != SQ_NONE || ( ep_square() != SQ_NONE
&& relative_rank(sideToMove, ep_square()) != RANK_6)) && relative_rank(sideToMove, ep_square()) != RANK_6))
return false; assert(0 && "pos_is_ok: Default");
if (step == King) if (Fast)
if ( std::count(board, board + SQUARE_NB, W_KING) != 1 return true;
|| std::count(board, board + SQUARE_NB, B_KING) != 1
if ( pieceCount[W_KING] != 1
|| pieceCount[B_KING] != 1
|| attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove)) || attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove))
return false; assert(0 && "pos_is_ok: Kings");
if ( (pieces(PAWN) & (Rank1BB | Rank8BB))
|| pieceCount[W_PAWN] > 8
|| pieceCount[B_PAWN] > 8)
assert(0 && "pos_is_ok: Pawns");
if (step == Bitboards)
{
if ( (pieces(WHITE) & pieces(BLACK)) if ( (pieces(WHITE) & pieces(BLACK))
||(pieces(WHITE) | pieces(BLACK)) != pieces()) || (pieces(WHITE) | pieces(BLACK)) != pieces()
return false; || popcount(pieces(WHITE)) > 16
|| popcount(pieces(BLACK)) > 16)
assert(0 && "pos_is_ok: Bitboards");
for (PieceType p1 = PAWN; p1 <= KING; ++p1) for (PieceType p1 = PAWN; p1 <= KING; ++p1)
for (PieceType p2 = PAWN; p2 <= KING; ++p2) for (PieceType p2 = PAWN; p2 <= KING; ++p2)
if (p1 != p2 && (pieces(p1) & pieces(p2))) if (p1 != p2 && (pieces(p1) & pieces(p2)))
return false; assert(0 && "pos_is_ok: Bitboards");
}
if (step == State)
{
StateInfo si = *st; StateInfo si = *st;
set_state(&si); set_state(&si);
if (std::memcmp(&si, st, sizeof(StateInfo))) if (std::memcmp(&si, st, sizeof(StateInfo)))
return false; assert(0 && "pos_is_ok: State");
}
if (step == Lists)
for (Piece pc : Pieces) for (Piece pc : Pieces)
{ {
if (pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))) if ( pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))
return false; || pieceCount[pc] != std::count(board, board + SQUARE_NB, pc))
assert(0 && "pos_is_ok: Pieces");
for (int i = 0; i < pieceCount[pc]; ++i) for (int i = 0; i < pieceCount[pc]; ++i)
if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i) if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i)
return false; assert(0 && "pos_is_ok: Index");
} }
if (step == Castling)
for (Color c = WHITE; c <= BLACK; ++c) for (Color c = WHITE; c <= BLACK; ++c)
for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1)) for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
{ {
@@ -1160,8 +1182,7 @@ bool Position::pos_is_ok(int* failedStep) const {
if ( piece_on(castlingRookSquare[c | s]) != make_piece(c, ROOK) if ( piece_on(castlingRookSquare[c | s]) != make_piece(c, ROOK)
|| castlingRightsMask[castlingRookSquare[c | s]] != (c | s) || castlingRightsMask[castlingRookSquare[c | s]] != (c | s)
|| (castlingRightsMask[square<KING>(c)] & (c | s)) != (c | s)) || (castlingRightsMask[square<KING>(c)] & (c | s)) != (c | s))
return false; assert(0 && "pos_is_ok: Castling");
}
} }
return true; return true;

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -56,7 +56,10 @@ struct StateInfo {
Bitboard checkSquares[PIECE_TYPE_NB]; Bitboard checkSquares[PIECE_TYPE_NB];
}; };
// In a std::deque references to elements are unaffected upon resizing /// A list to keep track of the position states along the setup moves (from the
/// start position to the position just before the search starts). Needed by
/// 'draw by repetition' detection. Use a std::deque because pointers to
/// elements are not invalidated upon list resizing.
typedef std::unique_ptr<std::deque<StateInfo>> StateListPtr; typedef std::unique_ptr<std::deque<StateInfo>> StateListPtr;
@@ -76,6 +79,7 @@ public:
// FEN string input/output // FEN string input/output
Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th); Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th);
Position& set(const std::string& code, Color c, StateInfo* si);
const std::string fen() const; const std::string fen() const;
// Position representation // Position representation
@@ -89,6 +93,7 @@ public:
Square ep_square() const; Square ep_square() const;
bool empty(Square s) const; bool empty(Square s) const;
template<PieceType Pt> int count(Color c) const; template<PieceType Pt> int count(Color c) const;
template<PieceType Pt> int count() const;
template<PieceType Pt> const Square* squares(Color c) const; template<PieceType Pt> const Square* squares(Color c) const;
template<PieceType Pt> Square square(Color c) const; template<PieceType Pt> Square square(Color c) const;
@@ -107,7 +112,7 @@ public:
// Attacks to/from a given square // Attacks to/from a given square
Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s) const;
Bitboard attackers_to(Square s, Bitboard occupied) const; Bitboard attackers_to(Square s, Bitboard occupied) const;
Bitboard attacks_from(Piece pc, Square s) const; Bitboard attacks_from(PieceType pt, Square s) const;
template<PieceType> Bitboard attacks_from(Square s) const; template<PieceType> Bitboard attacks_from(Square s) const;
template<PieceType> Bitboard attacks_from(Square s, Color c) const; template<PieceType> Bitboard attacks_from(Square s, Color c) const;
Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const; Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const;
@@ -127,13 +132,14 @@ public:
bool opposite_bishops() const; bool opposite_bishops() const;
// Doing and undoing moves // Doing and undoing moves
void do_move(Move m, StateInfo& st, bool givesCheck); void do_move(Move m, StateInfo& newSt);
void do_move(Move m, StateInfo& newSt, bool givesCheck);
void undo_move(Move m); void undo_move(Move m);
void do_null_move(StateInfo& st); void do_null_move(StateInfo& newSt);
void undo_null_move(); void undo_null_move();
// Static Exchange Evaluation // Static Exchange Evaluation
bool see_ge(Move m, Value value) const; bool see_ge(Move m, Value threshold = VALUE_ZERO) const;
// Accessing hash keys // Accessing hash keys
Key key() const; Key key() const;
@@ -143,18 +149,17 @@ public:
// Other properties of the position // Other properties of the position
Color side_to_move() const; Color side_to_move() const;
Phase game_phase() const;
int game_ply() const; int game_ply() const;
bool is_chess960() const; bool is_chess960() const;
Thread* this_thread() const; Thread* this_thread() const;
uint64_t nodes_searched() const; bool is_draw(int ply) const;
bool is_draw() const;
int rule50_count() const; int rule50_count() const;
Score psq_score() const; Score psq_score() const;
Value non_pawn_material(Color c) const; Value non_pawn_material(Color c) const;
Value non_pawn_material() const;
// Position consistency check, for debugging // Position consistency check, for debugging
bool pos_is_ok(int* failedStep = nullptr) const; bool pos_is_ok() const;
void flip(); void flip();
private: private:
@@ -180,7 +185,6 @@ private:
int castlingRightsMask[SQUARE_NB]; int castlingRightsMask[SQUARE_NB];
Square castlingRookSquare[CASTLING_RIGHT_NB]; Square castlingRookSquare[CASTLING_RIGHT_NB];
Bitboard castlingPath[CASTLING_RIGHT_NB]; Bitboard castlingPath[CASTLING_RIGHT_NB];
uint64_t nodes;
int gamePly; int gamePly;
Color sideToMove; Color sideToMove;
Thread* thisThread; Thread* thisThread;
@@ -234,6 +238,10 @@ template<PieceType Pt> inline int Position::count(Color c) const {
return pieceCount[make_piece(c, Pt)]; return pieceCount[make_piece(c, Pt)];
} }
template<PieceType Pt> inline int Position::count() const {
return pieceCount[make_piece(WHITE, Pt)] + pieceCount[make_piece(BLACK, Pt)];
}
template<PieceType Pt> inline const Square* Position::squares(Color c) const { template<PieceType Pt> inline const Square* Position::squares(Color c) const {
return pieceList[make_piece(c, Pt)]; return pieceList[make_piece(c, Pt)];
} }
@@ -265,18 +273,19 @@ inline Square Position::castling_rook_square(CastlingRight cr) const {
template<PieceType Pt> template<PieceType Pt>
inline Bitboard Position::attacks_from(Square s) const { inline Bitboard Position::attacks_from(Square s) const {
assert(Pt != PAWN);
return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, byTypeBB[ALL_PIECES]) return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, byTypeBB[ALL_PIECES])
: Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s) : Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s)
: StepAttacksBB[Pt][s]; : PseudoAttacks[Pt][s];
} }
template<> template<>
inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const { inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const {
return StepAttacksBB[make_piece(c, PAWN)][s]; return PawnAttacks[c][s];
} }
inline Bitboard Position::attacks_from(Piece pc, Square s) const { inline Bitboard Position::attacks_from(PieceType pt, Square s) const {
return attacks_bb(pc, s, byTypeBB[ALL_PIECES]); return attacks_bb(pt, s, byTypeBB[ALL_PIECES]);
} }
inline Bitboard Position::attackers_to(Square s) const { inline Bitboard Position::attackers_to(Square s) const {
@@ -328,6 +337,10 @@ inline Value Position::non_pawn_material(Color c) const {
return st->nonPawnMaterial[c]; return st->nonPawnMaterial[c];
} }
inline Value Position::non_pawn_material() const {
return st->nonPawnMaterial[WHITE] + st->nonPawnMaterial[BLACK];
}
inline int Position::game_ply() const { inline int Position::game_ply() const {
return gamePly; return gamePly;
} }
@@ -336,10 +349,6 @@ inline int Position::rule50_count() const {
return st->rule50; return st->rule50;
} }
inline uint64_t Position::nodes_searched() const {
return nodes;
}
inline bool Position::opposite_bishops() const { inline bool Position::opposite_bishops() const {
return pieceCount[W_BISHOP] == 1 return pieceCount[W_BISHOP] == 1
&& pieceCount[B_BISHOP] == 1 && pieceCount[B_BISHOP] == 1
@@ -411,4 +420,8 @@ inline void Position::move_piece(Piece pc, Square from, Square to) {
pieceList[pc][index[to]] = to; pieceList[pc][index[to]] = to;
} }
inline void Position::do_move(Move m, StateInfo& newSt) {
do_move(m, newSt, gives_check(m));
}
#endif // #ifndef POSITION_H_INCLUDED #endif // #ifndef POSITION_H_INCLUDED

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -39,32 +39,32 @@ const Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
{ }, { },
{ // Pawn { // Pawn
{ S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) }, { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) },
{ S(-16, 7), S( 1,-4), S( 7, 8), S( 3,-2) }, { S(-11, 7), S( 6,-4), S( 7, 8), S( 3,-2) },
{ S(-23,-4), S( -7,-5), S( 19, 5), S(24, 4) }, { S(-18,-4), S( -2,-5), S( 19, 5), S(24, 4) },
{ S(-22, 3), S(-14, 3), S( 20,-8), S(35,-3) }, { S(-17, 3), S( -9, 3), S( 20,-8), S(35,-3) },
{ S(-11, 8), S( 0, 9), S( 3, 7), S(21,-6) }, { S( -6, 8), S( 5, 9), S( 3, 7), S(21,-6) },
{ S(-11, 8), S(-13,-5), S( -6, 2), S(-2, 4) }, { S( -6, 8), S( -8,-5), S( -6, 2), S(-2, 4) },
{ S( -9, 3), S( 15,-9), S( -8, 1), S(-4,18) } { S( -4, 3), S( 20,-9), S( -8, 1), S(-4,18) }
}, },
{ // Knight { // Knight
{ S(-143, -97), S(-96,-82), S(-80,-46), S(-73,-14) }, { S(-161,-105), S(-96,-82), S(-80,-46), S(-73,-14) },
{ S( -83, -69), S(-43,-55), S(-21,-17), S(-10, 9) }, { S( -83, -69), S(-43,-54), S(-21,-17), S(-10, 9) },
{ S( -71, -50), S(-22,-39), S( 0, -8), S( 9, 28) }, { S( -71, -50), S(-22,-39), S( 0, -7), S( 9, 28) },
{ S( -25, -41), S( 18,-25), S( 43, 7), S( 47, 38) }, { S( -25, -41), S( 18,-25), S( 43, 6), S( 47, 38) },
{ S( -26, -46), S( 16,-25), S( 38, 2), S( 50, 41) }, { S( -26, -46), S( 16,-25), S( 38, 3), S( 50, 40) },
{ S( -11, -55), S( 37,-38), S( 56, -8), S( 71, 27) }, { S( -11, -54), S( 37,-38), S( 56, -7), S( 65, 27) },
{ S( -62, -64), S(-17,-50), S( 5,-24), S( 14, 13) }, { S( -63, -65), S(-19,-50), S( 5,-24), S( 14, 13) },
{ S(-195,-110), S(-66,-90), S(-42,-50), S(-29,-13) } { S(-195,-109), S(-67,-89), S(-42,-50), S(-29,-13) }
}, },
{ // Bishop { // Bishop
{ S(-54,-68), S(-23,-40), S(-35,-46), S(-44,-28) }, { S(-44,-58), S(-13,-31), S(-25,-37), S(-34,-19) },
{ S(-30,-43), S( 10,-17), S( 2,-23), S( -9, -5) }, { S(-20,-34), S( 20, -9), S( 12,-14), S( 1, 4) },
{ S(-19,-32), S( 17, -9), S( 11,-13), S( 1, 8) }, { S( -9,-23), S( 27, 0), S( 21, -3), S( 11, 16) },
{ S(-21,-36), S( 18,-13), S( 11,-15), S( 0, 7) }, { S(-11,-26), S( 28, -3), S( 21, -5), S( 10, 16) },
{ S(-21,-36), S( 14,-14), S( 6,-17), S( -1, 3) }, { S(-11,-26), S( 27, -4), S( 16, -7), S( 9, 14) },
{ S(-27,-35), S( 6,-13), S( 2,-10), S( -8, 1) }, { S(-17,-24), S( 16, -2), S( 12, 0), S( 2, 13) },
{ S(-33,-44), S( 7,-21), S( -4,-22), S(-12, -4) }, { S(-23,-34), S( 17,-10), S( 6,-12), S( -2, 6) },
{ S(-45,-65), S(-21,-42), S(-29,-46), S(-39,-27) } { S(-35,-55), S(-11,-32), S(-19,-36), S(-29,-17) }
}, },
{ // Rook { // Rook
{ S(-25, 0), S(-16, 0), S(-16, 0), S(-9, 0) }, { S(-25, 0), S(-16, 0), S(-16, 0), S(-9, 0) },
@@ -77,24 +77,24 @@ const Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
{ S(-23, 0), S(-15, 0), S(-11, 0), S(-5, 0) } { S(-23, 0), S(-15, 0), S(-11, 0), S(-5, 0) }
}, },
{ // Queen { // Queen
{ S( 0,-70), S(-3,-57), S(-4,-41), S(-1,-29) }, { S( 0,-71), S(-4,-56), S(-3,-42), S(-1,-29) },
{ S(-4,-58), S( 6,-30), S( 9,-21), S( 8, -4) }, { S(-4,-56), S( 6,-30), S( 9,-21), S( 8, -5) },
{ S(-2,-39), S( 6,-17), S( 9, -7), S( 9, 5) }, { S(-2,-39), S( 6,-17), S( 9, -8), S( 9, 5) },
{ S(-1,-29), S( 8, -5), S(10, 9), S( 7, 17) }, { S(-1,-29), S( 8, -5), S(10, 9), S( 7, 19) },
{ S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 23) }, { S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 21) },
{ S(-2,-40), S( 6,-16), S( 8,-11), S(10, 3) }, { S(-2,-40), S( 6,-16), S( 8,-10), S(10, 3) },
{ S(-2,-54), S( 7,-30), S( 7,-21), S( 6, -7) }, { S(-2,-55), S( 7,-30), S( 7,-21), S( 6, -6) },
{ S(-1,-75), S(-4,-54), S(-1,-44), S( 0,-30) } { S(-1,-74), S(-4,-55), S(-1,-43), S( 0,-30) }
}, },
{ // King { // King
{ S(291, 28), S(344, 76), S(294,103), S(219,112) }, { S(267, 0), S(320, 48), S(270, 75), S(195, 84) },
{ S(289, 70), S(329,119), S(263,170), S(205,159) }, { S(264, 43), S(304, 92), S(238,143), S(180,132) },
{ S(226,109), S(271,164), S(202,195), S(136,191) }, { S(200, 83), S(245,138), S(176,167), S(110,165) },
{ S(204,131), S(212,194), S(175,194), S(137,204) }, { S(177,106), S(185,169), S(148,169), S(110,179) },
{ S(177,132), S(205,187), S(143,224), S( 94,227) }, { S(149,108), S(177,163), S(115,200), S( 66,203) },
{ S(147,118), S(188,178), S(113,199), S( 70,197) }, { S(118, 95), S(159,155), S( 84,176), S( 41,174) },
{ S(116, 72), S(158,121), S( 93,142), S( 48,161) }, { S( 87, 50), S(128, 99), S( 63,122), S( 20,139) },
{ S( 94, 30), S(120, 76), S( 78,101), S( 31,111) } { S( 63, 9), S( 88, 55), S( 47, 80), S( 0, 90) }
} }
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,7 +21,6 @@
#ifndef SEARCH_H_INCLUDED #ifndef SEARCH_H_INCLUDED
#define SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED
#include <atomic>
#include <vector> #include <vector>
#include "misc.h" #include "misc.h"
@@ -32,21 +31,24 @@ class Position;
namespace Search { namespace Search {
/// Threshold used for countermoves based pruning
const int CounterMovePruneThreshold = 0;
/// Stack struct keeps track of the information we need to remember from nodes /// Stack struct keeps track of the information we need to remember from nodes
/// shallower and deeper in the tree during the search. Each search thread has /// shallower and deeper in the tree during the search. Each search thread has
/// its own array of Stack objects, indexed by the current ply. /// its own array of Stack objects, indexed by the current ply.
struct Stack { struct Stack {
Move* pv; Move* pv;
PieceToHistory* contHistory;
int ply; int ply;
Move currentMove; Move currentMove;
Move excludedMove; Move excludedMove;
Move killers[2]; Move killers[2];
Value staticEval; Value staticEval;
Value history; int statScore;
bool skipEarlyPruning;
int moveCount; int moveCount;
CounterMoveStats* counterMoves;
}; };
@@ -57,13 +59,16 @@ struct Stack {
struct RootMove { struct RootMove {
explicit RootMove(Move m) : pv(1, m) {} explicit RootMove(Move m) : pv(1, m) {}
bool operator<(const RootMove& m) const { return m.score < score; } // Descending sort
bool operator==(const Move& m) const { return pv[0] == m; }
bool extract_ponder_from_tt(Position& pos); bool extract_ponder_from_tt(Position& pos);
bool operator==(const Move& m) const { return pv[0] == m; }
bool operator<(const RootMove& m) const { // Sort in descending order
return m.score != score ? m.score < score
: m.previousScore < previousScore;
}
Value score = -VALUE_INFINITE; Value score = -VALUE_INFINITE;
Value previousScore = -VALUE_INFINITE; Value previousScore = -VALUE_INFINITE;
int selDepth = 0;
std::vector<Move> pv; std::vector<Move> pv;
}; };
@@ -71,40 +76,30 @@ typedef std::vector<RootMove> RootMoves;
/// LimitsType struct stores information sent by GUI about available time to /// LimitsType struct stores information sent by GUI about available time to
/// search the current move, maximum depth/time, if we are in analysis mode or /// search the current move, maximum depth/time, or if we are in analysis mode.
/// if we have to ponder while it's our opponent's turn to move.
struct LimitsType { struct LimitsType {
LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC
nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] =
npmsec = movestogo = depth = movetime = mate = infinite = ponder = 0; npmsec = movestogo = depth = movetime = mate = perft = infinite = 0;
} }
bool use_time_management() const { bool use_time_management() const {
return !(mate | movetime | depth | nodes | infinite); return !(mate | movetime | depth | nodes | perft | infinite);
} }
std::vector<Move> searchmoves; std::vector<Move> searchmoves;
int time[COLOR_NB], inc[COLOR_NB], npmsec, movestogo, depth, movetime, mate, infinite, ponder; int time[COLOR_NB], inc[COLOR_NB], npmsec, movestogo, depth,
movetime, mate, perft, infinite;
int64_t nodes; int64_t nodes;
TimePoint startTime; TimePoint startTime;
}; };
/// SignalsType struct stores atomic flags updated during the search, typically
/// in an async fashion e.g. to stop the search by the GUI.
struct SignalsType {
std::atomic_bool stop, stopOnPonderhit;
};
extern SignalsType Signals;
extern LimitsType Limits; extern LimitsType Limits;
void init(); void init();
void clear(); void clear();
template<bool Root = true> uint64_t perft(Position& pos, Depth depth);
} // namespace Search } // namespace Search

File diff suppressed because it is too large Load Diff

View File

@@ -1,169 +0,0 @@
/*
Copyright (c) 2011-2013 Ronald de Man
*/
#ifndef TBCORE_H
#define TBCORE_H
#ifndef _WIN32
#include <pthread.h>
#define SEP_CHAR ':'
#define FD int
#define FD_ERR -1
#else
#include <windows.h>
#define SEP_CHAR ';'
#define FD HANDLE
#define FD_ERR INVALID_HANDLE_VALUE
#endif
#ifndef _WIN32
#define LOCK_T pthread_mutex_t
#define LOCK_INIT(x) pthread_mutex_init(&(x), NULL)
#define LOCK(x) pthread_mutex_lock(&(x))
#define UNLOCK(x) pthread_mutex_unlock(&(x))
#else
#define LOCK_T HANDLE
#define LOCK_INIT(x) do { x = CreateMutex(NULL, FALSE, NULL); } while (0)
#define LOCK(x) WaitForSingleObject(x, INFINITE)
#define UNLOCK(x) ReleaseMutex(x)
#endif
#ifndef _MSC_VER
#define BSWAP32(v) __builtin_bswap32(v)
#define BSWAP64(v) __builtin_bswap64(v)
#else
#define BSWAP32(v) _byteswap_ulong(v)
#define BSWAP64(v) _byteswap_uint64(v)
#endif
#define WDLSUFFIX ".rtbw"
#define DTZSUFFIX ".rtbz"
#define WDLDIR "RTBWDIR"
#define DTZDIR "RTBZDIR"
#define TBPIECES 6
typedef unsigned long long uint64;
typedef unsigned int uint32;
typedef unsigned char ubyte;
typedef unsigned short ushort;
const ubyte WDL_MAGIC[4] = { 0x71, 0xe8, 0x23, 0x5d };
const ubyte DTZ_MAGIC[4] = { 0xd7, 0x66, 0x0c, 0xa5 };
#define TBHASHBITS 10
struct TBHashEntry;
typedef uint64 base_t;
struct PairsData {
char *indextable;
ushort *sizetable;
ubyte *data;
ushort *offset;
ubyte *symlen;
ubyte *sympat;
int blocksize;
int idxbits;
int min_len;
base_t base[1]; // C++ complains about base[]...
};
struct TBEntry {
char *data;
uint64 key;
uint64 mapping;
ubyte ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
}
#ifndef _WIN32
__attribute__((__may_alias__))
#endif
;
struct TBEntry_piece {
char *data;
uint64 key;
uint64 mapping;
ubyte ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
ubyte enc_type;
struct PairsData *precomp[2];
int factor[2][TBPIECES];
ubyte pieces[2][TBPIECES];
ubyte norm[2][TBPIECES];
};
struct TBEntry_pawn {
char *data;
uint64 key;
uint64 mapping;
ubyte ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
ubyte pawns[2];
struct {
struct PairsData *precomp[2];
int factor[2][TBPIECES];
ubyte pieces[2][TBPIECES];
ubyte norm[2][TBPIECES];
} file[4];
};
struct DTZEntry_piece {
char *data;
uint64 key;
uint64 mapping;
ubyte ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
ubyte enc_type;
struct PairsData *precomp;
int factor[TBPIECES];
ubyte pieces[TBPIECES];
ubyte norm[TBPIECES];
ubyte flags; // accurate, mapped, side
ushort map_idx[4];
ubyte *map;
};
struct DTZEntry_pawn {
char *data;
uint64 key;
uint64 mapping;
ubyte ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
ubyte pawns[2];
struct {
struct PairsData *precomp;
int factor[TBPIECES];
ubyte pieces[TBPIECES];
ubyte norm[TBPIECES];
} file[4];
ubyte flags[4];
ushort map_idx[4][4];
ubyte *map;
};
struct TBHashEntry {
uint64 key;
struct TBEntry *ptr;
};
struct DTZTableEntry {
uint64 key1;
uint64 key2;
struct TBEntry *entry;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,79 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (c) 2013 Ronald de Man
Copyright (C) 2016-2017 Marco Costalba, Lucas Braesch
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TBPROBE_H #ifndef TBPROBE_H
#define TBPROBE_H #define TBPROBE_H
#include <ostream>
#include "../search.h" #include "../search.h"
namespace Tablebases { namespace Tablebases {
enum WDLScore {
WDLLoss = -2, // Loss
WDLBlessedLoss = -1, // Loss, but draw under 50-move rule
WDLDraw = 0, // Draw
WDLCursedWin = 1, // Win, but draw under 50-move rule
WDLWin = 2, // Win
WDLScoreNone = -1000
};
// Possible states after a probing operation
enum ProbeState {
FAIL = 0, // Probe failed (missing file table)
OK = 1, // Probe succesful
CHANGE_STM = -1, // DTZ should check the other side
ZEROING_BEST_MOVE = 2 // Best move zeroes DTZ (capture or pawn move)
};
extern int MaxCardinality; extern int MaxCardinality;
void init(const std::string& path); void init(const std::string& paths);
int probe_wdl(Position& pos, int *success); WDLScore probe_wdl(Position& pos, ProbeState* result);
int probe_dtz(Position& pos, int *success); int probe_dtz(Position& pos, ProbeState* result);
bool root_probe(Position& pos, Search::RootMoves& rootMoves, Value& score); bool root_probe(Position& pos, Search::RootMoves& rootMoves, Value& score);
bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Value& score); bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Value& score);
void filter_root_moves(Position& pos, Search::RootMoves& rootMoves); void filter_root_moves(Position& pos, Search::RootMoves& rootMoves);
inline std::ostream& operator<<(std::ostream& os, const WDLScore v) {
os << (v == WDLLoss ? "Loss" :
v == WDLBlessedLoss ? "Blessed loss" :
v == WDLDraw ? "Draw" :
v == WDLCursedWin ? "Cursed win" :
v == WDLWin ? "Win" : "None");
return os;
}
inline std::ostream& operator<<(std::ostream& os, const ProbeState v) {
os << (v == FAIL ? "Failed" :
v == OK ? "Success" :
v == CHANGE_STM ? "Probed opponent side" :
v == ZEROING_BEST_MOVE ? "Best move zeroes DTZ" : "None");
return os;
}
} }
#endif #endif

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -24,177 +24,144 @@
#include "movegen.h" #include "movegen.h"
#include "search.h" #include "search.h"
#include "thread.h" #include "thread.h"
#include "uci.h"
#include "syzygy/tbprobe.h" #include "syzygy/tbprobe.h"
ThreadPool Threads; // Global object ThreadPool Threads; // Global object
/// Thread constructor launches the thread and then waits until it goes to sleep
/// in idle_loop().
Thread::Thread() { /// Thread constructor launches the thread and waits until it goes to sleep
/// in idle_loop(). Note that 'searching' and 'exit' should be alredy set.
resetCalls = exit = false; Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) {
maxPly = callsCnt = 0;
tbHits = 0;
history.clear();
counterMoves.clear();
idx = Threads.size(); // Start from 0
std::unique_lock<Mutex> lk(mutex); wait_for_search_finished();
searching = true; clear(); // Zero-init histories (based on std::array)
nativeThread = std::thread(&Thread::idle_loop, this);
sleepCondition.wait(lk, [&]{ return !searching; });
} }
/// Thread destructor waits for thread termination before returning /// Thread destructor wakes up the thread in idle_loop() and waits
/// for its termination. Thread should be already waiting.
Thread::~Thread() { Thread::~Thread() {
mutex.lock(); assert(!searching);
exit = true; exit = true;
sleepCondition.notify_one(); start_searching();
mutex.unlock(); stdThread.join();
nativeThread.join();
} }
/// Thread::wait_for_search_finished() waits on sleep condition /// Thread::clear() reset histories, usually before a new game
/// until not searching
void Thread::clear() {
counterMoves.fill(MOVE_NONE);
mainHistory.fill(0);
for (auto& to : contHistory)
for (auto& h : to)
h.fill(0);
contHistory[NO_PIECE][0].fill(Search::CounterMovePruneThreshold - 1);
}
/// Thread::start_searching() wakes up the thread that will start the search
void Thread::start_searching() {
std::lock_guard<Mutex> lk(mutex);
searching = true;
cv.notify_one(); // Wake up the thread in idle_loop()
}
/// Thread::wait_for_search_finished() blocks on the condition variable
/// until the thread has finished searching.
void Thread::wait_for_search_finished() { void Thread::wait_for_search_finished() {
std::unique_lock<Mutex> lk(mutex); std::unique_lock<Mutex> lk(mutex);
sleepCondition.wait(lk, [&]{ return !searching; }); cv.wait(lk, [&]{ return !searching; });
} }
/// Thread::wait() waits on sleep condition until condition is true /// Thread::idle_loop() is where the thread is parked, blocked on the
/// condition variable, when it has no work to do.
void Thread::wait(std::atomic_bool& condition) {
std::unique_lock<Mutex> lk(mutex);
sleepCondition.wait(lk, [&]{ return bool(condition); });
}
/// Thread::start_searching() wakes up the thread that will start the search
void Thread::start_searching(bool resume) {
std::unique_lock<Mutex> lk(mutex);
if (!resume)
searching = true;
sleepCondition.notify_one();
}
/// Thread::idle_loop() is where the thread is parked when it has no work to do
void Thread::idle_loop() { void Thread::idle_loop() {
while (!exit) WinProcGroup::bindThisThread(idx);
while (true)
{ {
std::unique_lock<Mutex> lk(mutex); std::unique_lock<Mutex> lk(mutex);
searching = false; searching = false;
cv.notify_one(); // Wake up anyone waiting for search finished
cv.wait(lk, [&]{ return searching; });
while (!searching && !exit) if (exit)
{ return;
sleepCondition.notify_one(); // Wake up any waiting thread
sleepCondition.wait(lk);
}
lk.unlock(); lk.unlock();
if (!exit)
search(); search();
} }
} }
/// ThreadPool::init() creates and launches requested threads that will go /// ThreadPool::init() creates and launches the threads that will go
/// immediately to sleep. We cannot use a constructor because Threads is a /// immediately to sleep in idle_loop. We cannot use the c'tor because
/// static object and we need a fully initialized engine at this point due to /// Threads is a static object and we need a fully initialized engine at
/// allocation of Endgames in the Thread constructor. /// this point due to allocation of Endgames in the Thread constructor.
void ThreadPool::init() { void ThreadPool::init(size_t requested) {
push_back(new MainThread); push_back(new MainThread(0));
read_uci_options(); set(requested);
} }
/// ThreadPool::exit() terminates threads before the program exits. Cannot be /// ThreadPool::exit() terminates threads before the program exits. Cannot be
/// done in destructor because threads must be terminated before deleting any /// done in the destructor because threads must be terminated before deleting
/// static objects while still in main(). /// any static object, so before main() returns.
void ThreadPool::exit() { void ThreadPool::exit() {
while (size()) main()->wait_for_search_finished();
delete back(), pop_back(); set(0);
} }
/// ThreadPool::read_uci_options() updates internal threads parameters from the /// ThreadPool::set() creates/destroys threads to match the requested number
/// corresponding UCI options and creates/destroys threads to match requested
/// number. Thread objects are dynamically allocated.
void ThreadPool::read_uci_options() { void ThreadPool::set(size_t requested) {
size_t requested = Options["Threads"];
assert(requested > 0);
while (size() < requested) while (size() < requested)
push_back(new Thread); push_back(new Thread(size()));
while (size() > requested) while (size() > requested)
delete back(), pop_back(); delete back(), pop_back();
} }
/// ThreadPool::nodes_searched() returns the number of nodes searched /// ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and
/// returns immediately. Main thread will wake up other threads and start the search.
uint64_t ThreadPool::nodes_searched() const {
uint64_t nodes = 0;
for (Thread* th : *this)
nodes += th->rootPos.nodes_searched();
return nodes;
}
/// ThreadPool::tb_hits() returns the number of TB hits
uint64_t ThreadPool::tb_hits() const {
uint64_t hits = 0;
for (Thread* th : *this)
hits += th->tbHits;
return hits;
}
/// ThreadPool::start_thinking() wakes up the main thread sleeping in idle_loop()
/// and starts a new search, then returns immediately.
void ThreadPool::start_thinking(Position& pos, StateListPtr& states, void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
const Search::LimitsType& limits) { const Search::LimitsType& limits, bool ponderMode) {
main()->wait_for_search_finished(); main()->wait_for_search_finished();
Search::Signals.stopOnPonderhit = Search::Signals.stop = false; stopOnPonderhit = stop = false;
ponder = ponderMode;
Search::Limits = limits; Search::Limits = limits;
Search::RootMoves rootMoves; Search::RootMoves rootMoves;
for (const auto& m : MoveList<LEGAL>(pos)) for (const auto& m : MoveList<LEGAL>(pos))
if ( limits.searchmoves.empty() if ( limits.searchmoves.empty()
|| std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m)) || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m))
rootMoves.push_back(Search::RootMove(m)); rootMoves.emplace_back(m);
if (!rootMoves.empty()) if (!rootMoves.empty())
Tablebases::filter_root_moves(pos, rootMoves); Tablebases::filter_root_moves(pos, rootMoves);
@@ -206,18 +173,22 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
if (states.get()) if (states.get())
setupStates = std::move(states); // Ownership transfer, states is now empty setupStates = std::move(states); // Ownership transfer, states is now empty
// We use Position::set() to set root position across threads. But there are
// some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot
// be deduced from a fen string, so set() clears them and to not lose the info
// we need to backup and later restore setupStates->back(). Note that setupStates
// is shared by threads but is accessed in read-only mode.
StateInfo tmp = setupStates->back(); StateInfo tmp = setupStates->back();
for (Thread* th : Threads) for (Thread* th : Threads)
{ {
th->maxPly = 0; th->nodes = th->tbHits = 0;
th->tbHits = 0; th->rootDepth = th->completedDepth = DEPTH_ZERO;
th->rootDepth = DEPTH_ZERO;
th->rootMoves = rootMoves; th->rootMoves = rootMoves;
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
} }
setupStates->back() = tmp; // Restore st->previous, cleared by Position::set() setupStates->back() = tmp;
main()->start_searching(); main()->start_searching();
} }

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -22,7 +22,6 @@
#define THREAD_H_INCLUDED #define THREAD_H_INCLUDED
#include <atomic> #include <atomic>
#include <bitset>
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
#include <thread> #include <thread>
@@ -36,74 +35,87 @@
#include "thread_win32.h" #include "thread_win32.h"
/// Thread struct keeps together all the thread-related stuff. We also use /// Thread class keeps together all the thread-related stuff. We use
/// per-thread pawn and material hash tables so that once we get a pointer to an /// per-thread pawn and material hash tables so that once we get a
/// entry its life time is unlimited and we don't have to care about someone /// pointer to an entry its life time is unlimited and we don't have
/// changing the entry under our feet. /// to care about someone changing the entry under our feet.
class Thread { class Thread {
std::thread nativeThread;
Mutex mutex; Mutex mutex;
ConditionVariable sleepCondition; ConditionVariable cv;
bool exit, searching; size_t idx;
bool exit = false, searching = true; // Set before starting std::thread
std::thread stdThread;
public: public:
Thread(); explicit Thread(size_t);
virtual ~Thread(); virtual ~Thread();
virtual void search(); virtual void search();
void clear();
void idle_loop(); void idle_loop();
void start_searching(bool resume = false); void start_searching();
void wait_for_search_finished(); void wait_for_search_finished();
void wait(std::atomic_bool& b);
Pawns::Table pawnsTable; Pawns::Table pawnsTable;
Material::Table materialTable; Material::Table materialTable;
Endgames endgames; Endgames endgames;
size_t idx, PVIdx; size_t PVIdx;
int maxPly, callsCnt; int selDepth;
uint64_t tbHits; std::atomic<uint64_t> nodes, tbHits;
Position rootPos; Position rootPos;
Search::RootMoves rootMoves; Search::RootMoves rootMoves;
Depth rootDepth; Depth rootDepth, completedDepth;
Depth completedDepth; CounterMoveHistory counterMoves;
std::atomic_bool resetCalls; ButterflyHistory mainHistory;
HistoryStats history; ContinuationHistory contHistory;
MoveStats counterMoves;
FromToStats fromTo;
CounterMoveHistoryStats counterMoveHistory;
}; };
/// MainThread is a derived class with a specific overload for the main thread /// MainThread is a derived class specific for main thread
struct MainThread : public Thread { struct MainThread : public Thread {
virtual void search();
using Thread::Thread;
void search() override;
void check_time();
bool easyMovePlayed, failedLow; bool easyMovePlayed, failedLow;
double bestMoveChanges; double bestMoveChanges;
Value previousScore; Value previousScore;
int callsCnt;
}; };
/// ThreadPool struct handles all the threads-related stuff like init, starting, /// ThreadPool struct handles all the threads-related stuff like init, starting,
/// parking and, most importantly, launching a thread. All the access to threads /// parking and, most importantly, launching a thread. All the access to threads
/// data is done through this class. /// is done through this class.
struct ThreadPool : public std::vector<Thread*> { struct ThreadPool : public std::vector<Thread*> {
void init(); // No constructor and destructor, threads rely on globals that should void init(size_t); // No constructor and destructor, threads rely on globals that should
void exit(); // be initialized and valid during the whole thread lifetime. void exit(); // be initialized and valid during the whole thread lifetime.
void start_thinking(Position&, StateListPtr&, const Search::LimitsType&, bool = false);
void set(size_t);
MainThread* main() { return static_cast<MainThread*>(at(0)); } MainThread* main() const { return static_cast<MainThread*>(front()); }
void start_thinking(Position&, StateListPtr&, const Search::LimitsType&); uint64_t nodes_searched() const { return accumulate(&Thread::nodes); }
void read_uci_options(); uint64_t tb_hits() const { return accumulate(&Thread::tbHits); }
uint64_t nodes_searched() const;
uint64_t tb_hits() const; std::atomic_bool stop, ponder, stopOnPonderhit;
private: private:
StateListPtr setupStates; StateListPtr setupStates;
uint64_t accumulate(std::atomic<uint64_t> Thread::* member) const {
uint64_t sum = 0;
for (Thread* th : *this)
sum += (th->*member).load(std::memory_order_relaxed);
return sum;
}
}; };
extern ThreadPool Threads; extern ThreadPool Threads;

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,8 +19,6 @@
*/ */
#include <algorithm> #include <algorithm>
#include <cfloat>
#include <cmath>
#include "search.h" #include "search.h"
#include "timeman.h" #include "timeman.h"
@@ -32,41 +30,43 @@ namespace {
enum TimeType { OptimumTime, MaxTime }; enum TimeType { OptimumTime, MaxTime };
const int MoveHorizon = 50; // Plan time management at most this many moves ahead int remaining(int myTime, int myInc, int moveOverhead, int movesToGo,
const double MaxRatio = 7.09; // When in trouble, we can step over reserved time with this ratio int moveNum, bool ponder, TimeType type) {
const double StealRatio = 0.35; // However we must not steal time from remaining moves over this ratio
if (myTime <= 0)
return 0;
// move_importance() is a skew-logistic function based on naive statistical double ratio; // Which ratio of myTime we are going to use
// analysis of "how many games are still undecided after n half-moves". Game
// is considered "undecided" as long as neither side has >275cp advantage.
// Data was extracted from the CCRL game database with some simple filtering criteria.
double move_importance(int ply) { // Usage of increment follows quadratic distribution with the maximum at move 25
double inc = myInc * std::max(55.0, 120 - 0.12 * (moveNum - 25) * (moveNum - 25));
const double XScale = 7.64; // In moves-to-go we distribute time according to a quadratic function with
const double XShift = 58.4; // the maximum around move 20 for 40 moves in y time case.
const double Skew = 0.183; if (movesToGo)
{
ratio = (type == OptimumTime ? 1.0 : 6.0) / std::min(50, movesToGo);
return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero if (moveNum <= 40)
ratio *= 1.1 - 0.001 * (moveNum - 20) * (moveNum - 20);
else
ratio *= 1.5;
ratio *= 1 + inc / (myTime * 8.5);
}
// Otherwise we increase usage of remaining time as the game goes on
else
{
double k = 1 + 20 * moveNum / (500.0 + moveNum);
ratio = (type == OptimumTime ? 0.017 : 0.07) * (k + inc / myTime);
} }
template<TimeType T> int time = int(std::min(1.0, ratio) * std::max(0, myTime - moveOverhead));
int remaining(int myTime, int movesToGo, int ply, int slowMover) {
const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio); if (type == OptimumTime && ponder)
const double TStealRatio = (T == OptimumTime ? 0 : StealRatio); time = 5 * time / 4;
double moveImportance = (move_importance(ply) * slowMover) / 100; return time;
double otherMovesImportance = 0;
for (int i = 1; i < movesToGo; ++i)
otherMovesImportance += move_importance(ply + 2 * i);
double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance);
double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance);
return int(myTime * std::min(ratio1, ratio2)); // Intel C++ asks for an explicit cast
} }
} // namespace } // namespace
@@ -81,12 +81,11 @@ namespace {
/// inc > 0 && movestogo == 0 means: x basetime + z increment /// inc > 0 && movestogo == 0 means: x basetime + z increment
/// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment /// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
{
int minThinkingTime = Options["Minimum Thinking Time"];
int moveOverhead = Options["Move Overhead"]; int moveOverhead = Options["Move Overhead"];
int slowMover = Options["Slow Mover"];
int npmsec = Options["nodestime"]; int npmsec = Options["nodestime"];
bool ponder = Options["Ponder"];
// If we have to play in 'nodes as time' mode, then convert from time // If we have to play in 'nodes as time' mode, then convert from time
// to nodes, and use resulting values in time management formulas. // to nodes, and use resulting values in time management formulas.
@@ -103,30 +102,11 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
limits.npmsec = npmsec; limits.npmsec = npmsec;
} }
int moveNum = (ply + 1) / 2;
startTime = limits.startTime; startTime = limits.startTime;
optimumTime = maximumTime = std::max(limits.time[us], minThinkingTime); optimumTime = remaining(limits.time[us], limits.inc[us], moveOverhead,
limits.movestogo, moveNum, ponder, OptimumTime);
const int MaxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon; maximumTime = remaining(limits.time[us], limits.inc[us], moveOverhead,
limits.movestogo, moveNum, ponder, MaxTime);
// We calculate optimum time usage for different hypothetical "moves to go"-values
// and choose the minimum of calculated search time values. Usually the greatest
// hypMTG gives the minimum values.
for (int hypMTG = 1; hypMTG <= MaxMTG; ++hypMTG)
{
// Calculate thinking time for hypothetical "moves to go"-value
int hypMyTime = limits.time[us]
+ limits.inc[us] * (hypMTG - 1)
- moveOverhead * (2 + std::min(hypMTG, 40));
hypMyTime = std::max(hypMyTime, 0);
int t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, ply, slowMover);
int t2 = minThinkingTime + remaining<MaxTime >(hypMyTime, hypMTG, ply, slowMover);
optimumTime = std::min(t1, optimumTime);
maximumTime = std::min(t2, maximumTime);
}
if (Options["Ponder"])
optimumTime += optimumTime / 4;
} }

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -76,7 +76,7 @@
# include <immintrin.h> // Header for _pext_u64() intrinsic # include <immintrin.h> // Header for _pext_u64() intrinsic
# define pext(b, m) _pext_u64(b, m) # define pext(b, m) _pext_u64(b, m)
#else #else
# define pext(b, m) (0) # define pext(b, m) 0
#endif #endif
#ifdef USE_POPCNT #ifdef USE_POPCNT
@@ -128,7 +128,7 @@ enum MoveType {
}; };
enum Color { enum Color {
WHITE, BLACK, NO_COLOR, COLOR_NB = 2 WHITE, BLACK, COLOR_NB = 2
}; };
enum CastlingSide { enum CastlingSide {
@@ -183,11 +183,11 @@ enum Value : int {
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY,
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY,
PawnValueMg = 188, PawnValueEg = 248, PawnValueMg = 171, PawnValueEg = 240,
KnightValueMg = 753, KnightValueEg = 832, KnightValueMg = 764, KnightValueEg = 848,
BishopValueMg = 826, BishopValueEg = 897, BishopValueMg = 826, BishopValueEg = 891,
RookValueMg = 1285, RookValueEg = 1371, RookValueMg = 1282, RookValueEg = 1373,
QueenValueMg = 2513, QueenValueEg = 2650, QueenValueMg = 2526, QueenValueEg = 2646,
MidgameLimit = 15258, EndgameLimit = 3915 MidgameLimit = 15258, EndgameLimit = 3915
}; };
@@ -205,11 +205,9 @@ enum Piece {
PIECE_NB = 16 PIECE_NB = 16
}; };
const Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING };
extern Value PieceValue[PHASE_NB][PIECE_NB]; extern Value PieceValue[PHASE_NB][PIECE_NB];
enum Depth { enum Depth : int {
ONE_PLY = 1, ONE_PLY = 1,
@@ -239,8 +237,8 @@ enum Square {
NORTH = 8, NORTH = 8,
EAST = 1, EAST = 1,
SOUTH = -8, SOUTH = -NORTH,
WEST = -1, WEST = -EAST,
NORTH_EAST = NORTH + EAST, NORTH_EAST = NORTH + EAST,
SOUTH_EAST = SOUTH + EAST, SOUTH_EAST = SOUTH + EAST,
@@ -285,19 +283,19 @@ inline Value mg_value(Score s) {
#define ENABLE_BASE_OPERATORS_ON(T) \ #define ENABLE_BASE_OPERATORS_ON(T) \
inline T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \ inline T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \
inline T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \ inline T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \
inline T operator*(int i, T d) { return T(i * int(d)); } \
inline T operator*(T d, int i) { return T(int(d) * i); } \
inline T operator-(T d) { return T(-int(d)); } \ inline T operator-(T d) { return T(-int(d)); } \
inline T& operator+=(T& d1, T d2) { return d1 = d1 + d2; } \ inline T& operator+=(T& d1, T d2) { return d1 = d1 + d2; } \
inline T& operator-=(T& d1, T d2) { return d1 = d1 - d2; } \ inline T& operator-=(T& d1, T d2) { return d1 = d1 - d2; } \
inline T& operator*=(T& d, int i) { return d = T(int(d) * i); }
#define ENABLE_FULL_OPERATORS_ON(T) \ #define ENABLE_FULL_OPERATORS_ON(T) \
ENABLE_BASE_OPERATORS_ON(T) \ ENABLE_BASE_OPERATORS_ON(T) \
inline T operator*(int i, T d) { return T(i * int(d)); } \
inline T operator*(T d, int i) { return T(int(d) * i); } \
inline T& operator++(T& d) { return d = T(int(d) + 1); } \ inline T& operator++(T& d) { return d = T(int(d) + 1); } \
inline T& operator--(T& d) { return d = T(int(d) - 1); } \ inline T& operator--(T& d) { return d = T(int(d) - 1); } \
inline T operator/(T d, int i) { return T(int(d) / i); } \ inline T operator/(T d, int i) { return T(int(d) / i); } \
inline int operator/(T d1, T d2) { return int(d1) / int(d2); } \ inline int operator/(T d1, T d2) { return int(d1) / int(d2); } \
inline T& operator*=(T& d, int i) { return d = T(int(d) * i); } \
inline T& operator/=(T& d, int i) { return d = T(int(d) / i); } inline T& operator/=(T& d, int i) { return d = T(int(d) / i); }
ENABLE_FULL_OPERATORS_ON(Value) ENABLE_FULL_OPERATORS_ON(Value)
@@ -329,6 +327,18 @@ inline Score operator/(Score s, int i) {
return make_score(mg_value(s) / i, eg_value(s) / i); return make_score(mg_value(s) / i, eg_value(s) / i);
} }
/// Multiplication of a Score by an integer. We check for overflow in debug mode.
inline Score operator*(Score s, int i) {
Score result = Score(int(s) * i);
assert(eg_value(result) == (i * eg_value(s)));
assert(mg_value(result) == (i * mg_value(s)));
assert((i == 0) || (result / i) == s );
return result;
}
inline Color operator~(Color c) { inline Color operator~(Color c) {
return Color(c ^ BLACK); // Toggle color return Color(c ^ BLACK); // Toggle color
} }
@@ -411,6 +421,10 @@ inline Square to_sq(Move m) {
return Square(m & 0x3F); return Square(m & 0x3F);
} }
inline int from_to(Move m) {
return m & 0xFFF;
}
inline MoveType type_of(Move m) { inline MoveType type_of(Move m) {
return MoveType(m & (3 << 14)); return MoveType(m & (3 << 14));
} }

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cassert>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <string> #include <string>
@@ -27,30 +28,27 @@
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "thread.h" #include "thread.h"
#include "tt.h"
#include "timeman.h" #include "timeman.h"
#include "uci.h" #include "uci.h"
#include "syzygy/tbprobe.h"
using namespace std; using namespace std;
extern void benchmark(const Position& pos, istream& is); extern vector<string> setup_bench(const Position&, istream&);
namespace { namespace {
// FEN string of the initial position, normal chess // FEN string of the initial position, normal chess
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
// A list to keep track of the position states along the setup moves (from the
// start position to the position just before the search starts). Needed by
// 'draw by repetition' detection.
StateListPtr States(new std::deque<StateInfo>(1));
// position() is called when engine receives the "position" UCI command. // position() is called when engine receives the "position" UCI command.
// The function sets up the position described in the given FEN string ("fen") // The function sets up the position described in the given FEN string ("fen")
// or the starting position ("startpos") and then makes the moves given in the // or the starting position ("startpos") and then makes the moves given in the
// following move list ("moves"). // following move list ("moves").
void position(Position& pos, istringstream& is) { void position(Position& pos, istringstream& is, StateListPtr& states) {
Move m; Move m;
string token, fen; string token, fen;
@@ -68,14 +66,14 @@ namespace {
else else
return; return;
States = StateListPtr(new std::deque<StateInfo>(1)); states = StateListPtr(new std::deque<StateInfo>(1)); // Drop old and create a new one
pos.set(fen, Options["UCI_Chess960"], &States->back(), Threads.main()); pos.set(fen, Options["UCI_Chess960"], &states->back(), Threads.main());
// Parse move list (if any) // Parse move list (if any)
while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE)
{ {
States->push_back(StateInfo()); states->emplace_back();
pos.do_move(m, States->back(), pos.gives_check(m)); pos.do_move(m, states->back());
} }
} }
@@ -108,10 +106,11 @@ namespace {
// the thinking time and other parameters from the input string, then starts // the thinking time and other parameters from the input string, then starts
// the search. // the search.
void go(Position& pos, istringstream& is) { void go(Position& pos, istringstream& is, StateListPtr& states) {
Search::LimitsType limits; Search::LimitsType limits;
string token; string token;
bool ponderMode = false;
limits.startTime = now(); // As early as possible! limits.startTime = now(); // As early as possible!
@@ -129,10 +128,53 @@ namespace {
else if (token == "nodes") is >> limits.nodes; else if (token == "nodes") is >> limits.nodes;
else if (token == "movetime") is >> limits.movetime; else if (token == "movetime") is >> limits.movetime;
else if (token == "mate") is >> limits.mate; else if (token == "mate") is >> limits.mate;
else if (token == "perft") is >> limits.perft;
else if (token == "infinite") limits.infinite = 1; else if (token == "infinite") limits.infinite = 1;
else if (token == "ponder") limits.ponder = 1; else if (token == "ponder") ponderMode = true;
Threads.start_thinking(pos, States, limits); Threads.start_thinking(pos, states, limits, ponderMode);
}
// bench() is called when engine receives the "bench" command. Firstly
// a list of UCI commands is setup according to bench parameters, then
// it is run one by one printing a summary at the end.
void bench(Position& pos, istream& args, StateListPtr& states) {
string token;
uint64_t num, nodes = 0, cnt = 1;
vector<string> list = setup_bench(pos, args);
num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0; });
TimePoint elapsed = now();
for (const auto& cmd : list)
{
istringstream is(cmd);
is >> skipws >> token;
if (token == "go")
{
cerr << "\nPosition: " << cnt++ << '/' << num << endl;
go(pos, is, states);
Threads.main()->wait_for_search_finished();
nodes += Threads.nodes_searched();
}
else if (token == "setoption") setoption(is);
else if (token == "position") position(pos, is, states);
else if (token == "ucinewgame") Search::clear();
}
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
dbg_print(); // Just before exiting
cerr << "\n==========================="
<< "\nTotal time (ms) : " << elapsed
<< "\nNodes searched : " << nodes
<< "\nNodes/second : " << 1000 * nodes / elapsed << endl;
} }
} // namespace } // namespace
@@ -148,8 +190,10 @@ void UCI::loop(int argc, char* argv[]) {
Position pos; Position pos;
string token, cmd; string token, cmd;
StateListPtr states(new std::deque<StateInfo>(1));
auto uiThread = std::make_shared<Thread>(0);
pos.set(StartFEN, false, &States->back(), Threads.main()); pos.set(StartFEN, false, &states->back(), uiThread.get());
for (int i = 1; i < argc; ++i) for (int i = 1; i < argc; ++i)
cmd += std::string(argv[i]) + " "; cmd += std::string(argv[i]) + " ";
@@ -160,61 +204,42 @@ void UCI::loop(int argc, char* argv[]) {
istringstream is(cmd); istringstream is(cmd);
token.clear(); // getline() could return empty or blank line token.clear(); // Avoid a stale if getline() returns empty or blank line
is >> skipws >> token; is >> skipws >> token;
// The GUI sends 'ponderhit' to tell us to ponder on the same move the // The GUI sends 'ponderhit' to tell us the user has played the expected move.
// opponent has played. In case Signals.stopOnPonderhit is set we are // So 'ponderhit' will be sent if we were told to ponder on the same move the
// waiting for 'ponderhit' to stop the search (for instance because we // user has played. We should continue searching but switch from pondering to
// already ran out of time), otherwise we should continue searching but // normal search. In case Threads.stopOnPonderhit is set we are waiting for
// switching from pondering to normal search. // 'ponderhit' to stop the search, for instance if max search depth is reached.
if ( token == "quit" if ( token == "quit"
|| token == "stop" || token == "stop"
|| (token == "ponderhit" && Search::Signals.stopOnPonderhit)) || (token == "ponderhit" && Threads.stopOnPonderhit))
{ Threads.stop = true;
Search::Signals.stop = true;
Threads.main()->start_searching(true); // Could be sleeping
}
else if (token == "ponderhit") else if (token == "ponderhit")
Search::Limits.ponder = 0; // Switch to normal search Threads.ponder = false; // Switch to normal search
else if (token == "uci") else if (token == "uci")
sync_cout << "id name " << engine_info(true) sync_cout << "id name " << engine_info(true)
<< "\n" << Options << "\n" << Options
<< "\nuciok" << sync_endl; << "\nuciok" << sync_endl;
else if (token == "ucinewgame")
{
Search::clear();
Time.availableNodes = 0;
}
else if (token == "isready") sync_cout << "readyok" << sync_endl;
else if (token == "go") go(pos, is);
else if (token == "position") position(pos, is);
else if (token == "setoption") setoption(is); else if (token == "setoption") setoption(is);
else if (token == "go") go(pos, is, states);
else if (token == "position") position(pos, is, states);
else if (token == "ucinewgame") Search::clear();
else if (token == "isready") sync_cout << "readyok" << sync_endl;
// Additional custom non-UCI commands, useful for debugging // Additional custom non-UCI commands, mainly for debugging
else if (token == "flip") pos.flip(); else if (token == "flip") pos.flip();
else if (token == "bench") benchmark(pos, is); else if (token == "bench") bench(pos, is, states);
else if (token == "d") sync_cout << pos << sync_endl; else if (token == "d") sync_cout << pos << sync_endl;
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
else if (token == "perft")
{
int depth;
stringstream ss;
is >> depth;
ss << Options["Hash"] << " "
<< Options["Threads"] << " " << depth << " current perft";
benchmark(pos, ss);
}
else else
sync_cout << "Unknown command: " << cmd << sync_endl; sync_cout << "Unknown command: " << cmd << sync_endl;
} while (token != "quit" && argc == 1); // Passed args have one-shot behaviour } while (token != "quit" && argc == 1); // Command line args are one-shot
Threads.main()->wait_for_search_finished();
} }
@@ -227,6 +252,8 @@ void UCI::loop(int argc, char* argv[]) {
string UCI::value(Value v) { string UCI::value(Value v) {
assert(-VALUE_INFINITE < v && v < VALUE_INFINITE);
stringstream ss; stringstream ss;
if (abs(v) < VALUE_MATE - MAX_PLY) if (abs(v) < VALUE_MATE - MAX_PLY)

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -49,7 +49,7 @@ public:
Option(OnChange = nullptr); Option(OnChange = nullptr);
Option(bool v, OnChange = nullptr); Option(bool v, OnChange = nullptr);
Option(const char* v, OnChange = nullptr); Option(const char* v, OnChange = nullptr);
Option(int v, int min, int max, OnChange = nullptr); Option(int v, int minv, int maxv, OnChange = nullptr);
Option& operator=(const std::string&); Option& operator=(const std::string&);
void operator<<(const Option&); void operator<<(const Option&);

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -39,7 +39,7 @@ namespace UCI {
void on_clear_hash(const Option&) { Search::clear(); } void on_clear_hash(const Option&) { Search::clear(); }
void on_hash_size(const Option& o) { TT.resize(o); } void on_hash_size(const Option& o) { TT.resize(o); }
void on_logger(const Option& o) { start_logger(o); } void on_logger(const Option& o) { start_logger(o); }
void on_threads(const Option&) { Threads.read_uci_options(); } void on_threads(const Option& o) { Threads.set(o); }
void on_tb_path(const Option& o) { Tablebases::init(o); } void on_tb_path(const Option& o) { Tablebases::init(o); }
@@ -59,15 +59,13 @@ void init(OptionsMap& o) {
o["Debug Log File"] << Option("", on_logger); o["Debug Log File"] << Option("", on_logger);
o["Contempt"] << Option(0, -100, 100); o["Contempt"] << Option(0, -100, 100);
o["Threads"] << Option(1, 1, 128, on_threads); o["Threads"] << Option(1, 1, 512, on_threads);
o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size);
o["Clear Hash"] << Option(on_clear_hash); o["Clear Hash"] << Option(on_clear_hash);
o["Ponder"] << Option(false); o["Ponder"] << Option(false);
o["MultiPV"] << Option(1, 1, 500); o["MultiPV"] << Option(1, 1, 500);
o["Skill Level"] << Option(20, 0, 20); o["Skill Level"] << Option(20, 0, 20);
o["Move Overhead"] << Option(30, 0, 5000); o["Move Overhead"] << Option(60, 0, 5000);
o["Minimum Thinking Time"] << Option(20, 0, 5000);
o["Slow Mover"] << Option(89, 10, 1000);
o["nodestime"] << Option(0, 0, 10000); o["nodestime"] << Option(0, 0, 10000);
o["UCI_Chess960"] << Option(false); o["UCI_Chess960"] << Option(false);
o["SyzygyPath"] << Option("<empty>", on_tb_path); o["SyzygyPath"] << Option("<empty>", on_tb_path);