DroidFish: Updated stockfish to version 231015.

This commit is contained in:
Peter Osterlund
2015-10-23 22:58:14 +02:00
parent e768c9408a
commit 0d72a21f27
39 changed files with 2118 additions and 2574 deletions

View File

@@ -1,7 +1,7 @@
LOCAL_PATH := $(call my-dir) LOCAL_PATH := $(call my-dir)
SF_SRC_FILES := \ SF_SRC_FILES := \
benchmark.cpp main.cpp movegen.cpp pawns.cpp thread.cpp uci.cpp \ benchmark.cpp main.cpp movegen.cpp pawns.cpp thread.cpp uci.cpp psqt.cpp \
bitbase.cpp endgame.cpp material.cpp movepick.cpp position.cpp timeman.cpp ucioption.cpp \ bitbase.cpp endgame.cpp material.cpp movepick.cpp position.cpp timeman.cpp ucioption.cpp \
bitboard.cpp evaluate.cpp misc.cpp search.cpp tt.cpp syzygy/tbprobe.cpp bitboard.cpp evaluate.cpp misc.cpp search.cpp tt.cpp syzygy/tbprobe.cpp

View File

@@ -17,7 +17,6 @@
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 <algorithm>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <istream> #include <istream>
@@ -27,14 +26,13 @@
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "thread.h" #include "thread.h"
#include "tt.h"
#include "uci.h" #include "uci.h"
using namespace std; using namespace std;
namespace { namespace {
const char* Defaults[] = { const vector<string> Defaults = {
"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",
@@ -105,22 +103,22 @@ void benchmark(const Position& current, istream& is) {
Options["Hash"] = ttSize; Options["Hash"] = ttSize;
Options["Threads"] = threads; Options["Threads"] = threads;
TT.clear(); Search::reset();
if (limitType == "time") if (limitType == "time")
limits.movetime = atoi(limit.c_str()); // movetime is in ms limits.movetime = stoi(limit); // movetime is in ms
else if (limitType == "nodes") else if (limitType == "nodes")
limits.nodes = atoi(limit.c_str()); limits.nodes = stoi(limit);
else if (limitType == "mate") else if (limitType == "mate")
limits.mate = atoi(limit.c_str()); limits.mate = stoi(limit);
else else
limits.depth = atoi(limit.c_str()); limits.depth = stoi(limit);
if (fenFile == "default") if (fenFile == "default")
fens.assign(Defaults, Defaults + 37); fens = Defaults;
else if (fenFile == "current") else if (fenFile == "current")
fens.push_back(current.fen()); fens.push_back(current.fen());
@@ -128,7 +126,7 @@ void benchmark(const Position& current, istream& is) {
else else
{ {
string fen; string fen;
ifstream file(fenFile.c_str()); ifstream file(fenFile);
if (!file.is_open()) if (!file.is_open())
{ {
@@ -144,8 +142,7 @@ void benchmark(const Position& current, istream& is) {
} }
uint64_t nodes = 0; uint64_t nodes = 0;
Search::StateStackPtr st; TimePoint elapsed = now();
Time::point elapsed = Time::now();
for (size_t i = 0; i < fens.size(); ++i) for (size_t i = 0; i < fens.size(); ++i)
{ {
@@ -158,13 +155,15 @@ void benchmark(const Position& current, istream& is) {
else else
{ {
Search::StateStackPtr st;
limits.startTime = now();
Threads.start_thinking(pos, limits, st); Threads.start_thinking(pos, limits, st);
Threads.wait_for_think_finished(); Threads.main()->join();
nodes += Search::RootPos.nodes_searched(); nodes += Threads.nodes_searched();
} }
} }
elapsed = std::max(Time::now() - elapsed, Time::point(1)); // Avoid a 'divide by zero' elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
dbg_print(); // Just before to exit dbg_print(); // Just before to exit

View File

@@ -17,7 +17,9 @@
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 <algorithm>
#include <cassert> #include <cassert>
#include <numeric>
#include <vector> #include <vector>
#include "bitboard.h" #include "bitboard.h"
@@ -51,20 +53,19 @@ namespace {
WIN = 4 WIN = 4
}; };
inline Result& operator|=(Result& r, Result v) { return r = Result(r | v); } Result& operator|=(Result& r, Result v) { return r = Result(r | v); }
struct KPKPosition { struct KPKPosition {
KPKPosition() = default;
KPKPosition(unsigned idx); explicit KPKPosition(unsigned idx);
operator Result() const { return result; } operator Result() const { return result; }
Result classify(const std::vector<KPKPosition>& db) Result classify(const std::vector<KPKPosition>& db)
{ return us == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); } { return us == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); }
private:
template<Color Us> Result classify(const std::vector<KPKPosition>& db); template<Color Us> Result classify(const std::vector<KPKPosition>& db);
Color us; Color us;
Square bksq, wksq, psq; Square ksq[COLOR_NB], psq;
Result result; Result result;
}; };
@@ -82,13 +83,12 @@ bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color us) {
void Bitbases::init() { void Bitbases::init() {
std::vector<KPKPosition> db(MAX_INDEX);
unsigned idx, repeat = 1; unsigned idx, repeat = 1;
std::vector<KPKPosition> db;
db.reserve(MAX_INDEX);
// Initialize db with known win / draw positions // Initialize db with known win / draw positions
for (idx = 0; idx < MAX_INDEX; ++idx) for (idx = 0; idx < MAX_INDEX; ++idx)
db.push_back(KPKPosition(idx)); db[idx] = KPKPosition(idx);
// Iterate through the positions until none of the unknown positions can be // Iterate through the positions until none of the unknown positions can be
// changed to either wins or draws (15 cycles needed). // changed to either wins or draws (15 cycles needed).
@@ -107,69 +107,73 @@ namespace {
KPKPosition::KPKPosition(unsigned idx) { KPKPosition::KPKPosition(unsigned idx) {
wksq = Square((idx >> 0) & 0x3F); ksq[WHITE] = Square((idx >> 0) & 0x3F);
bksq = Square((idx >> 6) & 0x3F); ksq[BLACK] = Square((idx >> 6) & 0x3F);
us = Color ((idx >> 12) & 0x01); us = Color ((idx >> 12) & 0x01);
psq = make_square(File((idx >> 13) & 0x3), RANK_7 - Rank((idx >> 15) & 0x7)); psq = make_square(File((idx >> 13) & 0x3), RANK_7 - Rank((idx >> 15) & 0x7));
result = UNKNOWN;
// Check if two pieces are on the same square or if a king can be captured // Check if two pieces are on the same square or if a king can be captured
if ( distance(wksq, bksq) <= 1 if ( distance(ksq[WHITE], ksq[BLACK]) <= 1
|| wksq == psq || ksq[WHITE] == psq
|| bksq == psq || ksq[BLACK] == psq
|| (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq))) || (us == WHITE && (StepAttacksBB[PAWN][psq] & ksq[BLACK])))
result = INVALID; result = INVALID;
else if (us == WHITE)
{
// Immediate win if a pawn can be promoted without getting captured // Immediate win if a pawn can be promoted without getting captured
if ( rank_of(psq) == RANK_7 else if ( us == WHITE
&& wksq != psq + DELTA_N && rank_of(psq) == RANK_7
&& ( distance(bksq, psq + DELTA_N) > 1 && ksq[us] != psq + DELTA_N
||(StepAttacksBB[KING][wksq] & (psq + DELTA_N)))) && ( distance(ksq[~us], psq + DELTA_N) > 1
|| (StepAttacksBB[KING][ksq[us]] & (psq + DELTA_N))))
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 ( !(StepAttacksBB[KING][bksq] & ~(StepAttacksBB[KING][wksq] | StepAttacksBB[PAWN][psq])) else if ( us == BLACK
|| (StepAttacksBB[KING][bksq] & psq & ~StepAttacksBB[KING][wksq])) && ( !(StepAttacksBB[KING][ksq[us]] & ~(StepAttacksBB[KING][ksq[~us]] | StepAttacksBB[PAWN][psq]))
|| (StepAttacksBB[KING][ksq[us]] & psq & ~StepAttacksBB[KING][ksq[~us]])))
result = DRAW; result = DRAW;
// Position will be classified later
else
result = UNKNOWN;
} }
template<Color Us> template<Color Us>
Result KPKPosition::classify(const std::vector<KPKPosition>& db) { Result KPKPosition::classify(const std::vector<KPKPosition>& db) {
// White to Move: If one move leads to a position classified as WIN, the result // White to move: If one move leads to a position classified as WIN, the result
// of the current position is WIN. If all moves lead to positions classified // of the current position is WIN. If all moves lead to positions classified
// as DRAW, the current position is classified as DRAW, otherwise the current // as DRAW, the current position is classified as DRAW, otherwise the current
// position is classified as UNKNOWN. // position is classified as UNKNOWN.
// //
// Black to Move: If one move leads to a position classified as DRAW, the result // Black to move: If one move leads to a position classified as DRAW, the result
// of the current position is DRAW. If all moves lead to positions classified // of the current position is DRAW. If all moves lead to positions classified
// as WIN, the position is classified as WIN, otherwise the current position is // as WIN, the position is classified as WIN, otherwise the current position is
// classified as UNKNOWN. // classified as UNKNOWN.
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
const Result Good = (Us == WHITE ? WIN : DRAW);
const Result Bad = (Us == WHITE ? DRAW : WIN);
Result r = INVALID; Result r = INVALID;
Bitboard b = StepAttacksBB[KING][Us == WHITE ? wksq : bksq]; Bitboard b = StepAttacksBB[KING][ksq[Us]];
while (b) while (b)
r |= Us == WHITE ? db[index(Them, bksq, pop_lsb(&b), psq)] r |= Us == WHITE ? db[index(Them, ksq[Them] , pop_lsb(&b), psq)]
: db[index(Them, pop_lsb(&b), wksq, psq)]; : db[index(Them, pop_lsb(&b), ksq[Them] , psq)];
if (Us == WHITE && rank_of(psq) < RANK_7)
{
Square s = psq + DELTA_N;
r |= db[index(BLACK, bksq, wksq, s)]; // Single push
if (rank_of(psq) == RANK_2 && s != wksq && s != bksq)
r |= db[index(BLACK, bksq, wksq, s + DELTA_N)]; // Double push
}
if (Us == WHITE) if (Us == WHITE)
return result = r & WIN ? WIN : r & UNKNOWN ? UNKNOWN : DRAW; {
else if (rank_of(psq) < RANK_7) // Single push
return result = r & DRAW ? DRAW : r & UNKNOWN ? UNKNOWN : WIN; r |= db[index(Them, ksq[Them], ksq[Us], psq + DELTA_N)];
if ( rank_of(psq) == RANK_2 // Double push
&& psq + DELTA_N != ksq[Us]
&& psq + DELTA_N != ksq[Them])
r |= db[index(Them, ksq[Them], ksq[Us], psq + DELTA_N + DELTA_N)];
}
return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad;
} }
} // namespace } // namespace

View File

@@ -18,7 +18,6 @@
*/ */
#include <algorithm> #include <algorithm>
#include <cstring> // For std::memset
#include "bitboard.h" #include "bitboard.h"
#include "bitcount.h" #include "bitcount.h"
@@ -56,7 +55,7 @@ namespace {
const uint64_t DeBruijn64 = 0x3F79D71B4CB0A89ULL; const uint64_t DeBruijn64 = 0x3F79D71B4CB0A89ULL;
const uint32_t DeBruijn32 = 0x783A9B23; const uint32_t DeBruijn32 = 0x783A9B23;
int MS1BTable[256]; // To implement software msb() int MSBTable[256]; // To implement software msb()
Square BSFTable[SQUARE_NB]; // To implement software bitscan Square BSFTable[SQUARE_NB]; // To implement software bitscan
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
@@ -69,7 +68,7 @@ namespace {
// 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.
FORCE_INLINE unsigned bsf_index(Bitboard b) { unsigned bsf_index(Bitboard b) {
b ^= b - 1; b ^= b - 1;
return Is64Bit ? (b * DeBruijn64) >> 58 return Is64Bit ? (b * DeBruijn64) >> 58
: ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn32) >> 26; : ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn32) >> 26;
@@ -109,7 +108,7 @@ Square msb(Bitboard b) {
result += 8; result += 8;
} }
return Square(result + MS1BTable[b32]); return Square(result + MSBTable[b32]);
} }
#endif // ifndef USE_BSFQ #endif // ifndef USE_BSFQ
@@ -125,9 +124,9 @@ const std::string Bitboards::pretty(Bitboard b) {
for (Rank r = RANK_8; r >= RANK_1; --r) for (Rank r = RANK_8; r >= RANK_1; --r)
{ {
for (File f = FILE_A; f <= FILE_H; ++f) for (File f = FILE_A; f <= FILE_H; ++f)
s.append(b & make_square(f, r) ? "| X " : "| "); s += b & make_square(f, r) ? "| X " : "| ";
s.append("|\n+---+---+---+---+---+---+---+---+\n"); s += "|\n+---+---+---+---+---+---+---+---+\n";
} }
return s; return s;
@@ -145,8 +144,8 @@ void Bitboards::init() {
BSFTable[bsf_index(SquareBB[s])] = s; BSFTable[bsf_index(SquareBB[s])] = s;
} }
for (Bitboard b = 1; b < 256; ++b) for (Bitboard b = 2; b < 256; ++b)
MS1BTable[b] = more_than_one(b) ? MS1BTable[b - 1] : lsb(b); MSBTable[b] = MSBTable[b - 1] + !more_than_one(b);
for (File f = FILE_A; f <= FILE_H; ++f) for (File f = FILE_A; f <= FILE_H; ++f)
FileBB[f] = f > FILE_A ? FileBB[f - 1] << 1 : FileABB; FileBB[f] = f > FILE_A ? FileBB[f - 1] << 1 : FileABB;
@@ -201,12 +200,10 @@ void Bitboards::init() {
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 (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
{ {
Piece pc = (PseudoAttacks[BISHOP][s1] & s2) ? W_BISHOP : if (!(PseudoAttacks[pc][s1] & s2))
(PseudoAttacks[ROOK][s1] & s2) ? W_ROOK : NO_PIECE;
if (pc == NO_PIECE)
continue; continue;
LineBB[s1][s2] = (attacks_bb(pc, s1, 0) & attacks_bb(pc, s2, 0)) | s1 | s2; LineBB[s1][s2] = (attacks_bb(pc, s1, 0) & attacks_bb(pc, s2, 0)) | s1 | s2;
@@ -249,7 +246,7 @@ namespace {
{ 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 i, size; int age[4096] = {0}, current = 0, i, size;
// attacks[s] is a pointer to the beginning of the attacks table for square 's' // attacks[s] is a pointer to the beginning of the attacks table for square 's'
attacks[SQ_A1] = table; attacks[SQ_A1] = table;
@@ -298,22 +295,21 @@ namespace {
magics[s] = rng.sparse_rand<Bitboard>(); magics[s] = rng.sparse_rand<Bitboard>();
while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6); while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6);
std::memset(attacks[s], 0, size * sizeof(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.
for (i = 0; i < size; ++i) for (++current, i = 0; i < size; ++i)
{ {
Bitboard& attack = attacks[s][index(s, occupancy[i])]; unsigned idx = index(s, occupancy[i]);
if (attack && attack != reference[i]) if (age[idx] < current)
{
age[idx] = current;
attacks[s][idx] = reference[i];
}
else if (attacks[s][idx] != reference[i])
break; break;
assert(reference[i]);
attack = reference[i];
} }
} while (i < size); } while (i < size);
} }

View File

@@ -200,14 +200,6 @@ inline Bitboard passed_pawn_mask(Color c, Square s) {
} }
/// squares_of_color() returns a bitboard representing all the squares of the
/// same color of the given one.
inline Bitboard squares_of_color(Square s) {
return DarkSquares & s ? DarkSquares : ~DarkSquares;
}
/// aligned() returns true if the squares s1, s2 and s3 are aligned either on a /// aligned() returns true if the squares s1, s2 and s3 are aligned either on a
/// straight or on a diagonal line. /// straight or on a diagonal line.
@@ -231,7 +223,7 @@ template<> inline int distance<Rank>(Square x, Square y) { return distance(rank_
/// piece of type Pt (bishop or rook) placed on 's'. The helper magic_index() /// piece of type Pt (bishop or rook) placed on 's'. The helper magic_index()
/// looks up the index using the 'magic bitboards' approach. /// looks up the index using the 'magic bitboards' approach.
template<PieceType Pt> template<PieceType Pt>
FORCE_INLINE unsigned magic_index(Square s, Bitboard occupied) { inline unsigned magic_index(Square s, Bitboard occupied) {
Bitboard* const Masks = Pt == ROOK ? RookMasks : BishopMasks; Bitboard* const Masks = Pt == ROOK ? RookMasks : BishopMasks;
Bitboard* const Magics = Pt == ROOK ? RookMagics : BishopMagics; Bitboard* const Magics = Pt == ROOK ? RookMagics : BishopMagics;
@@ -271,13 +263,13 @@ inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) {
# if defined(_MSC_VER) && !defined(__INTEL_COMPILER) # if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
FORCE_INLINE Square lsb(Bitboard b) { inline Square lsb(Bitboard b) {
unsigned long idx; unsigned long idx;
_BitScanForward64(&idx, b); _BitScanForward64(&idx, b);
return (Square) idx; return (Square) idx;
} }
FORCE_INLINE Square msb(Bitboard b) { inline Square msb(Bitboard b) {
unsigned long idx; unsigned long idx;
_BitScanReverse64(&idx, b); _BitScanReverse64(&idx, b);
return (Square) idx; return (Square) idx;
@@ -285,28 +277,28 @@ FORCE_INLINE Square msb(Bitboard b) {
# elif defined(__arm__) # elif defined(__arm__)
FORCE_INLINE int lsb32(uint32_t v) { inline int lsb32(uint32_t v) {
__asm__("rbit %0, %1" : "=r"(v) : "r"(v)); __asm__("rbit %0, %1" : "=r"(v) : "r"(v));
return __builtin_clz(v); return __builtin_clz(v);
} }
FORCE_INLINE Square msb(Bitboard b) { inline Square msb(Bitboard b) {
return (Square) (63 - __builtin_clzll(b)); return (Square) (63 - __builtin_clzll(b));
} }
FORCE_INLINE Square lsb(Bitboard b) { inline Square lsb(Bitboard b) {
return (Square) (uint32_t(b) ? lsb32(uint32_t(b)) : 32 + lsb32(uint32_t(b >> 32))); return (Square) (uint32_t(b) ? lsb32(uint32_t(b)) : 32 + lsb32(uint32_t(b >> 32)));
} }
# else // Assumed gcc or compatible compiler # else // Assumed gcc or compatible compiler
FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen inline Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen
Bitboard idx; Bitboard idx;
__asm__("bsfq %1, %0": "=r"(idx): "rm"(b) ); __asm__("bsfq %1, %0": "=r"(idx): "rm"(b) );
return (Square) idx; return (Square) idx;
} }
FORCE_INLINE Square msb(Bitboard b) { inline Square msb(Bitboard b) {
Bitboard idx; Bitboard idx;
__asm__("bsrq %1, %0": "=r"(idx): "rm"(b) ); __asm__("bsrq %1, %0": "=r"(idx): "rm"(b) );
return (Square) idx; return (Square) idx;
@@ -324,7 +316,7 @@ Square msb(Bitboard b);
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard /// pop_lsb() finds and clears the least significant bit in a non-zero bitboard
FORCE_INLINE Square pop_lsb(Bitboard* b) { inline Square pop_lsb(Bitboard* b) {
const Square s = lsb(*b); const Square s = lsb(*b);
*b &= *b - 1; *b &= *b - 1;
return s; return s;

View File

@@ -71,7 +71,7 @@ namespace {
assert(pos.count<PAWN>(strongSide) == 1); assert(pos.count<PAWN>(strongSide) == 1);
if (file_of(pos.list<PAWN>(strongSide)[0]) >= FILE_E) if (file_of(pos.square<PAWN>(strongSide)) >= FILE_E)
sq = Square(sq ^ 7); // Mirror SQ_H1 -> SQ_A1 sq = Square(sq ^ 7); // Mirror SQ_H1 -> SQ_A1
if (strongSide == BLACK) if (strongSide == BLACK)
@@ -96,12 +96,9 @@ namespace {
string fen = sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/8/8/" 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"; + sides[1] + char(8 - sides[1].length() + '0') + " w - - 0 10";
return Position(fen, false, NULL).material_key(); return Position(fen, false, nullptr).material_key();
} }
template<typename M>
void delete_endgame(const typename M::value_type& p) { delete p.second; }
} // namespace } // namespace
@@ -128,17 +125,11 @@ Endgames::Endgames() {
add<KRPPKRP>("KRPPKRP"); add<KRPPKRP>("KRPPKRP");
} }
Endgames::~Endgames() {
for_each(m1.begin(), m1.end(), delete_endgame<M1>); template<EndgameType E, typename T>
for_each(m2.begin(), m2.end(), delete_endgame<M2>);
}
template<EndgameType E>
void Endgames::add(const string& code) { void Endgames::add(const string& code) {
map<T>()[key(code, WHITE)] = std::unique_ptr<EndgameBase<T>>(new Endgame<E>(WHITE));
map((Endgame<E>*)0)[key(code, WHITE)] = new Endgame<E>(WHITE); map<T>()[key(code, BLACK)] = std::unique_ptr<EndgameBase<T>>(new Endgame<E>(BLACK));
map((Endgame<E>*)0)[key(code, BLACK)] = new Endgame<E>(BLACK);
} }
@@ -156,8 +147,8 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
if (pos.side_to_move() == weakSide && !MoveList<LEGAL>(pos).size()) if (pos.side_to_move() == weakSide && !MoveList<LEGAL>(pos).size())
return VALUE_DRAW; return VALUE_DRAW;
Square winnerKSq = pos.king_square(strongSide); Square winnerKSq = pos.square<KING>(strongSide);
Square loserKSq = pos.king_square(weakSide); Square loserKSq = pos.square<KING>(weakSide);
Value result = pos.non_pawn_material(strongSide) Value result = pos.non_pawn_material(strongSide)
+ pos.count<PAWN>(strongSide) * PawnValueEg + pos.count<PAWN>(strongSide) * PawnValueEg
@@ -167,8 +158,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.list<BISHOP>(strongSide)[0], ||(pos.count<BISHOP>(strongSide) > 1 && opposite_colors(pos.squares<BISHOP>(strongSide)[0],
pos.list<BISHOP>(strongSide)[1]))) pos.squares<BISHOP>(strongSide)[1])))
result += VALUE_KNOWN_WIN; result += VALUE_KNOWN_WIN;
return strongSide == pos.side_to_move() ? result : -result; return strongSide == pos.side_to_move() ? result : -result;
@@ -183,9 +174,9 @@ Value Endgame<KBNK>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, KnightValueMg + BishopValueMg, 0)); assert(verify_material(pos, strongSide, KnightValueMg + BishopValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
Square winnerKSq = pos.king_square(strongSide); Square winnerKSq = pos.square<KING>(strongSide);
Square loserKSq = pos.king_square(weakSide); Square loserKSq = pos.square<KING>(weakSide);
Square bishopSq = pos.list<BISHOP>(strongSide)[0]; Square bishopSq = pos.square<BISHOP>(strongSide);
// kbnk_mate_table() tries to drive toward corners A1 or H8. If we have a // kbnk_mate_table() tries to drive toward corners A1 or H8. If we have a
// bishop that cannot reach the above squares, we flip the kings in order // bishop that cannot reach the above squares, we flip the kings in order
@@ -212,9 +203,9 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
// Assume strongSide is white and the pawn is on files A-D // Assume strongSide is white and the pawn is on files A-D
Square wksq = normalize(pos, strongSide, pos.king_square(strongSide)); Square wksq = normalize(pos, strongSide, pos.square<KING>(strongSide));
Square bksq = normalize(pos, strongSide, pos.king_square(weakSide)); Square bksq = normalize(pos, strongSide, pos.square<KING>(weakSide));
Square psq = normalize(pos, strongSide, pos.list<PAWN>(strongSide)[0]); Square psq = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;
@@ -237,10 +228,10 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, strongSide, RookValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
Square wksq = relative_square(strongSide, pos.king_square(strongSide)); Square wksq = relative_square(strongSide, pos.square<KING>(strongSide));
Square bksq = relative_square(strongSide, pos.king_square(weakSide)); Square bksq = relative_square(strongSide, pos.square<KING>(weakSide));
Square rsq = relative_square(strongSide, pos.list<ROOK>(strongSide)[0]); Square rsq = relative_square(strongSide, pos.square<ROOK>(strongSide));
Square psq = relative_square(strongSide, pos.list<PAWN>(weakSide)[0]); Square psq = relative_square(strongSide, pos.square<PAWN>(weakSide));
Square queeningSq = make_square(file_of(psq), RANK_1); Square queeningSq = make_square(file_of(psq), RANK_1);
Value result; Value result;
@@ -280,7 +271,7 @@ Value Endgame<KRKB>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, strongSide, RookValueMg, 0));
assert(verify_material(pos, weakSide, BishopValueMg, 0)); assert(verify_material(pos, weakSide, BishopValueMg, 0));
Value result = Value(PushToEdges[pos.king_square(weakSide)]); Value result = Value(PushToEdges[pos.square<KING>(weakSide)]);
return strongSide == pos.side_to_move() ? result : -result; return strongSide == pos.side_to_move() ? result : -result;
} }
@@ -293,8 +284,8 @@ Value Endgame<KRKN>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, strongSide, RookValueMg, 0));
assert(verify_material(pos, weakSide, KnightValueMg, 0)); assert(verify_material(pos, weakSide, KnightValueMg, 0));
Square bksq = pos.king_square(weakSide); Square bksq = pos.square<KING>(weakSide);
Square bnsq = pos.list<KNIGHT>(weakSide)[0]; Square bnsq = pos.square<KNIGHT>(weakSide);
Value result = Value(PushToEdges[bksq] + PushAway[distance(bksq, bnsq)]); Value result = Value(PushToEdges[bksq] + PushAway[distance(bksq, bnsq)]);
return strongSide == pos.side_to_move() ? result : -result; return strongSide == pos.side_to_move() ? result : -result;
} }
@@ -310,9 +301,9 @@ Value Endgame<KQKP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, QueenValueMg, 0)); assert(verify_material(pos, strongSide, QueenValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
Square winnerKSq = pos.king_square(strongSide); Square winnerKSq = pos.square<KING>(strongSide);
Square loserKSq = pos.king_square(weakSide); Square loserKSq = pos.square<KING>(weakSide);
Square pawnSq = pos.list<PAWN>(weakSide)[0]; Square pawnSq = pos.square<PAWN>(weakSide);
Value result = Value(PushClose[distance(winnerKSq, loserKSq)]); Value result = Value(PushClose[distance(winnerKSq, loserKSq)]);
@@ -335,8 +326,8 @@ Value Endgame<KQKR>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, QueenValueMg, 0)); assert(verify_material(pos, strongSide, QueenValueMg, 0));
assert(verify_material(pos, weakSide, RookValueMg, 0)); assert(verify_material(pos, weakSide, RookValueMg, 0));
Square winnerKSq = pos.king_square(strongSide); Square winnerKSq = pos.square<KING>(strongSide);
Square loserKSq = pos.king_square(weakSide); Square loserKSq = pos.square<KING>(weakSide);
Value result = QueenValueEg Value result = QueenValueEg
- RookValueEg - RookValueEg
@@ -365,15 +356,15 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
// be detected even when the weaker side has some pawns. // be detected even when the weaker side has some pawns.
Bitboard pawns = pos.pieces(strongSide, PAWN); Bitboard pawns = pos.pieces(strongSide, PAWN);
File pawnFile = file_of(pos.list<PAWN>(strongSide)[0]); File pawnsFile = file_of(lsb(pawns));
// All pawns are on a single rook file ? // All pawns are on a single rook file?
if ( (pawnFile == FILE_A || pawnFile == FILE_H) if ( (pawnsFile == FILE_A || pawnsFile == FILE_H)
&& !(pawns & ~file_bb(pawnFile))) && !(pawns & ~file_bb(pawnsFile)))
{ {
Square bishopSq = pos.list<BISHOP>(strongSide)[0]; Square bishopSq = pos.square<BISHOP>(strongSide);
Square queeningSq = relative_square(strongSide, make_square(pawnFile, RANK_8)); Square queeningSq = relative_square(strongSide, make_square(pawnsFile, RANK_8));
Square kingSq = pos.king_square(weakSide); Square kingSq = pos.square<KING>(weakSide);
if ( opposite_colors(queeningSq, bishopSq) if ( opposite_colors(queeningSq, bishopSq)
&& distance(queeningSq, kingSq) <= 1) && distance(queeningSq, kingSq) <= 1)
@@ -381,17 +372,17 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
} }
// If all the pawns are on the same B or G file, then it's potentially a draw // If all the pawns are on the same B or G file, then it's potentially a draw
if ( (pawnFile == FILE_B || pawnFile == FILE_G) if ( (pawnsFile == FILE_B || pawnsFile == FILE_G)
&& !(pos.pieces(PAWN) & ~file_bb(pawnFile)) && !(pos.pieces(PAWN) & ~file_bb(pawnsFile))
&& pos.non_pawn_material(weakSide) == 0 && pos.non_pawn_material(weakSide) == 0
&& pos.count<PAWN>(weakSide) >= 1) && pos.count<PAWN>(weakSide) >= 1)
{ {
// Get weakSide pawn that is closest to the home rank // Get weakSide pawn that is closest to the home rank
Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN)); Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN));
Square strongKingSq = pos.king_square(strongSide); Square strongKingSq = pos.square<KING>(strongSide);
Square weakKingSq = pos.king_square(weakSide); Square weakKingSq = pos.square<KING>(weakSide);
Square bishopSq = pos.list<BISHOP>(strongSide)[0]; Square bishopSq = pos.square<BISHOP>(strongSide);
// There's potential for a draw if our pawn is blocked on the 7th rank, // There's potential for a draw if our pawn is blocked on the 7th rank,
// the bishop cannot attack it or they only have one pawn left // the bishop cannot attack it or they only have one pawn left
@@ -428,11 +419,11 @@ ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
assert(pos.count<ROOK>(weakSide) == 1); assert(pos.count<ROOK>(weakSide) == 1);
assert(pos.count<PAWN>(weakSide) >= 1); assert(pos.count<PAWN>(weakSide) >= 1);
Square kingSq = pos.king_square(weakSide); Square kingSq = pos.square<KING>(weakSide);
Square rsq = pos.list<ROOK>(weakSide)[0]; Square rsq = pos.square<ROOK>(weakSide);
if ( relative_rank(weakSide, kingSq) <= RANK_2 if ( relative_rank(weakSide, kingSq) <= RANK_2
&& relative_rank(weakSide, pos.king_square(strongSide)) >= RANK_4 && relative_rank(weakSide, pos.square<KING>(strongSide)) >= RANK_4
&& relative_rank(weakSide, rsq) == RANK_3 && relative_rank(weakSide, rsq) == RANK_3
&& ( pos.pieces(weakSide, PAWN) && ( pos.pieces(weakSide, PAWN)
& pos.attacks_from<KING>(kingSq) & pos.attacks_from<KING>(kingSq)
@@ -456,11 +447,11 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
assert(verify_material(pos, weakSide, RookValueMg, 0)); assert(verify_material(pos, weakSide, RookValueMg, 0));
// Assume strongSide is white and the pawn is on files A-D // Assume strongSide is white and the pawn is on files A-D
Square wksq = normalize(pos, strongSide, pos.king_square(strongSide)); Square wksq = normalize(pos, strongSide, pos.square<KING>(strongSide));
Square bksq = normalize(pos, strongSide, pos.king_square(weakSide)); Square bksq = normalize(pos, strongSide, pos.square<KING>(weakSide));
Square wrsq = normalize(pos, strongSide, pos.list<ROOK>(strongSide)[0]); Square wrsq = normalize(pos, strongSide, pos.square<ROOK>(strongSide));
Square wpsq = normalize(pos, strongSide, pos.list<PAWN>(strongSide)[0]); Square wpsq = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
Square brsq = normalize(pos, strongSide, pos.list<ROOK>(weakSide)[0]); Square brsq = normalize(pos, strongSide, pos.square<ROOK>(weakSide));
File f = file_of(wpsq); File f = file_of(wpsq);
Rank r = rank_of(wpsq); Rank r = rank_of(wpsq);
@@ -480,7 +471,7 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
if ( r == RANK_6 if ( r == RANK_6
&& distance(bksq, queeningSq) <= 1 && distance(bksq, queeningSq) <= 1
&& rank_of(wksq) + tempo <= RANK_6 && rank_of(wksq) + tempo <= RANK_6
&& (rank_of(brsq) == RANK_1 || (!tempo && distance(file_of(brsq), f) >= 3))) && (rank_of(brsq) == RANK_1 || (!tempo && distance<File>(brsq, wpsq) >= 3)))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
if ( r >= RANK_6 if ( r >= RANK_6
@@ -552,9 +543,9 @@ ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
// Test for a rook pawn // Test for a rook pawn
if (pos.pieces(PAWN) & (FileABB | FileHBB)) if (pos.pieces(PAWN) & (FileABB | FileHBB))
{ {
Square ksq = pos.king_square(weakSide); Square ksq = pos.square<KING>(weakSide);
Square bsq = pos.list<BISHOP>(weakSide)[0]; Square bsq = pos.square<BISHOP>(weakSide);
Square psq = pos.list<PAWN>(strongSide)[0]; Square psq = pos.square<PAWN>(strongSide);
Rank rk = relative_rank(strongSide, psq); Rank rk = relative_rank(strongSide, psq);
Square push = pawn_push(strongSide); Square push = pawn_push(strongSide);
@@ -567,7 +558,7 @@ ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
{ {
int d = distance(psq + 3 * push, ksq); int d = distance(psq + 3 * push, ksq);
if (d <= 2 && !(d == 0 && ksq == pos.king_square(strongSide) + 2 * push)) if (d <= 2 && !(d == 0 && ksq == pos.square<KING>(strongSide) + 2 * push))
return ScaleFactor(24); return ScaleFactor(24);
else else
return ScaleFactor(48); return ScaleFactor(48);
@@ -595,9 +586,9 @@ ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 2)); assert(verify_material(pos, strongSide, RookValueMg, 2));
assert(verify_material(pos, weakSide, RookValueMg, 1)); assert(verify_material(pos, weakSide, RookValueMg, 1));
Square wpsq1 = pos.list<PAWN>(strongSide)[0]; Square wpsq1 = pos.squares<PAWN>(strongSide)[0];
Square wpsq2 = pos.list<PAWN>(strongSide)[1]; Square wpsq2 = pos.squares<PAWN>(strongSide)[1];
Square bksq = pos.king_square(weakSide); Square bksq = pos.square<KING>(weakSide);
// Does the stronger side have a passed pawn? // Does the stronger side have a passed pawn?
if (pos.pawn_passed(strongSide, wpsq1) || pos.pawn_passed(strongSide, wpsq2)) if (pos.pawn_passed(strongSide, wpsq1) || pos.pawn_passed(strongSide, wpsq2))
@@ -610,11 +601,11 @@ ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
&& relative_rank(strongSide, bksq) > r) && relative_rank(strongSide, bksq) > r)
{ {
switch (r) { switch (r) {
case RANK_2: return ScaleFactor(10); case RANK_2: return ScaleFactor(9);
case RANK_3: return ScaleFactor(10); case RANK_3: return ScaleFactor(10);
case RANK_4: return ScaleFactor(15); case RANK_4: return ScaleFactor(14);
case RANK_5: return ScaleFactor(20); case RANK_5: return ScaleFactor(21);
case RANK_6: return ScaleFactor(40); case RANK_6: return ScaleFactor(44);
default: assert(false); default: assert(false);
} }
} }
@@ -631,15 +622,14 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
assert(pos.count<PAWN>(strongSide) >= 2); assert(pos.count<PAWN>(strongSide) >= 2);
assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
Square ksq = pos.king_square(weakSide); Square ksq = pos.square<KING>(weakSide);
Bitboard pawns = pos.pieces(strongSide, PAWN); Bitboard pawns = pos.pieces(strongSide, PAWN);
Square psq = pos.list<PAWN>(strongSide)[0];
// 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 & ~in_front_bb(weakSide, rank_of(ksq)))
&& !((pawns & ~FileABB) && (pawns & ~FileHBB)) && !((pawns & ~FileABB) && (pawns & ~FileHBB))
&& distance<File>(ksq, psq) <= 1) && distance<File>(ksq, lsb(pawns)) <= 1)
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@@ -656,10 +646,10 @@ ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, BishopValueMg, 1)); assert(verify_material(pos, strongSide, BishopValueMg, 1));
assert(verify_material(pos, weakSide, BishopValueMg, 0)); assert(verify_material(pos, weakSide, BishopValueMg, 0));
Square pawnSq = pos.list<PAWN>(strongSide)[0]; Square pawnSq = pos.square<PAWN>(strongSide);
Square strongBishopSq = pos.list<BISHOP>(strongSide)[0]; Square strongBishopSq = pos.square<BISHOP>(strongSide);
Square weakBishopSq = pos.list<BISHOP>(weakSide)[0]; Square weakBishopSq = pos.square<BISHOP>(weakSide);
Square weakKingSq = pos.king_square(weakSide); Square weakKingSq = pos.square<KING>(weakSide);
// Case 1: Defending king blocks the pawn, and cannot be driven away // Case 1: Defending king blocks the pawn, and cannot be driven away
if ( file_of(weakKingSq) == file_of(pawnSq) if ( file_of(weakKingSq) == file_of(pawnSq)
@@ -706,15 +696,15 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, BishopValueMg, 2)); assert(verify_material(pos, strongSide, BishopValueMg, 2));
assert(verify_material(pos, weakSide, BishopValueMg, 0)); assert(verify_material(pos, weakSide, BishopValueMg, 0));
Square wbsq = pos.list<BISHOP>(strongSide)[0]; Square wbsq = pos.square<BISHOP>(strongSide);
Square bbsq = pos.list<BISHOP>(weakSide)[0]; Square bbsq = pos.square<BISHOP>(weakSide);
if (!opposite_colors(wbsq, bbsq)) if (!opposite_colors(wbsq, bbsq))
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
Square ksq = pos.king_square(weakSide); Square ksq = pos.square<KING>(weakSide);
Square psq1 = pos.list<PAWN>(strongSide)[0]; Square psq1 = pos.squares<PAWN>(strongSide)[0];
Square psq2 = pos.list<PAWN>(strongSide)[1]; Square psq2 = pos.squares<PAWN>(strongSide)[1];
Rank r1 = rank_of(psq1); Rank r1 = rank_of(psq1);
Rank r2 = rank_of(psq2); Rank r2 = rank_of(psq2);
Square blockSq1, blockSq2; Square blockSq1, blockSq2;
@@ -777,9 +767,9 @@ ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, BishopValueMg, 1)); assert(verify_material(pos, strongSide, BishopValueMg, 1));
assert(verify_material(pos, weakSide, KnightValueMg, 0)); assert(verify_material(pos, weakSide, KnightValueMg, 0));
Square pawnSq = pos.list<PAWN>(strongSide)[0]; Square pawnSq = pos.square<PAWN>(strongSide);
Square strongBishopSq = pos.list<BISHOP>(strongSide)[0]; Square strongBishopSq = pos.square<BISHOP>(strongSide);
Square weakKingSq = pos.king_square(weakSide); Square weakKingSq = pos.square<KING>(weakSide);
if ( file_of(weakKingSq) == file_of(pawnSq) if ( file_of(weakKingSq) == file_of(pawnSq)
&& relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq) && relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq)
@@ -800,8 +790,8 @@ ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
// Assume strongSide is white and the pawn is on files A-D // Assume strongSide is white and the pawn is on files A-D
Square pawnSq = normalize(pos, strongSide, pos.list<PAWN>(strongSide)[0]); Square pawnSq = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
Square weakKingSq = normalize(pos, strongSide, pos.king_square(weakSide)); Square weakKingSq = normalize(pos, strongSide, pos.square<KING>(weakSide));
if (pawnSq == SQ_A7 && distance(SQ_A8, weakKingSq) <= 1) if (pawnSq == SQ_A7 && distance(SQ_A8, weakKingSq) <= 1)
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
@@ -815,9 +805,9 @@ ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
template<> template<>
ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const { ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
Square pawnSq = pos.list<PAWN>(strongSide)[0]; Square pawnSq = pos.square<PAWN>(strongSide);
Square bishopSq = pos.list<BISHOP>(weakSide)[0]; Square bishopSq = pos.square<BISHOP>(weakSide);
Square weakKingSq = pos.king_square(weakSide); Square weakKingSq = pos.square<KING>(weakSide);
// 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.
@@ -840,9 +830,9 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
// Assume strongSide is white and the pawn is on files A-D // Assume strongSide is white and the pawn is on files A-D
Square wksq = normalize(pos, strongSide, pos.king_square(strongSide)); Square wksq = normalize(pos, strongSide, pos.square<KING>(strongSide));
Square bksq = normalize(pos, strongSide, pos.king_square(weakSide)); Square bksq = normalize(pos, strongSide, pos.square<KING>(weakSide));
Square psq = normalize(pos, strongSide, pos.list<PAWN>(strongSide)[0]); Square psq = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;

View File

@@ -21,7 +21,10 @@
#define ENDGAME_H_INCLUDED #define ENDGAME_H_INCLUDED
#include <map> #include <map>
#include <memory>
#include <string> #include <string>
#include <type_traits>
#include <utility>
#include "position.h" #include "position.h"
#include "types.h" #include "types.h"
@@ -63,11 +66,9 @@ 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. Type eg_fun<int>::type returns either ScaleFactor /// Value or a ScaleFactor.
/// or Value depending on whether the template parameter is 0 or 1. template<EndgameType E> using
eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type;
template<int> struct eg_fun { typedef Value type; };
template<> struct eg_fun<1> { typedef ScaleFactor type; };
/// Base and derived templates for endgame evaluation and scaling functions /// Base and derived templates for endgame evaluation and scaling functions
@@ -75,13 +76,13 @@ template<> struct eg_fun<1> { typedef ScaleFactor type; };
template<typename T> template<typename T>
struct EndgameBase { struct EndgameBase {
virtual ~EndgameBase() {} virtual ~EndgameBase() = default;
virtual Color strong_side() const = 0; virtual Color strong_side() const = 0;
virtual T operator()(const Position&) const = 0; virtual T operator()(const Position&) const = 0;
}; };
template<EndgameType E, typename T = typename eg_fun<(E > SCALING_FUNCTIONS)>::type> template<EndgameType 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) : strongSide(c), weakSide(~c) {}
@@ -99,23 +100,24 @@ private:
class Endgames { class Endgames {
typedef std::map<Key, EndgameBase<eg_fun<0>::type>*> M1; template<typename T> using Map = std::map<Key, std::unique_ptr<EndgameBase<T>>>;
typedef std::map<Key, EndgameBase<eg_fun<1>::type>*> M2;
M1 m1; template<EndgameType E, typename T = eg_type<E>>
M2 m2; void add(const std::string& code);
M1& map(M1::mapped_type) { return m1; } template<typename T>
M2& map(M2::mapped_type) { return m2; } Map<T>& map() {
return std::get<std::is_same<T, ScaleFactor>::value>(maps);
}
template<EndgameType E> void add(const std::string& code); std::pair<Map<Value>, Map<ScaleFactor>> maps;
public: public:
Endgames(); Endgames();
~Endgames();
template<typename T> T probe(Key key, T& eg) { template<typename T>
return eg = map(eg).count(key) ? map(eg)[key] : NULL; EndgameBase<T>* probe(Key key) {
return map<T>().count(key) ? map<T>()[key].get() : nullptr;
} }
}; };

View File

@@ -30,17 +30,50 @@
namespace { namespace {
namespace Trace {
enum Term { // First 8 entries are for PieceType
MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERM_NB
};
double scores[TERM_NB][COLOR_NB][PHASE_NB];
double to_cp(Value v) { return double(v) / PawnValueEg; }
void add(int idx, Color c, Score s) {
scores[idx][c][MG] = to_cp(mg_value(s));
scores[idx][c][EG] = to_cp(eg_value(s));
}
void add(int idx, Score w, Score b = SCORE_ZERO) {
add(idx, WHITE, w); add(idx, BLACK, b);
}
std::ostream& operator<<(std::ostream& os, Term t) {
if (t == MATERIAL || t == IMBALANCE || t == Term(PAWN) || t == TOTAL)
os << " --- --- | --- --- | ";
else
os << std::setw(5) << scores[t][WHITE][MG] << " "
<< std::setw(5) << scores[t][WHITE][EG] << " | "
<< std::setw(5) << scores[t][BLACK][MG] << " "
<< std::setw(5) << scores[t][BLACK][EG] << " | ";
os << std::setw(5) << scores[t][WHITE][MG] - scores[t][BLACK][MG] << " "
<< std::setw(5) << scores[t][WHITE][EG] - scores[t][BLACK][EG] << " \n";
return os;
}
}
using namespace Trace;
// Struct EvalInfo contains various information computed and collected // Struct EvalInfo contains various information computed and collected
// by the evaluation functions. // by the evaluation functions.
struct EvalInfo { struct EvalInfo {
// Pointers to material and pawn hash table entries
Material::Entry* mi;
Pawns::Entry* pi;
// attackedBy[color][piece type] is a bitboard representing all squares // attackedBy[color][piece type] is a bitboard representing all squares
// attacked by a given color and piece type, attackedBy[color][ALL_PIECES] // attacked by a given color and piece type (can be also ALL_PIECES).
// contains all squares attacked by the given color.
Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB]; Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB];
// kingRing[color] is the zone around the king which is considered // kingRing[color] is the zone around the king which is considered
@@ -61,38 +94,29 @@ namespace {
// KingAttackWeights array. // KingAttackWeights array.
int kingAttackersWeight[COLOR_NB]; int kingAttackersWeight[COLOR_NB];
// kingAdjacentZoneAttacksCount[color] is the number of attacks to squares // kingAdjacentZoneAttacksCount[color] is the number of attacks by the given
// directly adjacent to the king of the given color. Pieces which attack // color to squares directly adjacent to the enemy king. Pieces which attack
// more than one square are counted multiple times. For instance, if black's // more than one square are counted multiple times. For instance, if there is
// king is on g8 and there's a white knight on g5, this knight adds // a white knight on g5 and black's king is on g8, this white knight adds 2
// 2 to kingAdjacentZoneAttacksCount[BLACK]. // to kingAdjacentZoneAttacksCount[WHITE].
int kingAdjacentZoneAttacksCount[COLOR_NB]; int kingAdjacentZoneAttacksCount[COLOR_NB];
Bitboard pinnedPieces[COLOR_NB]; Bitboard pinnedPieces[COLOR_NB];
Pawns::Entry* pi;
}; };
namespace Tracing {
enum Terms { // First 8 entries are for PieceType // Evaluation weights, indexed by the corresponding evaluation term
MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERMS_NB enum { Mobility, PawnStructure, PassedPawns, Space, KingSafety };
const struct Weight { int mg, eg; } Weights[] = {
{289, 344}, {233, 201}, {221, 273}, {46, 0}, {322, 0}
}; };
Score scores[COLOR_NB][TERMS_NB]; Score operator*(Score s, const Weight& w) {
EvalInfo ei; return make_score(mg_value(s) * w.mg / 256, eg_value(s) * w.eg / 256);
ScaleFactor sf;
double to_cp(Value v);
void write(int idx, Color c, Score s);
void write(int idx, Score w, Score b = SCORE_ZERO);
void print(std::stringstream& ss, const char* name, int idx);
std::string do_trace(const Position& pos);
} }
// Evaluation weights, indexed by evaluation term
enum { Mobility, PawnStructure, PassedPawns, Space, KingSafety };
const struct Weight { int mg, eg; } Weights[] = {
{289, 344}, {233, 201}, {221, 273}, {46, 0}, {321, 0}
};
#define V(v) Value(v) #define V(v) Value(v)
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
@@ -102,55 +126,58 @@ namespace {
// friendly pieces. // friendly pieces.
const Score MobilityBonus[][32] = { const Score MobilityBonus[][32] = {
{}, {}, {}, {},
{ S(-65,-50), S(-42,-30), S(-9,-10), S( 3, 0), S(15, 10), S(27, 20), // Knights { S(-70,-52), S(-52,-37), S( -7,-17), S( 0, -6), S( 8, 5), S( 16, 9), // Knights
S( 37, 28), S( 42, 31), S(44, 33) }, S( 23, 20), S( 31, 21), S( 36, 22) },
{ S(-52,-47), S(-28,-23), S( 6, 1), S(20, 15), S(34, 29), S(48, 43), // Bishops { S(-49,-44), S(-22,-13), S( 16, 0), S( 27, 11), S( 38, 19), S( 52, 34), // Bishops
S( 60, 55), S( 68, 63), S(74, 68), S(77, 72), S(80, 75), S(82, 77), S( 56, 44), S( 65, 47), S( 67, 51), S( 73, 56), S( 81, 59), S( 83, 69),
S( 84, 79), S( 86, 81) }, S( 95, 72), S(100, 75) },
{ S(-47,-53), S(-31,-26), S(-5, 0), S( 1, 16), S( 7, 32), S(13, 48), // Rooks { S(-49,-57), S(-22,-14), S(-10, 18), S( -5, 39), S( -4, 50), S( -2, 58), // Rooks
S( 18, 64), S( 22, 80), S(26, 96), S(29,109), S(31,115), S(33,119), S( 6, 78), S( 11, 86), S( 17, 92), S( 19,103), S( 26,111), S( 27,115),
S( 35,122), S( 36,123), S(37,124) }, S( 36,119), S( 41,121), S( 50,122) },
{ S(-42,-40), S(-28,-23), S(-5, -7), S( 0, 0), S( 6, 10), S(11, 19), // Queens { S(-41,-24), S(-26, -8), S( 0, 6), S( 2, 14), S( 12, 27), S( 21, 40), // Queens
S( 13, 29), S( 18, 38), S(20, 40), S(21, 41), S(22, 41), S(22, 41), S( 22, 45), S( 37, 55), S( 40, 57), S( 43, 63), S( 50, 68), S( 52, 74),
S( 22, 41), S( 23, 41), S(24, 41), S(25, 41), S(25, 41), S(25, 41), S( 56, 80), S( 66, 84), S( 68, 85), S( 69, 88), S( 71, 92), S( 72, 94),
S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41), S( 80, 96), S( 89, 98), S( 94,101), S(102,113), S(106,114), S(107,116),
S( 25, 41), S( 25, 41), S(25, 41), S(25, 41) } S(112,125), S(113,127), S(117,137), S(122,143) }
}; };
// Outpost[PieceType][Square] contains bonuses for knights and bishops outposts, // Outpost[knight/bishop][supported by pawn] contains bonuses for knights and
// indexed by piece type and square (from white's point of view). // bishops outposts, bigger if outpost piece is supported by a pawn.
const Value Outpost[][SQUARE_NB] = { const Score Outpost[][2] = {
{// A B C D E F G H { S(42,11), S(63,17) }, // Knights
V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), // Knights { S(18, 5), S(27, 8) } // Bishops
V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0),
V(0), V(0), V(4), V(8), V(8), V(4), V(0), V(0),
V(0), V(4),V(17),V(26),V(26),V(17), V(4), V(0),
V(0), V(8),V(26),V(35),V(35),V(26), V(8), V(0),
V(0), V(4),V(17),V(17),V(17),V(17), V(4), V(0) },
{
V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), // Bishops
V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0),
V(0), V(0), V(5), V(5), V(5), V(5), V(0), V(0),
V(0), V(5),V(10),V(10),V(10),V(10), V(5), V(0),
V(0),V(10),V(21),V(21),V(21),V(21),V(10), V(0),
V(0), V(5), V(8), V(8), V(8), V(8), V(5), V(0) }
}; };
// Threat[defended/weak][minor/major attacking][attacked PieceType] contains // Threat[defended/weak][minor/rook attacking][attacked PieceType] contains
// bonuses according to which piece type attacks which one. // bonuses according to which piece type attacks which one.
const Score Threat[][2][PIECE_TYPE_NB] = { const Score Threat[][2][PIECE_TYPE_NB] = {
{ { S(0, 0), S( 0, 0), S(19, 37), S(24, 37), S(44, 97), S(35,106) }, // Defended Minor { { S(0, 0), S( 0, 0), S(19, 37), S(24, 37), S(44, 97), S(35,106) }, // Minor on Defended
{ S(0, 0), S( 0, 0), S( 9, 14), S( 9, 14), S( 7, 14), S(24, 48) } }, // Defended Major { S(0, 0), S( 0, 0), S( 9, 14), S( 9, 14), S( 7, 14), S(24, 48) } }, // Rook on Defended
{ { S(0, 0), S( 0,32), S(33, 41), S(31, 50), S(41,100), S(35,104) }, // Weak Minor { { S(0, 0), S( 0,32), S(33, 41), S(31, 50), S(41,100), S(35,104) }, // Minor on Weak
{ S(0, 0), S( 0,27), S(26, 57), S(26, 57), S(0 , 43), S(23, 51) } } // Weak Major { S(0, 0), S( 0,27), S(26, 57), S(26, 57), S(0 , 43), S(23, 51) } } // Rook on Weak
}; };
// ThreatenedByPawn[PieceType] contains a penalty according to which piece // ThreatenedByPawn[PieceType] contains a penalty according to which piece
// type is attacked by an enemy pawn. // type is attacked by an enemy pawn.
const Score ThreatenedByPawn[] = { const Score ThreatenedByPawn[PIECE_TYPE_NB] = {
S(0, 0), S(0, 0), S(87, 118), S(84, 122), S(114, 203), S(121, 217) S(0, 0), S(0, 0), S(107, 138), S(84, 122), S(114, 203), S(121, 217)
}; };
// Passed[mg/eg][rank] contains midgame and endgame bonuses for passed pawns.
// We don't use a Score because we process the two components independently.
const Value Passed[][RANK_NB] = {
{ V(0), V( 1), V(34), V(90), V(214), V(328) },
{ V(7), V(14), V(37), V(63), V(134), V(189) }
};
// PassedFile[File] contains a bonus according to the file of a passed pawn.
const Score PassedFile[] = {
S( 12, 10), S( 3, 10), S( 1, -8), S(-27, -12),
S(-27, -12), S( 1, -8), S( 3, 10), S( 12, 10)
};
const Score ThreatenedByHangingPawn = S(40, 60);
// Assorted bonuses and penalties used by evaluation // Assorted bonuses and penalties used by evaluation
const Score KingOnOne = S( 2, 58); const Score KingOnOne = S( 2, 58);
const Score KingOnMany = S( 6,125); const Score KingOnMany = S( 6,125);
@@ -162,6 +189,8 @@ namespace {
const Score TrappedRook = S(92, 0); const Score TrappedRook = S(92, 0);
const Score Unstoppable = S( 0, 20); const Score Unstoppable = S( 0, 20);
const Score Hanging = S(31, 26); const Score Hanging = S(31, 26);
const Score PawnAttackThreat = S(20, 20);
const Score Checked = S(20, 20);
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by // Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
// a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only // a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only
@@ -175,7 +204,7 @@ namespace {
// by the space evaluation. In the middlegame, each side is given a bonus // by the space evaluation. In the middlegame, each side is given a bonus
// based on how many squares inside this area are safe and available for // based on how many squares inside this area are safe and available for
// friendly minor pieces. // friendly minor pieces.
const Bitboard SpaceMask[] = { const Bitboard SpaceMask[COLOR_NB] = {
(FileCBB | FileDBB | FileEBB | FileFBB) & (Rank2BB | Rank3BB | Rank4BB), (FileCBB | FileDBB | FileEBB | FileFBB) & (Rank2BB | Rank3BB | Rank4BB),
(FileCBB | FileDBB | FileEBB | FileFBB) & (Rank7BB | Rank6BB | Rank5BB) (FileCBB | FileDBB | FileEBB | FileFBB) & (Rank7BB | Rank6BB | Rank5BB)
}; };
@@ -184,26 +213,17 @@ namespace {
// in KingDanger[]. Various little "meta-bonuses" measuring the strength // in KingDanger[]. Various little "meta-bonuses" measuring the strength
// of the enemy attack are added up into an integer, which is used as an // of the enemy attack are added up into an integer, which is used as an
// index to KingDanger[]. // index to KingDanger[].
//
// KingAttackWeights[PieceType] contains king attack weights by piece type
const int KingAttackWeights[] = { 0, 0, 6, 2, 5, 5 };
// Bonuses for enemy's safe checks
const int QueenContactCheck = 92;
const int RookContactCheck = 68;
const int QueenCheck = 50;
const int RookCheck = 36;
const int BishopCheck = 7;
const int KnightCheck = 14;
// KingDanger[attackUnits] contains the actual king danger weighted
// scores, indexed by a calculated integer number.
Score KingDanger[512]; Score KingDanger[512];
// apply_weight() weighs score 's' by weight 'w' trying to prevent overflow // KingAttackWeights[PieceType] contains king attack weights by piece type
Score apply_weight(Score s, const Weight& w) { const int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 7, 5, 4, 1 };
return make_score(mg_value(s) * w.mg / 256, eg_value(s) * w.eg / 256);
} // Penalties for enemy's safe checks
const int QueenContactCheck = 89;
const int QueenCheck = 50;
const int RookCheck = 45;
const int BishopCheck = 6;
const int KnightCheck = 14;
// init_eval_info() initializes king bitboards for given color adding // init_eval_info() initializes king bitboards for given color adding
@@ -216,9 +236,9 @@ namespace {
const Square Down = (Us == WHITE ? DELTA_S : DELTA_N); const Square Down = (Us == WHITE ? DELTA_S : DELTA_N);
ei.pinnedPieces[Us] = pos.pinned_pieces(Us); ei.pinnedPieces[Us] = pos.pinned_pieces(Us);
Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.square<KING>(Them));
Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them)); ei.attackedBy[Them][ALL_PIECES] |= b;
ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us); ei.attackedBy[Us][ALL_PIECES] |= ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us);
// Init king safety tables only if we are going to use them // Init king safety tables only if we are going to use them
if (pos.non_pawn_material(Us) >= QueenValueMg) if (pos.non_pawn_material(Us) >= QueenValueMg)
@@ -233,37 +253,10 @@ namespace {
} }
// evaluate_outpost() evaluates bishop and knight outpost squares
template<PieceType Pt, Color Us>
Score evaluate_outpost(const Position& pos, const EvalInfo& ei, Square s) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
assert (Pt == BISHOP || Pt == KNIGHT);
// Initial bonus based on square
Value bonus = Outpost[Pt == BISHOP][relative_square(Us, s)];
// Increase bonus if supported by pawn, especially if the opponent has
// no minor piece which can trade with the outpost piece.
if (bonus && (ei.attackedBy[Us][PAWN] & s))
{
if ( !pos.pieces(Them, KNIGHT)
&& !(squares_of_color(s) & pos.pieces(Them, BISHOP)))
bonus += bonus + bonus / 2;
else
bonus += bonus / 2;
}
return make_score(bonus * 2, bonus / 2);
}
// evaluate_pieces() assigns bonuses and penalties to the pieces of a given color // evaluate_pieces() assigns bonuses and penalties to the pieces of a given color
template<PieceType Pt, Color Us, bool Trace> template<PieceType Pt, Color Us, bool DoTrace>
Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility, Bitboard* mobilityArea) { Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility, const Bitboard* mobilityArea) {
Bitboard b; Bitboard b;
Square s; Square s;
@@ -271,7 +264,7 @@ namespace {
const PieceType NextPt = (Us == WHITE ? Pt : PieceType(Pt + 1)); const PieceType NextPt = (Us == WHITE ? Pt : PieceType(Pt + 1));
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square* pl = pos.list<Pt>(Us); const Square* pl = pos.squares<Pt>(Us);
ei.attackedBy[Us][Pt] = 0; ei.attackedBy[Us][Pt] = 0;
@@ -283,7 +276,7 @@ namespace {
: pos.attacks_from<Pt>(s); : pos.attacks_from<Pt>(s);
if (ei.pinnedPieces[Us] & s) if (ei.pinnedPieces[Us] & s)
b &= LineBB[pos.king_square(Us)][s]; b &= LineBB[pos.square<KING>(Us)][s];
ei.attackedBy[Us][ALL_PIECES] |= ei.attackedBy[Us][Pt] |= b; ei.attackedBy[Us][ALL_PIECES] |= ei.attackedBy[Us][Pt] |= b;
@@ -301,21 +294,17 @@ namespace {
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][BISHOP]
| ei.attackedBy[Them][ROOK]); | ei.attackedBy[Them][ROOK]);
int mob = Pt != QUEEN ? popcount<Max15>(b & mobilityArea[Us]) int mob = popcount<Pt == QUEEN ? Full : Max15>(b & mobilityArea[Us]);
: popcount<Full >(b & mobilityArea[Us]);
mobility[Us] += MobilityBonus[Pt][mob]; mobility[Us] += MobilityBonus[Pt][mob];
// Decrease score if we are attacked by an enemy pawn. The remaining part
// of threat evaluation must be done later when we have full attack info.
if (ei.attackedBy[Them][PAWN] & s)
score -= ThreatenedByPawn[Pt];
if (Pt == BISHOP || Pt == KNIGHT) if (Pt == BISHOP || Pt == KNIGHT)
{ {
// Bonus for outpost square // Bonus for outpost square
if (!(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s))) if ( relative_rank(Us, s) >= RANK_4
score += evaluate_outpost<Pt, Us>(pos, ei, s); && relative_rank(Us, s) <= RANK_6
&& !(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s)))
score += Outpost[Pt == BISHOP][!!(ei.attackedBy[Us][PAWN] & s)];
// Bonus when behind a pawn // Bonus when behind a pawn
if ( relative_rank(Us, s) < RANK_5 if ( relative_rank(Us, s) < RANK_5
@@ -358,7 +347,7 @@ namespace {
// Penalize when trapped by the king, even more if king cannot castle // Penalize when trapped by the king, even more if king cannot castle
if (mob <= 3 && !ei.pi->semiopen_file(Us, file_of(s))) if (mob <= 3 && !ei.pi->semiopen_file(Us, file_of(s)))
{ {
Square ksq = pos.king_square(Us); Square ksq = pos.square<KING>(Us);
if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq))) if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq)))
&& (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1) && (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1)
@@ -368,29 +357,29 @@ namespace {
} }
} }
if (Trace) if (DoTrace)
Tracing::write(Pt, Us, score); Trace::add(Pt, Us, score);
// Recursively call evaluate_pieces() of next piece type until KING excluded // Recursively call evaluate_pieces() of next piece type until KING excluded
return score - evaluate_pieces<NextPt, Them, Trace>(pos, ei, mobility, mobilityArea); return score - evaluate_pieces<NextPt, Them, DoTrace>(pos, ei, mobility, mobilityArea);
} }
template<> template<>
Score evaluate_pieces<KING, WHITE, false>(const Position&, EvalInfo&, Score*, Bitboard*) { return SCORE_ZERO; } Score evaluate_pieces<KING, WHITE, false>(const Position&, EvalInfo&, Score*, const Bitboard*) { return SCORE_ZERO; }
template<> template<>
Score evaluate_pieces<KING, WHITE, true>(const Position&, EvalInfo&, Score*, Bitboard*) { return SCORE_ZERO; } Score evaluate_pieces<KING, WHITE, true>(const Position&, EvalInfo&, Score*, const Bitboard*) { return SCORE_ZERO; }
// evaluate_king() assigns bonuses and penalties to a king of a given color // evaluate_king() assigns bonuses and penalties to a king of a given color
template<Color Us, bool Trace> template<Color Us, bool DoTrace>
Score evaluate_king(const Position& pos, const EvalInfo& ei) { Score evaluate_king(const Position& pos, const EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard undefended, b, b1, b2, safe; Bitboard undefended, b, b1, b2, safe;
int attackUnits; int attackUnits;
const Square ksq = pos.king_square(Us); const Square ksq = pos.square<KING>(Us);
// King shelter and enemy pawns storm // King shelter and enemy pawns storm
Score score = ei.pi->king_safety<Us>(pos, ksq); Score score = ei.pi->king_safety<Us>(pos, ksq);
@@ -411,12 +400,12 @@ namespace {
// number and types of the enemy's attacking pieces, the number of // number and types of the enemy's attacking pieces, the number of
// attacked and undefended squares around our king and the quality of // attacked and undefended squares around our king and the quality of
// the pawn shelter (current 'score' value). // the pawn shelter (current 'score' value).
attackUnits = std::min(77, ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) attackUnits = std::min(72, ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them])
+ 10 * ei.kingAdjacentZoneAttacksCount[Them] + 9 * ei.kingAdjacentZoneAttacksCount[Them]
+ 19 * popcount<Max15>(undefended) + 27 * popcount<Max15>(undefended)
+ 9 * (ei.pinnedPieces[Us] != 0) + 11 * !!ei.pinnedPieces[Us]
- mg_value(score) * 63 / 512 - 64 * !pos.count<QUEEN>(Them)
- !pos.count<QUEEN>(Them) * 60; - mg_value(score) / 8;
// Analyse the enemy's safe queen contact checks. Firstly, find the // Analyse the enemy's safe queen contact checks. Firstly, find the
// undefended squares around the king reachable by the enemy queen... // undefended squares around the king reachable by the enemy queen...
@@ -425,29 +414,13 @@ namespace {
{ {
// ...and then remove squares not supported by another enemy piece // ...and then remove squares not supported by another enemy piece
b &= ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT] b &= ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT]
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]; | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]
| ei.attackedBy[Them][KING];
if (b) if (b)
attackUnits += QueenContactCheck * popcount<Max15>(b); attackUnits += QueenContactCheck * popcount<Max15>(b);
} }
// Analyse the enemy's safe rook contact checks. Firstly, find the
// undefended squares around the king reachable by the enemy rooks...
b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces(Them);
// Consider only squares where the enemy's rook gives check
b &= PseudoAttacks[ROOK][ksq];
if (b)
{
// ...and then remove squares not supported by another enemy piece
b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT]
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]);
if (b)
attackUnits += RookContactCheck * popcount<Max15>(b);
}
// Analyse the enemy's safe distance checks for sliders and knights // Analyse the enemy's safe distance checks for sliders and knights
safe = ~(ei.attackedBy[Us][ALL_PIECES] | pos.pieces(Them)); safe = ~(ei.attackedBy[Us][ALL_PIECES] | pos.pieces(Them));
@@ -457,30 +430,42 @@ namespace {
// Enemy queen safe checks // Enemy queen safe checks
b = (b1 | b2) & ei.attackedBy[Them][QUEEN]; b = (b1 | b2) & ei.attackedBy[Them][QUEEN];
if (b) if (b)
{
attackUnits += QueenCheck * popcount<Max15>(b); attackUnits += QueenCheck * popcount<Max15>(b);
score -= Checked;
}
// Enemy rooks safe checks // Enemy rooks safe checks
b = b1 & ei.attackedBy[Them][ROOK]; b = b1 & ei.attackedBy[Them][ROOK];
if (b) if (b)
{
attackUnits += RookCheck * popcount<Max15>(b); attackUnits += RookCheck * popcount<Max15>(b);
score -= Checked;
}
// Enemy bishops safe checks // Enemy bishops safe checks
b = b2 & ei.attackedBy[Them][BISHOP]; b = b2 & ei.attackedBy[Them][BISHOP];
if (b) if (b)
{
attackUnits += BishopCheck * popcount<Max15>(b); attackUnits += BishopCheck * popcount<Max15>(b);
score -= Checked;
}
// Enemy knights safe checks // Enemy knights safe checks
b = pos.attacks_from<KNIGHT>(ksq) & ei.attackedBy[Them][KNIGHT] & safe; b = pos.attacks_from<KNIGHT>(ksq) & ei.attackedBy[Them][KNIGHT] & safe;
if (b) if (b)
{
attackUnits += KnightCheck * popcount<Max15>(b); attackUnits += KnightCheck * popcount<Max15>(b);
score -= Checked;
}
// Finally, extract the king danger score from the KingDanger[] // Finally, extract the king danger score from the KingDanger[]
// array and subtract the score from evaluation. // array and subtract the score from evaluation.
score -= KingDanger[std::max(std::min(attackUnits, 399), 0)]; score -= KingDanger[std::max(std::min(attackUnits, 399), 0)];
} }
if (Trace) if (DoTrace)
Tracing::write(KING, Us, score); Trace::add(KING, Us, score);
return score; return score;
} }
@@ -489,20 +474,41 @@ namespace {
// evaluate_threats() assigns bonuses according to the type of attacking piece // evaluate_threats() assigns bonuses according to the type of attacking piece
// and the type of attacked one. // and the type of attacked one.
template<Color Us, bool Trace> template<Color Us, bool DoTrace>
Score evaluate_threats(const Position& pos, const EvalInfo& ei) { Score evaluate_threats(const Position& pos, const EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Up = (Us == WHITE ? DELTA_N : DELTA_S);
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Bitboard TRank2BB = (Us == WHITE ? Rank2BB : Rank7BB);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
enum { Defended, Weak }; enum { Defended, Weak };
enum { Minor, Major }; enum { Minor, Rook };
Bitboard b, weak, defended; Bitboard b, weak, defended, safeThreats;
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
// Non-pawn enemies attacked by a pawn
weak = (pos.pieces(Them) ^ pos.pieces(Them, PAWN)) & ei.attackedBy[Us][PAWN];
if (weak)
{
b = pos.pieces(Us, PAWN) & ( ~ei.attackedBy[Them][ALL_PIECES]
| ei.attackedBy[Us][ALL_PIECES]);
safeThreats = (shift_bb<Right>(b) | shift_bb<Left>(b)) & weak;
if (weak ^ safeThreats)
score += ThreatenedByHangingPawn;
while (safeThreats)
score += ThreatenedByPawn[type_of(pos.piece_on(pop_lsb(&safeThreats)))];
}
// Non-pawn enemies defended by a pawn // Non-pawn enemies defended by a pawn
defended = (pos.pieces(Them) ^ pos.pieces(Them, PAWN)) defended = (pos.pieces(Them) ^ pos.pieces(Them, PAWN)) & ei.attackedBy[Them][PAWN];
& ei.attackedBy[Them][PAWN];
// Add a bonus according to the kind of attacking pieces // Add a bonus according to the kind of attacking pieces
if (defended) if (defended)
@@ -511,9 +517,9 @@ namespace {
while (b) while (b)
score += Threat[Defended][Minor][type_of(pos.piece_on(pop_lsb(&b)))]; score += Threat[Defended][Minor][type_of(pos.piece_on(pop_lsb(&b)))];
b = defended & (ei.attackedBy[Us][ROOK]); b = defended & ei.attackedBy[Us][ROOK];
while (b) while (b)
score += Threat[Defended][Major][type_of(pos.piece_on(pop_lsb(&b)))]; score += Threat[Defended][Rook][type_of(pos.piece_on(pop_lsb(&b)))];
} }
// Enemies not defended by a pawn and under our attack // Enemies not defended by a pawn and under our attack
@@ -528,9 +534,9 @@ namespace {
while (b) while (b)
score += Threat[Weak][Minor][type_of(pos.piece_on(pop_lsb(&b)))]; score += Threat[Weak][Minor][type_of(pos.piece_on(pop_lsb(&b)))];
b = weak & (ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][QUEEN]); b = weak & ei.attackedBy[Us][ROOK];
while (b) while (b)
score += Threat[Weak][Major][type_of(pos.piece_on(pop_lsb(&b)))]; score += Threat[Weak][Rook][type_of(pos.piece_on(pop_lsb(&b)))];
b = weak & ~ei.attackedBy[Them][ALL_PIECES]; b = weak & ~ei.attackedBy[Them][ALL_PIECES];
if (b) if (b)
@@ -541,8 +547,23 @@ namespace {
score += more_than_one(b) ? KingOnMany : KingOnOne; score += more_than_one(b) ? KingOnMany : KingOnOne;
} }
if (Trace) // Bonus if some pawns can safely push and attack an enemy piece
Tracing::write(Tracing::THREAT, Us, score); b = pos.pieces(Us, PAWN) & ~TRank7BB;
b = shift_bb<Up>(b | (shift_bb<Up>(b & TRank2BB) & ~pos.pieces()));
b &= ~pos.pieces()
& ~ei.attackedBy[Them][PAWN]
& (ei.attackedBy[Us][ALL_PIECES] | ~ei.attackedBy[Them][ALL_PIECES]);
b = (shift_bb<Left>(b) | shift_bb<Right>(b))
& pos.pieces(Them)
& ~ei.attackedBy[Us][PAWN];
if (b)
score += popcount<Max15>(b) * PawnAttackThreat;
if (DoTrace)
Trace::add(THREAT, Us, score);
return score; return score;
} }
@@ -550,7 +571,7 @@ namespace {
// evaluate_passed_pawns() evaluates the passed pawns of the given color // evaluate_passed_pawns() evaluates the passed pawns of the given color
template<Color Us, bool Trace> template<Color Us, bool DoTrace>
Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei) { Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -569,20 +590,19 @@ namespace {
int r = relative_rank(Us, s) - RANK_2; int r = relative_rank(Us, s) - RANK_2;
int rr = r * (r - 1); int rr = r * (r - 1);
// Base bonus based on rank Value mbonus = Passed[MG][r], ebonus = Passed[EG][r];
Value mbonus = Value(17 * rr), ebonus = Value(7 * (rr + r + 1));
if (rr) if (rr)
{ {
Square blockSq = s + pawn_push(Us); Square blockSq = s + pawn_push(Us);
// Adjust bonus based on the king's proximity // Adjust bonus based on the king's proximity
ebonus += distance(pos.king_square(Them), blockSq) * 5 * rr ebonus += distance(pos.square<KING>(Them), blockSq) * 5 * rr
- distance(pos.king_square(Us ), blockSq) * 2 * rr; - distance(pos.square<KING>(Us ), blockSq) * 2 * rr;
// If blockSq is not the queening square then consider also a second push // If blockSq is not the queening square then consider also a second push
if (relative_rank(Us, blockSq) != RANK_8) if (relative_rank(Us, blockSq) != RANK_8)
ebonus -= distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr; ebonus -= distance(pos.square<KING>(Us), blockSq + pawn_push(Us)) * rr;
// If the pawn is free to advance, then increase the bonus // If the pawn is free to advance, then increase the bonus
if (pos.empty(blockSq)) if (pos.empty(blockSq))
@@ -602,7 +622,7 @@ namespace {
// If there aren't any enemy attacks, assign a big bonus. Otherwise // If there aren't any enemy attacks, assign a big bonus. Otherwise
// assign a smaller bonus if the block square isn't attacked. // assign a smaller bonus if the block square isn't attacked.
int k = !unsafeSquares ? 15 : !(unsafeSquares & blockSq) ? 9 : 0; int k = !unsafeSquares ? 18 : !(unsafeSquares & blockSq) ? 8 : 0;
// If the path to queen is fully defended, assign a big bonus. // If the path to queen is fully defended, assign a big bonus.
// Otherwise assign a smaller bonus if the block square is defended. // Otherwise assign a smaller bonus if the block square is defended.
@@ -621,14 +641,14 @@ namespace {
if (pos.count<PAWN>(Us) < pos.count<PAWN>(Them)) if (pos.count<PAWN>(Us) < pos.count<PAWN>(Them))
ebonus += ebonus / 4; ebonus += ebonus / 4;
score += make_score(mbonus, ebonus); score += make_score(mbonus, ebonus) + PassedFile[file_of(s)];
} }
if (Trace) if (DoTrace)
Tracing::write(Tracing::PASSED, Us, apply_weight(score, Weights[PassedPawns])); Trace::add(PASSED, Us, score * Weights[PassedPawns]);
// Add the scores to the middlegame and endgame eval // Add the scores to the middlegame and endgame eval
return apply_weight(score, Weights[PassedPawns]); return score * Weights[PassedPawns];
} }
@@ -656,10 +676,10 @@ namespace {
behind |= (Us == WHITE ? behind >> 8 : behind << 8); behind |= (Us == WHITE ? behind >> 8 : behind << 8);
behind |= (Us == WHITE ? behind >> 16 : behind << 16); behind |= (Us == WHITE ? behind >> 16 : behind << 16);
// Since SpaceMask[Us] is fully on our half of the board // Since SpaceMask[Us] is fully on our half of the board...
assert(unsigned(safe >> (Us == WHITE ? 32 : 0)) == 0); assert(unsigned(safe >> (Us == WHITE ? 32 : 0)) == 0);
// Count safe + (behind & safe) with a single popcount // ...count safe + (behind & safe) with a single popcount
int bonus = popcount<Full>((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe)); int bonus = popcount<Full>((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe));
int weight = pos.count<KNIGHT>(Us) + pos.count<BISHOP>(Us) int weight = pos.count<KNIGHT>(Us) + pos.count<BISHOP>(Us)
+ pos.count<KNIGHT>(Them) + pos.count<BISHOP>(Them); + pos.count<KNIGHT>(Them) + pos.count<BISHOP>(Them);
@@ -668,10 +688,34 @@ namespace {
} }
// do_evaluate() is the evaluation entry point, called directly from evaluate() // evaluate_initiative() computes the initiative correction value for the position, i.e.
// second order bonus/malus based on the known attacking/defending status of the players.
Score evaluate_initiative(const Position& pos, const EvalInfo& ei, const Score positional_score) {
template<bool Trace> int pawns = pos.count<PAWN>(WHITE) + pos.count<PAWN>(BLACK);
Value do_evaluate(const Position& pos) { int king_separation = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
int asymmetry = ei.pi->pawn_asymmetry();
// Compute the initiative bonus for the attacking side
int attacker_bonus = 8 * (pawns + asymmetry + king_separation) - 120;
// Now apply the bonus: note that we find the attacking side by extracting the sign
// of the endgame value of "positional_score", and that we carefully cap the bonus so
// that the endgame score with the correction will never be divided by more than two.
int eg = eg_value(positional_score);
int value = ((eg > 0) - (eg < 0)) * std::max( attacker_bonus , -abs( eg / 2 ) );
return make_score( 0 , value ) ;
}
} // namespace
/// evaluate() is the main evaluation function. It returns a static evaluation
/// of the position always from the point of view of the side to move.
template<bool DoTrace>
Value Eval::evaluate(const Position& pos) {
assert(!pos.checkers()); assert(!pos.checkers());
@@ -684,45 +728,52 @@ namespace {
score = pos.psq_score(); score = pos.psq_score();
// Probe the material hash table // Probe the material hash table
ei.mi = Material::probe(pos); Material::Entry* me = Material::probe(pos);
score += ei.mi->imbalance(); score += me->imbalance();
// If we have a specialized evaluation function for the current material // If we have a specialized evaluation function for the current material
// configuration, call it and return. // configuration, call it and return.
if (ei.mi->specialized_eval_exists()) if (me->specialized_eval_exists())
return ei.mi->evaluate(pos); return me->evaluate(pos);
// Probe the pawn hash table // Probe the pawn hash table
ei.pi = Pawns::probe(pos); ei.pi = Pawns::probe(pos);
score += apply_weight(ei.pi->pawns_score(), Weights[PawnStructure]); score += ei.pi->pawns_score() * Weights[PawnStructure];
// Initialize attack and king safety bitboards // Initialize attack and king safety bitboards
ei.attackedBy[WHITE][ALL_PIECES] = ei.attackedBy[BLACK][ALL_PIECES] = 0;
init_eval_info<WHITE>(pos, ei); init_eval_info<WHITE>(pos, ei);
init_eval_info<BLACK>(pos, ei); init_eval_info<BLACK>(pos, ei);
ei.attackedBy[WHITE][ALL_PIECES] |= ei.attackedBy[WHITE][KING]; // Pawns blocked or on ranks 2 and 3. Will be excluded from the mobility area
ei.attackedBy[BLACK][ALL_PIECES] |= ei.attackedBy[BLACK][KING]; Bitboard blockedPawns[] = {
pos.pieces(WHITE, PAWN) & (shift_bb<DELTA_S>(pos.pieces()) | Rank2BB | Rank3BB),
pos.pieces(BLACK, PAWN) & (shift_bb<DELTA_N>(pos.pieces()) | Rank7BB | Rank6BB)
};
// Do not include in mobility squares protected by enemy pawns or occupied by our pawns or king // Do not include in mobility squares protected by enemy pawns, or occupied
Bitboard mobilityArea[] = { ~(ei.attackedBy[BLACK][PAWN] | pos.pieces(WHITE, PAWN, KING)), // by our blocked pawns or king.
~(ei.attackedBy[WHITE][PAWN] | pos.pieces(BLACK, PAWN, KING)) }; Bitboard mobilityArea[] = {
~(ei.attackedBy[BLACK][PAWN] | blockedPawns[WHITE] | pos.square<KING>(WHITE)),
~(ei.attackedBy[WHITE][PAWN] | blockedPawns[BLACK] | pos.square<KING>(BLACK))
};
// Evaluate pieces and mobility // Evaluate pieces and mobility
score += evaluate_pieces<KNIGHT, WHITE, Trace>(pos, ei, mobility, mobilityArea); score += evaluate_pieces<KNIGHT, WHITE, DoTrace>(pos, ei, mobility, mobilityArea);
score += apply_weight(mobility[WHITE] - mobility[BLACK], Weights[Mobility]); score += (mobility[WHITE] - mobility[BLACK]) * Weights[Mobility];
// Evaluate kings after all other pieces because we need complete attack // Evaluate kings after all other pieces because we need complete attack
// information when computing the king safety evaluation. // information when computing the king safety evaluation.
score += evaluate_king<WHITE, Trace>(pos, ei) score += evaluate_king<WHITE, DoTrace>(pos, ei)
- evaluate_king<BLACK, Trace>(pos, ei); - evaluate_king<BLACK, DoTrace>(pos, ei);
// Evaluate tactical threats, we need full attack information including king // Evaluate tactical threats, we need full attack information including king
score += evaluate_threats<WHITE, Trace>(pos, ei) score += evaluate_threats<WHITE, DoTrace>(pos, ei)
- evaluate_threats<BLACK, Trace>(pos, ei); - evaluate_threats<BLACK, DoTrace>(pos, ei);
// Evaluate passed pawns, we need full attack information including king // Evaluate passed pawns, we need full attack information including king
score += evaluate_passed_pawns<WHITE, Trace>(pos, ei) score += evaluate_passed_pawns<WHITE, DoTrace>(pos, ei)
- evaluate_passed_pawns<BLACK, Trace>(pos, ei); - evaluate_passed_pawns<BLACK, DoTrace>(pos, ei);
// If both sides have only pawns, score for potential unstoppable pawns // If both sides have only pawns, score for potential unstoppable pawns
if (!pos.non_pawn_material(WHITE) && !pos.non_pawn_material(BLACK)) if (!pos.non_pawn_material(WHITE) && !pos.non_pawn_material(BLACK))
@@ -736,19 +787,19 @@ namespace {
} }
// Evaluate space for both sides, only during opening // Evaluate space for both sides, only during opening
if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg) if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >= 12222)
{ score += (evaluate_space<WHITE>(pos, ei) - evaluate_space<BLACK>(pos, ei)) * Weights[Space];
Score s = evaluate_space<WHITE>(pos, ei) - evaluate_space<BLACK>(pos, ei);
score += apply_weight(s, Weights[Space]); // Evaluate initiative
} score += evaluate_initiative(pos, ei, score);
// Scale winning side if position is more drawish than it appears // Scale winning side if position is more drawish than it appears
Color strongSide = eg_value(score) > VALUE_DRAW ? WHITE : BLACK; Color strongSide = eg_value(score) > VALUE_DRAW ? WHITE : BLACK;
ScaleFactor sf = ei.mi->scale_factor(pos, strongSide); ScaleFactor sf = me->scale_factor(pos, strongSide);
// If we don't already have an unusual scale factor, check for certain // If we don't already have an unusual scale factor, check for certain
// types of endgames, and use a lower scale for those. // types of endgames, and use a lower scale for those.
if ( ei.mi->game_phase() < PHASE_MIDGAME if ( me->game_phase() < PHASE_MIDGAME
&& (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN)) && (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN))
{ {
if (pos.opposite_bishops()) if (pos.opposite_bishops())
@@ -757,149 +808,96 @@ namespace {
// is almost a draw, in case of KBP vs KB is even more a draw. // is almost a draw, in case of KBP vs KB is even more a draw.
if ( pos.non_pawn_material(WHITE) == BishopValueMg if ( pos.non_pawn_material(WHITE) == BishopValueMg
&& pos.non_pawn_material(BLACK) == BishopValueMg) && pos.non_pawn_material(BLACK) == BishopValueMg)
sf = more_than_one(pos.pieces(PAWN)) ? ScaleFactor(32) : ScaleFactor(8); sf = more_than_one(pos.pieces(PAWN)) ? ScaleFactor(31) : ScaleFactor(9);
// Endgame with opposite-colored bishops, but also other pieces. Still // Endgame with opposite-colored bishops, but also other pieces. Still
// a bit drawish, but not as drawish as with only the two bishops. // a bit drawish, but not as drawish as with only the two bishops.
else else
sf = ScaleFactor(50 * sf / SCALE_FACTOR_NORMAL); sf = ScaleFactor(46 * sf / SCALE_FACTOR_NORMAL);
} }
// Endings where weaker side can place his king in front of the opponent's // Endings where weaker side can place his king in front of the opponent's
// pawns are drawish. // pawns are drawish.
else if ( abs(eg_value(score)) <= BishopValueEg else if ( abs(eg_value(score)) <= BishopValueEg
&& ei.pi->pawn_span(strongSide) <= 1 && ei.pi->pawn_span(strongSide) <= 1
&& !pos.pawn_passed(~strongSide, pos.king_square(~strongSide))) && !pos.pawn_passed(~strongSide, pos.square<KING>(~strongSide)))
sf = ei.pi->pawn_span(strongSide) ? ScaleFactor(56) : ScaleFactor(38); sf = ei.pi->pawn_span(strongSide) ? ScaleFactor(51) : ScaleFactor(37);
} }
// Interpolate between a middlegame and a (scaled by 'sf') endgame score // Interpolate between a middlegame and a (scaled by 'sf') endgame score
Value v = mg_value(score) * int(ei.mi->game_phase()) Value v = mg_value(score) * int(me->game_phase())
+ eg_value(score) * int(PHASE_MIDGAME - ei.mi->game_phase()) * sf / SCALE_FACTOR_NORMAL; + eg_value(score) * int(PHASE_MIDGAME - me->game_phase()) * sf / SCALE_FACTOR_NORMAL;
v /= int(PHASE_MIDGAME); v /= int(PHASE_MIDGAME);
// In case of tracing add all single evaluation contributions for both white and black // In case of tracing add all single evaluation terms
if (Trace) if (DoTrace)
{ {
Tracing::write(Tracing::MATERIAL, pos.psq_score()); Trace::add(MATERIAL, pos.psq_score());
Tracing::write(Tracing::IMBALANCE, ei.mi->imbalance()); Trace::add(IMBALANCE, me->imbalance());
Tracing::write(PAWN, ei.pi->pawns_score()); Trace::add(PAWN, ei.pi->pawns_score());
Tracing::write(Tracing::MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility]) Trace::add(MOBILITY, mobility[WHITE] * Weights[Mobility]
, apply_weight(mobility[BLACK], Weights[Mobility])); , mobility[BLACK] * Weights[Mobility]);
Tracing::write(Tracing::SPACE, apply_weight(evaluate_space<WHITE>(pos, ei), Weights[Space]) Trace::add(SPACE, evaluate_space<WHITE>(pos, ei) * Weights[Space]
, apply_weight(evaluate_space<BLACK>(pos, ei), Weights[Space])); , evaluate_space<BLACK>(pos, ei) * Weights[Space]);
Tracing::write(Tracing::TOTAL, score); Trace::add(TOTAL, score);
Tracing::ei = ei;
Tracing::sf = sf;
} }
return (pos.side_to_move() == WHITE ? v : -v) + Eval::Tempo; return (pos.side_to_move() == WHITE ? v : -v) + Eval::Tempo; // Side to move point of view
} }
// Explicit template instantiations
template Value Eval::evaluate<true >(const Position&);
template Value Eval::evaluate<false>(const Position&);
// Tracing function definitions /// trace() is like evaluate(), but instead of returning a value, it returns
/// a string (suitable for outputting to stdout) that contains the detailed
/// descriptions and values of each evaluation term. Useful for debugging.
double Tracing::to_cp(Value v) { return double(v) / PawnValueEg; } std::string Eval::trace(const Position& pos) {
void Tracing::write(int idx, Color c, Score s) { scores[c][idx] = s; }
void Tracing::write(int idx, Score w, Score b) {
write(idx, WHITE, w);
write(idx, BLACK, b);
}
void Tracing::print(std::stringstream& ss, const char* name, int idx) {
Score wScore = scores[WHITE][idx];
Score bScore = scores[BLACK][idx];
switch (idx) {
case MATERIAL: case IMBALANCE: case PAWN: case TOTAL:
ss << std::setw(15) << name << " | --- --- | --- --- | "
<< std::setw(5) << to_cp(mg_value(wScore - bScore)) << " "
<< std::setw(5) << to_cp(eg_value(wScore - bScore)) << " \n";
break;
default:
ss << std::setw(15) << name << " | " << std::noshowpos
<< std::setw(5) << to_cp(mg_value(wScore)) << " "
<< std::setw(5) << to_cp(eg_value(wScore)) << " | "
<< std::setw(5) << to_cp(mg_value(bScore)) << " "
<< std::setw(5) << to_cp(eg_value(bScore)) << " | "
<< std::setw(5) << to_cp(mg_value(wScore - bScore)) << " "
<< std::setw(5) << to_cp(eg_value(wScore - bScore)) << " \n";
}
}
std::string Tracing::do_trace(const Position& pos) {
std::memset(scores, 0, sizeof(scores)); std::memset(scores, 0, sizeof(scores));
Value v = do_evaluate<true>(pos); Value v = evaluate<true>(pos);
v = pos.side_to_move() == WHITE ? v : -v; // White's point of view v = pos.side_to_move() == WHITE ? v : -v; // White's point of view
std::stringstream ss; std::stringstream ss;
ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2) ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
<< " Eval term | White | Black | Total \n" << " Eval term | White | Black | Total \n"
<< " | MG EG | MG EG | MG EG \n" << " | MG EG | MG EG | MG EG \n"
<< "----------------+-------------+-------------+-------------\n"; << "----------------+-------------+-------------+-------------\n"
<< " Material | " << Term(MATERIAL)
print(ss, "Material", MATERIAL); << " Imbalance | " << Term(IMBALANCE)
print(ss, "Imbalance", IMBALANCE); << " Pawns | " << Term(PAWN)
print(ss, "Pawns", PAWN); << " Knights | " << Term(KNIGHT)
print(ss, "Knights", KNIGHT); << " Bishop | " << Term(BISHOP)
print(ss, "Bishops", BISHOP); << " Rooks | " << Term(ROOK)
print(ss, "Rooks", ROOK); << " Queens | " << Term(QUEEN)
print(ss, "Queens", QUEEN); << " Mobility | " << Term(MOBILITY)
print(ss, "Mobility", MOBILITY); << " King safety | " << Term(KING)
print(ss, "King safety", KING); << " Threats | " << Term(THREAT)
print(ss, "Threats", THREAT); << " Passed pawns | " << Term(PASSED)
print(ss, "Passed pawns", PASSED); << " Space | " << Term(SPACE)
print(ss, "Space", SPACE); << "----------------+-------------+-------------+-------------\n"
<< " Total | " << Term(TOTAL);
ss << "----------------+-------------+-------------+-------------\n";
print(ss, "Total", TOTAL);
ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n"; ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n";
return ss.str(); return ss.str();
} }
} // namespace
namespace Eval { /// init() computes evaluation weights, usually at startup
/// evaluate() is the main evaluation function. It returns a static evaluation void Eval::init() {
/// of the position always from the point of view of the side to move.
Value evaluate(const Position& pos) { const int MaxSlope = 8700;
return do_evaluate<false>(pos); const int Peak = 1280000;
} int t = 0;
for (int i = 0; i < 400; ++i)
/// trace() is like evaluate(), but instead of returning a value, it returns
/// a string (suitable for outputting to stdout) that contains the detailed
/// descriptions and values of each evaluation term. It's mainly used for
/// debugging.
std::string trace(const Position& pos) {
return Tracing::do_trace(pos);
}
/// init() computes evaluation weights, usually at startup
void init() {
const double MaxSlope = 7.5;
const double Peak = 1280;
double t = 0.0;
for (int i = 1; i < 400; ++i)
{ {
t = std::min(Peak, std::min(0.025 * i * i, t + MaxSlope)); t = std::min(Peak, std::min(i * i * 27, t + MaxSlope));
KingDanger[i] = apply_weight(make_score(int(t), 0), Weights[KingSafety]); KingDanger[i] = make_score(t / 1000, 0) * Weights[KingSafety];
} }
} }
} // namespace Eval

