mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-12-17 19:22:18 +01:00
DroidFish: Updated stockfish engine to development version 2017-09-06.
This commit is contained in:
@@ -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"/>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)];
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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,16 +33,17 @@ 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;
|
||||||
*q = *(q-1);
|
for (q = sortedEnd; q != begin && *(q - 1) < tmp; --q)
|
||||||
|
*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)
|
||||||
|
|||||||
@@ -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];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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,19 +32,16 @@ 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);
|
||||||
|
|
||||||
// Lever bonus by rank
|
// Lever bonus by rank
|
||||||
const Score Lever[RANK_NB] = {
|
const Score Lever[RANK_NB] = {
|
||||||
@@ -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,13 +100,13 @@ 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);
|
||||||
|
|
||||||
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0;
|
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0;
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
@@ -61,7 +65,7 @@ PieceType min_attacker(const Bitboard* bb, Square to, Bitboard stmAttackers,
|
|||||||
|
|
||||||
Bitboard b = stmAttackers & bb[Pt];
|
Bitboard b = stmAttackers & bb[Pt];
|
||||||
if (!b)
|
if (!b)
|
||||||
return min_attacker<Pt+1>(bb, to, stmAttackers, occupied, attackers);
|
return min_attacker<Pt + 1>(bb, to, stmAttackers, occupied, attackers);
|
||||||
|
|
||||||
occupied ^= b & ~(b - 1);
|
occupied ^= b & ~(b - 1);
|
||||||
|
|
||||||
@@ -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
|
||||||
@@ -433,7 +463,7 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners
|
|||||||
pinners = 0;
|
pinners = 0;
|
||||||
|
|
||||||
// Snipers are sliders that attack 's' when a piece is removed
|
// Snipers are sliders that attack 's' when a piece is removed
|
||||||
Bitboard snipers = ( (PseudoAttacks[ROOK ][s] & pieces(QUEEN, ROOK))
|
Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK))
|
||||||
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
|
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
|
||||||
|
|
||||||
while (snipers)
|
while (snipers)
|
||||||
@@ -460,7 +490,7 @@ Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
|
|||||||
return (attacks_from<PAWN>(s, BLACK) & pieces(WHITE, PAWN))
|
return (attacks_from<PAWN>(s, BLACK) & pieces(WHITE, PAWN))
|
||||||
| (attacks_from<PAWN>(s, WHITE) & pieces(BLACK, PAWN))
|
| (attacks_from<PAWN>(s, WHITE) & pieces(BLACK, PAWN))
|
||||||
| (attacks_from<KNIGHT>(s) & pieces(KNIGHT))
|
| (attacks_from<KNIGHT>(s) & pieces(KNIGHT))
|
||||||
| (attacks_bb<ROOK >(s, occupied) & pieces(ROOK, QUEEN))
|
| (attacks_bb< ROOK>(s, occupied) & pieces( ROOK, QUEEN))
|
||||||
| (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN))
|
| (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN))
|
||||||
| (attacks_from<KING>(s) & pieces(KING));
|
| (attacks_from<KING>(s) & pieces(KING));
|
||||||
}
|
}
|
||||||
@@ -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))
|
||||||
{
|
{
|
||||||
@@ -1159,9 +1181,8 @@ 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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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&);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user