View File

@@ -28,12 +28,13 @@ class Position;
namespace Eval { namespace Eval {
const Value Tempo = Value(17); // Must be visible to search const Value Tempo = Value(20); // Must be visible to search
void init(); void init();
Value evaluate(const Position& pos);
std::string trace(const Position& pos); std::string trace(const Position& pos);
template<bool DoTrace = false>
Value evaluate(const Position& pos);
} }
#endif // #ifndef EVALUATE_H_INCLUDED #endif // #ifndef EVALUATE_H_INCLUDED

View File

@@ -33,6 +33,7 @@ int main(int argc, char* argv[]) {
std::cout << engine_info() << std::endl; std::cout << engine_info() << std::endl;
UCI::init(Options); UCI::init(Options);
PSQT::init();
Bitboards::init(); Bitboards::init();
Position::init(); Position::init();
Bitbases::init(); Bitbases::init();
@@ -46,4 +47,5 @@ int main(int argc, char* argv[]) {
UCI::loop(argc, argv); UCI::loop(argc, argv);
Threads.exit(); Threads.exit();
return 0;
} }

View File

@@ -31,28 +31,28 @@ namespace {
// Polynomial material imbalance parameters // Polynomial material imbalance parameters
// pair pawn knight bishop rook queen // pair pawn knight bishop rook queen
const int Linear[6] = { 1852, -162, -1122, -183, 249, -154 }; const int Linear[6] = { 1667, -168, -1027, -166, 238, -138 };
const int QuadraticOurs[][PIECE_TYPE_NB] = { const int QuadraticOurs[][PIECE_TYPE_NB] = {
// OUR PIECES // OUR PIECES
// pair pawn knight bishop rook queen // pair pawn knight bishop rook queen
{ 0 }, // Bishop pair { 0 }, // Bishop pair
{ 39, 2 }, // Pawn { 40, 2 }, // Pawn
{ 35, 271, -4 }, // Knight OUR PIECES { 32, 255, -3 }, // Knight OUR PIECES
{ 0, 105, 4, 0 }, // Bishop { 0, 104, 4, 0 }, // Bishop
{ -27, -2, 46, 100, -141 }, // Rook { -26, -2, 47, 105, -149 }, // Rook
{-177, 25, 129, 142, -137, 0 } // Queen {-185, 24, 122, 137, -134, 0 } // Queen
}; };
const int QuadraticTheirs[][PIECE_TYPE_NB] = { const int QuadraticTheirs[][PIECE_TYPE_NB] = {
// THEIR PIECES // THEIR PIECES
// pair pawn knight bishop rook queen // pair pawn knight bishop rook queen
{ 0 }, // Bishop pair { 0 }, // Bishop pair
{ 37, 0 }, // Pawn { 36, 0 }, // Pawn
{ 10, 62, 0 }, // Knight OUR PIECES { 9, 63, 0 }, // Knight OUR PIECES
{ 57, 64, 39, 0 }, // Bishop { 59, 65, 42, 0 }, // Bishop
{ 50, 40, 23, -22, 0 }, // Rook { 46, 39, 24, -24, 0 }, // Rook
{ 98, 105, -39, 141, 274, 0 } // Queen { 101, 100, -37, 141, 268, 0 } // Queen
}; };
// Endgame evaluation and scaling functions are accessed directly and not through // Endgame evaluation and scaling functions are accessed directly and not through
@@ -64,31 +64,28 @@ namespace {
Endgame<KPsK> ScaleKPsK[] = { Endgame<KPsK>(WHITE), Endgame<KPsK>(BLACK) }; Endgame<KPsK> ScaleKPsK[] = { Endgame<KPsK>(WHITE), Endgame<KPsK>(BLACK) };
Endgame<KPKP> ScaleKPKP[] = { Endgame<KPKP>(WHITE), Endgame<KPKP>(BLACK) }; Endgame<KPKP> ScaleKPKP[] = { Endgame<KPKP>(WHITE), Endgame<KPKP>(BLACK) };
// Helper templates used to detect a given material distribution // Helper used to detect a given material distribution
template<Color Us> bool is_KXK(const Position& pos) { bool is_KXK(const Position& pos, Color us) {
const Color Them = (Us == WHITE ? BLACK : WHITE); return !more_than_one(pos.pieces(~us))
return !more_than_one(pos.pieces(Them)) && pos.non_pawn_material(us) >= RookValueMg;
&& pos.non_pawn_material(Us) >= RookValueMg;
} }
template<Color Us> bool is_KBPsKs(const Position& pos) { bool is_KBPsKs(const Position& pos, Color us) {
return pos.non_pawn_material(Us) == BishopValueMg return pos.non_pawn_material(us) == BishopValueMg
&& pos.count<BISHOP>(Us) == 1 && pos.count<BISHOP>(us) == 1
&& pos.count<PAWN >(Us) >= 1; && pos.count<PAWN >(us) >= 1;
} }
template<Color Us> bool is_KQKRPs(const Position& pos) { bool is_KQKRPs(const Position& pos, Color us) {
const Color Them = (Us == WHITE ? BLACK : WHITE); return !pos.count<PAWN>(us)
return !pos.count<PAWN>(Us) && pos.non_pawn_material(us) == QueenValueMg
&& pos.non_pawn_material(Us) == QueenValueMg && pos.count<QUEEN>(us) == 1
&& pos.count<QUEEN>(Us) == 1 && pos.count<ROOK>(~us) == 1
&& pos.count<ROOK>(Them) == 1 && pos.count<PAWN>(~us) >= 1;
&& pos.count<PAWN>(Them) >= 1;
} }
/// imbalance() calculates the imbalance by comparing the piece count of each /// imbalance() calculates the imbalance by comparing the piece count of each
/// piece type for both colors. /// piece type for both colors.
template<Color Us> template<Color Us>
int imbalance(const int pieceCount[][PIECE_TYPE_NB]) { int imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
@@ -139,18 +136,13 @@ Entry* probe(const Position& pos) {
// 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
// for a generic one if the previous search failed. // for a generic one if the previous search failed.
if (pos.this_thread()->endgames.probe(key, e->evaluationFunction)) if ((e->evaluationFunction = pos.this_thread()->endgames.probe<Value>(key)) != nullptr)
return e; return e;
if (is_KXK<WHITE>(pos)) for (Color c = WHITE; c <= BLACK; ++c)
if (is_KXK(pos, c))
{ {
e->evaluationFunction = &EvaluateKXK[WHITE]; e->evaluationFunction = &EvaluateKXK[c];
return e;
}
if (is_KXK<BLACK>(pos))
{
e->evaluationFunction = &EvaluateKXK[BLACK];
return e; return e;
} }
@@ -158,7 +150,7 @@ Entry* probe(const Position& pos) {
// configuration. Is there a suitable specialized scaling function? // configuration. Is there a suitable specialized scaling function?
EndgameBase<ScaleFactor>* sf; EndgameBase<ScaleFactor>* sf;
if (pos.this_thread()->endgames.probe(key, sf)) if ((sf = pos.this_thread()->endgames.probe<ScaleFactor>(key)) != nullptr)
{ {
e->scalingFunction[sf->strong_side()] = sf; // Only strong color assigned e->scalingFunction[sf->strong_side()] = sf; // Only strong color assigned
return e; return e;
@@ -167,17 +159,14 @@ Entry* probe(const Position& pos) {
// We didn't find any specialized scaling function, so fall back on generic // We didn't find any specialized scaling function, so fall back on generic
// ones that refer to more than one material distribution. Note that in this // ones that refer to more than one material distribution. Note that in this
// case we don't return after setting the function. // case we don't return after setting the function.
if (is_KBPsKs<WHITE>(pos)) for (Color c = WHITE; c <= BLACK; ++c)
e->scalingFunction[WHITE] = &ScaleKBPsK[WHITE]; {
if (is_KBPsKs(pos, c))
e->scalingFunction[c] = &ScaleKBPsK[c];
if (is_KBPsKs<BLACK>(pos)) else if (is_KQKRPs(pos, c))
e->scalingFunction[BLACK] = &ScaleKBPsK[BLACK]; e->scalingFunction[c] = &ScaleKQKRPs[c];
}
if (is_KQKRPs<WHITE>(pos))
e->scalingFunction[WHITE] = &ScaleKQKRPs[WHITE];
else if (is_KQKRPs<BLACK>(pos))
e->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK];
Value npm_w = pos.non_pawn_material(WHITE); Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK); Value npm_b = pos.non_pawn_material(BLACK);
@@ -210,11 +199,11 @@ Entry* probe(const Position& pos) {
// drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN). // drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN).
if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg) if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg)
e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW : e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW :
npm_b <= BishopValueMg ? 4 : 12); npm_b <= BishopValueMg ? 4 : 14);
if (!pos.count<PAWN>(BLACK) && npm_b - npm_w <= BishopValueMg) if (!pos.count<PAWN>(BLACK) && npm_b - npm_w <= BishopValueMg)
e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW : e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW :
npm_w <= BishopValueMg ? 4 : 12); npm_w <= BishopValueMg ? 4 : 14);
if (pos.count<PAWN>(WHITE) == 1 && npm_w - npm_b <= BishopValueMg) if (pos.count<PAWN>(WHITE) == 1 && npm_w - npm_b <= BishopValueMg)
e->factor[WHITE] = (uint8_t) SCALE_FACTOR_ONEPAWN; e->factor[WHITE] = (uint8_t) SCALE_FACTOR_ONEPAWN;

View File

@@ -40,7 +40,7 @@ struct Entry {
Score imbalance() const { return make_score(value, value); } Score imbalance() const { return make_score(value, value); }
Phase game_phase() const { return gamePhase; } Phase game_phase() const { return gamePhase; }
bool specialized_eval_exists() const { return evaluationFunction != NULL; } bool specialized_eval_exists() const { return evaluationFunction != nullptr; }
Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); } Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); }
// scale_factor takes a position and a color as input and returns a scale factor // scale_factor takes a position and a color as input and returns a scale factor

View File

@@ -31,43 +31,39 @@ 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 = "6"; const string Version = "231015";
/// Debug counters
int64_t hits[2], means[2];
/// 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
/// can toggle the logging of std::cout and std:cin at runtime whilst preserving /// can toggle the logging of std::cout and std:cin at runtime whilst preserving
/// usual i/o functionality, all without changing a single line of code! /// usual I/O functionality, all without changing a single line of code!
/// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81 /// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
struct Tie: public streambuf { // MSVC requires splitted streambuf for cin and cout struct Tie: public streambuf { // MSVC requires splitted streambuf for cin and cout
Tie(streambuf* b, ofstream* f) : buf(b), file(f) {} Tie(streambuf* b, streambuf* l) : buf(b), logBuf(l) {}
int sync() { return file->rdbuf()->pubsync(), buf->pubsync(); } int sync() { return logBuf->pubsync(), buf->pubsync(); }
int overflow(int c) { return log(buf->sputc((char)c), "<< "); } int overflow(int c) { return log(buf->sputc((char)c), "<< "); }
int underflow() { return buf->sgetc(); } int underflow() { return buf->sgetc(); }
int uflow() { return log(buf->sbumpc(), ">> "); } int uflow() { return log(buf->sbumpc(), ">> "); }
streambuf* buf; streambuf *buf, *logBuf;
ofstream* file;
int log(int c, const char* prefix) { int log(int c, const char* prefix) {
static int last = '\n'; static int last = '\n'; // Single log file
if (last == '\n') if (last == '\n')
file->rdbuf()->sputn(prefix, 3); logBuf->sputn(prefix, 3);
return last = file->rdbuf()->sputc((char)c); return last = logBuf->sputc((char)c);
} }
}; };
class Logger { class Logger {
Logger() : in(cin.rdbuf(), &file), out(cout.rdbuf(), &file) {} Logger() : in(cin.rdbuf(), file.rdbuf()), out(cout.rdbuf(), file.rdbuf()) {}
~Logger() { start(false); } ~Logger() { start(false); }
ofstream file; ofstream file;
@@ -80,7 +76,7 @@ public:
if (b && !l.file.is_open()) if (b && !l.file.is_open())
{ {
l.file.open("io_log.txt", ifstream::out | ifstream::app); l.file.open("io_log.txt", ifstream::out);
cin.rdbuf(&l.in); cin.rdbuf(&l.in);
cout.rdbuf(&l.out); cout.rdbuf(&l.out);
} }
@@ -117,16 +113,17 @@ const string engine_info(bool to_uci) {
ss << (Is64Bit ? " 64" : "") ss << (Is64Bit ? " 64" : "")
<< (HasPext ? " BMI2" : (HasPopCnt ? " POPCNT" : "")) << (HasPext ? " BMI2" : (HasPopCnt ? " POPCNT" : ""))
<< (to_uci ? "\nid author ": " by ") << (to_uci ? "\nid author ": " by ")
<< "Tord Romstad, Marco Costalba and Joona Kiiski"; << "T. Romstad, M. Costalba, J. Kiiski, G. Linscott";
return ss.str(); return ss.str();
} }
/// Debug functions used mainly to collect run-time statistics /// Debug functions used mainly to collect run-time statistics
static int64_t hits[2], means[2];
void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; } void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; }
void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); } void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); }
void dbg_mean_of(int v) { ++means[0]; means[1] += v; } void dbg_mean_of(int v) { ++means[0]; means[1] += v; }
void dbg_print() { void dbg_print() {
@@ -162,35 +159,16 @@ std::ostream& operator<<(std::ostream& os, SyncCout sc) {
void start_logger(bool b) { Logger::start(b); } void start_logger(bool b) { Logger::start(b); }
/// timed_wait() waits for msec milliseconds. It is mainly a helper to wrap
/// the conversion from milliseconds to struct timespec, as used by pthreads.
void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) {
#ifdef _WIN32
int tm = msec;
#else
timespec ts, *tm = &ts;
uint64_t ms = Time::now() + msec;
ts.tv_sec = ms / 1000;
ts.tv_nsec = (ms % 1000) * 1000000LL;
#endif
cond_timedwait(sleepCond, sleepLock, tm);
}
/// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking /// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking
/// function that doesn't stall the CPU waiting for data to be loaded from memory, /// function that doesn't stall the CPU waiting for data to be loaded from memory,
/// which can be quite slow. /// which can be quite slow.
#ifdef NO_PREFETCH #ifdef NO_PREFETCH
void prefetch(char*) {} void prefetch(void*) {}
#else #else
void prefetch(char* addr) { void prefetch(void* addr) {
# if defined(__INTEL_COMPILER) # if defined(__INTEL_COMPILER)
// This hack prevents prefetches from being optimized away by // This hack prevents prefetches from being optimized away by
@@ -199,7 +177,7 @@ void prefetch(char* addr) {
# endif # endif
# if defined(__INTEL_COMPILER) || defined(_MSC_VER) # if defined(__INTEL_COMPILER) || defined(_MSC_VER)
_mm_prefetch(addr, _MM_HINT_T0); _mm_prefetch((char*)addr, _MM_HINT_T0);
# else # else
__builtin_prefetch(addr); __builtin_prefetch(addr);
# endif # endif

View File

@@ -21,36 +21,36 @@
#define MISC_H_INCLUDED #define MISC_H_INCLUDED
#include <cassert> #include <cassert>
#include <chrono>
#include <ostream> #include <ostream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <sstream>
#include "types.h" #include "types.h"
const std::string engine_info(bool to_uci = false); const std::string engine_info(bool to_uci = false);
void timed_wait(WaitCondition&, Lock&, int); void prefetch(void* addr);
void prefetch(char* addr);
void start_logger(bool b); void start_logger(bool b);
void dbg_hit_on(bool b); void dbg_hit_on(bool b);
void dbg_hit_on_c(bool c, bool b); void dbg_hit_on(bool c, bool b);
void dbg_mean_of(int v); void dbg_mean_of(int v);
void dbg_print(); void dbg_print();
typedef std::chrono::milliseconds::rep TimePoint; // A value in milliseconds
namespace Time { inline TimePoint now() {
typedef int64_t point; return std::chrono::duration_cast<std::chrono::milliseconds>
inline point now() { return system_time_to_msec(); } (std::chrono::steady_clock::now().time_since_epoch()).count();
} }
template<class Entry, int Size> template<class Entry, int Size>
struct HashTable { struct HashTable {
HashTable() : table(Size, Entry()) {}
Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; } Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; }
private: private:
std::vector<Entry> table; std::vector<Entry> table = std::vector<Entry>(Size);
}; };
@@ -97,4 +97,17 @@ public:
{ return T(rand64() & rand64() & rand64()); } { return T(rand64() & rand64() & rand64()); }
}; };
inline int stoi(const std::string& s) {
std::stringstream ss(s);
int result = 0;
ss >> result;
return result;
}
inline std::string to_string(int v) {
char buf[32];
sprintf(buf, "%d", v);
return buf;
}
#endif // #ifndef MISC_H_INCLUDED #endif // #ifndef MISC_H_INCLUDED

View File

@@ -34,7 +34,7 @@ namespace {
// After castling, the rook and king final positions are the same in Chess960 // After castling, the rook and king final positions are the same in Chess960
// as they would be in standard chess. // as they would be in standard chess.
Square kfrom = pos.king_square(us); Square kfrom = pos.square<KING>(us);
Square rfrom = pos.castling_rook_square(Cr); Square rfrom = pos.castling_rook_square(Cr);
Square kto = relative_square(us, KingSide ? SQ_G1 : SQ_C1); Square kto = relative_square(us, KingSide ? SQ_G1 : SQ_C1);
Bitboard enemies = pos.pieces(~us); Bitboard enemies = pos.pieces(~us);
@@ -58,30 +58,31 @@ namespace {
if (Checks && !pos.gives_check(m, *ci)) if (Checks && !pos.gives_check(m, *ci))
return moveList; return moveList;
else
(void)ci; // Silence a warning under MSVC
(moveList++)->move = m; *moveList++ = m;
return moveList; return moveList;
} }
template<GenType Type, Square Delta> template<GenType Type, Square Delta>
inline ExtMove* make_promotions(ExtMove* moveList, Square to, const CheckInfo* ci) { ExtMove* make_promotions(ExtMove* moveList, Square to, const CheckInfo* ci) {
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
(moveList++)->move = make<PROMOTION>(to - Delta, to, QUEEN); *moveList++ = make<PROMOTION>(to - Delta, to, QUEEN);
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
{ {
(moveList++)->move = make<PROMOTION>(to - Delta, to, ROOK); *moveList++ = make<PROMOTION>(to - Delta, to, ROOK);
(moveList++)->move = make<PROMOTION>(to - Delta, to, BISHOP); *moveList++ = make<PROMOTION>(to - Delta, to, BISHOP);
(moveList++)->move = make<PROMOTION>(to - Delta, to, KNIGHT); *moveList++ = make<PROMOTION>(to - Delta, to, KNIGHT);
} }
// 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] & ci->ksq)) if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq))
(moveList++)->move = make<PROMOTION>(to - Delta, to, KNIGHT); *moveList++ = make<PROMOTION>(to - Delta, to, KNIGHT);
else else
(void)ci; // Silence a warning under MSVC (void)ci; // Silence a warning under MSVC
@@ -147,13 +148,13 @@ namespace {
while (b1) while (b1)
{ {
Square to = pop_lsb(&b1); Square to = pop_lsb(&b1);
(moveList++)->move = make_move(to - Up, to); *moveList++ = make_move(to - Up, to);
} }
while (b2) while (b2)
{ {
Square to = pop_lsb(&b2); Square to = pop_lsb(&b2);
(moveList++)->move = make_move(to - Up - Up, to); *moveList++ = make_move(to - Up - Up, to);
} }
} }
@@ -189,13 +190,13 @@ namespace {
while (b1) while (b1)
{ {
Square to = pop_lsb(&b1); Square to = pop_lsb(&b1);
(moveList++)->move = make_move(to - Right, to); *moveList++ = make_move(to - Right, to);
} }
while (b2) while (b2)
{ {
Square to = pop_lsb(&b2); Square to = pop_lsb(&b2);
(moveList++)->move = make_move(to - Left, to); *moveList++ = make_move(to - Left, to);
} }
if (pos.ep_square() != SQ_NONE) if (pos.ep_square() != SQ_NONE)
@@ -213,7 +214,7 @@ namespace {
assert(b1); assert(b1);
while (b1) while (b1)
(moveList++)->move = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square()); *moveList++ = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
} }
} }
@@ -221,20 +222,20 @@ namespace {
} }
template<PieceType Pt, bool Checks> FORCE_INLINE template<PieceType Pt, bool Checks>
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us, ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us,
Bitboard target, const CheckInfo* ci) { Bitboard target, const CheckInfo* ci) {
assert(Pt != KING && Pt != PAWN); assert(Pt != KING && Pt != PAWN);
const Square* pl = pos.list<Pt>(us); const Square* pl = pos.squares<Pt>(us);
for (Square from = *pl; from != SQ_NONE; from = *++pl) for (Square from = *pl; from != SQ_NONE; from = *++pl)
{ {
if (Checks) if (Checks)
{ {
if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
&& !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt])) && !(PseudoAttacks[Pt][from] & target & ci->checkSquares[Pt]))
continue; continue;
if (ci->dcCandidates && (ci->dcCandidates & from)) if (ci->dcCandidates && (ci->dcCandidates & from))
@@ -244,19 +245,19 @@ namespace {
Bitboard b = pos.attacks_from<Pt>(from) & target; Bitboard b = pos.attacks_from<Pt>(from) & target;
if (Checks) if (Checks)
b &= ci->checkSq[Pt]; b &= ci->checkSquares[Pt];
while (b) while (b)
(moveList++)->move = make_move(from, pop_lsb(&b)); *moveList++ = make_move(from, pop_lsb(&b));
} }
return moveList; return moveList;
} }
template<Color Us, GenType Type> FORCE_INLINE template<Color Us, GenType Type>
ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target, ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target,
const CheckInfo* ci = NULL) { const CheckInfo* ci = nullptr) {
const bool Checks = Type == QUIET_CHECKS; const bool Checks = Type == QUIET_CHECKS;
@@ -268,10 +269,10 @@ namespace {
if (Type != QUIET_CHECKS && Type != EVASIONS) if (Type != QUIET_CHECKS && Type != EVASIONS)
{ {
Square ksq = pos.king_square(Us); Square ksq = pos.square<KING>(Us);
Bitboard b = pos.attacks_from<KING>(ksq) & target; Bitboard b = pos.attacks_from<KING>(ksq) & target;
while (b) while (b)
(moveList++)->move = make_move(ksq, pop_lsb(&b)); *moveList++ = make_move(ksq, pop_lsb(&b));
} }
if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us)) if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us))
@@ -350,7 +351,7 @@ ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
b &= ~PseudoAttacks[QUEEN][ci.ksq]; b &= ~PseudoAttacks[QUEEN][ci.ksq];
while (b) while (b)
(moveList++)->move = make_move(from, pop_lsb(&b)); *moveList++ = make_move(from, pop_lsb(&b));
} }
return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, moveList, ~pos.pieces(), &ci) return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, moveList, ~pos.pieces(), &ci)
@@ -366,7 +367,7 @@ ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* moveList) {
assert(pos.checkers()); assert(pos.checkers());
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Square ksq = pos.king_square(us); Square ksq = pos.square<KING>(us);
Bitboard sliderAttacks = 0; Bitboard sliderAttacks = 0;
Bitboard sliders = pos.checkers() & ~pos.pieces(KNIGHT, PAWN); Bitboard sliders = pos.checkers() & ~pos.pieces(KNIGHT, PAWN);
@@ -382,7 +383,7 @@ ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* moveList) {
// Generate evasions for king, capture and non capture moves // Generate evasions for king, capture and non capture moves
Bitboard b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks; Bitboard b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
while (b) while (b)
(moveList++)->move = make_move(ksq, pop_lsb(&b)); *moveList++ = make_move(ksq, pop_lsb(&b));
if (more_than_one(pos.checkers())) if (more_than_one(pos.checkers()))
return moveList; // Double check, only a king move can save the day return moveList; // Double check, only a king move can save the day
@@ -402,15 +403,15 @@ template<>
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) { ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
Bitboard pinned = pos.pinned_pieces(pos.side_to_move()); Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
Square ksq = pos.king_square(pos.side_to_move()); Square ksq = pos.square<KING>(pos.side_to_move());
ExtMove* cur = moveList; ExtMove* cur = moveList;
moveList = pos.checkers() ? generate<EVASIONS >(pos, moveList) moveList = pos.checkers() ? generate<EVASIONS >(pos, moveList)
: generate<NON_EVASIONS>(pos, moveList); : generate<NON_EVASIONS>(pos, moveList);
while (cur != moveList) while (cur != moveList)
if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT) if ( (pinned || from_sq(*cur) == ksq || type_of(*cur) == ENPASSANT)
&& !pos.legal(cur->move, pinned)) && !pos.legal(*cur, pinned))
cur->move = (--moveList)->move; *cur = (--moveList)->move;
else else
++cur; ++cur;

View File

@@ -36,6 +36,9 @@ enum GenType {
struct ExtMove { struct ExtMove {
Move move; Move move;
Value value; Value value;
operator Move() const { return move; }
void operator=(Move m) { move = m; }
}; };
inline bool operator<(const ExtMove& f, const ExtMove& s) { inline bool operator<(const ExtMove& f, const ExtMove& s) {
@@ -50,18 +53,17 @@ ExtMove* generate(const Position& pos, ExtMove* moveList);
template<GenType T> template<GenType T>
struct MoveList { struct MoveList {
explicit MoveList(const Position& pos) : cur(moveList), last(generate<T>(pos, moveList)) { last->move = MOVE_NONE; } explicit MoveList(const Position& pos) : last(generate<T>(pos, moveList)) {}
void operator++() { ++cur; } const ExtMove* begin() const { return moveList; }
Move operator*() const { return cur->move; } const ExtMove* end() const { return last; }
size_t size() const { return last - moveList; } size_t size() const { return last - moveList; }
bool contains(Move m) const { bool contains(Move move) const {
for (const ExtMove* it(moveList); it != last; ++it) if (it->move == m) return true; for (const auto& m : *this) if (m == move) return true;
return false; return false;
} }
private: private:
ExtMove moveList[MAX_MOVES]; ExtMove moveList[MAX_MOVES], *last;
ExtMove *cur, *last;
}; };
#endif // #ifndef MOVEGEN_H_INCLUDED #endif // #ifndef MOVEGEN_H_INCLUDED

View File

@@ -26,16 +26,16 @@
namespace { namespace {
enum Stages { enum Stages {
MAIN_SEARCH, CAPTURES_S1, KILLERS_S1, QUIETS_1_S1, QUIETS_2_S1, BAD_CAPTURES_S1, MAIN_SEARCH, GOOD_CAPTURES, KILLERS, GOOD_QUIETS, BAD_QUIETS, BAD_CAPTURES,
EVASION, EVASIONS_S2, EVASION, ALL_EVASIONS,
QSEARCH_0, CAPTURES_S3, QUIET_CHECKS_S3, QSEARCH_WITH_CHECKS, QCAPTURES_1, CHECKS,
QSEARCH_1, CAPTURES_S4, QSEARCH_WITHOUT_CHECKS, QCAPTURES_2,
PROBCUT, CAPTURES_S5, PROBCUT, PROBCUT_CAPTURES,
RECAPTURE, CAPTURES_S6, RECAPTURE, RECAPTURES,
STOP STOP
}; };
// Our insertion sort, which is guaranteed (and also needed) to be stable // Our insertion sort, which is guaranteed to be stable, as it should be
void insertion_sort(ExtMove* begin, ExtMove* end) void insertion_sort(ExtMove* begin, ExtMove* end)
{ {
ExtMove tmp, *p, *q; ExtMove tmp, *p, *q;
@@ -49,18 +49,15 @@ namespace {
} }
} }
// Unary predicate used by std::partition to split positive values from remaining // pick_best() finds the best move in the range (begin, end) and moves it to
// ones so as to sort the two sets separately, with the second sort delayed. // the front. It's faster than sorting all the moves in advance when there
inline bool has_positive_value(const ExtMove& move) { return move.value > VALUE_ZERO; } // are few moves e.g. the possible captures.
Move pick_best(ExtMove* begin, ExtMove* end)
// Picks 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 are few
// moves e.g. possible captures.
inline ExtMove* 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;
} }
} // namespace } // namespace
@@ -71,28 +68,19 @@ namespace {
/// ordering is at the current node. /// ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h, MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h,
Move* cm, Move* fm, Search::Stack* s) : pos(p), history(h), depth(d) { const CounterMovesHistoryStats& cmh, Move cm, Search::Stack* s)
: pos(p), history(h), counterMovesHistory(cmh), ss(s), countermove(cm), depth(d) {
assert(d > DEPTH_ZERO); assert(d > DEPTH_ZERO);
cur = end = moves; stage = pos.checkers() ? EVASION : MAIN_SEARCH;
endBadCaptures = moves + MAX_MOVES - 1; ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
countermoves = cm; endMoves += (ttMove != MOVE_NONE);
followupmoves = fm;
ss = s;
if (pos.checkers())
stage = EVASION;
else
stage = MAIN_SEARCH;
ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE);
end += (ttMove != MOVE_NONE);
} }
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h, MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h,
Square s) : pos(p), history(h), cur(moves), end(moves) { const CounterMovesHistoryStats& cmh, Square s)
: pos(p), history(h), counterMovesHistory(cmh) {
assert(d <= DEPTH_ZERO); assert(d <= DEPTH_ZERO);
@@ -100,10 +88,10 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
stage = EVASION; stage = EVASION;
else if (d > DEPTH_QS_NO_CHECKS) else if (d > DEPTH_QS_NO_CHECKS)
stage = QSEARCH_0; stage = QSEARCH_WITH_CHECKS;
else if (d > DEPTH_QS_RECAPTURES) else if (d > DEPTH_QS_RECAPTURES)
stage = QSEARCH_1; stage = QSEARCH_WITHOUT_CHECKS;
else else
{ {
@@ -112,94 +100,71 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
ttm = MOVE_NONE; ttm = MOVE_NONE;
} }
ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE); ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
end += (ttMove != MOVE_NONE); endMoves += (ttMove != MOVE_NONE);
} }
MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, PieceType pt) MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h,
: pos(p), history(h), cur(moves), end(moves) { const CounterMovesHistoryStats& cmh, Value th)
: pos(p), history(h), counterMovesHistory(cmh), threshold(th) {
assert(!pos.checkers()); assert(!pos.checkers());
stage = PROBCUT; stage = PROBCUT;
// In ProbCut we generate only captures that are better than the parent's // In ProbCut we generate captures with SEE higher than the given threshold
// captured piece. ttMove = ttm
captureThreshold = PieceValue[MG][pt]; && pos.pseudo_legal(ttm)
ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE); && pos.capture(ttm)
&& pos.see(ttm) > threshold ? ttm : MOVE_NONE;
if (ttMove && (!pos.capture(ttMove) || pos.see(ttMove) <= captureThreshold)) endMoves += (ttMove != MOVE_NONE);
ttMove = MOVE_NONE;
end += (ttMove != MOVE_NONE);
} }
/// score() assign a numerical value to each move in a move list. The moves with /// score() assigns a numerical value to each move in a move list. The moves with
/// highest values will be picked first. /// highest values will be picked first.
template<> template<>
void MovePicker::score<CAPTURES>() { void MovePicker::score<CAPTURES>() {
// Winning and equal captures in the main search are ordered by MVV/LVA. // Winning and equal captures in the main search are ordered by MVV, preferring
// Suprisingly, this appears to perform slightly better than SEE based // captures near our home rank. Suprisingly, this appears to perform slightly
// move ordering. The reason is probably that in a position with a winning // better than SEE based move ordering: exchanging big pieces before capturing
// capture, capturing a more valuable (but sufficiently defended) piece // a hanging piece probably helps to reduce the subtree size.
// first usually doesn't hurt. The opponent will have to recapture, and
// the hanging piece will still be hanging (except in the unusual cases
// where it is possible to recapture with the hanging piece). Exchanging
// big pieces before capturing a hanging piece probably helps to reduce
// the subtree size.
// In main search we want to push captures with negative SEE values to the // In 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 // badCaptures[] array, but instead of doing it now we delay until the move
// has been picked up in pick_move_from_list(). This way we save some SEE // has been picked up, saving some SEE calls in case we get a cutoff.
// calls in case we get a cutoff. for (auto& m : *this)
Move m; m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
- Value(200 * relative_rank(pos.side_to_move(), to_sq(m)));
for (ExtMove* it = moves; it != end; ++it)
{
m = it->move;
it->value = PieceValue[MG][pos.piece_on(to_sq(m))]
- Value(type_of(pos.moved_piece(m)));
if (type_of(m) == ENPASSANT)
it->value += PieceValue[MG][PAWN];
else if (type_of(m) == PROMOTION)
it->value += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN];
}
} }
template<> template<>
void MovePicker::score<QUIETS>() { void MovePicker::score<QUIETS>() {
Move m; Square prevSq = to_sq((ss-1)->currentMove);
const HistoryStats& cmh = counterMovesHistory[pos.piece_on(prevSq)][prevSq];
for (ExtMove* it = moves; it != end; ++it) for (auto& m : *this)
{ m.value = history[pos.moved_piece(m)][to_sq(m)]
m = it->move; + cmh[pos.moved_piece(m)][to_sq(m)];
it->value = history[pos.moved_piece(m)][to_sq(m)];
}
} }
template<> template<>
void MovePicker::score<EVASIONS>() { void MovePicker::score<EVASIONS>() {
// Try good captures ordered by MVV/LVA, then non-captures if destination square // Try winning and equal captures captures ordered by MVV/LVA, then non-captures
// is not under attack, ordered by history value, then bad-captures and quiet // ordered by history value, then bad-captures and quiet moves with a negative
// moves with a negative SEE. This last group is ordered by the SEE value. // SEE ordered by SEE value.
Move m;
Value see; Value see;
for (ExtMove* it = moves; it != end; ++it) for (auto& m : *this)
{
m = it->move;
if ((see = pos.see_sign(m)) < VALUE_ZERO) if ((see = pos.see_sign(m)) < VALUE_ZERO)
it->value = see - HistoryStats::Max; // At the bottom m.value = see - HistoryStats::Max; // At the bottom
else if (pos.capture(m)) else if (pos.capture(m))
it->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))) + HistoryStats::Max;
else else
it->value = history[pos.moved_piece(m)][to_sq(m)]; m.value = history[pos.moved_piece(m)][to_sq(m)];
}
} }
@@ -208,79 +173,60 @@ void MovePicker::score<EVASIONS>() {
void MovePicker::generate_next_stage() { void MovePicker::generate_next_stage() {
assert(stage != STOP);
cur = moves; cur = moves;
switch (++stage) { switch (++stage) {
case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6: case GOOD_CAPTURES: case QCAPTURES_1: case QCAPTURES_2:
end = generate<CAPTURES>(pos, moves); case PROBCUT_CAPTURES: case RECAPTURES:
endMoves = generate<CAPTURES>(pos, moves);
score<CAPTURES>(); score<CAPTURES>();
return; break;
case KILLERS_S1: case KILLERS:
killers[0] = ss->killers[0];
killers[1] = ss->killers[1];
killers[2] = countermove;
cur = killers; cur = killers;
end = cur + 2; endMoves = cur + 2 + (countermove != killers[0] && countermove != killers[1]);
break;
killers[0].move = ss->killers[0]; case GOOD_QUIETS:
killers[1].move = ss->killers[1]; endQuiets = endMoves = generate<QUIETS>(pos, moves);
killers[2].move = killers[3].move = MOVE_NONE;
killers[4].move = killers[5].move = MOVE_NONE;
// Please note that following code is racy and could yield to rare (less
// than 1 out of a million) duplicated entries in SMP case. This is harmless.
// Be sure countermoves are different from killers
for (int i = 0; i < 2; ++i)
if ( countermoves[i] != (cur+0)->move
&& countermoves[i] != (cur+1)->move)
(end++)->move = countermoves[i];
// Be sure followupmoves are different from killers and countermoves
for (int i = 0; i < 2; ++i)
if ( followupmoves[i] != (cur+0)->move
&& followupmoves[i] != (cur+1)->move
&& followupmoves[i] != (cur+2)->move
&& followupmoves[i] != (cur+3)->move)
(end++)->move = followupmoves[i];
return;
case QUIETS_1_S1:
endQuiets = end = generate<QUIETS>(pos, moves);
score<QUIETS>(); score<QUIETS>();
end = std::partition(cur, end, has_positive_value); endMoves = std::partition(cur, endMoves, [](const ExtMove& m) { return m.value > VALUE_ZERO; });
insertion_sort(cur, end); insertion_sort(cur, endMoves);
return; break;
case QUIETS_2_S1: case BAD_QUIETS:
cur = end; cur = endMoves;
end = endQuiets; endMoves = endQuiets;
if (depth >= 3 * ONE_PLY) if (depth >= 3 * ONE_PLY)
insertion_sort(cur, end); insertion_sort(cur, endMoves);
return; break;
case BAD_CAPTURES_S1: case BAD_CAPTURES:
// Just pick them in reverse order to get MVV/LVA ordering // Just pick them in reverse order to get correct ordering
cur = moves + MAX_MOVES - 1; cur = moves + MAX_MOVES - 1;
end = endBadCaptures; endMoves = endBadCaptures;
return; break;
case EVASIONS_S2: case ALL_EVASIONS:
end = generate<EVASIONS>(pos, moves); endMoves = generate<EVASIONS>(pos, moves);
if (end > moves + 1) if (endMoves - moves > 1)
score<EVASIONS>(); score<EVASIONS>();
return; break;
case QUIET_CHECKS_S3: case CHECKS:
end = generate<QUIET_CHECKS>(pos, moves); endMoves = generate<QUIET_CHECKS>(pos, moves);
return; break;
case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE: case EVASION: case QSEARCH_WITH_CHECKS: case QSEARCH_WITHOUT_CHECKS:
case PROBCUT: case RECAPTURE: case STOP:
stage = STOP; stage = STOP;
/* Fall through */ break;
case STOP:
end = cur + 1; // Avoid another next_phase() call
return;
default: default:
assert(false); assert(false);
@@ -292,36 +238,37 @@ void MovePicker::generate_next_stage() {
/// 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.
template<>
Move MovePicker::next_move<false>() { Move MovePicker::next_move() {
Move move; Move move;
while (true) while (true)
{ {
while (cur == end) while (cur == endMoves && stage != STOP)
generate_next_stage(); generate_next_stage();
switch (stage) { switch (stage) {
case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case MAIN_SEARCH: case EVASION: case QSEARCH_WITH_CHECKS:
case QSEARCH_WITHOUT_CHECKS: case PROBCUT:
++cur; ++cur;
return ttMove; return ttMove;
case CAPTURES_S1: case GOOD_CAPTURES:
move = pick_best(cur++, end)->move; move = pick_best(cur++, endMoves);
if (move != ttMove) if (move != ttMove)
{ {
if (pos.see_sign(move) >= VALUE_ZERO) if (pos.see_sign(move) >= VALUE_ZERO)
return move; return move;
// Losing capture, move it to the tail of the array // Losing capture, move it to the tail of the array
(endBadCaptures--)->move = move; *endBadCaptures-- = move;
} }
break; break;
case KILLERS_S1: case KILLERS:
move = (cur++)->move; move = *cur++;
if ( move != MOVE_NONE if ( move != MOVE_NONE
&& move != ttMove && move != ttMove
&& pos.pseudo_legal(move) && pos.pseudo_legal(move)
@@ -329,41 +276,38 @@ Move MovePicker::next_move<false>() {
return move; return move;
break; break;
case QUIETS_1_S1: case QUIETS_2_S1: case GOOD_QUIETS: case BAD_QUIETS:
move = (cur++)->move; move = *cur++;
if ( move != ttMove if ( move != ttMove
&& move != killers[0].move && move != killers[0]
&& move != killers[1].move && move != killers[1]
&& move != killers[2].move && move != killers[2])
&& move != killers[3].move
&& move != killers[4].move
&& move != killers[5].move)
return move; return move;
break; break;
case BAD_CAPTURES_S1: case BAD_CAPTURES:
return (cur--)->move; return *cur--;
case EVASIONS_S2: case CAPTURES_S3: case CAPTURES_S4: case ALL_EVASIONS: case QCAPTURES_1: case QCAPTURES_2:
move = pick_best(cur++, end)->move; move = pick_best(cur++, endMoves);
if (move != ttMove) if (move != ttMove)
return move; return move;
break; break;
case CAPTURES_S5: case PROBCUT_CAPTURES:
move = pick_best(cur++, end)->move; move = pick_best(cur++, endMoves);
if (move != ttMove && pos.see(move) > captureThreshold) if (move != ttMove && pos.see(move) > threshold)
return move; return move;
break; break;
case CAPTURES_S6: case RECAPTURES:
move = pick_best(cur++, end)->move; move = pick_best(cur++, endMoves);
if (to_sq(move) == recaptureSquare) if (to_sq(move) == recaptureSquare)
return move; return move;
break; break;
case QUIET_CHECKS_S3: case CHECKS:
move = (cur++)->move; move = *cur++;
if (move != ttMove) if (move != ttMove)
return move; return move;
break; break;
@@ -376,10 +320,3 @@ Move MovePicker::next_move<false>() {
} }
} }
} }
/// Version of next_move() to use at split point nodes where the move is grabbed
/// from the split point's shared MovePicker object. This function is not thread
/// safe so must be lock protected by the caller.
template<>
Move MovePicker::next_move<true>() { return ss->splitPoint->movePicker->next_move<false>(); }

View File

@@ -30,46 +30,50 @@
/// The Stats struct stores moves statistics. According to the template parameter /// The Stats struct stores moves statistics. According to the template parameter
/// the class can store History, Gains and Countermoves. History records how often /// the class can store History and Countermoves. History records how often
/// different moves have been successful or unsuccessful during the current search /// different moves have been successful or unsuccessful during the current search
/// and is used for reduction and move ordering decisions. Gains records the move's /// and is used for reduction and move ordering decisions.
/// best evaluation gain from one ply to the next and is used for pruning decisions.
/// Countermoves store the move that refute a previous one. Entries are stored /// Countermoves store the move that refute a previous one. Entries are stored
/// using only the moving piece and destination square, hence two moves with /// using only the moving piece and destination square, hence two moves with
/// different origin but same destination and piece will be considered identical. /// different origin but same destination and piece will be considered identical.
template<bool Gain, typename T> template<typename T>
struct Stats { struct Stats {
static const Value Max = Value(250); static const Value Max = Value(1<<28);
const T* operator[](Piece pc) const { return table[pc]; } 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 clear() { std::memset(table, 0, sizeof(table)); }
void update(Piece pc, Square to, Move m) { void update(Piece pc, Square to, Move m) {
if (m == table[pc][to].first) if (m != table[pc][to])
return; table[pc][to] = m;
table[pc][to].second = table[pc][to].first;
table[pc][to].first = m;
} }
void update(Piece pc, Square to, Value v) { void updateH(Piece pc, Square to, Value v) {
if (Gain) if (abs(int(v)) >= 324)
table[pc][to] = std::max(v, table[pc][to] - 1); return;
table[pc][to] -= table[pc][to] * abs(int(v)) / 324;
table[pc][to] += int(v) * 32;
}
else if (abs(table[pc][to] + v) < Max) void updateCMH(Piece pc, Square to, Value v) {
table[pc][to] += v;
if (abs(int(v)) >= 324)
return;
table[pc][to] -= table[pc][to] * abs(int(v)) / 512;
table[pc][to] += int(v) * 64;
} }
private: private:
T table[PIECE_NB][SQUARE_NB]; T table[PIECE_NB][SQUARE_NB];
}; };
typedef Stats< true, Value> GainsStats; typedef Stats<Value> HistoryStats;
typedef Stats<false, Value> HistoryStats; typedef Stats<Move> MovesStats;
typedef Stats<false, std::pair<Move, Move> > MovesStats; typedef Stats<HistoryStats> CounterMovesHistoryStats;
/// 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
@@ -80,33 +84,35 @@ typedef Stats<false, std::pair<Move, Move> > MovesStats;
/// to get a cut-off first. /// to get a cut-off first.
class MovePicker { class MovePicker {
MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC
public: public:
MovePicker(const Position&, Move, Depth, const HistoryStats&, Square); MovePicker(const MovePicker&) = delete;
MovePicker(const Position&, Move, const HistoryStats&, PieceType); MovePicker& operator=(const MovePicker&) = delete;
MovePicker(const Position&, Move, Depth, const HistoryStats&, Move*, Move*, Search::Stack*);
template<bool SpNode> Move next_move(); MovePicker(const Position&, Move, Depth, const HistoryStats&, const CounterMovesHistoryStats&, Square);
MovePicker(const Position&, Move, const HistoryStats&, const CounterMovesHistoryStats&, Value);
MovePicker(const Position&, Move, Depth, const HistoryStats&, const CounterMovesHistoryStats&, Move, Search::Stack*);
Move next_move();
private: private:
template<GenType> void score(); template<GenType> void score();
void generate_next_stage(); void generate_next_stage();
ExtMove* begin() { return moves; }
ExtMove* end() { return endMoves; }
const Position& pos; const Position& pos;
const HistoryStats& history; const HistoryStats& history;
const CounterMovesHistoryStats& counterMovesHistory;
Search::Stack* ss; Search::Stack* ss;
Move* countermoves; Move countermove;
Move* followupmoves;
Depth depth; Depth depth;
Move ttMove; Move ttMove;
ExtMove killers[6]; ExtMove killers[3];
Square recaptureSquare; Square recaptureSquare;
Value captureThreshold; Value threshold;
int stage; int stage;
ExtMove *cur, *end, *endQuiets, *endBadCaptures; ExtMove *endQuiets, *endBadCaptures = moves + MAX_MOVES - 1;
ExtMove moves[MAX_MOVES]; ExtMove moves[MAX_MOVES], *cur = moves, *endMoves = moves;
}; };
#endif // #ifndef MOVEPICK_H_INCLUDED #endif // #ifndef MOVEPICK_H_INCLUDED

View File

@@ -43,15 +43,11 @@ namespace {
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35), { S(25, 30), S(36, 35), S(40, 35), S(40, 35),
S(40, 35), S(40, 35), S(36, 35), S(25, 30) } }; S(40, 35), S(40, 35), S(36, 35), S(25, 30) } };
// Backward pawn penalty by opposed flag and file // Backward pawn penalty by opposed flag
const Score Backward[2][FILE_NB] = { const Score Backward[2] = { S(67, 42), S(49, 24) };
{ S(30, 42), S(43, 46), S(49, 46), S(49, 46),
S(49, 46), S(49, 46), S(43, 46), S(30, 42) },
{ S(20, 28), S(29, 31), S(33, 31), S(33, 31),
S(33, 31), S(33, 31), S(29, 31), S(20, 28) } };
// Connected pawn bonus by opposed, phalanx flags and rank // Connected pawn bonus by opposed, phalanx, twice supported and rank
Score Connected[2][2][RANK_NB]; Score Connected[2][2][2][RANK_NB];
// Levers bonus by rank // Levers bonus by rank
const Score Lever[RANK_NB] = { const Score Lever[RANK_NB] = {
@@ -61,35 +57,43 @@ namespace {
// Unsupported pawn penalty // Unsupported pawn penalty
const Score UnsupportedPawnPenalty = S(20, 10); const Score UnsupportedPawnPenalty = S(20, 10);
// Center bind bonus: Two pawns controlling the same central square
const Bitboard CenterBindMask[COLOR_NB] = {
(FileDBB | FileEBB) & (Rank5BB | Rank6BB | Rank7BB),
(FileDBB | FileEBB) & (Rank4BB | Rank3BB | Rank2BB)
};
const Score CenterBind = S(16, 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 [distance from edge][rank]
const Value ShelterWeakness[][RANK_NB] = { const Value ShelterWeakness[][RANK_NB] = {
{ V(100), V(13), V(24), V(64), V(89), V( 93), V(104) }, { V( 97), V(21), V(26), V(51), V(87), V( 89), V( 99) },
{ V(110), V( 1), V(29), V(75), V(96), V(102), V(107) }, { V(120), V( 0), V(28), V(76), V(88), V(103), V(104) },
{ V(102), V( 0), V(39), V(74), V(88), V(101), V( 98) }, { V(101), V( 7), V(54), V(78), V(77), V( 92), V(101) },
{ V( 88), V( 4), V(33), V(67), V(92), V( 94), V(107) } }; { V( 80), V(11), V(44), V(68), V(87), V( 90), V(119) } };
// 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]
const Value StormDanger[][4][RANK_NB] = { const Value StormDanger[][4][RANK_NB] = {
{ { V( 0), V( 63), V( 128), V(43), V(27) }, { { V( 0), V( 67), V( 134), V(38), V(32) },
{ V( 0), V( 62), V( 131), V(44), V(26) }, { V( 0), V( 57), V( 139), V(37), V(22) },
{ V( 0), V( 59), V( 121), V(50), V(28) }, { V( 0), V( 43), V( 115), V(43), V(27) },
{ V( 0), V( 62), V( 127), V(54), V(28) } }, { V( 0), V( 68), V( 124), V(57), V(32) } },
{ { V(24), V( 40), V( 93), V(42), V(22) }, { { V(20), V( 43), V( 100), V(56), V(20) },
{ V(24), V( 28), V( 101), V(38), V(20) }, { V(23), V( 20), V( 98), V(40), V(15) },
{ V(24), V( 32), V( 95), V(36), V(23) }, { V(23), V( 39), V( 103), V(36), V(18) },
{ V(27), V( 24), V( 99), V(36), V(24) } }, { V(28), V( 19), V( 108), V(42), V(26) } },
{ { V( 0), V( 0), V( 81), V(16), V( 6) }, { { V( 0), V( 0), V( 75), V(14), V( 2) },
{ V( 0), V( 0), V( 165), V(29), V( 9) }, { V( 0), V( 0), V( 150), V(30), V( 4) },
{ V( 0), V( 0), V( 163), V(23), V(12) }, { V( 0), V( 0), V( 160), V(22), V( 5) },
{ V( 0), V( 0), V( 161), V(28), V(13) } }, { V( 0), V( 0), V( 166), V(24), V(13) } },
{ { V( 0), V(-296), V(-299), V(55), V(25) }, { { V( 0), V(-283), V(-281), V(57), V(31) },
{ V( 0), V( 67), V( 131), V(46), V(21) }, { V( 0), V( 58), V( 141), V(39), V(18) },
{ V( 0), V( 65), V( 135), V(50), V(31) }, { V( 0), V( 65), V( 142), V(48), V(32) },
{ V( 0), V( 62), V( 128), V(51), V(24) } } }; { V( 0), V( 60), V( 126), V(51), V(19) } } };
// 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
// in front of the king and no enemy pawn on the horizon. // in front of the king and no enemy pawn on the horizon.
const Value MaxSafetyBonus = V(257); const Value MaxSafetyBonus = V(258);
#undef S #undef S
#undef V #undef V
@@ -102,11 +106,11 @@ namespace {
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW); const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE); const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
Bitboard b, p, doubled, connected; Bitboard b, neighbours, doubled, supported, phalanx;
Square s; Square s;
bool passed, isolated, opposed, phalanx, backward, unsupported, lever; bool passed, isolated, opposed, backward, lever, connected;
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
const Square* pl = pos.list<PAWN>(Us); const Square* pl = pos.squares<PAWN>(Us);
const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)]; const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)];
Bitboard ourPawns = pos.pieces(Us , PAWN); Bitboard ourPawns = pos.pieces(Us , PAWN);
@@ -129,31 +133,28 @@ namespace {
// This file cannot be semi-open // This file cannot be semi-open
e->semiopenFiles[Us] &= ~(1 << f); e->semiopenFiles[Us] &= ~(1 << f);
// Previous rank // Flag the pawn
p = rank_bb(s - pawn_push(Us)); neighbours = ourPawns & adjacent_files_bb(f);
// Flag the pawn as passed, isolated, doubled,
// unsupported or connected (but not the backward one).
connected = ourPawns & adjacent_files_bb(f) & (rank_bb(s) | p);
phalanx = connected & rank_bb(s);
unsupported = !(ourPawns & adjacent_files_bb(f) & p);
isolated = !(ourPawns & adjacent_files_bb(f));
doubled = ourPawns & forward_bb(Us, s); doubled = ourPawns & forward_bb(Us, s);
opposed = theirPawns & forward_bb(Us, s); opposed = theirPawns & forward_bb(Us, s);
passed = !(theirPawns & passed_pawn_mask(Us, s)); passed = !(theirPawns & passed_pawn_mask(Us, s));
lever = theirPawns & pawnAttacksBB[s]; lever = theirPawns & pawnAttacksBB[s];
phalanx = neighbours & rank_bb(s);
supported = neighbours & rank_bb(s - Up);
connected = supported | phalanx;
isolated = !neighbours;
// Test for backward pawn. // Test for backward pawn.
// If the pawn is passed, isolated, or connected it cannot be // If the pawn is passed, isolated, lever or connected it cannot be
// backward. If there are friendly pawns behind on adjacent files // backward. If there are friendly pawns behind on adjacent files
// or if it can capture an enemy pawn it cannot be backward either. // or if it is sufficiently advanced, it cannot be backward either.
if ( (passed | isolated | connected) if ( (passed | isolated | lever | connected)
|| (ourPawns & pawn_attack_span(Them, s)) || (ourPawns & pawn_attack_span(Them, s))
|| (pos.attacks_from<PAWN>(s, Us) & theirPawns)) || (relative_rank(Us, s) >= RANK_5))
backward = false; backward = false;
else else
{ {
// We now know that there are no friendly pawns beside or behind this // We now know there are no friendly pawns beside or behind this
// pawn on adjacent files. We now check whether the pawn is // pawn on adjacent files. We now check whether the pawn is
// backward by looking in the forward direction on the adjacent // backward by looking in the forward direction on the adjacent
// files, and picking the closest pawn there. // files, and picking the closest pawn there.
@@ -177,18 +178,18 @@ namespace {
if (isolated) if (isolated)
score -= Isolated[opposed][f]; score -= Isolated[opposed][f];
if (unsupported && !isolated) else if (backward)
score -= Backward[opposed];
else if (!supported)
score -= UnsupportedPawnPenalty; score -= UnsupportedPawnPenalty;
if (connected)
score += Connected[opposed][!!phalanx][more_than_one(supported)][relative_rank(Us, s)];
if (doubled) if (doubled)
score -= Doubled[f] / distance<Rank>(s, frontmost_sq(Us, doubled)); score -= Doubled[f] / distance<Rank>(s, frontmost_sq(Us, doubled));
if (backward)
score -= Backward[opposed][f];
if (connected)
score += Connected[opposed][phalanx][relative_rank(Us, s)];
if (lever) if (lever)
score += Lever[relative_rank(Us, s)]; score += Lever[relative_rank(Us, s)];
} }
@@ -196,6 +197,10 @@ namespace {
b = e->semiopenFiles[Us] ^ 0xFF; b = e->semiopenFiles[Us] ^ 0xFF;
e->pawnSpan[Us] = b ? int(msb(b) - lsb(b)) : 0; e->pawnSpan[Us] = b ? int(msb(b) - lsb(b)) : 0;
// Center binds: Two pawns controlling the same central square
b = shift_bb<Right>(ourPawns) & shift_bb<Left>(ourPawns) & CenterBindMask[Us];
score += popcount<Max15>(b) * CenterBind;
return score; return score;
} }
@@ -213,10 +218,12 @@ void init()
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 (Rank r = RANK_2; r < RANK_8; ++r) for (Rank r = RANK_2; r < RANK_8; ++r)
{ {
int bonus = Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0); int v = (Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0)) >> opposed;
Connected[opposed][phalanx][r] = make_score(bonus / 2, bonus >> opposed); v += (apex ? v / 2 : 0);
Connected[opposed][phalanx][apex][r] = make_score(3 * v / 2, v);
} }
} }
@@ -236,6 +243,7 @@ Entry* probe(const Position& pos) {
e->key = key; e->key = key;
e->score = evaluate<WHITE>(pos, e) - evaluate<BLACK>(pos, e); e->score = evaluate<WHITE>(pos, e) - evaluate<BLACK>(pos, e);
e->asymmetry = popcount<Max15>( e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK] );
return e; return e;
} }
@@ -284,14 +292,14 @@ Score Entry::do_king_safety(const Position& pos, Square ksq) {
kingSquares[Us] = ksq; kingSquares[Us] = ksq;
castlingRights[Us] = pos.can_castle(Us); castlingRights[Us] = pos.can_castle(Us);
minKingPawnDistance[Us] = 0; int minKingPawnDistance = 0;
Bitboard pawns = pos.pieces(Us, PAWN); Bitboard pawns = pos.pieces(Us, PAWN);
if (pawns) if (pawns)
while (!(DistanceRingBB[ksq][minKingPawnDistance[Us]++] & pawns)) {} while (!(DistanceRingBB[ksq][minKingPawnDistance++] & pawns)) {}
if (relative_rank(Us, ksq) > RANK_4) if (relative_rank(Us, ksq) > RANK_4)
return make_score(0, -16 * minKingPawnDistance[Us]); return make_score(0, -16 * minKingPawnDistance);
Value bonus = shelter_storm<Us>(pos, ksq); Value bonus = shelter_storm<Us>(pos, ksq);
@@ -302,7 +310,7 @@ Score Entry::do_king_safety(const Position& pos, Square ksq) {
if (pos.can_castle(MakeCastling<Us, QUEEN_SIDE>::right)) if (pos.can_castle(MakeCastling<Us, QUEEN_SIDE>::right))
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1))); bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1)));
return make_score(bonus, -16 * minKingPawnDistance[Us]); return make_score(bonus, -16 * minKingPawnDistance);
} }
// Explicit template instantiation // Explicit template instantiation

View File

@@ -36,6 +36,7 @@ struct Entry {
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
int pawn_span(Color c) const { return pawnSpan[c]; } int pawn_span(Color c) const { return pawnSpan[c]; }
int pawn_asymmetry() const { return asymmetry; }
int semiopen_file(Color c, File f) const { int semiopen_file(Color c, File f) const {
return semiopenFiles[c] & (1 << f); return semiopenFiles[c] & (1 << f);
@@ -67,11 +68,11 @@ struct Entry {
Bitboard pawnAttacks[COLOR_NB]; Bitboard pawnAttacks[COLOR_NB];
Square kingSquares[COLOR_NB]; Square kingSquares[COLOR_NB];
Score kingSafety[COLOR_NB]; Score kingSafety[COLOR_NB];
int minKingPawnDistance[COLOR_NB];
int castlingRights[COLOR_NB]; int castlingRights[COLOR_NB];
int semiopenFiles[COLOR_NB]; int semiopenFiles[COLOR_NB];
int pawnSpan[COLOR_NB]; int pawnSpan[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares] int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
int asymmetry;
}; };
typedef HashTable<Entry, 16384> Table; typedef HashTable<Entry, 16384> Table;

View File

@@ -1,116 +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
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 PLATFORM_H_INCLUDED
#define PLATFORM_H_INCLUDED
#ifdef _MSC_VER
// Disable some silly and noisy warnings from MSVC compiler
#pragma warning(disable: 4127) // Conditional expression is constant
#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
#pragma warning(disable: 4996) // Function _ftime() may be unsafe
// MSVC does not support <inttypes.h>
typedef signed __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef signed __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef signed __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
# include <inttypes.h>
#endif
#ifndef _WIN32 // Linux - Unix
# include <sys/time.h>
inline int64_t system_time_to_msec() {
timeval t;
gettimeofday(&t, NULL);
return t.tv_sec * 1000LL + t.tv_usec / 1000;
}
# include <pthread.h>
typedef pthread_mutex_t Lock;
typedef pthread_cond_t WaitCondition;
typedef pthread_t NativeHandle;
typedef void*(*pt_start_fn)(void*);
# define lock_init(x) pthread_mutex_init(&(x), NULL)
# define lock_grab(x) pthread_mutex_lock(&(x))
# define lock_release(x) pthread_mutex_unlock(&(x))
# define lock_destroy(x) pthread_mutex_destroy(&(x))
# define cond_destroy(x) pthread_cond_destroy(&(x))
# define cond_init(x) pthread_cond_init(&(x), NULL)
# define cond_signal(x) pthread_cond_signal(&(x))
# define cond_wait(x,y) pthread_cond_wait(&(x),&(y))
# define cond_timedwait(x,y,z) pthread_cond_timedwait(&(x),&(y),z)
# define thread_create(x,f,t) pthread_create(&(x),NULL,(pt_start_fn)f,t)
# define thread_join(x) pthread_join(x, NULL)
#else // Windows and MinGW
# include <sys/timeb.h>
inline int64_t system_time_to_msec() {
_timeb t;
_ftime(&t);
return t.time * 1000LL + t.millitm;
}
#ifndef NOMINMAX
# define NOMINMAX // disable macros min() and max()
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX
// We use critical sections on Windows to support Windows XP and older versions.
// Unfortunately, cond_wait() is racy between lock_release() and WaitForSingleObject()
// but apart from this they have the same speed performance of SRW locks.
typedef CRITICAL_SECTION Lock;
typedef HANDLE WaitCondition;
typedef HANDLE NativeHandle;
// On Windows 95 and 98 parameter lpThreadId may not be null
inline DWORD* dwWin9xKludge() { static DWORD dw; return &dw; }
# define lock_init(x) InitializeCriticalSection(&(x))
# define lock_grab(x) EnterCriticalSection(&(x))
# define lock_release(x) LeaveCriticalSection(&(x))
# define lock_destroy(x) DeleteCriticalSection(&(x))
# define cond_init(x) { x = CreateEvent(0, FALSE, FALSE, 0); }
# define cond_destroy(x) CloseHandle(x)
# define cond_signal(x) SetEvent(x)
# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(x, INFINITE); lock_grab(y); }
# define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(x,z); lock_grab(y); }
# define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,dwWin9xKludge()))
# define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); }
#endif
#endif // #ifndef PLATFORM_H_INCLUDED

View File

@@ -19,7 +19,7 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cstring> // For std::memset #include <cstring> // For std::memset, std::memcmp
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
@@ -27,7 +27,6 @@
#include "misc.h" #include "misc.h"
#include "movegen.h" #include "movegen.h"
#include "position.h" #include "position.h"
#include "psqtab.h"
#include "thread.h" #include "thread.h"
#include "tt.h" #include "tt.h"
#include "uci.h" #include "uci.h"
@@ -47,19 +46,18 @@ namespace Zobrist {
Key exclusion; Key exclusion;
} }
Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion;} Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion; }
namespace { namespace {
const string PieceToChar(" PNBRQK pnbrqk"); const string PieceToChar(" PNBRQK pnbrqk");
Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
// min_attacker() is a helper function used by see() to locate the least // min_attacker() is a helper function used by see() 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.
template<int Pt> FORCE_INLINE template<int Pt>
PieceType min_attacker(const Bitboard* bb, const Square& to, const Bitboard& stmAttackers, PieceType min_attacker(const Bitboard* bb, Square to, Bitboard stmAttackers,
Bitboard& occupied, Bitboard& attackers) { Bitboard& occupied, Bitboard& attackers) {
Bitboard b = stmAttackers & bb[Pt]; Bitboard b = stmAttackers & bb[Pt];
@@ -78,8 +76,8 @@ PieceType min_attacker(const Bitboard* bb, const Square& to, const Bitboard& stm
return (PieceType)Pt; return (PieceType)Pt;
} }
template<> FORCE_INLINE template<>
PieceType min_attacker<KING>(const Bitboard*, const Square&, const Bitboard&, Bitboard&, Bitboard&) { PieceType min_attacker<KING>(const Bitboard*, Square, Bitboard, Bitboard&, Bitboard&) {
return KING; // No need to update bitboards: it is the last cycle return KING; // No need to update bitboards: it is the last cycle
} }
@@ -91,17 +89,17 @@ PieceType min_attacker<KING>(const Bitboard*, const Square&, const Bitboard&, Bi
CheckInfo::CheckInfo(const Position& pos) { CheckInfo::CheckInfo(const Position& pos) {
Color them = ~pos.side_to_move(); Color them = ~pos.side_to_move();
ksq = pos.king_square(them); ksq = pos.square<KING>(them);
pinned = pos.pinned_pieces(pos.side_to_move()); pinned = pos.pinned_pieces(pos.side_to_move());
dcCandidates = pos.discovered_check_candidates(); dcCandidates = pos.discovered_check_candidates();
checkSq[PAWN] = pos.attacks_from<PAWN>(ksq, them); checkSquares[PAWN] = pos.attacks_from<PAWN>(ksq, them);
checkSq[KNIGHT] = pos.attacks_from<KNIGHT>(ksq); checkSquares[KNIGHT] = pos.attacks_from<KNIGHT>(ksq);
checkSq[BISHOP] = pos.attacks_from<BISHOP>(ksq); checkSquares[BISHOP] = pos.attacks_from<BISHOP>(ksq);
checkSq[ROOK] = pos.attacks_from<ROOK>(ksq); checkSquares[ROOK] = pos.attacks_from<ROOK>(ksq);
checkSq[QUEEN] = checkSq[BISHOP] | checkSq[ROOK]; checkSquares[QUEEN] = checkSquares[BISHOP] | checkSquares[ROOK];
checkSq[KING] = 0; checkSquares[KING] = 0;
} }
@@ -130,10 +128,7 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) {
/// Position::init() initializes at startup the various arrays used to compute /// Position::init() initializes at startup the various arrays used to compute
/// hash keys and the piece square tables. The latter is a two-step operation: /// hash keys.
/// Firstly, the white halves of the tables are copied from PSQT[] tables.
/// Secondly, the black halves of the tables are initialized by flipping and
/// changing the sign of the white scores.
void Position::init() { void Position::init() {
@@ -149,6 +144,7 @@ void Position::init() {
for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr) for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr)
{ {
Zobrist::castling[cr] = 0;
Bitboard b = cr; Bitboard b = cr;
while (b) while (b)
{ {
@@ -159,20 +155,6 @@ void Position::init() {
Zobrist::side = rng.rand<Key>(); Zobrist::side = rng.rand<Key>();
Zobrist::exclusion = rng.rand<Key>(); Zobrist::exclusion = rng.rand<Key>();
for (PieceType pt = PAWN; pt <= KING; ++pt)
{
PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt];
PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt];
Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]);
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
psq[WHITE][pt][ s] = (v + PSQT[pt][s]);
psq[BLACK][pt][~s] = -(v + PSQT[pt][s]);
}
}
} }
@@ -182,7 +164,7 @@ void Position::init() {
Position& Position::operator=(const Position& pos) { Position& Position::operator=(const Position& pos) {
std::memcpy(this, &pos, sizeof(Position)); std::memcpy(this, &pos, sizeof(Position));
startState = *st; std::memcpy(&startState, st, sizeof(StateInfo));
st = &startState; st = &startState;
nodes = 0; nodes = 0;
@@ -265,7 +247,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
else if ((idx = PieceToChar.find(token)) != string::npos) else if ((idx = PieceToChar.find(token)) != string::npos)
{ {
put_piece(sq, color_of(Piece(idx)), type_of(Piece(idx))); put_piece(color_of(Piece(idx)), type_of(Piece(idx)), sq);
++sq; ++sq;
} }
} }
@@ -284,14 +266,15 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
{ {
Square rsq; Square rsq;
Color c = islower(token) ? BLACK : WHITE; Color c = islower(token) ? BLACK : WHITE;
Piece rook = make_piece(c, ROOK);
token = char(toupper(token)); token = char(toupper(token));
if (token == 'K') if (token == 'K')
for (rsq = relative_square(c, SQ_H1); type_of(piece_on(rsq)) != ROOK; --rsq) {} for (rsq = relative_square(c, SQ_H1); piece_on(rsq) != rook; --rsq) {}
else if (token == 'Q') else if (token == 'Q')
for (rsq = relative_square(c, SQ_A1); type_of(piece_on(rsq)) != ROOK; ++rsq) {} for (rsq = relative_square(c, SQ_A1); piece_on(rsq) != rook; ++rsq) {}
else if (token >= 'A' && token <= 'H') else if (token >= 'A' && token <= 'H')
rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1)); rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1));
@@ -332,7 +315,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
void Position::set_castling_right(Color c, Square rfrom) { void Position::set_castling_right(Color c, Square rfrom) {
Square kfrom = king_square(c); Square kfrom = square<KING>(c);
CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE; CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE;
CastlingRight cr = (c | cs); CastlingRight cr = (c | cs);
@@ -365,23 +348,23 @@ void Position::set_state(StateInfo* si) const {
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(king_square(sideToMove)) & pieces(~sideToMove); si->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove);
for (Bitboard b = pieces(); b; ) for (Bitboard b = pieces(); b; )
{ {
Square s = pop_lsb(&b); Square s = pop_lsb(&b);
Piece pc = piece_on(s); Piece pc = piece_on(s);
si->key ^= Zobrist::psq[color_of(pc)][type_of(pc)][s]; si->key ^= Zobrist::psq[color_of(pc)][type_of(pc)][s];
si->psq += psq[color_of(pc)][type_of(pc)][s]; si->psq += PSQT::psq[color_of(pc)][type_of(pc)][s];
} }
if (ep_square() != SQ_NONE) if (si->epSquare != SQ_NONE)
si->key ^= Zobrist::enpassant[file_of(ep_square())]; si->key ^= Zobrist::enpassant[file_of(si->epSquare)];
if (sideToMove == BLACK) if (sideToMove == BLACK)
si->key ^= Zobrist::side; si->key ^= Zobrist::side;
si->key ^= Zobrist::castling[st->castlingRights]; si->key ^= Zobrist::castling[si->castlingRights];
for (Bitboard b = pieces(PAWN); b; ) for (Bitboard b = pieces(PAWN); b; )
{ {
@@ -473,7 +456,7 @@ Phase Position::game_phase() const {
Bitboard Position::check_blockers(Color c, Color kingColor) const { Bitboard Position::check_blockers(Color c, Color kingColor) const {
Bitboard b, pinners, result = 0; Bitboard b, pinners, result = 0;
Square ksq = king_square(kingColor); Square ksq = square<KING>(kingColor);
// Pinners are sliders that give check when a pinned piece is removed // Pinners are sliders that give check when a pinned piece is removed
pinners = ( (pieces( ROOK, QUEEN) & PseudoAttacks[ROOK ][ksq]) pinners = ( (pieces( ROOK, QUEEN) & PseudoAttacks[ROOK ][ksq])
@@ -498,7 +481,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));
} }
@@ -515,14 +498,14 @@ bool Position::legal(Move m, Bitboard pinned) const {
Square from = from_sq(m); Square from = from_sq(m);
assert(color_of(moved_piece(m)) == us); assert(color_of(moved_piece(m)) == us);
assert(piece_on(king_square(us)) == make_piece(us, KING)); assert(piece_on(square<KING>(us)) == make_piece(us, KING));
// En passant captures are a tricky special case. Because they are rather // En passant captures are a tricky special case. Because they are rather
// uncommon, we do it simply by testing whether the king is attacked after // uncommon, we do it simply by testing whether the king is attacked after
// the move is made. // the move is made.
if (type_of(m) == ENPASSANT) if (type_of(m) == ENPASSANT)
{ {
Square ksq = king_square(us); Square ksq = square<KING>(us);
Square to = to_sq(m); Square to = to_sq(m);
Square capsq = to - pawn_push(us); Square capsq = to - pawn_push(us);
Bitboard occupied = (pieces() ^ from ^ capsq) | to; Bitboard occupied = (pieces() ^ from ^ capsq) | to;
@@ -546,7 +529,7 @@ bool Position::legal(Move m, Bitboard pinned) const {
// is moving along the ray towards or away from the king. // is moving along the ray towards or away from the king.
return !pinned return !pinned
|| !(pinned & from) || !(pinned & from)
|| aligned(from, to_sq(m), king_square(us)); || aligned(from, to_sq(m), square<KING>(us));
} }
@@ -566,7 +549,7 @@ bool Position::pseudo_legal(const Move m) const {
return MoveList<LEGAL>(*this).contains(m); return MoveList<LEGAL>(*this).contains(m);
// Is not a promotion, so promotion piece must be empty // Is not a promotion, so promotion piece must be empty
if (promotion_type(m) - 2 != NO_PIECE_TYPE) if (promotion_type(m) - KNIGHT != NO_PIECE_TYPE)
return false; return false;
// If the 'from' square is not occupied by a piece belonging to the side to // If the 'from' square is not occupied by a piece belonging to the side to
@@ -587,9 +570,7 @@ bool Position::pseudo_legal(const Move m) const {
return false; return false;
if ( !(attacks_from<PAWN>(from, us) & pieces(~us) & to) // Not a capture if ( !(attacks_from<PAWN>(from, us) & pieces(~us) & to) // Not a capture
&& !((from + pawn_push(us) == to) && empty(to)) // Not a single push && !((from + pawn_push(us) == to) && empty(to)) // Not a single push
&& !( (from + 2 * pawn_push(us) == to) // Not a double push && !( (from + 2 * pawn_push(us) == to) // Not a double push
&& (rank_of(from) == relative_rank(us, RANK_2)) && (rank_of(from) == relative_rank(us, RANK_2))
&& empty(to) && empty(to)
@@ -611,7 +592,7 @@ bool Position::pseudo_legal(const Move m) const {
return false; return false;
// Our move must be a blocking evasion or a capture of the checking piece // Our move must be a blocking evasion or a capture of the checking piece
if (!((between_bb(lsb(checkers()), king_square(us)) | checkers()) & to)) if (!((between_bb(lsb(checkers()), square<KING>(us)) | checkers()) & to))
return false; return false;
} }
// In case of king moves under check we have to remove king so as to catch // In case of king moves under check we have to remove king so as to catch
@@ -634,10 +615,9 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const {
Square from = from_sq(m); Square from = from_sq(m);
Square to = to_sq(m); Square to = to_sq(m);
PieceType pt = type_of(piece_on(from));
// Is there a direct check? // Is there a direct check?
if (ci.checkSq[pt] & to) if (ci.checkSquares[type_of(piece_on(from))] & to)
return true; return true;
// Is there a discovered check? // Is there a discovered check?
@@ -687,31 +667,21 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const {
/// to a StateInfo object. The move is assumed to be legal. Pseudo-legal /// to a StateInfo object. The move is assumed to be legal. Pseudo-legal
/// moves should be filtered out before this function is called. /// moves should be filtered out before this function is called.
void Position::do_move(Move m, StateInfo& newSt) { void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
CheckInfo ci(*this);
do_move(m, newSt, ci, gives_check(m, ci));
}
void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveIsCheck) {
assert(is_ok(m)); assert(is_ok(m));
assert(&newSt != st); assert(&newSt != st);
++nodes; ++nodes;
Key k = st->key; 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
// ones which are going to be recalculated from scratch anyway and then switch // ones which are going to be recalculated from scratch anyway and then switch
// our state pointer to point to the new (ready to be updated) state. // our state pointer to point to the new (ready to be updated) state.
std::memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t)); std::memcpy(&newSt, st, offsetof(StateInfo, key));
newSt.previous = st; newSt.previous = st;
st = &newSt; st = &newSt;
// Update side to move
k ^= Zobrist::side;
// Increment ply counters. In particular, rule50 will be reset to zero later on // Increment ply counters. In particular, rule50 will be reset to zero later on
// in case of a capture or a pawn move. // in case of a capture or a pawn move.
++gamePly; ++gamePly;
@@ -722,23 +692,22 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
Color them = ~us; Color them = ~us;
Square from = from_sq(m); Square from = from_sq(m);
Square to = to_sq(m); Square to = to_sq(m);
Piece pc = piece_on(from); PieceType pt = type_of(piece_on(from));
PieceType pt = type_of(pc);
PieceType captured = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to)); PieceType captured = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to));
assert(color_of(pc) == us); assert(color_of(piece_on(from)) == us);
assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLING); assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == (type_of(m) != CASTLING ? them : us));
assert(captured != KING); assert(captured != KING);
if (type_of(m) == CASTLING) if (type_of(m) == CASTLING)
{ {
assert(pc == make_piece(us, KING)); assert(pt == KING);
Square rfrom, rto; Square rfrom, rto;
do_castling<true>(from, to, rfrom, rto); do_castling<true>(us, from, to, rfrom, rto);
captured = NO_PIECE_TYPE; captured = NO_PIECE_TYPE;
st->psq += psq[us][ROOK][rto] - psq[us][ROOK][rfrom]; st->psq += PSQT::psq[us][ROOK][rto] - PSQT::psq[us][ROOK][rfrom];
k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
} }
@@ -752,7 +721,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
{ {
if (type_of(m) == ENPASSANT) if (type_of(m) == ENPASSANT)
{ {
capsq += pawn_push(them); capsq -= pawn_push(us);
assert(pt == PAWN); assert(pt == PAWN);
assert(to == st->epSquare); assert(to == st->epSquare);
@@ -760,7 +729,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
assert(piece_on(to) == NO_PIECE); assert(piece_on(to) == NO_PIECE);
assert(piece_on(capsq) == make_piece(them, PAWN)); assert(piece_on(capsq) == make_piece(them, PAWN));
board[capsq] = NO_PIECE; board[capsq] = NO_PIECE; // Not done by remove_piece()
} }
st->pawnKey ^= Zobrist::psq[them][PAWN][capsq]; st->pawnKey ^= Zobrist::psq[them][PAWN][capsq];
@@ -769,15 +738,15 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
st->nonPawnMaterial[them] -= PieceValue[MG][captured]; st->nonPawnMaterial[them] -= PieceValue[MG][captured];
// Update board and piece lists // Update board and piece lists
remove_piece(capsq, them, captured); remove_piece(them, captured, capsq);
// Update material hash key and prefetch access to materialTable // Update material hash key and prefetch access to materialTable
k ^= Zobrist::psq[them][captured][capsq]; k ^= Zobrist::psq[them][captured][capsq];
st->materialKey ^= Zobrist::psq[them][captured][pieceCount[them][captured]]; st->materialKey ^= Zobrist::psq[them][captured][pieceCount[them][captured]];
prefetch((char*)thisThread->materialTable[st->materialKey]); prefetch(thisThread->materialTable[st->materialKey]);
// Update incremental scores // Update incremental scores
st->psq -= psq[them][captured][capsq]; st->psq -= PSQT::psq[them][captured][capsq];
// Reset rule 50 counter // Reset rule 50 counter
st->rule50 = 0; st->rule50 = 0;
@@ -803,16 +772,16 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Move the piece. The tricky Chess960 castling is handled earlier // Move the piece. The tricky Chess960 castling is handled earlier
if (type_of(m) != CASTLING) if (type_of(m) != CASTLING)
move_piece(from, to, us, pt); move_piece(us, pt, from, to);
// If the moving piece is a pawn do some special extra work // If the moving piece is a pawn do some special extra work
if (pt == PAWN) if (pt == PAWN)
{ {
// Set en-passant square if the moved pawn can be captured // Set en-passant square if the moved pawn can be captured
if ( (int(to) ^ int(from)) == 16 if ( (int(to) ^ int(from)) == 16
&& (attacks_from<PAWN>(from + pawn_push(us), us) & pieces(them, PAWN))) && (attacks_from<PAWN>(to - pawn_push(us), us) & pieces(them, PAWN)))
{ {
st->epSquare = Square((from + to) / 2); st->epSquare = (from + to) / 2;
k ^= Zobrist::enpassant[file_of(st->epSquare)]; k ^= Zobrist::enpassant[file_of(st->epSquare)];
} }
@@ -823,8 +792,8 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
assert(relative_rank(us, to) == RANK_8); assert(relative_rank(us, to) == RANK_8);
assert(promotion >= KNIGHT && promotion <= QUEEN); assert(promotion >= KNIGHT && promotion <= QUEEN);
remove_piece(to, us, PAWN); remove_piece(us, PAWN, to);
put_piece(to, us, promotion); put_piece(us, promotion, to);
// Update hash keys // Update hash keys
k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to]; k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to];
@@ -833,7 +802,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]]; ^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]];
// Update incremental score // Update incremental score
st->psq += psq[us][promotion][to] - psq[us][PAWN][to]; st->psq += PSQT::psq[us][promotion][to] - PSQT::psq[us][PAWN][to];
// Update material // Update material
st->nonPawnMaterial[us] += PieceValue[MG][promotion]; st->nonPawnMaterial[us] += PieceValue[MG][promotion];
@@ -841,14 +810,14 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Update pawn hash key and prefetch access to pawnsTable // Update pawn hash key and prefetch access to pawnsTable
st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to]; st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to];
prefetch((char*)thisThread->pawnsTable[st->pawnKey]); prefetch(thisThread->pawnsTable[st->pawnKey]);
// Reset rule 50 draw counter // Reset rule 50 draw counter
st->rule50 = 0; st->rule50 = 0;
} }
// Update incremental scores // Update incremental scores
st->psq += psq[us][pt][to] - psq[us][pt][from]; st->psq += PSQT::psq[us][pt][to] - PSQT::psq[us][pt][from];
// Set capture piece // Set capture piece
st->capturedType = captured; st->capturedType = captured;
@@ -856,30 +825,8 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Update the key with the final value // Update the key with the final value
st->key = k; st->key = k;
// Update checkers bitboard: piece must be already moved due to attacks_from() // Calculate checkers bitboard (if move gives check)
st->checkersBB = 0; st->checkersBB = givesCheck ? attackers_to(square<KING>(them)) & pieces(us) : 0;
if (moveIsCheck)
{
if (type_of(m) != NORMAL)
st->checkersBB = attackers_to(king_square(them)) & pieces(us);
else
{
// Direct checks
if (ci.checkSq[pt] & to)
st->checkersBB |= to;
// Discovered checks
if (ci.dcCandidates && (ci.dcCandidates & from))
{
if (pt != ROOK)
st->checkersBB |= attacks_from<ROOK>(king_square(them)) & pieces(us, QUEEN, ROOK);
if (pt != BISHOP)
st->checkersBB |= attacks_from<BISHOP>(king_square(them)) & pieces(us, QUEEN, BISHOP);
}
}
}
sideToMove = ~sideToMove; sideToMove = ~sideToMove;
@@ -906,23 +853,23 @@ void Position::undo_move(Move m) {
if (type_of(m) == PROMOTION) if (type_of(m) == PROMOTION)
{ {
assert(pt == promotion_type(m));
assert(relative_rank(us, to) == RANK_8); assert(relative_rank(us, to) == RANK_8);
assert(promotion_type(m) >= KNIGHT && promotion_type(m) <= QUEEN); assert(pt == promotion_type(m));
assert(pt >= KNIGHT && pt <= QUEEN);
remove_piece(to, us, promotion_type(m)); remove_piece(us, pt, to);
put_piece(to, us, PAWN); put_piece(us, PAWN, to);
pt = PAWN; pt = PAWN;
} }
if (type_of(m) == CASTLING) if (type_of(m) == CASTLING)
{ {
Square rfrom, rto; Square rfrom, rto;
do_castling<false>(from, to, rfrom, rto); do_castling<false>(us, from, to, rfrom, rto);
} }
else else
{ {
move_piece(to, from, us, pt); // Put the piece back at the source square move_piece(us, pt, to, from); // Put the piece back at the source square
if (st->capturedType) if (st->capturedType)
{ {
@@ -936,9 +883,10 @@ void Position::undo_move(Move m) {
assert(to == st->previous->epSquare); assert(to == st->previous->epSquare);
assert(relative_rank(us, to) == RANK_6); assert(relative_rank(us, to) == RANK_6);
assert(piece_on(capsq) == NO_PIECE); assert(piece_on(capsq) == NO_PIECE);
assert(st->capturedType == PAWN);
} }
put_piece(capsq, ~us, st->capturedType); // Restore the captured piece put_piece(~us, st->capturedType, capsq); // Restore the captured piece
} }
} }
@@ -953,19 +901,19 @@ void Position::undo_move(Move m) {
/// Position::do_castling() is a helper used to do/undo a castling move. This /// Position::do_castling() is a helper used to do/undo a castling move. This
/// is a bit tricky, especially in Chess960. /// is a bit tricky, especially in Chess960.
template<bool Do> template<bool Do>
void Position::do_castling(Square from, Square& to, Square& rfrom, Square& rto) { void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto) {
bool kingSide = to > from; bool kingSide = to > from;
rfrom = to; // Castling is encoded as "king captures friendly rook" rfrom = to; // Castling is encoded as "king captures friendly rook"
rto = relative_square(sideToMove, kingSide ? SQ_F1 : SQ_D1); rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
to = relative_square(sideToMove, kingSide ? SQ_G1 : SQ_C1); to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
// Remove both pieces first since squares could overlap in Chess960 // Remove both pieces first since squares could overlap in Chess960
remove_piece(Do ? from : to, sideToMove, KING); remove_piece(us, KING, Do ? from : to);
remove_piece(Do ? rfrom : rto, sideToMove, ROOK); remove_piece(us, ROOK, Do ? rfrom : rto);
board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us
put_piece(Do ? to : from, sideToMove, KING); put_piece(us, KING, Do ? to : from);
put_piece(Do ? rto : rfrom, sideToMove, ROOK); put_piece(us, ROOK, Do ? rto : rfrom);
} }
@@ -975,9 +923,9 @@ void Position::do_castling(Square from, Square& to, Square& rfrom, Square& rto)
void Position::do_null_move(StateInfo& newSt) { void Position::do_null_move(StateInfo& newSt) {
assert(!checkers()); assert(!checkers());
assert(&newSt != st);
std::memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here std::memcpy(&newSt, st, sizeof(StateInfo));
newSt.previous = st; newSt.previous = st;
st = &newSt; st = &newSt;
@@ -988,7 +936,7 @@ void Position::do_null_move(StateInfo& newSt) {
} }
st->key ^= Zobrist::side; st->key ^= Zobrist::side;
prefetch((char*)TT.first_entry(st->key)); prefetch(TT.first_entry(st->key));
++st->rule50; ++st->rule50;
st->pliesFromNull = 0; st->pliesFromNull = 0;
@@ -1060,8 +1008,8 @@ Value Position::see(Move m) const {
stm = color_of(piece_on(from)); stm = color_of(piece_on(from));
occupied = pieces() ^ from; occupied = pieces() ^ from;
// Castling moves are implemented as king capturing the rook so cannot be // Castling moves are implemented as king capturing the rook so cannot
// handled correctly. Simply return 0 that is always the correct value // be handled correctly. Simply return VALUE_ZERO that is always correct
// unless in the rare case the rook ends up under attack. // unless in the rare case the rook ends up under attack.
if (type_of(m) == CASTLING) if (type_of(m) == CASTLING)
return VALUE_ZERO; return VALUE_ZERO;
@@ -1098,21 +1046,11 @@ Value Position::see(Move m) const {
// Locate and remove the next least valuable attacker // Locate and remove the next least valuable attacker
captured = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers); captured = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
// Stop before processing a king capture
if (captured == KING)
{
if (stmAttackers == attackers)
++slIndex;
break;
}
stm = ~stm; stm = ~stm;
stmAttackers = attackers & pieces(stm); stmAttackers = attackers & pieces(stm);
++slIndex; ++slIndex;
} while (stmAttackers); } while (stmAttackers && (captured != KING || (--slIndex, false))); // Stop before a king capture
// Having built the swap list, we negamax through it to find the best // Having built the swap list, we negamax through it to find the best
// achievable score from the point of view of the side to move. // achievable score from the point of view of the side to move.
@@ -1123,8 +1061,8 @@ Value Position::see(Move m) const {
} }
/// Position::is_draw() tests whether the position is drawn by material, 50 moves /// Position::is_draw() tests whether the position is drawn by 50-move rule
/// rule or repetition. It does not detect stalemates. /// or by repetition. It does not detect stalemates.
bool Position::is_draw() const { bool Position::is_draw() const {
@@ -1147,10 +1085,6 @@ bool Position::is_draw() const {
/// Position::flip() flips position with the white and black sides reversed. This /// Position::flip() flips position with the white and black sides reversed. This
/// is only useful for debugging e.g. for finding evaluation symmetry bugs. /// is only useful for debugging e.g. for finding evaluation symmetry bugs.
static char toggle_case(char c) {
return char(islower(c) ? toupper(c) : tolower(c));
}
void Position::flip() { void Position::flip() {
string f, token; string f, token;
@@ -1168,7 +1102,8 @@ void Position::flip() {
ss >> token; // Castling availability ss >> token; // Castling availability
f += token + " "; f += token + " ";
std::transform(f.begin(), f.end(), f.begin(), toggle_case); std::transform(f.begin(), f.end(), f.begin(),
[](char c) { return char(islower(c) ? toupper(c) : tolower(c)); });
ss >> token; // En passant square ss >> token; // En passant square
f += (token == "-" ? token : token.replace(1, 1, token[1] == '3' ? "6" : "3")); f += (token == "-" ? token : token.replace(1, 1, token[1] == '3' ? "6" : "3"));
@@ -1185,96 +1120,77 @@ 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.
/// This is meant to be helpful when debugging. /// This is meant to be helpful when debugging.
bool Position::pos_is_ok(int* step) const { bool Position::pos_is_ok(int* failedStep) const {
// Which parts of the position should be verified? const bool Fast = true; // Quick (default) or full check?
const bool all = false;
const bool testBitboards = all || false; enum { Default, King, Bitboards, State, Lists, Castling };
const bool testState = all || false;
const bool testKingCount = all || false;
const bool testKingCapture = all || false;
const bool testPieceCounts = all || false;
const bool testPieceList = all || false;
const bool testCastlingSquares = all || false;
if (step) for (int step = Default; step <= (Fast ? Default : Castling); step++)
*step = 1; {
if (failedStep)
*failedStep = step;
if (step == Default)
if ( (sideToMove != WHITE && sideToMove != BLACK) if ( (sideToMove != WHITE && sideToMove != BLACK)
|| piece_on(king_square(WHITE)) != W_KING || piece_on(square<KING>(WHITE)) != W_KING
|| piece_on(king_square(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; return false;
if (step && ++*step, testBitboards) if (step == King)
if ( std::count(board, board + SQUARE_NB, W_KING) != 1
|| std::count(board, board + SQUARE_NB, B_KING) != 1
|| attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove))
return false;
if (step == Bitboards)
{ {
// The intersection of the white and black pieces must be empty if ( (pieces(WHITE) & pieces(BLACK))
if (pieces(WHITE) & pieces(BLACK)) ||(pieces(WHITE) | pieces(BLACK)) != pieces())
return false; return false;
// The union of the white and black pieces must be equal to all
// occupied squares
if ((pieces(WHITE) | pieces(BLACK)) != pieces())
return false;
// Separate piece type bitboards must have empty intersections
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; return false;
} }
if (step && ++*step, testState) if (step == State)
{ {
StateInfo si; StateInfo si = *st;
set_state(&si); set_state(&si);
if ( st->key != si.key if (std::memcmp(&si, st, sizeof(StateInfo)))
|| st->pawnKey != si.pawnKey
|| st->materialKey != si.materialKey
|| st->nonPawnMaterial[WHITE] != si.nonPawnMaterial[WHITE]
|| st->nonPawnMaterial[BLACK] != si.nonPawnMaterial[BLACK]
|| st->psq != si.psq
|| st->checkersBB != si.checkersBB)
return false; return false;
} }
if (step && ++*step, testKingCount) if (step == Lists)
if ( std::count(board, board + SQUARE_NB, W_KING) != 1
|| std::count(board, board + SQUARE_NB, B_KING) != 1)
return false;
if (step && ++*step, testKingCapture)
if (attackers_to(king_square(~sideToMove)) & pieces(sideToMove))
return false;
if (step && ++*step, testPieceCounts)
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; pt <= KING; ++pt)
{
if (pieceCount[c][pt] != popcount<Full>(pieces(c, pt))) if (pieceCount[c][pt] != popcount<Full>(pieces(c, pt)))
return false; return false;
if (step && ++*step, testPieceList)
for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt = PAWN; pt <= KING; ++pt)
for (int i = 0; i < pieceCount[c][pt]; ++i) for (int i = 0; i < pieceCount[c][pt]; ++i)
if ( board[pieceList[c][pt][i]] != make_piece(c, pt) if ( board[pieceList[c][pt][i]] != make_piece(c, pt)
|| index[pieceList[c][pt][i]] != i) || index[pieceList[c][pt][i]] != i)
return false; return false;
}
if (step && ++*step, testCastlingSquares) 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))
{ {
if (!can_castle(c | s)) if (!can_castle(c | s))
continue; continue;
if ( (castlingRightsMask[king_square(c)] & (c | s)) != (c | s) if ( piece_on(castlingRookSquare[c | s]) != make_piece(c, ROOK)
|| 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))
return false; return false;
} }
}
return true; return true;
} }

View File

@@ -30,6 +30,13 @@
class Position; class Position;
struct Thread; struct Thread;
namespace PSQT {
extern Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
void init();
}
/// CheckInfo struct is initialized at c'tor time and keeps info used to detect /// CheckInfo struct is initialized at c'tor time and keeps info used to detect
/// if a move gives check. /// if a move gives check.
@@ -39,7 +46,7 @@ struct CheckInfo {
Bitboard dcCandidates; Bitboard dcCandidates;
Bitboard pinned; Bitboard pinned;
Bitboard checkSq[PIECE_TYPE_NB]; Bitboard checkSquares[PIECE_TYPE_NB];
Square ksq; Square ksq;
}; };
@@ -68,11 +75,6 @@ struct StateInfo {
}; };
/// When making a move the current StateInfo up to 'key' excluded is copied to
/// the new one. Here we calculate the quad words (64 bit) needed to be copied.
const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1;
/// Position class stores information regarding the board representation as /// Position class stores information regarding the board representation as
/// pieces, side to move, hash keys, castling info, etc. Important methods are /// pieces, side to move, hash keys, castling info, etc. Important methods are
/// do_move() and undo_move(), used by the search to update node info when /// do_move() and undo_move(), used by the search to update node info when
@@ -82,12 +84,11 @@ class Position {
friend std::ostream& operator<<(std::ostream&, const Position&); friend std::ostream& operator<<(std::ostream&, const Position&);
Position(const Position&); // Disable the default copy constructor
public: public:
static void init(); static void init();
Position() {} // To define the global object RootPos Position() = default; // To define the global object RootPos
Position(const Position&) = delete;
Position(const Position& pos, Thread* th) { *this = pos; thisThread = th; } Position(const Position& pos, Thread* th) { *this = pos; thisThread = th; }
Position(const std::string& f, bool c960, Thread* th) { set(f, c960, th); } Position(const std::string& f, bool c960, Thread* th) { set(f, c960, th); }
Position& operator=(const Position&); // To assign RootPos from UCI Position& operator=(const Position&); // To assign RootPos from UCI
@@ -104,11 +105,11 @@ public:
Bitboard pieces(Color c, PieceType pt) const; Bitboard pieces(Color c, PieceType pt) const;
Bitboard pieces(Color c, PieceType pt1, PieceType pt2) const; Bitboard pieces(Color c, PieceType pt1, PieceType pt2) const;
Piece piece_on(Square s) const; Piece piece_on(Square s) const;
Square king_square(Color c) const;
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> const Square* list(Color c) const; template<PieceType Pt> const Square* squares(Color c) const;
template<PieceType Pt> Square square(Color c) const;
// Castling // Castling
int can_castle(Color c) const; int can_castle(Color c) const;
@@ -140,12 +141,10 @@ public:
// Piece specific // Piece specific
bool pawn_passed(Color c, Square s) const; bool pawn_passed(Color c, Square s) const;
bool pawn_on_7th(Color c) const;
bool opposite_bishops() const; bool opposite_bishops() const;
// Doing and undoing moves // Doing and undoing moves
void do_move(Move m, StateInfo& st); void do_move(Move m, StateInfo& st, bool givesCheck);
void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck);
void undo_move(Move m); void undo_move(Move m);
void do_null_move(StateInfo& st); void do_null_move(StateInfo& st);
void undo_null_move(); void undo_null_move();
@@ -175,7 +174,7 @@ public:
Value non_pawn_material(Color c) const; Value non_pawn_material(Color c) const;
// Position consistency check, for debugging // Position consistency check, for debugging
bool pos_is_ok(int* step = NULL) const; bool pos_is_ok(int* failedStep = nullptr) const;
void flip(); void flip();
private: private:
@@ -186,11 +185,11 @@ private:
// Other helpers // Other helpers
Bitboard check_blockers(Color c, Color kingColor) const; Bitboard check_blockers(Color c, Color kingColor) const;
void put_piece(Square s, Color c, PieceType pt); void put_piece(Color c, PieceType pt, Square s);
void remove_piece(Square s, Color c, PieceType pt); void remove_piece(Color c, PieceType pt, Square s);
void move_piece(Square from, Square to, Color c, PieceType pt); void move_piece(Color c, PieceType pt, Square from, Square to);
template<bool Do> template<bool Do>
void do_castling(Square from, Square& to, Square& rfrom, Square& rto); void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto);
// Data members // Data members
Piece board[SQUARE_NB]; Piece board[SQUARE_NB];
@@ -255,12 +254,13 @@ template<PieceType Pt> inline int Position::count(Color c) const {
return pieceCount[c][Pt]; return pieceCount[c][Pt];
} }
template<PieceType Pt> inline const Square* Position::list(Color c) const { template<PieceType Pt> inline const Square* Position::squares(Color c) const {
return pieceList[c][Pt]; return pieceList[c][Pt];
} }
inline Square Position::king_square(Color c) const { template<PieceType Pt> inline Square Position::square(Color c) const {
return pieceList[c][KING][0]; assert(pieceCount[c][Pt] == 1);
return pieceList[c][Pt][0];
} }
inline Square Position::ep_square() const { inline Square Position::ep_square() const {
@@ -363,11 +363,7 @@ inline void Position::set_nodes_searched(uint64_t n) {
inline bool Position::opposite_bishops() const { inline bool Position::opposite_bishops() const {
return pieceCount[WHITE][BISHOP] == 1 return pieceCount[WHITE][BISHOP] == 1
&& pieceCount[BLACK][BISHOP] == 1 && pieceCount[BLACK][BISHOP] == 1
&& opposite_colors(pieceList[WHITE][BISHOP][0], pieceList[BLACK][BISHOP][0]); && opposite_colors(square<BISHOP>(WHITE), square<BISHOP>(BLACK));
}
inline bool Position::pawn_on_7th(Color c) const {
return pieces(c, PAWN) & rank_bb(relative_rank(c, RANK_7));
} }
inline bool Position::is_chess960() const { inline bool Position::is_chess960() const {
@@ -395,7 +391,7 @@ inline Thread* Position::this_thread() const {
return thisThread; return thisThread;
} }
inline void Position::put_piece(Square s, Color c, PieceType pt) { inline void Position::put_piece(Color c, PieceType pt, Square s) {
board[s] = make_piece(c, pt); board[s] = make_piece(c, pt);
byTypeBB[ALL_PIECES] |= s; byTypeBB[ALL_PIECES] |= s;
@@ -406,21 +402,7 @@ inline void Position::put_piece(Square s, Color c, PieceType pt) {
pieceCount[c][ALL_PIECES]++; pieceCount[c][ALL_PIECES]++;
} }
inline void Position::move_piece(Square from, Square to, Color c, PieceType pt) { inline void Position::remove_piece(Color c, PieceType pt, Square s) {
// index[from] is not updated and becomes stale. This works as long as index[]
// is accessed just by known occupied squares.
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb;
byColorBB[c] ^= from_to_bb;
board[from] = NO_PIECE;
board[to] = make_piece(c, pt);
index[to] = index[from];
pieceList[c][pt][index[to]] = to;
}
inline void Position::remove_piece(Square s, Color c, PieceType pt) {
// WARNING: This is not a reversible operation. If we remove a piece in // WARNING: This is not a reversible operation. If we remove a piece in
// do_move() and then replace it in undo_move() we will put it at the end of // do_move() and then replace it in undo_move() we will put it at the end of
@@ -437,4 +419,18 @@ inline void Position::remove_piece(Square s, Color c, PieceType pt) {
pieceCount[c][ALL_PIECES]--; pieceCount[c][ALL_PIECES]--;
} }
inline void Position::move_piece(Color c, PieceType pt, Square from, Square to) {
// index[from] is not updated and becomes stale. This works as long as index[]
// is accessed just by known occupied squares.
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb;
byColorBB[c] ^= from_to_bb;
board[from] = NO_PIECE;
board[to] = make_piece(c, pt);
index[to] = index[from];
pieceList[c][pt][index[to]] = to;
}
#endif // #ifndef POSITION_H_INCLUDED #endif // #ifndef POSITION_H_INCLUDED

View File

@@ -0,0 +1,118 @@
/*
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
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/>.
*/
#include "types.h"
namespace PSQT {
#define S(mg, eg) make_score(mg, eg)
// Bonus[PieceType][Square / 2] contains Piece-Square scores. For each piece
// type on a given square a (middlegame, endgame) score pair is assigned. Table
// is defined for files A..D and white side: it is symmetric for black side and
// second half of the files.
const Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
{ },
{ // Pawn
{ S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) },
{ S(-19, 5), S( 1,-4), S( 7, 8), S( 3,-2) },
{ S(-26,-6), S( -7,-5), S( 19, 5), S(24, 4) },
{ S(-25, 1), S(-14, 3), S( 16,-8), S(31,-3) },
{ S(-14, 6), S( 0, 9), S( -1, 7), S(17,-6) },
{ S(-14, 6), S(-13,-5), S(-10, 2), S(-6, 4) },
{ S(-12, 1), S( 15,-9), S( -8, 1), S(-4,18) },
{ S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) }
},
{ // Knight
{ S(-143, -97), S(-96,-82), S(-80,-46), S(-73,-14) },
{ S( -83, -69), S(-43,-55), S(-21,-17), S(-10, 9) },
{ S( -71, -50), S(-22,-39), S( 0, -8), S( 9, 28) },
{ S( -25, -41), S( 18,-25), S( 43, 7), S( 47, 38) },
{ S( -26, -46), S( 16,-25), S( 38, 2), S( 50, 41) },
{ S( -11, -55), S( 37,-38), S( 56, -8), S( 71, 27) },
{ S( -62, -64), S(-17,-50), S( 5,-24), S( 14, 13) },
{ S(-195,-110), S(-66,-90), S(-42,-50), S(-29,-13) }
},
{ // Bishop
{ S(-54,-68), S(-23,-40), S(-35,-46), S(-44,-28) },
{ S(-30,-43), S( 10,-17), S( 2,-23), S( -9, -5) },
{ S(-19,-32), S( 17, -9), S( 11,-13), S( 1, 8) },
{ S(-21,-36), S( 18,-13), S( 11,-15), S( 0, 7) },
{ S(-21,-36), S( 14,-14), S( 6,-17), S( -1, 3) },
{ S(-27,-35), S( 6,-13), S( 2,-10), S( -8, 1) },
{ S(-33,-44), S( 7,-21), S( -4,-22), S(-12, -4) },
{ S(-45,-65), S(-21,-42), S(-29,-46), S(-39,-27) }
},
{ // Rook
{ S(-25, 0), S(-16, 0), S(-16, 0), S(-9, 0) },
{ S(-21, 0), S( -8, 0), S( -3, 0), S( 0, 0) },
{ S(-21, 0), S( -9, 0), S( -4, 0), S( 2, 0) },
{ S(-22, 0), S( -6, 0), S( -1, 0), S( 2, 0) },
{ S(-22, 0), S( -7, 0), S( 0, 0), S( 1, 0) },
{ S(-21, 0), S( -7, 0), S( 0, 0), S( 2, 0) },
{ S(-12, 0), S( 4, 0), S( 8, 0), S(12, 0) },
{ S(-23, 0), S(-15, 0), S(-11, 0), S(-5, 0) }
},
{ // Queen
{ S( 0,-70), S(-3,-57), S(-4,-41), S(-1,-29) },
{ S(-4,-58), S( 6,-30), S( 9,-21), S( 8, -4) },
{ S(-2,-39), S( 6,-17), S( 9, -7), S( 9, 5) },
{ S(-1,-29), S( 8, -5), S(10, 9), S( 7, 17) },
{ S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 23) },
{ S(-2,-40), S( 6,-16), S( 8,-11), S(10, 3) },
{ S(-2,-54), S( 7,-30), S( 7,-21), S( 6, -7) },
{ S(-1,-75), S(-4,-54), S(-1,-44), S( 0,-30) }
},
{ // King
{ S(291, 28), S(344, 76), S(294,103), S(219,112) },
{ S(289, 70), S(329,119), S(263,170), S(205,159) },
{ S(226,109), S(271,164), S(202,195), S(136,191) },
{ S(204,131), S(212,194), S(175,194), S(137,204) },
{ S(177,132), S(205,187), S(143,224), S( 94,227) },
{ S(147,118), S(188,178), S(113,199), S( 70,197) },
{ S(116, 72), S(158,121), S( 93,142), S( 48,161) },
{ S( 94, 30), S(120, 76), S( 78,101), S( 31,111) }
}
};
#undef S
Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
// init() initializes piece square tables: the white halves of the tables are
// copied from Bonus[] adding the piece value, then the black halves of the
// tables are initialized by flipping and changing the sign of the white scores.
void init() {
for (PieceType pt = PAWN; pt <= KING; ++pt)
{
PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt];
PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt];
Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]);
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
int edgeDistance = file_of(s) < FILE_E ? file_of(s) : FILE_H - file_of(s);
psq[BLACK][pt][~s] = -(psq[WHITE][pt][s] = v + Bonus[pt][rank_of(s)][edgeDistance]);
}
}
}
} // namespace PSQT

View File

@@ -1,98 +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
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 PSQTAB_H_INCLUDED
#define PSQTAB_H_INCLUDED
#include "types.h"
#define S(mg, eg) make_score(mg, eg)
/// PSQT[PieceType][Square] contains Piece-Square scores. For each piece type on
/// a given square a (middlegame, endgame) score pair is assigned. PSQT is defined
/// for the white side and the tables are symmetric for the black side.
static const Score PSQT[][SQUARE_NB] = {
{ },
{ // 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(-20, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S(-20, 0),
S(-20, 0), S( 0, 0), S(10, 0), S(20, 0), S(20, 0), S(10, 0), S( 0, 0), S(-20, 0),
S(-20, 0), S( 0, 0), S(20, 0), S(40, 0), S(40, 0), S(20, 0), S( 0, 0), S(-20, 0),
S(-20, 0), S( 0, 0), S(10, 0), S(20, 0), S(20, 0), S(10, 0), S( 0, 0), S(-20, 0),
S(-20, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S(-20, 0),
S(-20, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S(-20, 0),
S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0)
},
{ // Knight
S(-144,-98), S(-109,-83), S(-85,-51), S(-73,-16), S(-73,-16), S(-85,-51), S(-109,-83), S(-144,-98),
S( -88,-68), S( -43,-53), S(-19,-21), S( -7, 14), S( -7, 14), S(-19,-21), S( -43,-53), S( -88,-68),
S( -69,-53), S( -24,-38), S( 0, -6), S( 12, 29), S( 12, 29), S( 0, -6), S( -24,-38), S( -69,-53),
S( -28,-42), S( 17,-27), S( 41, 5), S( 53, 40), S( 53, 40), S( 41, 5), S( 17,-27), S( -28,-42),
S( -30,-42), S( 15,-27), S( 39, 5), S( 51, 40), S( 51, 40), S( 39, 5), S( 15,-27), S( -30,-42),
S( -10,-53), S( 35,-38), S( 59, -6), S( 71, 29), S( 71, 29), S( 59, -6), S( 35,-38), S( -10,-53),
S( -64,-68), S( -19,-53), S( 5,-21), S( 17, 14), S( 17, 14), S( 5,-21), S( -19,-53), S( -64,-68),
S(-200,-98), S( -65,-83), S(-41,-51), S(-29,-16), S(-29,-16), S(-41,-51), S( -65,-83), S(-200,-98)
},
{ // Bishop
S(-54,-65), S(-27,-42), S(-34,-44), S(-43,-26), S(-43,-26), S(-34,-44), S(-27,-42), S(-54,-65),
S(-29,-43), S( 8,-20), S( 1,-22), S( -8, -4), S( -8, -4), S( 1,-22), S( 8,-20), S(-29,-43),
S(-20,-33), S( 17,-10), S( 10,-12), S( 1, 6), S( 1, 6), S( 10,-12), S( 17,-10), S(-20,-33),
S(-19,-35), S( 18,-12), S( 11,-14), S( 2, 4), S( 2, 4), S( 11,-14), S( 18,-12), S(-19,-35),
S(-22,-35), S( 15,-12), S( 8,-14), S( -1, 4), S( -1, 4), S( 8,-14), S( 15,-12), S(-22,-35),
S(-28,-33), S( 9,-10), S( 2,-12), S( -7, 6), S( -7, 6), S( 2,-12), S( 9,-10), S(-28,-33),
S(-32,-43), S( 5,-20), S( -2,-22), S(-11, -4), S(-11, -4), S( -2,-22), S( 5,-20), S(-32,-43),
S(-49,-65), S(-22,-42), S(-29,-44), S(-38,-26), S(-38,-26), S(-29,-44), S(-22,-42), S(-49,-65)
},
{ // Rook
S(-22, 3), S(-17, 3), S(-12, 3), S(-8, 3), S(-8, 3), S(-12, 3), S(-17, 3), S(-22, 3),
S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3),
S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3),
S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3),
S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3),
S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3),
S(-11, 3), S( 4, 3), S( 9, 3), S(13, 3), S(13, 3), S( 9, 3), S( 4, 3), S(-11, 3),
S(-22, 3), S(-17, 3), S(-12, 3), S(-8, 3), S(-8, 3), S(-12, 3), S(-17, 3), S(-22, 3)
},
{ // Queen
S(-2,-80), S(-2,-54), S(-2,-42), S(-2,-30), S(-2,-30), S(-2,-42), S(-2,-54), S(-2,-80),
S(-2,-54), S( 8,-30), S( 8,-18), S( 8, -6), S( 8, -6), S( 8,-18), S( 8,-30), S(-2,-54),
S(-2,-42), S( 8,-18), S( 8, -6), S( 8, 6), S( 8, 6), S( 8, -6), S( 8,-18), S(-2,-42),
S(-2,-30), S( 8, -6), S( 8, 6), S( 8, 18), S( 8, 18), S( 8, 6), S( 8, -6), S(-2,-30),
S(-2,-30), S( 8, -6), S( 8, 6), S( 8, 18), S( 8, 18), S( 8, 6), S( 8, -6), S(-2,-30),
S(-2,-42), S( 8,-18), S( 8, -6), S( 8, 6), S( 8, 6), S( 8, -6), S( 8,-18), S(-2,-42),
S(-2,-54), S( 8,-30), S( 8,-18), S( 8, -6), S( 8, -6), S( 8,-18), S( 8,-30), S(-2,-54),
S(-2,-80), S(-2,-54), S(-2,-42), S(-2,-30), S(-2,-30), S(-2,-42), S(-2,-54), S(-2,-80)
},
{ // King
S(298, 27), S(332, 81), S(273,108), S(225,116), S(225,116), S(273,108), S(332, 81), S(298, 27),
S(287, 74), S(321,128), S(262,155), S(214,163), S(214,163), S(262,155), S(321,128), S(287, 74),
S(224,111), S(258,165), S(199,192), S(151,200), S(151,200), S(199,192), S(258,165), S(224,111),
S(196,135), S(230,189), S(171,216), S(123,224), S(123,224), S(171,216), S(230,189), S(196,135),
S(173,135), S(207,189), S(148,216), S(100,224), S(100,224), S(148,216), S(207,189), S(173,135),
S(146,111), S(180,165), S(121,192), S( 73,200), S( 73,200), S(121,192), S(180,165), S(146,111),
S(119, 74), S(153,128), S( 94,155), S( 46,163), S( 46,163), S( 94,155), S(153,128), S(119, 74),
S( 98, 27), S(132, 81), S( 73,108), S( 25,116), S( 25,116), S( 73,108), S(132, 81), S( 98, 27)
}
};
#undef S
#endif // #ifndef PSQTAB_H_INCLUDED

File diff suppressed because it is too large Load Diff

View File

@@ -47,6 +47,7 @@ struct Stack {
Depth reduction; Depth reduction;
Value staticEval; Value staticEval;
bool skipEarlyPruning; bool skipEarlyPruning;
int moveCount;
}; };
/// RootMove struct is used for moves at the root of the tree. For each root move /// RootMove struct is used for moves at the root of the tree. For each root move
@@ -55,15 +56,15 @@ struct Stack {
struct RootMove { struct RootMove {
RootMove(Move m) : score(-VALUE_INFINITE), previousScore(-VALUE_INFINITE), pv(1, m) {} explicit RootMove(Move m) : pv(1, m) {}
bool operator<(const RootMove& m) const { return score > m.score; } // Ascending sort bool operator<(const RootMove& m) const { return score > m.score; } // Ascending sort
bool operator==(const Move& m) const { return pv[0] == m; } bool operator==(const Move& m) const { return pv[0] == m; }
void insert_pv_in_tt(Position& pos); void insert_pv_in_tt(Position& pos);
Move extract_ponder_from_tt(Position& pos); bool extract_ponder_from_tt(Position& pos);
Value score; Value score = -VALUE_INFINITE;
Value previousScore; Value previousScore = -VALUE_INFINITE;
std::vector<Move> pv; std::vector<Move> pv;
}; };
@@ -76,7 +77,7 @@ typedef std::vector<RootMove> RootMoveVector;
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] = movestogo = nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movestogo =
depth = movetime = mate = infinite = ponder = 0; depth = movetime = mate = infinite = ponder = 0;
} }
@@ -85,8 +86,9 @@ struct LimitsType {
} }
std::vector<Move> searchmoves; std::vector<Move> searchmoves;
int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, movetime, mate, infinite, ponder; int time[COLOR_NB], inc[COLOR_NB], npmsec, movestogo, depth, movetime, mate, infinite, ponder;
int64_t nodes; int64_t nodes;
TimePoint startTime;
}; };
/// The SignalsType struct stores volatile flags updated during the search /// The SignalsType struct stores volatile flags updated during the search
@@ -96,17 +98,14 @@ struct SignalsType {
bool stop, stopOnPonderhit, firstRootMove, failedLowAtRoot; bool stop, stopOnPonderhit, firstRootMove, failedLowAtRoot;
}; };
typedef std::auto_ptr<std::stack<StateInfo> > StateStackPtr; typedef std::unique_ptr<std::stack<StateInfo>> StateStackPtr;
extern volatile SignalsType Signals; extern volatile SignalsType Signals;
extern LimitsType Limits; extern LimitsType Limits;
extern RootMoveVector RootMoves;
extern Position RootPos;
extern Time::point SearchTime;
extern StateStackPtr SetupStates; extern StateStackPtr SetupStates;
void init(); void init();
void think(); void reset();
template<bool Root> uint64_t perft(Position& pos, Depth depth); template<bool Root> uint64_t perft(Position& pos, Depth depth);
} // namespace Search } // namespace Search

View File

@@ -7,6 +7,8 @@
this code to other chess engines. this code to other chess engines.
*/ */
#define NOMINMAX
#include <algorithm> #include <algorithm>
#include "../position.h" #include "../position.h"
@@ -367,7 +369,7 @@ static int probe_ab(Position& pos, int alpha, int beta, int *success)
if (!pos.capture(capture) || type_of(capture) == ENPASSANT if (!pos.capture(capture) || type_of(capture) == ENPASSANT
|| !pos.legal(capture, ci.pinned)) || !pos.legal(capture, ci.pinned))
continue; continue;
pos.do_move(capture, st, ci, pos.gives_check(capture, ci)); pos.do_move(capture, st, pos.gives_check(capture, ci));
v = -probe_ab(pos, -beta, -alpha, success); v = -probe_ab(pos, -beta, -alpha, success);
pos.undo_move(capture); pos.undo_move(capture);
if (*success == 0) return 0; if (*success == 0) return 0;
@@ -430,7 +432,7 @@ int Tablebases::probe_wdl(Position& pos, int *success)
if (type_of(capture) != ENPASSANT if (type_of(capture) != ENPASSANT
|| !pos.legal(capture, ci.pinned)) || !pos.legal(capture, ci.pinned))
continue; continue;
pos.do_move(capture, st, ci, pos.gives_check(capture, ci)); pos.do_move(capture, st, pos.gives_check(capture, ci));
int v0 = -probe_ab(pos, -2, 2, success); int v0 = -probe_ab(pos, -2, 2, success);
pos.undo_move(capture); pos.undo_move(capture);
if (*success == 0) return 0; if (*success == 0) return 0;
@@ -493,7 +495,7 @@ static int probe_dtz_no_ep(Position& pos, int *success)
if (type_of(pos.moved_piece(move)) != PAWN || pos.capture(move) if (type_of(pos.moved_piece(move)) != PAWN || pos.capture(move)
|| !pos.legal(move, ci.pinned)) || !pos.legal(move, ci.pinned))
continue; continue;
pos.do_move(move, st, ci, pos.gives_check(move, ci)); pos.do_move(move, st, pos.gives_check(move, ci));
int v = -probe_ab(pos, -2, -wdl + 1, success); int v = -probe_ab(pos, -2, -wdl + 1, success);
pos.undo_move(move); pos.undo_move(move);
if (*success == 0) return 0; if (*success == 0) return 0;
@@ -515,7 +517,7 @@ static int probe_dtz_no_ep(Position& pos, int *success)
if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN
|| !pos.legal(move, ci.pinned)) || !pos.legal(move, ci.pinned))
continue; continue;
pos.do_move(move, st, ci, pos.gives_check(move, ci)); pos.do_move(move, st, pos.gives_check(move, ci));
int v = -Tablebases::probe_dtz(pos, success); int v = -Tablebases::probe_dtz(pos, success);
pos.undo_move(move); pos.undo_move(move);
if (*success == 0) return 0; if (*success == 0) return 0;
@@ -534,7 +536,7 @@ static int probe_dtz_no_ep(Position& pos, int *success)
Move move = moves->move; Move move = moves->move;
if (!pos.legal(move, ci.pinned)) if (!pos.legal(move, ci.pinned))
continue; continue;
pos.do_move(move, st, ci, pos.gives_check(move, ci)); pos.do_move(move, st, pos.gives_check(move, ci));
if (st.rule50 == 0) { if (st.rule50 == 0) {
if (wdl == -2) v = -1; if (wdl == -2) v = -1;
else { else {
@@ -610,7 +612,7 @@ int Tablebases::probe_dtz(Position& pos, int *success)
if (type_of(capture) != ENPASSANT if (type_of(capture) != ENPASSANT
|| !pos.legal(capture, ci.pinned)) || !pos.legal(capture, ci.pinned))
continue; continue;
pos.do_move(capture, st, ci, pos.gives_check(capture, ci)); pos.do_move(capture, st, pos.gives_check(capture, ci));
int v0 = -probe_ab(pos, -2, 2, success); int v0 = -probe_ab(pos, -2, 2, success);
pos.undo_move(capture); pos.undo_move(capture);
if (*success == 0) return 0; if (*success == 0) return 0;
@@ -622,7 +624,7 @@ int Tablebases::probe_dtz(Position& pos, int *success)
if (v1 >= 0) if (v1 >= 0)
v = v1; v = v1;
} else if (v < 0) { } else if (v < 0) {
if (v1 >= 0 || v1 < 100) if (v1 >= 0 || v1 < -100)
v = v1; v = v1;
} else if (v > 100) { } else if (v > 100) {
if (v1 > 0) if (v1 > 0)
@@ -700,7 +702,7 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoveVector& rootMoves, Va
// Probe each move. // Probe each move.
for (size_t i = 0; i < rootMoves.size(); i++) { for (size_t i = 0; i < rootMoves.size(); i++) {
Move move = rootMoves[i].pv[0]; Move move = rootMoves[i].pv[0];
pos.do_move(move, st, ci, pos.gives_check(move, ci)); pos.do_move(move, st, pos.gives_check(move, ci));
int v = 0; int v = 0;
if (pos.checkers() && dtz > 0) { if (pos.checkers() && dtz > 0) {
ExtMove s[192]; ExtMove s[192];
@@ -810,7 +812,7 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoveVector& rootMoves
// Probe each move. // Probe each move.
for (size_t i = 0; i < rootMoves.size(); i++) { for (size_t i = 0; i < rootMoves.size(); i++) {
Move move = rootMoves[i].pv[0]; Move move = rootMoves[i].pv[0];
pos.do_move(move, st, ci, pos.gives_check(move, ci)); pos.do_move(move, st, pos.gives_check(move, ci));
int v = -Tablebases::probe_wdl(pos, &success); int v = -Tablebases::probe_wdl(pos, &success);
pos.undo_move(move); pos.undo_move(move);
if (!success) return false; if (!success) return false;

View File

@@ -33,20 +33,14 @@ extern void check_time();
namespace { namespace {
// start_routine() is the C function which is called when a new thread
// is launched. It is a wrapper to the virtual function idle_loop().
extern "C" { long start_routine(ThreadBase* th) { th->idle_loop(); return 0; } }
// Helpers to launch a thread after creation and joining before delete. Must be // Helpers to launch a thread after creation and joining before delete. Must be
// outside Thread c'tor and d'tor because the object must be fully initialized // outside Thread c'tor and d'tor because the object must be fully initialized
// when start_routine (and hence virtual idle_loop) is called and when joining. // when start_routine (and hence virtual idle_loop) is called and when joining.
template<typename T> T* new_thread() { template<typename T> T* new_thread() {
T* th = new T(); std::thread* th = new T;
thread_create(th->handle, start_routine, th); // Will go to sleep *th = std::thread(&T::idle_loop, (T*)th); // Will go to sleep
return th; return (T*)th;
} }
void delete_thread(ThreadBase* th) { void delete_thread(ThreadBase* th) {
@@ -56,7 +50,7 @@ namespace {
th->mutex.unlock(); th->mutex.unlock();
th->notify_one(); th->notify_one();
thread_join(th->handle); // Wait for thread termination th->join(); // Wait for thread termination
delete th; delete th;
} }
@@ -67,19 +61,26 @@ namespace {
void ThreadBase::notify_one() { void ThreadBase::notify_one() {
mutex.lock(); std::unique_lock<Mutex> lk(mutex);
sleepCondition.notify_one(); sleepCondition.notify_one();
mutex.unlock();
} }
// ThreadBase::wait_for() set the thread to sleep until 'condition' turns true // ThreadBase::wait() set the thread to sleep until 'condition' turns true
void ThreadBase::wait_for(volatile const bool& condition) { void ThreadBase::wait(volatile const bool& condition) {
mutex.lock(); std::unique_lock<Mutex> lk(mutex);
while (!condition) sleepCondition.wait(mutex); sleepCondition.wait(lk, [&]{ return condition; });
mutex.unlock(); }
// ThreadBase::wait_while() set the thread to sleep until 'condition' turns false
void ThreadBase::wait_while(volatile const bool& condition) {
std::unique_lock<Mutex> lk(mutex);
sleepCondition.wait(lk, [&]{ return !condition; });
} }
@@ -89,141 +90,11 @@ void ThreadBase::wait_for(volatile const bool& condition) {
Thread::Thread() /* : splitPoints() */ { // Initialization of non POD broken in MSVC Thread::Thread() /* : splitPoints() */ { // Initialization of non POD broken in MSVC
searching = false; searching = false;
maxPly = splitPointsSize = 0; maxPly = 0;
activeSplitPoint = NULL;
activePosition = NULL;
idx = Threads.size(); // Starts from 0 idx = Threads.size(); // Starts from 0
} }
// Thread::cutoff_occurred() checks whether a beta cutoff has occurred in the
// current active split point, or in some ancestor of the split point.
bool Thread::cutoff_occurred() const {
for (SplitPoint* sp = activeSplitPoint; sp; sp = sp->parentSplitPoint)
if (sp->cutoff)
return true;
return false;
}
// Thread::available_to() checks whether the thread is available to help the
// thread 'master' at a split point. An obvious requirement is that thread must
// be idle. With more than two threads, this is not sufficient: If the thread is
// the master of some split point, it is only available as a slave to the slaves
// which are busy searching the split point at the top of slave's split point
// stack (the "helpful master concept" in YBWC terminology).
bool Thread::available_to(const Thread* master) const {
if (searching)
return false;
// Make a local copy to be sure it doesn't become zero under our feet while
// testing next condition and so leading to an out of bounds access.
const int size = splitPointsSize;
// No split points means that the thread is available as a slave for any
// other thread otherwise apply the "helpful master" concept if possible.
return !size || splitPoints[size - 1].slavesMask.test(master->idx);
}
// Thread::split() does the actual work of distributing the work at a node between
// several available threads. If it does not succeed in splitting the node
// (because no idle threads are available), the function immediately returns.
// If splitting is possible, a SplitPoint object is initialized with all the
// data that must be copied to the helper threads and then helper threads are
// informed that they have been assigned work. This will cause them to instantly
// leave their idle loops and call search(). When all threads have returned from
// search() then split() returns.
void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bestValue,
Move* bestMove, Depth depth, int moveCount,
MovePicker* movePicker, int nodeType, bool cutNode) {
assert(searching);
assert(-VALUE_INFINITE < *bestValue && *bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE);
assert(depth >= Threads.minimumSplitDepth);
assert(splitPointsSize < MAX_SPLITPOINTS_PER_THREAD);
// Pick and init the next available split point
SplitPoint& sp = splitPoints[splitPointsSize];
sp.masterThread = this;
sp.parentSplitPoint = activeSplitPoint;
sp.slavesMask = 0, sp.slavesMask.set(idx);
sp.depth = depth;
sp.bestValue = *bestValue;
sp.bestMove = *bestMove;
sp.alpha = alpha;
sp.beta = beta;
sp.nodeType = nodeType;
sp.cutNode = cutNode;
sp.movePicker = movePicker;
sp.moveCount = moveCount;
sp.pos = &pos;
sp.nodes = 0;
sp.cutoff = false;
sp.ss = ss;
// Try to allocate available threads and ask them to start searching setting
// 'searching' flag. This must be done under lock protection to avoid concurrent
// allocation of the same slave by another master.
Threads.mutex.lock();
sp.mutex.lock();
sp.allSlavesSearching = true; // Must be set under lock protection
++splitPointsSize;
activeSplitPoint = &sp;
activePosition = NULL;
Thread* slave;
while ((slave = Threads.available_slave(this)) != NULL)
{
sp.slavesMask.set(slave->idx);
slave->activeSplitPoint = &sp;
slave->searching = true; // Slave leaves idle_loop()
slave->notify_one(); // Could be sleeping
}
// Everything is set up. The master thread enters the idle loop, from which
// it will instantly launch a search, because its 'searching' flag is set.
// The thread will return from the idle loop when all slaves have finished
// their work at this split point.
sp.mutex.unlock();
Threads.mutex.unlock();
Thread::idle_loop(); // Force a call to base class idle_loop()
// In the helpful master concept, a master can help only a sub-tree of its
// split point and because everything is finished here, it's not possible
// for the master to be booked.
assert(!searching);
assert(!activePosition);
// We have returned from the idle loop, which means that all threads are
// finished. Note that setting 'searching' and decreasing splitPointsSize must
// be done under lock protection to avoid a race with Thread::available_to().
Threads.mutex.lock();
sp.mutex.lock();
searching = true;
--splitPointsSize;
activeSplitPoint = sp.parentSplitPoint;
activePosition = &pos;
pos.set_nodes_searched(pos.nodes_searched() + sp.nodes);
*bestMove = sp.bestMove;
*bestValue = sp.bestValue;
sp.mutex.unlock();
Threads.mutex.unlock();
}
// TimerThread::idle_loop() is where the timer thread waits Resolution milliseconds // TimerThread::idle_loop() is where the timer thread waits Resolution milliseconds
// and then calls check_time(). When not searching, thread sleeps until it's woken up. // and then calls check_time(). When not searching, thread sleeps until it's woken up.
@@ -231,19 +102,38 @@ void TimerThread::idle_loop() {
while (!exit) while (!exit)
{ {
mutex.lock(); std::unique_lock<Mutex> lk(mutex);
if (!exit) if (!exit)
sleepCondition.wait_for(mutex, run ? Resolution : INT_MAX); sleepCondition.wait_for(lk, std::chrono::milliseconds(run ? Resolution : INT_MAX));
mutex.unlock(); lk.unlock();
if (run) if (!exit && run)
check_time(); check_time();
} }
} }
// Thread::idle_loop() is where the thread is parked when it has no work to do
void Thread::idle_loop() {
while (!exit)
{
std::unique_lock<Mutex> lk(mutex);
while (!searching && !exit)
sleepCondition.wait(lk);
lk.unlock();
if (!exit && searching)
search();
}
}
// MainThread::idle_loop() is where the main thread is parked waiting to be started // MainThread::idle_loop() is where the main thread is parked waiting to be started
// when there is a new search. The main thread will launch all the slave threads. // when there is a new search. The main thread will launch all the slave threads.
@@ -251,32 +141,33 @@ void MainThread::idle_loop() {
while (!exit) while (!exit)
{ {
mutex.lock(); std::unique_lock<Mutex> lk(mutex);
thinking = false; thinking = false;
while (!thinking && !exit) while (!thinking && !exit)
{ {
Threads.sleepCondition.notify_one(); // Wake up the UI thread if needed sleepCondition.notify_one(); // Wake up the UI thread if needed
sleepCondition.wait(mutex); sleepCondition.wait(lk);
} }
mutex.unlock(); lk.unlock();
if (!exit) if (!exit)
{ think();
searching = true;
Search::think();
assert(searching);
searching = false;
}
} }
} }
// MainThread::join() waits for main thread to finish thinking
void MainThread::join() {
std::unique_lock<Mutex> lk(mutex);
sleepCondition.wait(lk, [&]{ return !thinking; });
}
// ThreadPool::init() is called at startup to create and launch requested threads, // ThreadPool::init() is called at startup to create and launch requested threads,
// that will go immediately to sleep. We cannot use a c'tor because Threads is a // that will go immediately to sleep. We cannot use a c'tor because Threads is a
// static object and we need a fully initialized engine at this point due to // static object and we need a fully initialized engine at this point due to
@@ -296,9 +187,12 @@ void ThreadPool::init() {
void ThreadPool::exit() { void ThreadPool::exit() {
delete_thread(timer); // As first because check_time() accesses threads data delete_thread(timer); // As first because check_time() accesses threads data
timer = nullptr;
for (iterator it = begin(); it != end(); ++it) for (Thread* th : *this)
delete_thread(*it); delete_thread(th);
clear(); // Get rid of stale pointers
} }
@@ -310,15 +204,10 @@ void ThreadPool::exit() {
void ThreadPool::read_uci_options() { void ThreadPool::read_uci_options() {
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
size_t requested = Options["Threads"]; size_t requested = Options["Threads"];
assert(requested > 0); assert(requested > 0);
// If zero (default) then set best minimum split depth automatically
if (!minimumSplitDepth)
minimumSplitDepth = requested < 8 ? 4 * ONE_PLY : 7 * ONE_PLY;
while (size() < requested) while (size() < requested)
push_back(new_thread<Thread>()); push_back(new_thread<Thread>());
@@ -330,27 +219,14 @@ void ThreadPool::read_uci_options() {
} }
// ThreadPool::available_slave() tries to find an idle thread which is available // ThreadPool::nodes_searched() returns the number of nodes searched
// as a slave for the thread 'master'.
Thread* ThreadPool::available_slave(const Thread* master) const { int64_t ThreadPool::nodes_searched() {
for (const_iterator it = begin(); it != end(); ++it) int64_t nodes = 0;
if ((*it)->available_to(master)) for (Thread *th : *this)
return *it; nodes += th->rootPos.nodes_searched();
return nodes;
return NULL;
}
// ThreadPool::wait_for_think_finished() waits for main thread to finish the search
void ThreadPool::wait_for_think_finished() {
MainThread* th = main();
th->mutex.lock();
while (th->thinking) sleepCondition.wait(th->mutex);
th->mutex.unlock();
} }
@@ -359,27 +235,25 @@ void ThreadPool::wait_for_think_finished() {
void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
StateStackPtr& states) { StateStackPtr& states) {
wait_for_think_finished(); main()->join();
SearchTime = Time::now(); // As early as possible
Signals.stopOnPonderhit = Signals.firstRootMove = false; Signals.stopOnPonderhit = Signals.firstRootMove = false;
Signals.stop = Signals.failedLowAtRoot = false; Signals.stop = Signals.failedLowAtRoot = false;
RootMoves.clear(); main()->rootMoves.clear();
RootPos = pos; main()->rootPos = pos;
Limits = limits; Limits = limits;
if (states.get()) // If we don't set a new position, preserve current state if (states.get()) // If we don't set a new position, preserve current state
{ {
SetupStates = states; // Ownership transfer here SetupStates = std::move(states); // Ownership transfer here
assert(!states.get()); assert(!states.get());
} }
for (MoveList<LEGAL> it(pos); *it; ++it) for (const auto& m : MoveList<LEGAL>(pos))
if ( limits.searchmoves.empty() if ( limits.searchmoves.empty()
|| std::count(limits.searchmoves.begin(), limits.searchmoves.end(), *it)) || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m))
RootMoves.push_back(RootMove(*it)); main()->rootMoves.push_back(RootMove(m));
main()->thinking = true; main()->thinking = true;
main()->notify_one(); // Starts main thread main()->notify_one(); // Wake up main thread: 'thinking' must be already set
} }

View File

@@ -20,7 +20,11 @@
#ifndef THREAD_H_INCLUDED #ifndef THREAD_H_INCLUDED
#define THREAD_H_INCLUDED #define THREAD_H_INCLUDED
#include <atomic>
#include <bitset> #include <bitset>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <vector> #include <vector>
#include "material.h" #include "material.h"
@@ -28,87 +32,27 @@
#include "pawns.h" #include "pawns.h"
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "thread_win32.h"
struct Thread; struct Thread;
const int MAX_THREADS = 128; const size_t MAX_THREADS = 128;
const int MAX_SPLITPOINTS_PER_THREAD = 8;
/// Mutex and ConditionVariable struct are wrappers of the low level locking
/// machinery and are modeled after the corresponding C++11 classes.
struct Mutex {
Mutex() { lock_init(l); }
~Mutex() { lock_destroy(l); }
void lock() { lock_grab(l); }
void unlock() { lock_release(l); }
private:
friend struct ConditionVariable;
Lock l;
};
struct ConditionVariable {
ConditionVariable() { cond_init(c); }
~ConditionVariable() { cond_destroy(c); }
void wait(Mutex& m) { cond_wait(c, m.l); }
void wait_for(Mutex& m, int ms) { timed_wait(c, m.l, ms); }
void notify_one() { cond_signal(c); }
private:
WaitCondition c;
};
/// SplitPoint struct stores information shared by the threads searching in
/// parallel below the same split point. It is populated at splitting time.
struct SplitPoint {
// Const data after split point has been setup
const Position* pos;
Search::Stack* ss;
Thread* masterThread;
Depth depth;
Value beta;
int nodeType;
bool cutNode;
// Const pointers to shared data
MovePicker* movePicker;
SplitPoint* parentSplitPoint;
// Shared variable data
Mutex mutex;
std::bitset<MAX_THREADS> slavesMask;
volatile bool allSlavesSearching;
volatile uint64_t nodes;
volatile Value alpha;
volatile Value bestValue;
volatile Move bestMove;
volatile int moveCount;
volatile bool cutoff;
};
/// ThreadBase struct is the base of the hierarchy from where we derive all the /// ThreadBase struct is the base of the hierarchy from where we derive all the
/// specialized thread classes. /// specialized thread classes.
struct ThreadBase { struct ThreadBase : public std::thread {
ThreadBase() : handle(NativeHandle()), exit(false) {} virtual ~ThreadBase() = default;
virtual ~ThreadBase() {}
virtual void idle_loop() = 0; virtual void idle_loop() = 0;
void notify_one(); void notify_one();
void wait_for(volatile const bool& b); void wait(volatile const bool& b);
void wait_while(volatile const bool& b);
Mutex mutex; Mutex mutex;
ConditionVariable sleepCondition; ConditionVariable sleepCondition;
NativeHandle handle; volatile bool exit = false;
volatile bool exit;
}; };
@@ -121,22 +65,21 @@ struct Thread : public ThreadBase {
Thread(); Thread();
virtual void idle_loop(); virtual void idle_loop();
bool cutoff_occurred() const; void search(bool isMainThread = false);
bool available_to(const Thread* master) const;
void split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove,
Depth depth, int moveCount, MovePicker* movePicker, int nodeType, bool cutNode);
SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD];
Pawns::Table pawnsTable; Pawns::Table pawnsTable;
Material::Table materialTable; Material::Table materialTable;
Endgames endgames; Endgames endgames;
Position* activePosition; size_t idx, PVIdx;
size_t idx;
int maxPly; int maxPly;
SplitPoint* volatile activeSplitPoint;
volatile int splitPointsSize;
volatile bool searching; volatile bool searching;
Position rootPos;
Search::RootMoveVector rootMoves;
Search::Stack stack[MAX_PLY+4];
HistoryStats History;
MovesStats Countermoves;
Depth depth;
}; };
@@ -144,19 +87,19 @@ struct Thread : public ThreadBase {
/// special threads: the main one and the recurring timer. /// special threads: the main one and the recurring timer.
struct MainThread : public Thread { struct MainThread : public Thread {
MainThread() : thinking(true) {} // Avoid a race with start_thinking()
virtual void idle_loop(); virtual void idle_loop();
volatile bool thinking; void join();
void think();
volatile bool thinking = true; // Avoid a race with start_thinking()
}; };
struct TimerThread : public ThreadBase { struct TimerThread : public ThreadBase {
static const int Resolution = 5; // Millisec between two check_time() calls static const int Resolution = 5; // Millisec between two check_time() calls
TimerThread() : run(false) {}
virtual void idle_loop(); virtual void idle_loop();
bool run; bool run = false;
}; };
@@ -171,13 +114,8 @@ struct ThreadPool : public std::vector<Thread*> {
MainThread* main() { return static_cast<MainThread*>(at(0)); } MainThread* main() { return static_cast<MainThread*>(at(0)); }
void read_uci_options(); void read_uci_options();
Thread* available_slave(const Thread* master) const;
void wait_for_think_finished();
void start_thinking(const Position&, const Search::LimitsType&, Search::StateStackPtr&); void start_thinking(const Position&, const Search::LimitsType&, Search::StateStackPtr&);
int64_t nodes_searched();
Depth minimumSplitDepth;
Mutex mutex;
ConditionVariable sleepCondition;
TimerThread* timer; TimerThread* timer;
}; };

View File

@@ -0,0 +1,69 @@
/*
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
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 THREAD_WIN32_H_INCLUDED
#define THREAD_WIN32_H_INCLUDED
/// STL thread library used by mingw and gcc when cross compiling for Windows
/// relies on libwinpthread. Currently libwinpthread implements mutexes directly
/// on top of Windows semaphores. Semaphores, being kernel objects, require kernel
/// mode transition in order to lock or unlock, which is very slow compared to
/// interlocked operations (about 30% slower on bench test). To workaround this
/// issue, we define our wrappers to the low level Win32 calls. We use critical
/// sections to support Windows XP and older versions. Unfortunately, cond_wait()
/// is racy between unlock() and WaitForSingleObject() but they have the same
/// speed performance of SRW locks.
#include <condition_variable>
#include <mutex>
#if defined(_WIN32) && !defined(_MSC_VER)
#ifndef NOMINMAX
# define NOMINMAX // Disable macros min() and max()
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX
/// Mutex and ConditionVariable struct are wrappers of the low level locking
/// machinery and are modeled after the corresponding C++11 classes.
struct Mutex {
Mutex() { InitializeCriticalSection(&cs); }
~Mutex() { DeleteCriticalSection(&cs); }
void lock() { EnterCriticalSection(&cs); }
void unlock() { LeaveCriticalSection(&cs); }
private:
CRITICAL_SECTION cs;
};
typedef std::condition_variable_any ConditionVariable;
#else // Default case: use STL classes
typedef std::mutex Mutex;
typedef std::condition_variable ConditionVariable;
#endif
#endif // #ifndef THREAD_WIN32_H_INCLUDED

View File

@@ -25,6 +25,8 @@
#include "timeman.h" #include "timeman.h"
#include "uci.h" #include "uci.h"
TimeManagement Time; // Our global time management object
namespace { namespace {
enum TimeType { OptimumTime, MaxTime }; enum TimeType { OptimumTime, MaxTime };
@@ -78,15 +80,31 @@ 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 TimeManager::init(const Search::LimitsType& limits, Color us, int ply) void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
{ {
int minThinkingTime = Options["Minimum Thinking Time"]; int minThinkingTime = Options["Minimum Thinking Time"];
int moveOverhead = Options["Move Overhead"]; int moveOverhead = Options["Move Overhead"];
int slowMover = Options["Slow Mover"]; int slowMover = Options["Slow Mover"];
int npmsec = Options["nodestime"];
// Initialize unstablePvFactor to 1 and search times to maximum values // If we have to play in 'nodes as time' mode, then convert from time
// to nodes, and use resulting values in time management formulas.
// WARNING: Given npms (nodes per millisecond) must be much lower then
// real engine speed to avoid time losses.
if (npmsec)
{
if (!availableNodes) // Only once at game start
availableNodes = npmsec * limits.time[us]; // Time is in msec
// Convert from millisecs to nodes
limits.time[us] = (int)availableNodes;
limits.inc[us] *= npmsec;
limits.npmsec = npmsec;
}
startTime = limits.startTime;
unstablePvFactor = 1; unstablePvFactor = 1;
optimumSearchTime = maximumSearchTime = std::max(limits.time[us], minThinkingTime); optimumTime = maximumTime = std::max(limits.time[us], minThinkingTime);
const int MaxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon; const int MaxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon;
@@ -105,12 +123,12 @@ void TimeManager::init(const Search::LimitsType& limits, Color us, int ply)
int t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, ply, slowMover); int t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, ply, slowMover);
int t2 = minThinkingTime + remaining<MaxTime >(hypMyTime, hypMTG, ply, slowMover); int t2 = minThinkingTime + remaining<MaxTime >(hypMyTime, hypMTG, ply, slowMover);
optimumSearchTime = std::min(t1, optimumSearchTime); optimumTime = std::min(t1, optimumTime);
maximumSearchTime = std::min(t2, maximumSearchTime); maximumTime = std::min(t2, maximumTime);
} }
if (Options["Ponder"]) if (Options["Ponder"])
optimumSearchTime += optimumSearchTime / 4; optimumTime += optimumTime / 4;
optimumSearchTime = std::min(optimumSearchTime, maximumSearchTime); optimumTime = std::min(optimumTime, maximumTime);
} }

View File

@@ -20,20 +20,30 @@
#ifndef TIMEMAN_H_INCLUDED #ifndef TIMEMAN_H_INCLUDED
#define TIMEMAN_H_INCLUDED #define TIMEMAN_H_INCLUDED
/// The TimeManager class computes the optimal time to think depending on the #include "misc.h"
/// maximum available time, the game move number and other parameters. #include "search.h"
#include "thread.h"
class TimeManager { /// The TimeManagement class computes the optimal time to think depending on
/// the maximum available time, the game move number and other parameters.
class TimeManagement {
public: public:
void init(const Search::LimitsType& limits, Color us, int ply); void init(Search::LimitsType& limits, Color us, int ply);
void pv_instability(double bestMoveChanges) { unstablePvFactor = 1 + bestMoveChanges; } void pv_instability(double bestMoveChanges) { unstablePvFactor = 1 + bestMoveChanges; }
int available_time() const { return int(optimumSearchTime * unstablePvFactor * 0.71); } int available() const { return int(optimumTime * unstablePvFactor * 0.76); }
int maximum_time() const { return maximumSearchTime; } int maximum() const { return maximumTime; }
int elapsed() const { return int(Search::Limits.npmsec ? Threads.nodes_searched() : now() - startTime); }
int64_t availableNodes; // When in 'nodes as time' mode
private: private:
int optimumSearchTime; TimePoint startTime;
int maximumSearchTime; int optimumTime;
int maximumTime;
double unstablePvFactor; double unstablePvFactor;
}; };
extern TimeManagement Time;
#endif // #ifndef TIMEMAN_H_INCLUDED #endif // #ifndef TIMEMAN_H_INCLUDED

View File

@@ -32,8 +32,6 @@ TranspositionTable TT; // Our global transposition table
void TranspositionTable::resize(size_t mbSize) { void TranspositionTable::resize(size_t mbSize) {
assert(sizeof(Cluster) == CacheLineSize / 2);
size_t newClusterCount = size_t(1) << msb((mbSize * 1024 * 1024) / sizeof(Cluster)); size_t newClusterCount = size_t(1) << msb((mbSize * 1024 * 1024) / sizeof(Cluster));
if (newClusterCount == clusterCount) if (newClusterCount == clusterCount)
@@ -68,9 +66,9 @@ void TranspositionTable::clear() {
/// TranspositionTable::probe() looks up the current position in the transposition /// TranspositionTable::probe() looks up the current position in the transposition
/// table. It returns true and a pointer to the TTEntry if the position is found. /// table. It returns true and a pointer to the TTEntry if the position is found.
/// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry /// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry
/// to be replaced later. A TTEntry t1 is considered to be more valuable than a /// to be replaced later. The replace value of an entry is calculated as its depth
/// TTEntry t2 if t1 is from the current search and t2 is from a previous search, /// minus 8 times its relative age. TTEntry t1 is considered more valuable than
/// or if the depth of t1 is bigger than the depth of t2. /// TTEntry t2 if its replace value is greater than that of t2.
TTEntry* TranspositionTable::probe(const Key key, bool& found) const { TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
@@ -80,7 +78,7 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
for (int i = 0; i < ClusterSize; ++i) for (int i = 0; i < ClusterSize; ++i)
if (!tte[i].key16 || tte[i].key16 == key16) if (!tte[i].key16 || tte[i].key16 == key16)
{ {
if (tte[i].key16) if ((tte[i].genBound8 & 0xFC) != generation8 && tte[i].key16)
tte[i].genBound8 = uint8_t(generation8 | tte[i].bound()); // Refresh tte[i].genBound8 = uint8_t(generation8 | tte[i].bound()); // Refresh
return found = (bool)tte[i].key16, &tte[i]; return found = (bool)tte[i].key16, &tte[i];
@@ -89,10 +87,30 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
// Find an entry to be replaced according to the replacement strategy // Find an entry to be replaced according to the replacement strategy
TTEntry* replace = tte; TTEntry* replace = tte;
for (int i = 1; i < ClusterSize; ++i) for (int i = 1; i < ClusterSize; ++i)
if ( (( tte[i].genBound8 & 0xFC) == generation8 || tte[i].bound() == BOUND_EXACT) // Due to our packed storage format for generation and its cyclic
- ((replace->genBound8 & 0xFC) == generation8) // nature we add 259 (256 is the modulus plus 3 to keep the lowest
- (tte[i].depth8 < replace->depth8) < 0) // two bound bits from affecting the result) to calculate the entry
// age correctly even after generation8 overflows into the next cycle.
if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2 * ONE_PLY
> tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2 * ONE_PLY)
replace = &tte[i]; replace = &tte[i];
return found = false, replace; return found = false, replace;
} }
/// Returns an approximation of the hashtable occupation during a search. The
/// hash is x permill full, as per UCI protocol.
int TranspositionTable::hashfull() const
{
int cnt = 0;
for (int i = 0; i < 1000 / ClusterSize; i++)
{
const TTEntry* tte = &table[i].entry[0];
for (int j = 0; j < ClusterSize; j++)
if ((tte[j].genBound8 & 0xFC) == generation8)
cnt++;
}
return cnt;
}

View File

@@ -43,15 +43,23 @@ struct TTEntry {
void save(Key k, Value v, Bound b, Depth d, Move m, Value ev, uint8_t g) { void save(Key k, Value v, Bound b, Depth d, Move m, Value ev, uint8_t g) {
if (m || (k >> 48) != key16) // Preserve any existing move for the same position // Preserve any existing move for the same position
if (m || (k >> 48) != key16)
move16 = (uint16_t)m; move16 = (uint16_t)m;
// Don't overwrite more valuable entries
if ( (k >> 48) != key16
|| d > depth8 - 2
/* || g != (genBound8 & 0xFC) // Matching non-zero keys are already refreshed by probe() */
|| b == BOUND_EXACT)
{
key16 = (uint16_t)(k >> 48); key16 = (uint16_t)(k >> 48);
value16 = (int16_t)v; value16 = (int16_t)v;
eval16 = (int16_t)ev; eval16 = (int16_t)ev;
genBound8 = (uint8_t)(g | b); genBound8 = (uint8_t)(g | b);
depth8 = (int8_t)d; depth8 = (int8_t)d;
} }
}
private: private:
friend class TranspositionTable; friend class TranspositionTable;
@@ -81,11 +89,14 @@ class TranspositionTable {
char padding[2]; // Align to the cache line size char padding[2]; // Align to the cache line size
}; };
static_assert(sizeof(Cluster) == CacheLineSize / 2, "Cluster size incorrect");
public: public:
~TranspositionTable() { free(mem); } ~TranspositionTable() { free(mem); }
void new_search() { generation8 += 4; } // Lower 2 bits are used by Bound void new_search() { generation8 += 4; } // Lower 2 bits are used by Bound
uint8_t generation() const { return generation8; } uint8_t generation() const { return generation8; }
TTEntry* probe(const Key key, bool& found) const; TTEntry* probe(const Key key, bool& found) const;
int hashfull() const;
void resize(size_t mbSize); void resize(size_t mbSize);
void clear(); void clear();

View File

@@ -33,13 +33,22 @@
/// ///
/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works /// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works
/// | only in 64-bit mode and requires hardware with popcnt support. /// | only in 64-bit mode and requires hardware with popcnt support.
///
/// -DUSE_PEXT | Add runtime support for use of pext asm-instruction. Works
/// | only in 64-bit mode and requires hardware with pext support.
#include <cassert> #include <cassert>
#include <cctype> #include <cctype>
#include <climits> #include <climits>
#include <cstdint>
#include <cstdlib> #include <cstdlib>
#include "platform.h" #if defined(_MSC_VER)
// Disable some silly and noisy warning from MSVC compiler
#pragma warning(disable: 4127) // Conditional expression is constant
#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
#endif
/// Predefined macros hell: /// Predefined macros hell:
/// ///
@@ -49,7 +58,7 @@
/// _WIN32 Building on Windows (any) /// _WIN32 Building on Windows (any)
/// _WIN64 Building on Windows 64 bit /// _WIN64 Building on Windows 64 bit
#if defined(_WIN64) && !defined(IS_64BIT) // Last condition means Makefile is not used #if defined(_WIN64) && defined(_MSC_VER) // No Makefile used
# include <intrin.h> // MSVC popcnt and bsfq instrinsics # include <intrin.h> // MSVC popcnt and bsfq instrinsics
# define IS_64BIT # define IS_64BIT
# define USE_BSFQ # define USE_BSFQ
@@ -70,14 +79,6 @@
# define pext(b, m) (0) # define pext(b, m) (0)
#endif #endif
#ifdef _MSC_VER
# define FORCE_INLINE __forceinline
#elif defined(__GNUC__)
# define FORCE_INLINE inline __attribute__((always_inline))
#else
# define FORCE_INLINE inline
#endif
#ifdef USE_POPCNT #ifdef USE_POPCNT
const bool HasPopCnt = true; const bool HasPopCnt = true;
#else #else
@@ -171,7 +172,7 @@ enum Bound {
BOUND_EXACT = BOUND_UPPER | BOUND_LOWER BOUND_EXACT = BOUND_UPPER | BOUND_LOWER
}; };
enum Value { enum Value : int {
VALUE_ZERO = 0, VALUE_ZERO = 0,
VALUE_DRAW = 0, VALUE_DRAW = 0,
VALUE_KNOWN_WIN = 10000, VALUE_KNOWN_WIN = 10000,
@@ -182,13 +183,10 @@ enum Value {
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,
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN,
PawnValueMg = 198, PawnValueEg = 258, PawnValueMg = 198, PawnValueEg = 258,
KnightValueMg = 817, KnightValueEg = 846, KnightValueMg = 817, KnightValueEg = 846,
BishopValueMg = 836, BishopValueEg = 857, BishopValueMg = 836, BishopValueEg = 857,
RookValueMg = 1270, RookValueEg = 1278, RookValueMg = 1270, RookValueEg = 1281,
QueenValueMg = 2521, QueenValueEg = 2558, QueenValueMg = 2521, QueenValueEg = 2558,
MidgameLimit = 15581, EndgameLimit = 3998 MidgameLimit = 15581, EndgameLimit = 3998
@@ -255,16 +253,10 @@ enum Rank {
}; };
/// Score enum stores a middlegame and an endgame value in a single integer. /// Score enum stores a middlegame and an endgame value in a single integer
/// The least significant 16 bits are used to store the endgame value and /// (enum). The least significant 16 bits are used to store the endgame value
/// the upper 16 bits are used to store the middlegame value. The compiler /// and the upper 16 bits are used to store the middlegame value.
/// is free to choose the enum type as long as it can store the data, so we enum Score : int { SCORE_ZERO };
/// ensure that Score is an integer type by assigning some big int values.
enum Score {
SCORE_ZERO,
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
};
inline Score make_score(int mg, int eg) { inline Score make_score(int mg, int eg) {
return Score((mg << 16) + eg); return Score((mg << 16) + eg);
@@ -417,7 +409,7 @@ inline MoveType type_of(Move m) {
} }
inline PieceType promotion_type(Move m) { inline PieceType promotion_type(Move m) {
return PieceType(((m >> 12) & 3) + 2); return PieceType(((m >> 12) & 3) + KNIGHT);
} }
inline Move make_move(Square from, Square to) { inline Move make_move(Square from, Square to) {

View File

@@ -26,7 +26,7 @@
#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 "uci.h" #include "uci.h"
using namespace std; using namespace std;
@@ -68,13 +68,13 @@ namespace {
return; return;
pos.set(fen, Options["UCI_Chess960"], Threads.main()); pos.set(fen, Options["UCI_Chess960"], Threads.main());
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>()); SetupStates = Search::StateStackPtr(new std::stack<StateInfo>);
// 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)
{ {
SetupStates->push(StateInfo()); SetupStates->push(StateInfo());
pos.do_move(m, SetupStates->top()); pos.do_move(m, SetupStates->top(), pos.gives_check(m, CheckInfo(pos)));
} }
} }
@@ -90,11 +90,11 @@ namespace {
// Read option name (can contain spaces) // Read option name (can contain spaces)
while (is >> token && token != "value") while (is >> token && token != "value")
name += string(" ", !name.empty()) + token; name += string(" ", name.empty() ? 0 : 1) + token;
// Read option value (can contain spaces) // Read option value (can contain spaces)
while (is >> token) while (is >> token)
value += string(" ", !value.empty()) + token; value += string(" ", value.empty() ? 0 : 1) + token;
if (Options.count(name)) if (Options.count(name))
Options[name] = value; Options[name] = value;
@@ -112,6 +112,8 @@ namespace {
Search::LimitsType limits; Search::LimitsType limits;
string token; string token;
limits.startTime = now(); // As early as possible!
while (is >> token) while (is >> token)
if (token == "searchmoves") if (token == "searchmoves")
while (is >> token) while (is >> token)
@@ -126,8 +128,8 @@ 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 == "infinite") limits.infinite = true; else if (token == "infinite") limits.infinite = 1;
else if (token == "ponder") limits.ponder = true; else if (token == "ponder") limits.ponder = 1;
Threads.start_thinking(pos, limits, SetupStates); Threads.start_thinking(pos, limits, SetupStates);
} }
@@ -171,15 +173,19 @@ void UCI::loop(int argc, char* argv[]) {
Threads.main()->notify_one(); // Could be sleeping Threads.main()->notify_one(); // Could be sleeping
} }
else if (token == "ponderhit") else if (token == "ponderhit")
Search::Limits.ponder = false; // Switch to normal search Search::Limits.ponder = 0; // 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::reset();
Time.availableNodes = 0;
}
else if (token == "isready") sync_cout << "readyok" << sync_endl; else if (token == "isready") sync_cout << "readyok" << sync_endl;
else if (token == "ucinewgame") TT.clear();
else if (token == "go") go(pos, is); else if (token == "go") go(pos, is);
else if (token == "position") position(pos, is); else if (token == "position") position(pos, is);
else if (token == "setoption") setoption(is); else if (token == "setoption") setoption(is);
@@ -205,7 +211,7 @@ void UCI::loop(int argc, char* argv[]) {
} while (token != "quit" && argc == 1); // Passed args have one-shot behaviour } while (token != "quit" && argc == 1); // Passed args have one-shot behaviour
Threads.wait_for_think_finished(); // Cannot quit whilst the search is running Threads.main()->join(); // Cannot quit whilst the search is running
} }
@@ -232,9 +238,7 @@ string UCI::value(Value v) {
/// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.) /// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.)
std::string UCI::square(Square s) { std::string UCI::square(Square s) {
return std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) };
char sq[] = { char('a' + file_of(s)), char('1' + rank_of(s)), 0 }; // NULL terminated
return sq;
} }
@@ -274,9 +278,9 @@ Move UCI::to_move(const Position& pos, string& str) {
if (str.length() == 5) // Junior could send promotion piece in uppercase if (str.length() == 5) // Junior could send promotion piece in uppercase
str[4] = char(tolower(str[4])); str[4] = char(tolower(str[4]));
for (MoveList<LEGAL> it(pos); *it; ++it) for (const auto& m : MoveList<LEGAL>(pos))
if (str == UCI::move(*it, pos.is_chess960())) if (str == UCI::move(m, pos.is_chess960()))
return *it; return m;
return MOVE_NONE; return MOVE_NONE;
} }

View File

@@ -17,8 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef UCIOPTION_H_INCLUDED #ifndef UCI_H_INCLUDED
#define UCIOPTION_H_INCLUDED #define UCI_H_INCLUDED
#include <map> #include <map>
#include <string> #include <string>
@@ -45,10 +45,10 @@ class Option {
typedef void (*OnChange)(const Option&); typedef void (*OnChange)(const Option&);
public: public:
Option(OnChange = NULL); Option(OnChange = nullptr);
Option(bool v, OnChange = NULL); Option(bool v, OnChange = nullptr);
Option(const char* v, OnChange = NULL); Option(const char* v, OnChange = nullptr);
Option(int v, int min, int max, OnChange = NULL); Option(int v, int min, int max, OnChange = nullptr);
Option& operator=(const std::string&); Option& operator=(const std::string&);
void operator<<(const Option&); void operator<<(const Option&);
@@ -69,10 +69,11 @@ void loop(int argc, char* argv[]);
std::string value(Value v); std::string value(Value v);
std::string square(Square s); std::string square(Square s);
std::string move(Move m, bool chess960); std::string move(Move m, bool chess960);
std::string pv(const Position& pos, Depth depth, Value alpha, Value beta);
Move to_move(const Position& pos, std::string& str); Move to_move(const Position& pos, std::string& str);
} // namespace UCI } // namespace UCI
extern UCI::OptionsMap Options; extern UCI::OptionsMap Options;
#endif // #ifndef UCIOPTION_H_INCLUDED #endif // #ifndef UCI_H_INCLUDED

View File

@@ -19,10 +19,10 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cstdlib> #include <ostream>
#include <sstream>
#include "misc.h" #include "misc.h"
#include "search.h"
#include "thread.h" #include "thread.h"
#include "tt.h" #include "tt.h"
#include "uci.h" #include "uci.h"
@@ -35,7 +35,7 @@ UCI::OptionsMap Options; // Global object
namespace UCI { namespace UCI {
/// 'On change' actions, triggered by an option's value change /// 'On change' actions, triggered by an option's value change
void on_clear_hash(const Option&) { TT.clear(); } void on_clear_hash(const Option&) { Search::reset(); }
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&) { Threads.read_uci_options(); }
@@ -43,10 +43,10 @@ void on_tb_path(const Option& o) { Tablebases::init(o); }
/// Our case insensitive less() function as required by UCI protocol /// Our case insensitive less() function as required by UCI protocol
bool ci_less(char c1, char c2) { return tolower(c1) < tolower(c2); }
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), ci_less);
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(),
[](char c1, char c2) { return tolower(c1) < tolower(c2); });
} }
@@ -58,7 +58,7 @@ void init(OptionsMap& o) {
o["Write Debug Log"] << Option(false, on_logger); o["Write Debug Log"] << Option(false, on_logger);
o["Contempt"] << Option(0, -100, 100); o["Contempt"] << Option(0, -100, 100);
o["Min Split Depth"] << Option(0, 0, 12, on_threads); o["Min Split Depth"] << Option(5, 0, 12, on_threads);
o["Threads"] << Option(1, 1, MAX_THREADS, on_threads); o["Threads"] << Option(1, 1, MAX_THREADS, 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);
@@ -68,6 +68,7 @@ void init(OptionsMap& o) {
o["Move Overhead"] << Option(30, 0, 5000); o["Move Overhead"] << Option(30, 0, 5000);
o["Minimum Thinking Time"] << Option(20, 0, 5000); o["Minimum Thinking Time"] << Option(20, 0, 5000);
o["Slow Mover"] << Option(80, 10, 1000); o["Slow Mover"] << Option(80, 10, 1000);
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);
o["SyzygyProbeDepth"] << Option(1, 1, 100); o["SyzygyProbeDepth"] << Option(1, 1, 100);
@@ -82,11 +83,11 @@ void init(OptionsMap& o) {
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
for (size_t idx = 0; idx < om.size(); ++idx) for (size_t idx = 0; idx < om.size(); ++idx)
for (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it) for (const auto& it : om)
if (it->second.idx == idx) if (it.second.idx == idx)
{ {
const Option& o = it->second; const Option& o = it.second;
os << "\noption name " << it->first << " type " << o.type; os << "\noption name " << it.first << " type " << o.type;
if (o.type != "button") if (o.type != "button")
os << " default " << o.defaultValue; os << " default " << o.defaultValue;
@@ -96,6 +97,7 @@ std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
break; break;
} }
return os; return os;
} }
@@ -112,12 +114,11 @@ Option::Option(OnChange f) : type("button"), min(0), max(0), on_change(f)
{} {}
Option::Option(int v, int minv, int maxv, OnChange f) : type("spin"), min(minv), max(maxv), on_change(f) Option::Option(int v, int minv, int maxv, OnChange f) : type("spin"), min(minv), max(maxv), on_change(f)
{ std::ostringstream ss; ss << v; defaultValue = currentValue = ss.str(); } { defaultValue = currentValue = to_string(v); }
Option::operator int() const { Option::operator int() const {
assert(type == "check" || type == "spin"); assert(type == "check" || type == "spin");
return (type == "spin" ? atoi(currentValue.c_str()) : currentValue == "true"); return (type == "spin" ? stoi(currentValue) : currentValue == "true");
} }
Option::operator std::string() const { Option::operator std::string() const {
@@ -147,7 +148,7 @@ Option& Option::operator=(const string& v) {
if ( (type != "button" && v.empty()) if ( (type != "button" && v.empty())
|| (type == "check" && v != "true" && v != "false") || (type == "check" && v != "true" && v != "false")
|| (type == "spin" && (atoi(v.c_str()) < min || atoi(v.c_str()) > max))) || (type == "spin" && (stoi(v) < min || stoi(v) > max)))
return *this; return *this;
if (type != "button") if (type != "button")