mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-12-17 19:22:18 +01:00
DroidFish: Updated stockfish to version 6.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
SF_SRC_FILES := \
|
||||
benchmark.cpp main.cpp movegen.cpp pawns.cpp thread.cpp uci.cpp \
|
||||
bitbase.cpp endgame.cpp material.cpp movepick.cpp position.cpp timeman.cpp ucioption.cpp \
|
||||
bitboard.cpp evaluate.cpp misc.cpp notation.cpp search.cpp tt.cpp tbprobe.cpp
|
||||
benchmark.cpp main.cpp movegen.cpp pawns.cpp thread.cpp uci.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
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := stockfish-nopie
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -24,16 +24,17 @@
|
||||
#include <vector>
|
||||
|
||||
#include "misc.h"
|
||||
#include "notation.h"
|
||||
#include "position.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
#include "tt.h"
|
||||
#include "ucioption.h"
|
||||
#include "uci.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static const char* Defaults[] = {
|
||||
namespace {
|
||||
|
||||
const char* Defaults[] = {
|
||||
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
|
||||
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10",
|
||||
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11",
|
||||
@@ -63,9 +64,23 @@ static const char* Defaults[] = {
|
||||
"6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1",
|
||||
"1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1",
|
||||
"6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1",
|
||||
"8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1"
|
||||
"8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1",
|
||||
|
||||
// 5-man positions
|
||||
"8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate
|
||||
"8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate
|
||||
"8/3k4/8/8/8/4B3/4KB2/2B5 w - - 0 1", // draw
|
||||
|
||||
// 6-man positions
|
||||
"8/8/1P6/5pr1/8/4R3/7k/2K5 w - - 0 1", // Re5 - mate
|
||||
"8/2p4P/8/kr6/6R1/8/8/1K6 w - - 0 1", // Ka2 - mate
|
||||
"8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1", // Nd2 - draw
|
||||
|
||||
// 7-man positions
|
||||
"8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/// benchmark() runs a simple benchmark by letting Stockfish analyze a set
|
||||
/// of positions for a given limit each. There are five parameters: the
|
||||
@@ -73,7 +88,7 @@ static const char* Defaults[] = {
|
||||
/// be used, the limit value spent for each position (optional, default is
|
||||
/// depth 13), an optional file name where to look for positions in FEN
|
||||
/// format (defaults are the positions defined above) and the type of the
|
||||
/// limit value: depth (default), time in secs or number of nodes.
|
||||
/// limit value: depth (default), time in millisecs or number of nodes.
|
||||
|
||||
void benchmark(const Position& current, istream& is) {
|
||||
|
||||
@@ -93,7 +108,7 @@ void benchmark(const Position& current, istream& is) {
|
||||
TT.clear();
|
||||
|
||||
if (limitType == "time")
|
||||
limits.movetime = 1000 * atoi(limit.c_str()); // movetime is in ms
|
||||
limits.movetime = atoi(limit.c_str()); // movetime is in ms
|
||||
|
||||
else if (limitType == "nodes")
|
||||
limits.nodes = atoi(limit.c_str());
|
||||
@@ -105,7 +120,7 @@ void benchmark(const Position& current, istream& is) {
|
||||
limits.depth = atoi(limit.c_str());
|
||||
|
||||
if (fenFile == "default")
|
||||
fens.assign(Defaults, Defaults + 30);
|
||||
fens.assign(Defaults, Defaults + 37);
|
||||
|
||||
else if (fenFile == "current")
|
||||
fens.push_back(current.fen());
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -41,7 +41,7 @@ namespace {
|
||||
// bit 13-14: white pawn file (from FILE_A to FILE_D)
|
||||
// bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2)
|
||||
unsigned index(Color us, Square bksq, Square wksq, Square psq) {
|
||||
return wksq + (bksq << 6) + (us << 12) + (file_of(psq) << 13) + ((RANK_7 - rank_of(psq)) << 15);
|
||||
return wksq | (bksq << 6) | (us << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15);
|
||||
}
|
||||
|
||||
enum Result {
|
||||
@@ -71,7 +71,7 @@ namespace {
|
||||
} // namespace
|
||||
|
||||
|
||||
bool Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color us) {
|
||||
bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color us) {
|
||||
|
||||
assert(file_of(wpsq) <= FILE_D);
|
||||
|
||||
@@ -80,7 +80,7 @@ bool Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color us) {
|
||||
}
|
||||
|
||||
|
||||
void Bitbases::init_kpk() {
|
||||
void Bitbases::init() {
|
||||
|
||||
unsigned idx, repeat = 1;
|
||||
std::vector<KPKPosition> db;
|
||||
@@ -114,7 +114,7 @@ namespace {
|
||||
result = UNKNOWN;
|
||||
|
||||
// Check if two pieces are on the same square or if a king can be captured
|
||||
if ( square_distance(wksq, bksq) <= 1
|
||||
if ( distance(wksq, bksq) <= 1
|
||||
|| wksq == psq
|
||||
|| bksq == psq
|
||||
|| (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq)))
|
||||
@@ -125,7 +125,7 @@ namespace {
|
||||
// Immediate win if a pawn can be promoted without getting captured
|
||||
if ( rank_of(psq) == RANK_7
|
||||
&& wksq != psq + DELTA_N
|
||||
&& ( square_distance(bksq, psq + DELTA_N) > 1
|
||||
&& ( distance(bksq, psq + DELTA_N) > 1
|
||||
||(StepAttacksBB[KING][wksq] & (psq + DELTA_N))))
|
||||
result = WIN;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -18,23 +18,23 @@
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring> // For memset
|
||||
#include <cstring> // For std::memset
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "bitcount.h"
|
||||
#include "rkiss.h"
|
||||
#include "misc.h"
|
||||
|
||||
CACHE_LINE_ALIGNMENT
|
||||
int SquareDistance[SQUARE_NB][SQUARE_NB];
|
||||
|
||||
Bitboard RMasks[SQUARE_NB];
|
||||
Bitboard RMagics[SQUARE_NB];
|
||||
Bitboard* RAttacks[SQUARE_NB];
|
||||
unsigned RShifts[SQUARE_NB];
|
||||
Bitboard RookMasks [SQUARE_NB];
|
||||
Bitboard RookMagics [SQUARE_NB];
|
||||
Bitboard* RookAttacks[SQUARE_NB];
|
||||
unsigned RookShifts [SQUARE_NB];
|
||||
|
||||
Bitboard BMasks[SQUARE_NB];
|
||||
Bitboard BMagics[SQUARE_NB];
|
||||
Bitboard* BAttacks[SQUARE_NB];
|
||||
unsigned BShifts[SQUARE_NB];
|
||||
Bitboard BishopMasks [SQUARE_NB];
|
||||
Bitboard BishopMagics [SQUARE_NB];
|
||||
Bitboard* BishopAttacks[SQUARE_NB];
|
||||
unsigned BishopShifts [SQUARE_NB];
|
||||
|
||||
Bitboard SquareBB[SQUARE_NB];
|
||||
Bitboard FileBB[FILE_NB];
|
||||
@@ -44,53 +44,44 @@ Bitboard InFrontBB[COLOR_NB][RANK_NB];
|
||||
Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
|
||||
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
||||
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
||||
Bitboard DistanceRingsBB[SQUARE_NB][8];
|
||||
Bitboard DistanceRingBB[SQUARE_NB][8];
|
||||
Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
|
||||
Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
|
||||
Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
|
||||
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||
|
||||
int SquareDistance[SQUARE_NB][SQUARE_NB];
|
||||
|
||||
namespace {
|
||||
|
||||
// De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan
|
||||
const uint64_t DeBruijn_64 = 0x3F79D71B4CB0A89ULL;
|
||||
const uint32_t DeBruijn_32 = 0x783A9B23;
|
||||
const uint64_t DeBruijn64 = 0x3F79D71B4CB0A89ULL;
|
||||
const uint32_t DeBruijn32 = 0x783A9B23;
|
||||
|
||||
CACHE_LINE_ALIGNMENT
|
||||
|
||||
int MS1BTable[256];
|
||||
Square BSFTable[SQUARE_NB];
|
||||
Bitboard RTable[0x19000]; // Storage space for rook attacks
|
||||
Bitboard BTable[0x1480]; // Storage space for bishop attacks
|
||||
int MS1BTable[256]; // To implement software msb()
|
||||
Square BSFTable[SQUARE_NB]; // To implement software bitscan
|
||||
Bitboard RookTable[0x19000]; // To store rook attacks
|
||||
Bitboard BishopTable[0x1480]; // To store bishop attacks
|
||||
|
||||
typedef unsigned (Fn)(Square, Bitboard);
|
||||
|
||||
void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[],
|
||||
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index);
|
||||
|
||||
FORCE_INLINE unsigned bsf_index(Bitboard b) {
|
||||
// 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 systems, extended to 64 bits by Kim Walisch
|
||||
b ^= (b - 1);
|
||||
return Is64Bit ? (b * DeBruijn_64) >> 58
|
||||
: ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26;
|
||||
FORCE_INLINE unsigned bsf_index(Bitboard b) {
|
||||
b ^= b - 1;
|
||||
return Is64Bit ? (b * DeBruijn64) >> 58
|
||||
: ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn32) >> 26;
|
||||
}
|
||||
}
|
||||
|
||||
/// lsb()/msb() finds the least/most significant bit in a non-zero bitboard.
|
||||
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard.
|
||||
|
||||
#ifndef USE_BSFQ
|
||||
|
||||
Square lsb(Bitboard b) { return BSFTable[bsf_index(b)]; }
|
||||
/// Software fall-back of lsb() and msb() for CPU lacking hardware support
|
||||
|
||||
Square pop_lsb(Bitboard* b) {
|
||||
|
||||
Bitboard bb = *b;
|
||||
*b = bb & (bb - 1);
|
||||
return BSFTable[bsf_index(bb)];
|
||||
Square lsb(Bitboard b) {
|
||||
return BSFTable[bsf_index(b)];
|
||||
}
|
||||
|
||||
Square msb(Bitboard b) {
|
||||
@@ -124,8 +115,8 @@ Square msb(Bitboard b) {
|
||||
#endif // ifndef USE_BSFQ
|
||||
|
||||
|
||||
/// Bitboards::pretty() returns an ASCII representation of a bitboard to be
|
||||
/// printed to standard output. This is sometimes useful for debugging.
|
||||
/// Bitboards::pretty() returns an ASCII representation of a bitboard suitable
|
||||
/// to be printed to standard output. Useful for debugging.
|
||||
|
||||
const std::string Bitboards::pretty(Bitboard b) {
|
||||
|
||||
@@ -181,8 +172,8 @@ void Bitboards::init() {
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
||||
if (s1 != s2)
|
||||
{
|
||||
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
|
||||
DistanceRingsBB[s1][SquareDistance[s1][s2] - 1] |= s2;
|
||||
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
|
||||
DistanceRingBB[s1][SquareDistance[s1][s2] - 1] |= s2;
|
||||
}
|
||||
|
||||
int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 },
|
||||
@@ -195,15 +186,15 @@ void Bitboards::init() {
|
||||
{
|
||||
Square to = s + Square(c == WHITE ? steps[pt][i] : -steps[pt][i]);
|
||||
|
||||
if (is_ok(to) && square_distance(s, to) < 3)
|
||||
if (is_ok(to) && distance(s, to) < 3)
|
||||
StepAttacksBB[make_piece(c, pt)][s] |= to;
|
||||
}
|
||||
|
||||
Square RDeltas[] = { DELTA_N, DELTA_E, DELTA_S, DELTA_W };
|
||||
Square BDeltas[] = { DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW };
|
||||
Square RookDeltas[] = { DELTA_N, DELTA_E, DELTA_S, DELTA_W };
|
||||
Square BishopDeltas[] = { DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW };
|
||||
|
||||
init_magics(RTable, RAttacks, RMagics, RMasks, RShifts, RDeltas, magic_index<ROOK>);
|
||||
init_magics(BTable, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index<BISHOP>);
|
||||
init_magics(RookTable, RookAttacks, RookMagics, RookMasks, RookShifts, RookDeltas, magic_index<ROOK>);
|
||||
init_magics(BishopTable, BishopAttacks, BishopMagics, BishopMasks, BishopShifts, BishopDeltas, magic_index<BISHOP>);
|
||||
|
||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
||||
{
|
||||
@@ -233,7 +224,7 @@ namespace {
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
for (Square s = sq + deltas[i];
|
||||
is_ok(s) && square_distance(s, s - deltas[i]) == 1;
|
||||
is_ok(s) && distance(s, s - deltas[i]) == 1;
|
||||
s += deltas[i])
|
||||
{
|
||||
attack |= s;
|
||||
@@ -254,11 +245,11 @@ namespace {
|
||||
void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[],
|
||||
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) {
|
||||
|
||||
int MagicBoosters[][RANK_NB] = { { 969, 1976, 2850, 542, 2069, 2852, 1708, 164 },
|
||||
{ 3101, 552, 3555, 926, 834, 26, 2131, 1117 } };
|
||||
RKISS rk;
|
||||
int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 },
|
||||
{ 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } };
|
||||
|
||||
Bitboard occupancy[4096], reference[4096], edges, b;
|
||||
int i, size, booster;
|
||||
int i, size;
|
||||
|
||||
// attacks[s] is a pointer to the beginning of the attacks table for square 's'
|
||||
attacks[SQ_A1] = table;
|
||||
@@ -283,8 +274,8 @@ namespace {
|
||||
occupancy[size] = b;
|
||||
reference[size] = sliding_attack(deltas, s, b);
|
||||
|
||||
// if (HasPext)
|
||||
// attacks[s][_pext_u64(b, masks[s])] = reference[size];
|
||||
if (HasPext)
|
||||
attacks[s][pext(b, masks[s])] = reference[size];
|
||||
|
||||
size++;
|
||||
b = (b - masks[s]) & masks[s];
|
||||
@@ -298,13 +289,13 @@ namespace {
|
||||
if (HasPext)
|
||||
continue;
|
||||
|
||||
booster = MagicBoosters[Is64Bit][rank_of(s)];
|
||||
PRNG rng(seeds[Is64Bit][rank_of(s)]);
|
||||
|
||||
// Find a magic for square 's' picking up an (almost) random number
|
||||
// until we find the one that passes the verification test.
|
||||
do {
|
||||
do
|
||||
magics[s] = rk.magic_rand<Bitboard>(booster);
|
||||
magics[s] = rng.sparse_rand<Bitboard>();
|
||||
while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6);
|
||||
|
||||
std::memset(attacks[s], 0, size * sizeof(Bitboard));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -25,6 +25,13 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace Bitbases {
|
||||
|
||||
void init();
|
||||
bool probe(Square wksq, Square wpsq, Square bksq, Color us);
|
||||
|
||||
}
|
||||
|
||||
namespace Bitboards {
|
||||
|
||||
void init();
|
||||
@@ -32,12 +39,7 @@ const std::string pretty(Bitboard b);
|
||||
|
||||
}
|
||||
|
||||
namespace Bitbases {
|
||||
|
||||
void init_kpk();
|
||||
bool probe_kpk(Square wksq, Square wpsq, Square bksq, Color us);
|
||||
|
||||
}
|
||||
const Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
|
||||
|
||||
const Bitboard FileABB = 0x0101010101010101ULL;
|
||||
const Bitboard FileBBB = FileABB << 1;
|
||||
@@ -57,17 +59,17 @@ const Bitboard Rank6BB = Rank1BB << (8 * 5);
|
||||
const Bitboard Rank7BB = Rank1BB << (8 * 6);
|
||||
const Bitboard Rank8BB = Rank1BB << (8 * 7);
|
||||
|
||||
CACHE_LINE_ALIGNMENT
|
||||
extern int SquareDistance[SQUARE_NB][SQUARE_NB];
|
||||
|
||||
extern Bitboard RMasks[SQUARE_NB];
|
||||
extern Bitboard RMagics[SQUARE_NB];
|
||||
extern Bitboard* RAttacks[SQUARE_NB];
|
||||
extern unsigned RShifts[SQUARE_NB];
|
||||
extern Bitboard RookMasks [SQUARE_NB];
|
||||
extern Bitboard RookMagics [SQUARE_NB];
|
||||
extern Bitboard* RookAttacks[SQUARE_NB];
|
||||
extern unsigned RookShifts [SQUARE_NB];
|
||||
|
||||
extern Bitboard BMasks[SQUARE_NB];
|
||||
extern Bitboard BMagics[SQUARE_NB];
|
||||
extern Bitboard* BAttacks[SQUARE_NB];
|
||||
extern unsigned BShifts[SQUARE_NB];
|
||||
extern Bitboard BishopMasks [SQUARE_NB];
|
||||
extern Bitboard BishopMagics [SQUARE_NB];
|
||||
extern Bitboard* BishopAttacks[SQUARE_NB];
|
||||
extern unsigned BishopShifts [SQUARE_NB];
|
||||
|
||||
extern Bitboard SquareBB[SQUARE_NB];
|
||||
extern Bitboard FileBB[FILE_NB];
|
||||
@@ -77,15 +79,12 @@ extern Bitboard InFrontBB[COLOR_NB][RANK_NB];
|
||||
extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
|
||||
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
||||
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
||||
extern Bitboard DistanceRingsBB[SQUARE_NB][8];
|
||||
extern Bitboard DistanceRingBB[SQUARE_NB][8];
|
||||
extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
|
||||
extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
|
||||
extern Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
|
||||
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||
|
||||
extern int SquareDistance[SQUARE_NB][SQUARE_NB];
|
||||
|
||||
const Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
|
||||
|
||||
/// Overloads of bitwise operators between a Bitboard and a Square for testing
|
||||
/// whether a given bit is set in a bitboard, and for setting and clearing bits.
|
||||
@@ -94,14 +93,6 @@ inline Bitboard operator&(Bitboard b, Square s) {
|
||||
return b & SquareBB[s];
|
||||
}
|
||||
|
||||
inline Bitboard& operator|=(Bitboard& b, Square s) {
|
||||
return b |= SquareBB[s];
|
||||
}
|
||||
|
||||
inline Bitboard& operator^=(Bitboard& b, Square s) {
|
||||
return b ^= SquareBB[s];
|
||||
}
|
||||
|
||||
inline Bitboard operator|(Bitboard b, Square s) {
|
||||
return b | SquareBB[s];
|
||||
}
|
||||
@@ -110,37 +101,21 @@ inline Bitboard operator^(Bitboard b, Square s) {
|
||||
return b ^ SquareBB[s];
|
||||
}
|
||||
|
||||
inline Bitboard& operator|=(Bitboard& b, Square s) {
|
||||
return b |= SquareBB[s];
|
||||
}
|
||||
|
||||
inline Bitboard& operator^=(Bitboard& b, Square s) {
|
||||
return b ^= SquareBB[s];
|
||||
}
|
||||
|
||||
inline bool more_than_one(Bitboard b) {
|
||||
return b & (b - 1);
|
||||
}
|
||||
|
||||
inline int square_distance(Square s1, Square s2) {
|
||||
return SquareDistance[s1][s2];
|
||||
}
|
||||
|
||||
inline int file_distance(Square s1, Square s2) {
|
||||
return abs(file_of(s1) - file_of(s2));
|
||||
}
|
||||
|
||||
inline int rank_distance(Square s1, Square s2) {
|
||||
return abs(rank_of(s1) - rank_of(s2));
|
||||
}
|
||||
|
||||
|
||||
/// shift_bb() moves bitboard one step along direction Delta. Mainly for pawns.
|
||||
|
||||
template<Square Delta>
|
||||
inline Bitboard shift_bb(Bitboard b) {
|
||||
|
||||
return Delta == DELTA_N ? b << 8 : Delta == DELTA_S ? b >> 8
|
||||
: Delta == DELTA_NE ? (b & ~FileHBB) << 9 : Delta == DELTA_SE ? (b & ~FileHBB) >> 7
|
||||
: Delta == DELTA_NW ? (b & ~FileABB) << 7 : Delta == DELTA_SW ? (b & ~FileABB) >> 9
|
||||
: 0;
|
||||
}
|
||||
|
||||
|
||||
/// rank_bb() and file_bb() take a file or a square as input and return
|
||||
/// a bitboard representing all squares on the given file or rank.
|
||||
/// rank_bb() and file_bb() return a bitboard representing all the squares on
|
||||
/// the given file or rank.
|
||||
|
||||
inline Bitboard rank_bb(Rank r) {
|
||||
return RankBB[r];
|
||||
@@ -159,119 +134,138 @@ inline Bitboard file_bb(Square s) {
|
||||
}
|
||||
|
||||
|
||||
/// adjacent_files_bb() takes a file as input and returns a bitboard representing
|
||||
/// all squares on the adjacent files.
|
||||
/// shift_bb() moves a bitboard one step along direction Delta. Mainly for pawns
|
||||
|
||||
template<Square Delta>
|
||||
inline Bitboard shift_bb(Bitboard b) {
|
||||
return Delta == DELTA_N ? b << 8 : Delta == DELTA_S ? b >> 8
|
||||
: Delta == DELTA_NE ? (b & ~FileHBB) << 9 : Delta == DELTA_SE ? (b & ~FileHBB) >> 7
|
||||
: Delta == DELTA_NW ? (b & ~FileABB) << 7 : Delta == DELTA_SW ? (b & ~FileABB) >> 9
|
||||
: 0;
|
||||
}
|
||||
|
||||
|
||||
/// adjacent_files_bb() returns a bitboard representing all the squares on the
|
||||
/// adjacent files of the given one.
|
||||
|
||||
inline Bitboard adjacent_files_bb(File f) {
|
||||
return AdjacentFilesBB[f];
|
||||
}
|
||||
|
||||
|
||||
/// in_front_bb() takes a color and a rank as input, and returns a bitboard
|
||||
/// representing all the squares on all ranks in front of the rank, from the
|
||||
/// given color's point of view. For instance, in_front_bb(BLACK, RANK_3) will
|
||||
/// give all squares on ranks 1 and 2.
|
||||
|
||||
inline Bitboard in_front_bb(Color c, Rank r) {
|
||||
return InFrontBB[c][r];
|
||||
}
|
||||
|
||||
|
||||
/// between_bb() returns a bitboard representing all squares between two squares.
|
||||
/// For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with the bits for
|
||||
/// square d5 and e6 set. If s1 and s2 are not on the same rank, file or diagonal,
|
||||
/// 0 is returned.
|
||||
/// between_bb() returns a bitboard representing all the squares between the two
|
||||
/// given ones. For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with
|
||||
/// the bits for square d5 and e6 set. If s1 and s2 are not on the same rank, file
|
||||
/// or diagonal, 0 is returned.
|
||||
|
||||
inline Bitboard between_bb(Square s1, Square s2) {
|
||||
return BetweenBB[s1][s2];
|
||||
}
|
||||
|
||||
|
||||
/// forward_bb() takes a color and a square as input, and returns a bitboard
|
||||
/// representing all squares along the line in front of the square, from the
|
||||
/// point of view of the given color. Definition of the table is:
|
||||
/// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s)
|
||||
/// in_front_bb() returns a bitboard representing all the squares on all the ranks
|
||||
/// in front of the given one, from the point of view of the given color. For
|
||||
/// instance, in_front_bb(BLACK, RANK_3) will return the squares on ranks 1 and 2.
|
||||
|
||||
inline Bitboard in_front_bb(Color c, Rank r) {
|
||||
return InFrontBB[c][r];
|
||||
}
|
||||
|
||||
|
||||
/// forward_bb() returns a bitboard representing all the squares along the line
|
||||
/// in front of the given one, from the point of view of the given color:
|
||||
/// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s)
|
||||
|
||||
inline Bitboard forward_bb(Color c, Square s) {
|
||||
return ForwardBB[c][s];
|
||||
}
|
||||
|
||||
|
||||
/// pawn_attack_span() takes a color and a square as input, and returns a bitboard
|
||||
/// representing all squares that can be attacked by a pawn of the given color
|
||||
/// when it moves along its file starting from the given square. Definition is:
|
||||
/// PawnAttackSpan[c][s] = in_front_bb(c, s) & adjacent_files_bb(s);
|
||||
/// pawn_attack_span() returns a bitboard representing all the squares that can be
|
||||
/// attacked by a pawn of the given color when it moves along its file, starting
|
||||
/// from the given square:
|
||||
/// PawnAttackSpan[c][s] = in_front_bb(c, s) & adjacent_files_bb(s);
|
||||
|
||||
inline Bitboard pawn_attack_span(Color c, Square s) {
|
||||
return PawnAttackSpan[c][s];
|
||||
}
|
||||
|
||||
|
||||
/// passed_pawn_mask() takes a color and a square as input, and returns a
|
||||
/// bitboard mask which can be used to test if a pawn of the given color on
|
||||
/// the given square is a passed pawn. Definition of the table is:
|
||||
/// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_bb(c, s)
|
||||
/// passed_pawn_mask() returns a bitboard mask which can be used to test if a
|
||||
/// pawn of the given color and on the given square is a passed pawn:
|
||||
/// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_bb(c, s)
|
||||
|
||||
inline Bitboard passed_pawn_mask(Color c, Square s) {
|
||||
return PassedPawnMask[c][s];
|
||||
}
|
||||
|
||||
|
||||
/// squares_of_color() returns a bitboard representing all squares with the same
|
||||
/// color of the given square.
|
||||
/// 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 straight or on a diagonal line.
|
||||
/// aligned() returns true if the squares s1, s2 and s3 are aligned either on a
|
||||
/// straight or on a diagonal line.
|
||||
|
||||
inline bool aligned(Square s1, Square s2, Square s3) {
|
||||
return LineBB[s1][s2] & s3;
|
||||
}
|
||||
|
||||
|
||||
/// Functions for computing sliding attack bitboards. Function attacks_bb() takes
|
||||
/// a square and a bitboard of occupied squares as input, and returns a bitboard
|
||||
/// representing all squares attacked by Pt (bishop or rook) on the given square.
|
||||
/// distance() functions return the distance between x and y, defined as the
|
||||
/// number of steps for a king in x to reach y. Works with squares, ranks, files.
|
||||
|
||||
template<typename T> inline int distance(T x, T y) { return x < y ? y - x : x - y; }
|
||||
template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; }
|
||||
|
||||
template<typename T1, typename T2> inline int distance(T2 x, T2 y);
|
||||
template<> inline int distance<File>(Square x, Square y) { return distance(file_of(x), file_of(y)); }
|
||||
template<> inline int distance<Rank>(Square x, Square y) { return distance(rank_of(x), rank_of(y)); }
|
||||
|
||||
|
||||
/// attacks_bb() returns a bitboard representing all the squares attacked by a
|
||||
/// piece of type Pt (bishop or rook) placed on 's'. The helper magic_index()
|
||||
/// looks up the index using the 'magic bitboards' approach.
|
||||
template<PieceType Pt>
|
||||
FORCE_INLINE unsigned magic_index(Square s, Bitboard occ) {
|
||||
FORCE_INLINE unsigned magic_index(Square s, Bitboard occupied) {
|
||||
|
||||
Bitboard* const Masks = Pt == ROOK ? RMasks : BMasks;
|
||||
Bitboard* const Magics = Pt == ROOK ? RMagics : BMagics;
|
||||
unsigned* const Shifts = Pt == ROOK ? RShifts : BShifts;
|
||||
Bitboard* const Masks = Pt == ROOK ? RookMasks : BishopMasks;
|
||||
Bitboard* const Magics = Pt == ROOK ? RookMagics : BishopMagics;
|
||||
unsigned* const Shifts = Pt == ROOK ? RookShifts : BishopShifts;
|
||||
|
||||
// if (HasPext)
|
||||
// return unsigned(_pext_u64(occ, Masks[s]));
|
||||
if (HasPext)
|
||||
return unsigned(pext(occupied, Masks[s]));
|
||||
|
||||
if (Is64Bit)
|
||||
return unsigned(((occ & Masks[s]) * Magics[s]) >> Shifts[s]);
|
||||
return unsigned(((occupied & Masks[s]) * Magics[s]) >> Shifts[s]);
|
||||
|
||||
unsigned lo = unsigned(occ) & unsigned(Masks[s]);
|
||||
unsigned hi = unsigned(occ >> 32) & unsigned(Masks[s] >> 32);
|
||||
unsigned lo = unsigned(occupied) & unsigned(Masks[s]);
|
||||
unsigned hi = unsigned(occupied >> 32) & unsigned(Masks[s] >> 32);
|
||||
return (lo * unsigned(Magics[s]) ^ hi * unsigned(Magics[s] >> 32)) >> Shifts[s];
|
||||
}
|
||||
|
||||
template<PieceType Pt>
|
||||
inline Bitboard attacks_bb(Square s, Bitboard occ) {
|
||||
return (Pt == ROOK ? RAttacks : BAttacks)[s][magic_index<Pt>(s, occ)];
|
||||
inline Bitboard attacks_bb(Square s, Bitboard occupied) {
|
||||
return (Pt == ROOK ? RookAttacks : BishopAttacks)[s][magic_index<Pt>(s, occupied)];
|
||||
}
|
||||
|
||||
inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occ) {
|
||||
inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) {
|
||||
|
||||
switch (type_of(pc))
|
||||
{
|
||||
case BISHOP: return attacks_bb<BISHOP>(s, occ);
|
||||
case ROOK : return attacks_bb<ROOK>(s, occ);
|
||||
case QUEEN : return attacks_bb<BISHOP>(s, occ) | attacks_bb<ROOK>(s, occ);
|
||||
case BISHOP: return attacks_bb<BISHOP>(s, occupied);
|
||||
case ROOK : return attacks_bb<ROOK>(s, occupied);
|
||||
case QUEEN : return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
|
||||
default : return StepAttacksBB[pc][s];
|
||||
}
|
||||
}
|
||||
|
||||
/// lsb()/msb() finds the least/most significant bit in a non-zero bitboard.
|
||||
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard.
|
||||
|
||||
/// lsb() and msb() return the least/most significant bit in a non-zero bitboard
|
||||
|
||||
#ifdef USE_BSFQ
|
||||
|
||||
@@ -304,7 +298,7 @@ FORCE_INLINE Square lsb(Bitboard b) {
|
||||
return (Square) (uint32_t(b) ? lsb32(uint32_t(b)) : 32 + lsb32(uint32_t(b >> 32)));
|
||||
}
|
||||
|
||||
# else
|
||||
# else // Assumed gcc or compatible compiler
|
||||
|
||||
FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen
|
||||
Bitboard idx;
|
||||
@@ -320,21 +314,24 @@ FORCE_INLINE Square msb(Bitboard b) {
|
||||
|
||||
# endif
|
||||
|
||||
#else // ifdef(USE_BSFQ)
|
||||
|
||||
Square lsb(Bitboard b);
|
||||
Square msb(Bitboard b);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard
|
||||
|
||||
FORCE_INLINE Square pop_lsb(Bitboard* b) {
|
||||
const Square s = lsb(*b);
|
||||
*b &= *b - 1;
|
||||
return s;
|
||||
}
|
||||
|
||||
#else // if defined(USE_BSFQ)
|
||||
|
||||
extern Square msb(Bitboard b);
|
||||
extern Square lsb(Bitboard b);
|
||||
extern Square pop_lsb(Bitboard* b);
|
||||
|
||||
#endif
|
||||
|
||||
/// frontmost_sq() and backmost_sq() find the square corresponding to the
|
||||
/// frontmost_sq() and backmost_sq() return the square corresponding to the
|
||||
/// most/least advanced bit relative to the given color.
|
||||
|
||||
inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -22,6 +22,7 @@
|
||||
#define BITCOUNT_H_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
enum BitCountType {
|
||||
@@ -35,7 +36,7 @@ enum BitCountType {
|
||||
/// Determine at compile time the best popcount<> specialization according to
|
||||
/// whether the platform is 32 or 64 bit, the maximum number of non-zero
|
||||
/// bits to count and if the hardware popcnt instruction is available.
|
||||
const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32;
|
||||
const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32;
|
||||
const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15;
|
||||
|
||||
|
||||
@@ -94,7 +95,7 @@ inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
|
||||
|
||||
return (int)__popcnt64(b);
|
||||
|
||||
#else
|
||||
#else // Assumed gcc or compatible compiler
|
||||
|
||||
return __builtin_popcountll(b);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -60,8 +60,8 @@ namespace {
|
||||
const int PushAway [8] = { 0, 5, 20, 40, 60, 80, 90, 100 };
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool verify_material(const Position& pos, Color c, Value npm, int num_pawns) {
|
||||
return pos.non_pawn_material(c) == npm && pos.count<PAWN>(c) == num_pawns;
|
||||
bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) {
|
||||
return pos.non_pawn_material(c) == npm && pos.count<PAWN>(c) == pawnsCnt;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -162,12 +162,13 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
|
||||
Value result = pos.non_pawn_material(strongSide)
|
||||
+ pos.count<PAWN>(strongSide) * PawnValueEg
|
||||
+ PushToEdges[loserKSq]
|
||||
+ PushClose[square_distance(winnerKSq, loserKSq)];
|
||||
+ PushClose[distance(winnerKSq, loserKSq)];
|
||||
|
||||
if ( pos.count<QUEEN>(strongSide)
|
||||
|| pos.count<ROOK>(strongSide)
|
||||
||(pos.count<BISHOP>(strongSide) && pos.count<KNIGHT>(strongSide))
|
||||
|| pos.bishop_pair(strongSide))
|
||||
||(pos.count<BISHOP>(strongSide) > 1 && opposite_colors(pos.list<BISHOP>(strongSide)[0],
|
||||
pos.list<BISHOP>(strongSide)[1])))
|
||||
result += VALUE_KNOWN_WIN;
|
||||
|
||||
return strongSide == pos.side_to_move() ? result : -result;
|
||||
@@ -196,7 +197,7 @@ Value Endgame<KBNK>::operator()(const Position& pos) const {
|
||||
}
|
||||
|
||||
Value result = VALUE_KNOWN_WIN
|
||||
+ PushClose[square_distance(winnerKSq, loserKSq)]
|
||||
+ PushClose[distance(winnerKSq, loserKSq)]
|
||||
+ PushToCorners[loserKSq];
|
||||
|
||||
return strongSide == pos.side_to_move() ? result : -result;
|
||||
@@ -217,7 +218,7 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
|
||||
|
||||
Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;
|
||||
|
||||
if (!Bitbases::probe_kpk(wksq, psq, bksq, us))
|
||||
if (!Bitbases::probe(wksq, psq, bksq, us))
|
||||
return VALUE_DRAW;
|
||||
|
||||
Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(psq));
|
||||
@@ -246,26 +247,26 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
|
||||
|
||||
// If the stronger side's king is in front of the pawn, it's a win
|
||||
if (wksq < psq && file_of(wksq) == file_of(psq))
|
||||
result = RookValueEg - square_distance(wksq, psq);
|
||||
result = RookValueEg - distance(wksq, psq);
|
||||
|
||||
// If the weaker side's king is too far from the pawn and the rook,
|
||||
// it's a win.
|
||||
else if ( square_distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide)
|
||||
&& square_distance(bksq, rsq) >= 3)
|
||||
result = RookValueEg - square_distance(wksq, psq);
|
||||
else if ( distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide)
|
||||
&& distance(bksq, rsq) >= 3)
|
||||
result = RookValueEg - distance(wksq, psq);
|
||||
|
||||
// If the pawn is far advanced and supported by the defending king,
|
||||
// the position is drawish
|
||||
else if ( rank_of(bksq) <= RANK_3
|
||||
&& square_distance(bksq, psq) == 1
|
||||
&& distance(bksq, psq) == 1
|
||||
&& rank_of(wksq) >= RANK_4
|
||||
&& square_distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide))
|
||||
result = Value(80) - 8 * square_distance(wksq, psq);
|
||||
&& distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide))
|
||||
result = Value(80) - 8 * distance(wksq, psq);
|
||||
|
||||
else
|
||||
result = Value(200) - 8 * ( square_distance(wksq, psq + DELTA_S)
|
||||
- square_distance(bksq, psq + DELTA_S)
|
||||
- square_distance(psq, queeningSq));
|
||||
result = Value(200) - 8 * ( distance(wksq, psq + DELTA_S)
|
||||
- distance(bksq, psq + DELTA_S)
|
||||
- distance(psq, queeningSq));
|
||||
|
||||
return strongSide == pos.side_to_move() ? result : -result;
|
||||
}
|
||||
@@ -294,7 +295,7 @@ Value Endgame<KRKN>::operator()(const Position& pos) const {
|
||||
|
||||
Square bksq = pos.king_square(weakSide);
|
||||
Square bnsq = pos.list<KNIGHT>(weakSide)[0];
|
||||
Value result = Value(PushToEdges[bksq] + PushAway[square_distance(bksq, bnsq)]);
|
||||
Value result = Value(PushToEdges[bksq] + PushAway[distance(bksq, bnsq)]);
|
||||
return strongSide == pos.side_to_move() ? result : -result;
|
||||
}
|
||||
|
||||
@@ -313,10 +314,10 @@ Value Endgame<KQKP>::operator()(const Position& pos) const {
|
||||
Square loserKSq = pos.king_square(weakSide);
|
||||
Square pawnSq = pos.list<PAWN>(weakSide)[0];
|
||||
|
||||
Value result = Value(PushClose[square_distance(winnerKSq, loserKSq)]);
|
||||
Value result = Value(PushClose[distance(winnerKSq, loserKSq)]);
|
||||
|
||||
if ( relative_rank(weakSide, pawnSq) != RANK_7
|
||||
|| square_distance(loserKSq, pawnSq) != 1
|
||||
|| distance(loserKSq, pawnSq) != 1
|
||||
|| !((FileABB | FileCBB | FileFBB | FileHBB) & pawnSq))
|
||||
result += QueenValueEg - PawnValueEg;
|
||||
|
||||
@@ -340,7 +341,7 @@ Value Endgame<KQKR>::operator()(const Position& pos) const {
|
||||
Value result = QueenValueEg
|
||||
- RookValueEg
|
||||
+ PushToEdges[loserKSq]
|
||||
+ PushClose[square_distance(winnerKSq, loserKSq)];
|
||||
+ PushClose[distance(winnerKSq, loserKSq)];
|
||||
|
||||
return strongSide == pos.side_to_move() ? result : -result;
|
||||
}
|
||||
@@ -375,7 +376,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
|
||||
Square kingSq = pos.king_square(weakSide);
|
||||
|
||||
if ( opposite_colors(queeningSq, bishopSq)
|
||||
&& square_distance(queeningSq, kingSq) <= 1)
|
||||
&& distance(queeningSq, kingSq) <= 1)
|
||||
return SCALE_FACTOR_DRAW;
|
||||
}
|
||||
|
||||
@@ -398,8 +399,8 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
|
||||
&& (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide)))
|
||||
&& (opposite_colors(bishopSq, weakPawnSq) || pos.count<PAWN>(strongSide) == 1))
|
||||
{
|
||||
int strongKingDist = square_distance(weakPawnSq, strongKingSq);
|
||||
int weakKingDist = square_distance(weakPawnSq, weakKingSq);
|
||||
int strongKingDist = distance(weakPawnSq, strongKingSq);
|
||||
int weakKingDist = distance(weakPawnSq, weakKingSq);
|
||||
|
||||
// It's a draw if the weak king is on its back two ranks, within 2
|
||||
// squares of the blocking pawn and the strong king is not
|
||||
@@ -469,7 +470,7 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
|
||||
// If the pawn is not too far advanced and the defending king defends the
|
||||
// queening square, use the third-rank defence.
|
||||
if ( r <= RANK_5
|
||||
&& square_distance(bksq, queeningSq) <= 1
|
||||
&& distance(bksq, queeningSq) <= 1
|
||||
&& wksq <= SQ_H5
|
||||
&& (rank_of(brsq) == RANK_6 || (r <= RANK_3 && rank_of(wrsq) != RANK_6)))
|
||||
return SCALE_FACTOR_DRAW;
|
||||
@@ -477,15 +478,15 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
|
||||
// The defending side saves a draw by checking from behind in case the pawn
|
||||
// has advanced to the 6th rank with the king behind.
|
||||
if ( r == RANK_6
|
||||
&& square_distance(bksq, queeningSq) <= 1
|
||||
&& distance(bksq, queeningSq) <= 1
|
||||
&& rank_of(wksq) + tempo <= RANK_6
|
||||
&& (rank_of(brsq) == RANK_1 || (!tempo && abs(file_of(brsq) - f) >= 3)))
|
||||
&& (rank_of(brsq) == RANK_1 || (!tempo && distance(file_of(brsq), f) >= 3)))
|
||||
return SCALE_FACTOR_DRAW;
|
||||
|
||||
if ( r >= RANK_6
|
||||
&& bksq == queeningSq
|
||||
&& rank_of(brsq) == RANK_1
|
||||
&& (!tempo || square_distance(wksq, wpsq) >= 2))
|
||||
&& (!tempo || distance(wksq, wpsq) >= 2))
|
||||
return SCALE_FACTOR_DRAW;
|
||||
|
||||
// White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7
|
||||
@@ -501,8 +502,8 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
|
||||
// away, it's a draw.
|
||||
if ( r <= RANK_5
|
||||
&& bksq == wpsq + DELTA_N
|
||||
&& square_distance(wksq, wpsq) - tempo >= 2
|
||||
&& square_distance(wksq, brsq) - tempo >= 2)
|
||||
&& distance(wksq, wpsq) - tempo >= 2
|
||||
&& distance(wksq, brsq) - tempo >= 2)
|
||||
return SCALE_FACTOR_DRAW;
|
||||
|
||||
// Pawn on the 7th rank supported by the rook from behind usually wins if the
|
||||
@@ -512,22 +513,22 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
|
||||
&& f != FILE_A
|
||||
&& file_of(wrsq) == f
|
||||
&& wrsq != queeningSq
|
||||
&& (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo)
|
||||
&& (square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo))
|
||||
return ScaleFactor(SCALE_FACTOR_MAX - 2 * square_distance(wksq, queeningSq));
|
||||
&& (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo)
|
||||
&& (distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo))
|
||||
return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(wksq, queeningSq));
|
||||
|
||||
// Similar to the above, but with the pawn further back
|
||||
if ( f != FILE_A
|
||||
&& file_of(wrsq) == f
|
||||
&& wrsq < wpsq
|
||||
&& (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo)
|
||||
&& (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wpsq + DELTA_N) - 2 + tempo)
|
||||
&& ( square_distance(bksq, wrsq) + tempo >= 3
|
||||
|| ( square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo
|
||||
&& (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wrsq) + tempo))))
|
||||
&& (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo)
|
||||
&& (distance(wksq, wpsq + DELTA_N) < distance(bksq, wpsq + DELTA_N) - 2 + tempo)
|
||||
&& ( distance(bksq, wrsq) + tempo >= 3
|
||||
|| ( distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo
|
||||
&& (distance(wksq, wpsq + DELTA_N) < distance(bksq, wrsq) + tempo))))
|
||||
return ScaleFactor( SCALE_FACTOR_MAX
|
||||
- 8 * square_distance(wpsq, queeningSq)
|
||||
- 2 * square_distance(wksq, queeningSq));
|
||||
- 8 * distance(wpsq, queeningSq)
|
||||
- 2 * distance(wksq, queeningSq));
|
||||
|
||||
// If the pawn is not far advanced and the defending king is somewhere in
|
||||
// the pawn's path, it's probably a draw.
|
||||
@@ -535,9 +536,9 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
|
||||
{
|
||||
if (file_of(bksq) == file_of(wpsq))
|
||||
return ScaleFactor(10);
|
||||
if ( abs(file_of(bksq) - file_of(wpsq)) == 1
|
||||
&& square_distance(wksq, bksq) > 2)
|
||||
return ScaleFactor(24 - 2 * square_distance(wksq, bksq));
|
||||
if ( distance<File>(bksq, wpsq) == 1
|
||||
&& distance(wksq, bksq) > 2)
|
||||
return ScaleFactor(24 - 2 * distance(wksq, bksq));
|
||||
}
|
||||
return SCALE_FACTOR_NONE;
|
||||
}
|
||||
@@ -564,7 +565,7 @@ ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
|
||||
// corner but not trapped there.
|
||||
if (rk == RANK_5 && !opposite_colors(bsq, psq))
|
||||
{
|
||||
int d = square_distance(psq + 3 * push, ksq);
|
||||
int d = distance(psq + 3 * push, ksq);
|
||||
|
||||
if (d <= 2 && !(d == 0 && ksq == pos.king_square(strongSide) + 2 * push))
|
||||
return ScaleFactor(24);
|
||||
@@ -577,9 +578,9 @@ ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
|
||||
// pawn from a reasonable distance and the defending king is near
|
||||
// the corner
|
||||
if ( rk == RANK_6
|
||||
&& square_distance(psq + 2 * push, ksq) <= 1
|
||||
&& distance(psq + 2 * push, ksq) <= 1
|
||||
&& (PseudoAttacks[BISHOP][bsq] & (psq + push))
|
||||
&& file_distance(bsq, psq) >= 2)
|
||||
&& distance<File>(bsq, psq) >= 2)
|
||||
return ScaleFactor(8);
|
||||
}
|
||||
|
||||
@@ -604,8 +605,8 @@ ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
|
||||
|
||||
Rank r = std::max(relative_rank(strongSide, wpsq1), relative_rank(strongSide, wpsq2));
|
||||
|
||||
if ( file_distance(bksq, wpsq1) <= 1
|
||||
&& file_distance(bksq, wpsq2) <= 1
|
||||
if ( distance<File>(bksq, wpsq1) <= 1
|
||||
&& distance<File>(bksq, wpsq2) <= 1
|
||||
&& relative_rank(strongSide, bksq) > r)
|
||||
{
|
||||
switch (r) {
|
||||
@@ -638,7 +639,7 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
|
||||
// the king is within one file of the pawns, it's a draw.
|
||||
if ( !(pawns & ~in_front_bb(weakSide, rank_of(ksq)))
|
||||
&& !((pawns & ~FileABB) && (pawns & ~FileHBB))
|
||||
&& file_distance(ksq, psq) <= 1)
|
||||
&& distance<File>(ksq, psq) <= 1)
|
||||
return SCALE_FACTOR_DRAW;
|
||||
|
||||
return SCALE_FACTOR_NONE;
|
||||
@@ -690,7 +691,7 @@ ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
|
||||
return SCALE_FACTOR_DRAW;
|
||||
|
||||
if ( (pos.attacks_from<BISHOP>(weakBishopSq) & path)
|
||||
&& square_distance(weakBishopSq, pawnSq) >= 3)
|
||||
&& distance(weakBishopSq, pawnSq) >= 3)
|
||||
return SCALE_FACTOR_DRAW;
|
||||
}
|
||||
}
|
||||
@@ -729,7 +730,7 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
|
||||
blockSq2 = make_square(file_of(psq1), rank_of(psq2));
|
||||
}
|
||||
|
||||
switch (file_distance(psq1, psq2))
|
||||
switch (distance<File>(psq1, psq2))
|
||||
{
|
||||
case 0:
|
||||
// Both pawns are on the same file. It's an easy draw if the defender firmly
|
||||
@@ -749,7 +750,7 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
|
||||
&& opposite_colors(ksq, wbsq)
|
||||
&& ( bbsq == blockSq2
|
||||
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP))
|
||||
|| abs(r1 - r2) >= 2))
|
||||
|| distance(r1, r2) >= 2))
|
||||
return SCALE_FACTOR_DRAW;
|
||||
|
||||
else if ( ksq == blockSq2
|
||||
@@ -802,7 +803,7 @@ ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
|
||||
Square pawnSq = normalize(pos, strongSide, pos.list<PAWN>(strongSide)[0]);
|
||||
Square weakKingSq = normalize(pos, strongSide, pos.king_square(weakSide));
|
||||
|
||||
if (pawnSq == SQ_A7 && square_distance(SQ_A8, weakKingSq) <= 1)
|
||||
if (pawnSq == SQ_A7 && distance(SQ_A8, weakKingSq) <= 1)
|
||||
return SCALE_FACTOR_DRAW;
|
||||
|
||||
return SCALE_FACTOR_NONE;
|
||||
@@ -821,7 +822,7 @@ ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
|
||||
// King needs to get close to promoting pawn to prevent knight from blocking.
|
||||
// Rules for this are very tricky, so just approximate.
|
||||
if (forward_bb(strongSide, pawnSq) & pos.attacks_from<BISHOP>(bishopSq))
|
||||
return ScaleFactor(square_distance(weakKingSq, pawnSq));
|
||||
return ScaleFactor(distance(weakKingSq, pawnSq));
|
||||
|
||||
return SCALE_FACTOR_NONE;
|
||||
}
|
||||
@@ -852,5 +853,5 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
|
||||
|
||||
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
|
||||
// it's probably at least a draw even with the pawn.
|
||||
return Bitbases::probe_kpk(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
|
||||
return Bitbases::probe(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -45,7 +45,7 @@ enum EndgameType {
|
||||
|
||||
|
||||
// Scaling functions
|
||||
SCALE_FUNS,
|
||||
SCALING_FUNCTIONS,
|
||||
|
||||
KBPsK, // KB and pawns vs K
|
||||
KQKRPs, // KQ vs KR and pawns
|
||||
@@ -76,26 +76,26 @@ template<typename T>
|
||||
struct EndgameBase {
|
||||
|
||||
virtual ~EndgameBase() {}
|
||||
virtual Color color() const = 0;
|
||||
virtual Color strong_side() const = 0;
|
||||
virtual T operator()(const Position&) const = 0;
|
||||
};
|
||||
|
||||
|
||||
template<EndgameType E, typename T = typename eg_fun<(E > SCALE_FUNS)>::type>
|
||||
template<EndgameType E, typename T = typename eg_fun<(E > SCALING_FUNCTIONS)>::type>
|
||||
struct Endgame : public EndgameBase<T> {
|
||||
|
||||
explicit Endgame(Color c) : strongSide(c), weakSide(~c) {}
|
||||
Color color() const { return strongSide; }
|
||||
Color strong_side() const { return strongSide; }
|
||||
T operator()(const Position&) const;
|
||||
|
||||
private:
|
||||
const Color strongSide, weakSide;
|
||||
Color strongSide, weakSide;
|
||||
};
|
||||
|
||||
|
||||
/// The Endgames class stores the pointers to endgame evaluation and scaling
|
||||
/// base objects in two std::map typedefs. We then use polymorphism to invoke
|
||||
/// the actual endgame function by calling its virtual operator().
|
||||
/// base objects in two std::map. We use polymorphism to invoke the actual
|
||||
/// endgame function by calling its virtual operator().
|
||||
|
||||
class Endgames {
|
||||
|
||||
@@ -114,8 +114,9 @@ public:
|
||||
Endgames();
|
||||
~Endgames();
|
||||
|
||||
template<typename T> T probe(Key key, T& eg)
|
||||
{ return eg = map(eg).count(key) ? map(eg)[key] : NULL; }
|
||||
template<typename T> T probe(Key key, T& eg) {
|
||||
return eg = map(eg).count(key) ? map(eg)[key] : NULL;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // #ifndef ENDGAME_H_INCLUDED
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring> // For std::memset
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
@@ -26,8 +27,6 @@
|
||||
#include "evaluate.h"
|
||||
#include "material.h"
|
||||
#include "pawns.h"
|
||||
#include "thread.h"
|
||||
#include "ucioption.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -58,9 +57,8 @@ namespace {
|
||||
|
||||
// kingAttackersWeight[color] is the sum of the "weight" of the pieces of the
|
||||
// given color which attack a square in the kingRing of the enemy king. The
|
||||
// weights of the individual piece types are given by the variables
|
||||
// QueenAttackWeight, RookAttackWeight, BishopAttackWeight and
|
||||
// KnightAttackWeight in evaluate.cpp
|
||||
// weights of the individual piece types are given by the elements in the
|
||||
// KingAttackWeights array.
|
||||
int kingAttackersWeight[COLOR_NB];
|
||||
|
||||
// kingAdjacentZoneAttacksCount[color] is the number of attacks to squares
|
||||
@@ -79,23 +77,24 @@ namespace {
|
||||
MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERMS_NB
|
||||
};
|
||||
|
||||
Score terms[COLOR_NB][TERMS_NB];
|
||||
Score scores[COLOR_NB][TERMS_NB];
|
||||
EvalInfo ei;
|
||||
ScaleFactor sf;
|
||||
|
||||
double to_cp(Value v);
|
||||
void add_term(int idx, Score term_w, Score term_b = SCORE_ZERO);
|
||||
void format_row(std::stringstream& ss, const char* name, int idx);
|
||||
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}, {318, 0}
|
||||
{289, 344}, {233, 201}, {221, 273}, {46, 0}, {321, 0}
|
||||
};
|
||||
|
||||
typedef Value V;
|
||||
#define V(v) Value(v)
|
||||
#define S(mg, eg) make_score(mg, eg)
|
||||
|
||||
// MobilityBonus[PieceType][attacked] contains bonuses for middle and end
|
||||
@@ -137,30 +136,32 @@ namespace {
|
||||
V(0), V(5), V(8), V(8), V(8), V(8), V(5), V(0) }
|
||||
};
|
||||
|
||||
// Threat[attacking][attacked] contains bonuses according to which piece
|
||||
// type attacks which one.
|
||||
const Score Threat[][PIECE_TYPE_NB] = {
|
||||
{ S(0, 0), S( 7, 39), S(24, 49), S(24, 49), S(41,100), S(41,100) }, // Minor
|
||||
{ S(0, 0), S(15, 39), S(15, 45), S(15, 45), S(15, 45), S(24, 49) } // Major
|
||||
// Threat[defended/weak][minor/major attacking][attacked PieceType] contains
|
||||
// bonuses according to which piece type attacks which one.
|
||||
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( 9, 14), S( 9, 14), S( 7, 14), S(24, 48) } }, // Defended Major
|
||||
{ { 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,27), S(26, 57), S(26, 57), S(0 , 43), S(23, 51) } } // Weak Major
|
||||
};
|
||||
|
||||
// ThreatenedByPawn[PieceType] contains a penalty according to which piece
|
||||
// type is attacked by an enemy pawn.
|
||||
const Score ThreatenedByPawn[] = {
|
||||
S(0, 0), S(0, 0), S(80, 119), S(80, 119), S(117, 199), S(127, 218)
|
||||
S(0, 0), S(0, 0), S(87, 118), S(84, 122), S(114, 203), S(121, 217)
|
||||
};
|
||||
|
||||
// Assorted bonuses and penalties used by evaluation
|
||||
const Score KingOnOne = S(2 , 58);
|
||||
const Score KingOnMany = S(6 ,125);
|
||||
const Score RookOnPawn = S(10, 28);
|
||||
const Score RookOpenFile = S(43, 21);
|
||||
const Score RookSemiOpenFile = S(19, 10);
|
||||
const Score BishopPawns = S( 8, 12);
|
||||
const Score MinorBehindPawn = S(16, 0);
|
||||
const Score TrappedRook = S(92, 0);
|
||||
const Score Unstoppable = S( 0, 20);
|
||||
const Score Hanging = S(23, 20);
|
||||
const Score KingOnOne = S( 2, 58);
|
||||
const Score KingOnMany = S( 6,125);
|
||||
const Score RookOnPawn = S( 7, 27);
|
||||
const Score RookOnOpenFile = S(43, 21);
|
||||
const Score RookOnSemiOpenFile = S(19, 10);
|
||||
const Score BishopPawns = S( 8, 12);
|
||||
const Score MinorBehindPawn = S(16, 0);
|
||||
const Score TrappedRook = S(92, 0);
|
||||
const Score Unstoppable = S( 0, 20);
|
||||
const Score Hanging = S(31, 26);
|
||||
|
||||
// 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
|
||||
@@ -168,6 +169,7 @@ namespace {
|
||||
const Score TrappedBishopA1H1 = S(50, 50);
|
||||
|
||||
#undef S
|
||||
#undef V
|
||||
|
||||
// SpaceMask[Color] contains the area of the board which is considered
|
||||
// by the space evaluation. In the middlegame, each side is given a bonus
|
||||
@@ -178,31 +180,29 @@ namespace {
|
||||
(FileCBB | FileDBB | FileEBB | FileFBB) & (Rank7BB | Rank6BB | Rank5BB)
|
||||
};
|
||||
|
||||
// King danger constants and variables. The king danger scores are taken
|
||||
// from KingDanger[]. Various little "meta-bonuses" measuring the strength
|
||||
// King danger constants and variables. The king danger scores are looked-up
|
||||
// in KingDanger[]. Various little "meta-bonuses" measuring the strength
|
||||
// of the enemy attack are added up into an integer, which is used as an
|
||||
// index to KingDanger[].
|
||||
//
|
||||
// KingAttackWeights[PieceType] contains king attack weights by piece type
|
||||
const int KingAttackWeights[] = { 0, 0, 2, 2, 3, 5 };
|
||||
const int KingAttackWeights[] = { 0, 0, 6, 2, 5, 5 };
|
||||
|
||||
// Bonuses for enemy's safe checks
|
||||
const int QueenContactCheck = 24;
|
||||
const int RookContactCheck = 16;
|
||||
const int QueenCheck = 12;
|
||||
const int RookCheck = 8;
|
||||
const int BishopCheck = 2;
|
||||
const int KnightCheck = 3;
|
||||
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[128];
|
||||
Score KingDanger[512];
|
||||
|
||||
const int ScalePawnSpan[2] = { 38, 56 };
|
||||
|
||||
// apply_weight() weighs score 'v' by weight 'w' trying to prevent overflow
|
||||
Score apply_weight(Score v, const Weight& w) {
|
||||
return make_score(mg_value(v) * w.mg / 256, eg_value(v) * w.eg / 256);
|
||||
// apply_weight() weighs score 's' by weight 'w' trying to prevent overflow
|
||||
Score apply_weight(Score s, const Weight& w) {
|
||||
return make_score(mg_value(s) * w.mg / 256, eg_value(s) * w.eg / 256);
|
||||
}
|
||||
|
||||
|
||||
@@ -221,7 +221,7 @@ namespace {
|
||||
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
|
||||
if (pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg)
|
||||
if (pos.non_pawn_material(Us) >= QueenValueMg)
|
||||
{
|
||||
ei.kingRing[Them] = b | shift_bb<Down>(b);
|
||||
b &= ei.attackedBy[Us][PAWN];
|
||||
@@ -313,65 +313,65 @@ namespace {
|
||||
|
||||
if (Pt == BISHOP || Pt == KNIGHT)
|
||||
{
|
||||
// Penalty for bishop with same colored pawns
|
||||
if (Pt == BISHOP)
|
||||
score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s);
|
||||
|
||||
// Bishop and knight outpost square
|
||||
// Bonus for outpost square
|
||||
if (!(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s)))
|
||||
score += evaluate_outpost<Pt, Us>(pos, ei, s);
|
||||
|
||||
// Bishop or knight behind a pawn
|
||||
// Bonus when behind a pawn
|
||||
if ( relative_rank(Us, s) < RANK_5
|
||||
&& (pos.pieces(PAWN) & (s + pawn_push(Us))))
|
||||
score += MinorBehindPawn;
|
||||
|
||||
// Penalty for pawns on same color square of bishop
|
||||
if (Pt == BISHOP)
|
||||
score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s);
|
||||
|
||||
// An important Chess960 pattern: A cornered bishop blocked by a friendly
|
||||
// pawn diagonally in front of it is a very serious problem, especially
|
||||
// when that pawn is also blocked.
|
||||
if ( Pt == BISHOP
|
||||
&& pos.is_chess960()
|
||||
&& (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
|
||||
{
|
||||
Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W);
|
||||
if (pos.piece_on(s + d) == make_piece(Us, PAWN))
|
||||
score -= !pos.empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4
|
||||
: pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? TrappedBishopA1H1 * 2
|
||||
: TrappedBishopA1H1;
|
||||
}
|
||||
}
|
||||
|
||||
if (Pt == ROOK)
|
||||
{
|
||||
// Rook piece attacking enemy pawns on the same rank/file
|
||||
// Bonus for aligning with enemy pawns on the same rank/file
|
||||
if (relative_rank(Us, s) >= RANK_5)
|
||||
{
|
||||
Bitboard pawns = pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s];
|
||||
if (pawns)
|
||||
score += popcount<Max15>(pawns) * RookOnPawn;
|
||||
Bitboard alignedPawns = pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s];
|
||||
if (alignedPawns)
|
||||
score += popcount<Max15>(alignedPawns) * RookOnPawn;
|
||||
}
|
||||
|
||||
// Give a bonus for a rook on a open or semi-open file
|
||||
// Bonus when on an open or semi-open file
|
||||
if (ei.pi->semiopen_file(Us, file_of(s)))
|
||||
score += ei.pi->semiopen_file(Them, file_of(s)) ? RookOpenFile : RookSemiOpenFile;
|
||||
score += ei.pi->semiopen_file(Them, file_of(s)) ? RookOnOpenFile : RookOnSemiOpenFile;
|
||||
|
||||
if (mob > 3 || ei.pi->semiopen_file(Us, file_of(s)))
|
||||
continue;
|
||||
// Penalize when trapped by the king, even more if king cannot castle
|
||||
if (mob <= 3 && !ei.pi->semiopen_file(Us, file_of(s)))
|
||||
{
|
||||
Square ksq = pos.king_square(Us);
|
||||
|
||||
Square ksq = pos.king_square(Us);
|
||||
|
||||
// Penalize rooks which are trapped by a king. Penalize more if the
|
||||
// king has lost its castling capability.
|
||||
if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq)))
|
||||
&& (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1)
|
||||
&& !ei.pi->semiopen_side(Us, file_of(ksq), file_of(s) < file_of(ksq)))
|
||||
score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.can_castle(Us));
|
||||
}
|
||||
|
||||
// An important Chess960 pattern: A cornered bishop blocked by a friendly
|
||||
// pawn diagonally in front of it is a very serious problem, especially
|
||||
// when that pawn is also blocked.
|
||||
if ( Pt == BISHOP
|
||||
&& pos.is_chess960()
|
||||
&& (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
|
||||
{
|
||||
Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W);
|
||||
if (pos.piece_on(s + d) == make_piece(Us, PAWN))
|
||||
score -= !pos.empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4
|
||||
: pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? TrappedBishopA1H1 * 2
|
||||
: TrappedBishopA1H1;
|
||||
if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq)))
|
||||
&& (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1)
|
||||
&& !ei.pi->semiopen_side(Us, file_of(ksq), file_of(s) < file_of(ksq)))
|
||||
score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.can_castle(Us));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Trace)
|
||||
Tracing::terms[Us][Pt] = score;
|
||||
Tracing::write(Pt, Us, score);
|
||||
|
||||
// Recursively call evaluate_pieces() of next piece type until KING excluded
|
||||
return score - evaluate_pieces<NextPt, Them, Trace>(pos, ei, mobility, mobilityArea);
|
||||
}
|
||||
|
||||
@@ -407,33 +407,32 @@ namespace {
|
||||
| ei.attackedBy[Us][QUEEN]);
|
||||
|
||||
// Initialize the 'attackUnits' variable, which is used later on as an
|
||||
// index to the KingDanger[] array. The initial value is based on the
|
||||
// index into the KingDanger[] array. The initial value is based on the
|
||||
// number and types of the enemy's attacking pieces, the number of
|
||||
// attacked and undefended squares around our king and the quality of
|
||||
// the pawn shelter (current 'score' value).
|
||||
attackUnits = std::min(20, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
|
||||
+ 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount<Max15>(undefended))
|
||||
+ 2 * (ei.pinnedPieces[Us] != 0)
|
||||
- mg_value(score) / 32
|
||||
- !pos.count<QUEEN>(Them) * 15;
|
||||
attackUnits = std::min(77, ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them])
|
||||
+ 10 * ei.kingAdjacentZoneAttacksCount[Them]
|
||||
+ 19 * popcount<Max15>(undefended)
|
||||
+ 9 * (ei.pinnedPieces[Us] != 0)
|
||||
- mg_value(score) * 63 / 512
|
||||
- !pos.count<QUEEN>(Them) * 60;
|
||||
|
||||
// Analyse the enemy's safe queen contact checks. Firstly, find the
|
||||
// undefended squares around the king that are attacked by the enemy's
|
||||
// queen...
|
||||
// undefended squares around the king reachable by the enemy queen...
|
||||
b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces(Them);
|
||||
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][ROOK]);
|
||||
b &= ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT]
|
||||
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK];
|
||||
|
||||
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 that are attacked by the enemy's
|
||||
// rooks...
|
||||
// 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
|
||||
@@ -446,13 +445,13 @@ namespace {
|
||||
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]);
|
||||
|
||||
if (b)
|
||||
attackUnits += RookContactCheck * popcount<Max15>(b);
|
||||
attackUnits += RookContactCheck * popcount<Max15>(b);
|
||||
}
|
||||
|
||||
// Analyse the enemy's safe distance checks for sliders and knights
|
||||
safe = ~(pos.pieces(Them) | ei.attackedBy[Us][ALL_PIECES]);
|
||||
safe = ~(ei.attackedBy[Us][ALL_PIECES] | pos.pieces(Them));
|
||||
|
||||
b1 = pos.attacks_from<ROOK>(ksq) & safe;
|
||||
b1 = pos.attacks_from<ROOK >(ksq) & safe;
|
||||
b2 = pos.attacks_from<BISHOP>(ksq) & safe;
|
||||
|
||||
// Enemy queen safe checks
|
||||
@@ -475,16 +474,13 @@ namespace {
|
||||
if (b)
|
||||
attackUnits += KnightCheck * popcount<Max15>(b);
|
||||
|
||||
// To index KingDanger[] attackUnits must be in [0, 99] range
|
||||
attackUnits = std::min(99, std::max(0, attackUnits));
|
||||
|
||||
// Finally, extract the king danger score from the KingDanger[]
|
||||
// array and subtract the score from evaluation.
|
||||
score -= KingDanger[attackUnits];
|
||||
score -= KingDanger[std::max(std::min(attackUnits, 399), 0)];
|
||||
}
|
||||
|
||||
if (Trace)
|
||||
Tracing::terms[Us][KING] = score;
|
||||
Tracing::write(KING, Us, score);
|
||||
|
||||
return score;
|
||||
}
|
||||
@@ -498,45 +494,55 @@ namespace {
|
||||
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
|
||||
Bitboard b, weakEnemies, protectedEnemies;
|
||||
Score score = SCORE_ZERO;
|
||||
enum { Defended, Weak };
|
||||
enum { Minor, Major };
|
||||
|
||||
// Protected enemies
|
||||
protectedEnemies = (pos.pieces(Them) ^ pos.pieces(Them,PAWN))
|
||||
& ei.attackedBy[Them][PAWN]
|
||||
& (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]);
|
||||
Bitboard b, weak, defended;
|
||||
Score score = SCORE_ZERO;
|
||||
|
||||
if (protectedEnemies)
|
||||
score += Threat[Minor][type_of(pos.piece_on(lsb(protectedEnemies)))];
|
||||
// Non-pawn enemies defended by a pawn
|
||||
defended = (pos.pieces(Them) ^ pos.pieces(Them, PAWN))
|
||||
& ei.attackedBy[Them][PAWN];
|
||||
|
||||
// Add a bonus according to the kind of attacking pieces
|
||||
if (defended)
|
||||
{
|
||||
b = defended & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]);
|
||||
while (b)
|
||||
score += Threat[Defended][Minor][type_of(pos.piece_on(pop_lsb(&b)))];
|
||||
|
||||
b = defended & (ei.attackedBy[Us][ROOK]);
|
||||
while (b)
|
||||
score += Threat[Defended][Major][type_of(pos.piece_on(pop_lsb(&b)))];
|
||||
}
|
||||
|
||||
// Enemies not defended by a pawn and under our attack
|
||||
weakEnemies = pos.pieces(Them)
|
||||
& ~ei.attackedBy[Them][PAWN]
|
||||
& ei.attackedBy[Us][ALL_PIECES];
|
||||
weak = pos.pieces(Them)
|
||||
& ~ei.attackedBy[Them][PAWN]
|
||||
& ei.attackedBy[Us][ALL_PIECES];
|
||||
|
||||
// Add a bonus according if the attacking pieces are minor or major
|
||||
if (weakEnemies)
|
||||
// Add a bonus according to the kind of attacking pieces
|
||||
if (weak)
|
||||
{
|
||||
b = weakEnemies & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]);
|
||||
if (b)
|
||||
score += Threat[Minor][type_of(pos.piece_on(lsb(b)))];
|
||||
b = weak & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]);
|
||||
while (b)
|
||||
score += Threat[Weak][Minor][type_of(pos.piece_on(pop_lsb(&b)))];
|
||||
|
||||
b = weakEnemies & (ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][QUEEN]);
|
||||
if (b)
|
||||
score += Threat[Major][type_of(pos.piece_on(lsb(b)))];
|
||||
b = weak & (ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][QUEEN]);
|
||||
while (b)
|
||||
score += Threat[Weak][Major][type_of(pos.piece_on(pop_lsb(&b)))];
|
||||
|
||||
b = weakEnemies & ~ei.attackedBy[Them][ALL_PIECES];
|
||||
b = weak & ~ei.attackedBy[Them][ALL_PIECES];
|
||||
if (b)
|
||||
score += more_than_one(b) ? Hanging * popcount<Max15>(b) : Hanging;
|
||||
score += Hanging * popcount<Max15>(b);
|
||||
|
||||
b = weakEnemies & ei.attackedBy[Us][KING];
|
||||
b = weak & ei.attackedBy[Us][KING];
|
||||
if (b)
|
||||
score += more_than_one(b) ? KingOnMany : KingOnOne;
|
||||
}
|
||||
|
||||
if (Trace)
|
||||
Tracing::terms[Us][Tracing::THREAT] = score;
|
||||
Tracing::write(Tracing::THREAT, Us, score);
|
||||
|
||||
return score;
|
||||
}
|
||||
@@ -571,12 +577,12 @@ namespace {
|
||||
Square blockSq = s + pawn_push(Us);
|
||||
|
||||
// Adjust bonus based on the king's proximity
|
||||
ebonus += square_distance(pos.king_square(Them), blockSq) * 5 * rr
|
||||
- square_distance(pos.king_square(Us ), blockSq) * 2 * rr;
|
||||
ebonus += distance(pos.king_square(Them), blockSq) * 5 * rr
|
||||
- distance(pos.king_square(Us ), blockSq) * 2 * rr;
|
||||
|
||||
// If blockSq is not the queening square then consider also a second push
|
||||
if (relative_rank(Us, blockSq) != RANK_8)
|
||||
ebonus -= square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr;
|
||||
ebonus -= distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr;
|
||||
|
||||
// If the pawn is free to advance, then increase the bonus
|
||||
if (pos.empty(blockSq))
|
||||
@@ -619,33 +625,21 @@ namespace {
|
||||
}
|
||||
|
||||
if (Trace)
|
||||
Tracing::terms[Us][Tracing::PASSED] = apply_weight(score, Weights[PassedPawns]);
|
||||
Tracing::write(Tracing::PASSED, Us, apply_weight(score, Weights[PassedPawns]));
|
||||
|
||||
// Add the scores to the middlegame and endgame eval
|
||||
return apply_weight(score, Weights[PassedPawns]);
|
||||
}
|
||||
|
||||
|
||||
// evaluate_unstoppable_pawns() scores the most advanced passed pawn. In case
|
||||
// both players have no pieces but pawns, this is somewhat related to the
|
||||
// possibility that pawns are unstoppable.
|
||||
|
||||
Score evaluate_unstoppable_pawns(Color us, const EvalInfo& ei) {
|
||||
|
||||
Bitboard b = ei.pi->passed_pawns(us);
|
||||
|
||||
return b ? Unstoppable * int(relative_rank(us, frontmost_sq(us, b))) : SCORE_ZERO;
|
||||
}
|
||||
|
||||
|
||||
// evaluate_space() computes the space evaluation for a given side. The
|
||||
// space evaluation is a simple bonus based on the number of safe squares
|
||||
// available for minor pieces on the central four files on ranks 2--4. Safe
|
||||
// squares one, two or three squares behind a friendly pawn are counted
|
||||
// twice. Finally, the space bonus is scaled by a weight taken from the
|
||||
// material hash table. The aim is to improve play on game opening.
|
||||
// twice. Finally, the space bonus is multiplied by a weight. The aim is to
|
||||
// improve play on game opening.
|
||||
template<Color Us>
|
||||
int evaluate_space(const Position& pos, const EvalInfo& ei) {
|
||||
Score evaluate_space(const Position& pos, const EvalInfo& ei) {
|
||||
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
|
||||
@@ -666,7 +660,11 @@ namespace {
|
||||
assert(unsigned(safe >> (Us == WHITE ? 32 : 0)) == 0);
|
||||
|
||||
// Count safe + (behind & safe) with a single popcount
|
||||
return 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)
|
||||
+ pos.count<KNIGHT>(Them) + pos.count<BISHOP>(Them);
|
||||
|
||||
return make_score(bonus * weight * weight, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -679,7 +677,6 @@ namespace {
|
||||
|
||||
EvalInfo ei;
|
||||
Score score, mobility[2] = { SCORE_ZERO, SCORE_ZERO };
|
||||
Thread* thisThread = pos.this_thread();
|
||||
|
||||
// Initialize score by reading the incrementally updated scores included
|
||||
// in the position object (material + piece square tables).
|
||||
@@ -687,8 +684,8 @@ namespace {
|
||||
score = pos.psq_score();
|
||||
|
||||
// Probe the material hash table
|
||||
ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames);
|
||||
score += ei.mi->material_value();
|
||||
ei.mi = Material::probe(pos);
|
||||
score += ei.mi->imbalance();
|
||||
|
||||
// If we have a specialized evaluation function for the current material
|
||||
// configuration, call it and return.
|
||||
@@ -696,8 +693,8 @@ namespace {
|
||||
return ei.mi->evaluate(pos);
|
||||
|
||||
// Probe the pawn hash table
|
||||
ei.pi = Pawns::probe(pos, thisThread->pawnsTable);
|
||||
score += apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]);
|
||||
ei.pi = Pawns::probe(pos);
|
||||
score += apply_weight(ei.pi->pawns_score(), Weights[PawnStructure]);
|
||||
|
||||
// Initialize attack and king safety bitboards
|
||||
init_eval_info<WHITE>(pos, ei);
|
||||
@@ -729,14 +726,20 @@ namespace {
|
||||
|
||||
// If both sides have only pawns, score for potential unstoppable pawns
|
||||
if (!pos.non_pawn_material(WHITE) && !pos.non_pawn_material(BLACK))
|
||||
score += evaluate_unstoppable_pawns(WHITE, ei)
|
||||
- evaluate_unstoppable_pawns(BLACK, ei);
|
||||
|
||||
// Evaluate space for both sides, only in middlegame
|
||||
if (ei.mi->space_weight())
|
||||
{
|
||||
int s = evaluate_space<WHITE>(pos, ei) - evaluate_space<BLACK>(pos, ei);
|
||||
score += apply_weight(s * ei.mi->space_weight(), Weights[Space]);
|
||||
Bitboard b;
|
||||
if ((b = ei.pi->passed_pawns(WHITE)) != 0)
|
||||
score += int(relative_rank(WHITE, frontmost_sq(WHITE, b))) * Unstoppable;
|
||||
|
||||
if ((b = ei.pi->passed_pawns(BLACK)) != 0)
|
||||
score -= int(relative_rank(BLACK, frontmost_sq(BLACK, b))) * Unstoppable;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
Score s = evaluate_space<WHITE>(pos, ei) - evaluate_space<BLACK>(pos, ei);
|
||||
score += apply_weight(s, Weights[Space]);
|
||||
}
|
||||
|
||||
// Scale winning side if position is more drawish than it appears
|
||||
@@ -748,27 +751,25 @@ namespace {
|
||||
if ( ei.mi->game_phase() < PHASE_MIDGAME
|
||||
&& (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN))
|
||||
{
|
||||
if (pos.opposite_bishops()) {
|
||||
// Ignoring any pawns, do both sides only have a single bishop and no
|
||||
// other pieces?
|
||||
if (pos.opposite_bishops())
|
||||
{
|
||||
// Endgame with opposite-colored bishops and no other pieces (ignoring pawns)
|
||||
// is almost a draw, in case of KBP vs KB is even more a draw.
|
||||
if ( pos.non_pawn_material(WHITE) == BishopValueMg
|
||||
&& pos.non_pawn_material(BLACK) == BishopValueMg)
|
||||
{
|
||||
// Check for KBP vs KB with only a single pawn that is almost
|
||||
// certainly a draw or at least two pawns.
|
||||
bool one_pawn = (pos.count<PAWN>(WHITE) + pos.count<PAWN>(BLACK) == 1);
|
||||
sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32);
|
||||
}
|
||||
sf = more_than_one(pos.pieces(PAWN)) ? ScaleFactor(32) : ScaleFactor(8);
|
||||
|
||||
// Endgame with opposite-colored bishops, but also other pieces. Still
|
||||
// a bit drawish, but not as drawish as with only the two bishops.
|
||||
else
|
||||
// Endgame with opposite-colored bishops, but also other pieces. Still
|
||||
// a bit drawish, but not as drawish as with only the two bishops.
|
||||
sf = ScaleFactor(50 * sf / SCALE_FACTOR_NORMAL);
|
||||
} else if ( abs(eg_value(score)) <= BishopValueEg
|
||||
&& ei.pi->pawn_span(strongSide) <= 1
|
||||
&& !pos.pawn_passed(~strongSide, pos.king_square(~strongSide))) {
|
||||
// Endings where weaker side can be place his king in front of the opponent's pawns are drawish.
|
||||
sf = ScaleFactor(ScalePawnSpan[ei.pi->pawn_span(strongSide)]);
|
||||
}
|
||||
// Endings where weaker side can place his king in front of the opponent's
|
||||
// pawns are drawish.
|
||||
else if ( abs(eg_value(score)) <= BishopValueEg
|
||||
&& ei.pi->pawn_span(strongSide) <= 1
|
||||
&& !pos.pawn_passed(~strongSide, pos.king_square(~strongSide)))
|
||||
sf = ei.pi->pawn_span(strongSide) ? ScaleFactor(56) : ScaleFactor(38);
|
||||
}
|
||||
|
||||
// Interpolate between a middlegame and a (scaled by 'sf') endgame score
|
||||
@@ -780,20 +781,19 @@ namespace {
|
||||
// In case of tracing add all single evaluation contributions for both white and black
|
||||
if (Trace)
|
||||
{
|
||||
Tracing::add_term(Tracing::MATERIAL, pos.psq_score());
|
||||
Tracing::add_term(Tracing::IMBALANCE, ei.mi->material_value());
|
||||
Tracing::add_term(PAWN, ei.pi->pawns_value());
|
||||
Tracing::add_term(Tracing::MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility])
|
||||
, apply_weight(mobility[BLACK], Weights[Mobility]));
|
||||
Score w = ei.mi->space_weight() * evaluate_space<WHITE>(pos, ei);
|
||||
Score b = ei.mi->space_weight() * evaluate_space<BLACK>(pos, ei);
|
||||
Tracing::add_term(Tracing::SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space]));
|
||||
Tracing::add_term(Tracing::TOTAL, score);
|
||||
Tracing::write(Tracing::MATERIAL, pos.psq_score());
|
||||
Tracing::write(Tracing::IMBALANCE, ei.mi->imbalance());
|
||||
Tracing::write(PAWN, ei.pi->pawns_score());
|
||||
Tracing::write(Tracing::MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility])
|
||||
, apply_weight(mobility[BLACK], Weights[Mobility]));
|
||||
Tracing::write(Tracing::SPACE, apply_weight(evaluate_space<WHITE>(pos, ei), Weights[Space])
|
||||
, apply_weight(evaluate_space<BLACK>(pos, ei), Weights[Space]));
|
||||
Tracing::write(Tracing::TOTAL, score);
|
||||
Tracing::ei = ei;
|
||||
Tracing::sf = sf;
|
||||
}
|
||||
|
||||
return pos.side_to_move() == WHITE ? v : -v;
|
||||
return (pos.side_to_move() == WHITE ? v : -v) + Eval::Tempo;
|
||||
}
|
||||
|
||||
|
||||
@@ -801,16 +801,18 @@ namespace {
|
||||
|
||||
double Tracing::to_cp(Value v) { return double(v) / PawnValueEg; }
|
||||
|
||||
void Tracing::add_term(int idx, Score wScore, Score bScore) {
|
||||
void Tracing::write(int idx, Color c, Score s) { scores[c][idx] = s; }
|
||||
|
||||
terms[WHITE][idx] = wScore;
|
||||
terms[BLACK][idx] = bScore;
|
||||
void Tracing::write(int idx, Score w, Score b) {
|
||||
|
||||
write(idx, WHITE, w);
|
||||
write(idx, BLACK, b);
|
||||
}
|
||||
|
||||
void Tracing::format_row(std::stringstream& ss, const char* name, int idx) {
|
||||
void Tracing::print(std::stringstream& ss, const char* name, int idx) {
|
||||
|
||||
Score wScore = terms[WHITE][idx];
|
||||
Score bScore = terms[BLACK][idx];
|
||||
Score wScore = scores[WHITE][idx];
|
||||
Score bScore = scores[BLACK][idx];
|
||||
|
||||
switch (idx) {
|
||||
case MATERIAL: case IMBALANCE: case PAWN: case TOTAL:
|
||||
@@ -831,7 +833,7 @@ namespace {
|
||||
|
||||
std::string Tracing::do_trace(const Position& pos) {
|
||||
|
||||
std::memset(terms, 0, sizeof(terms));
|
||||
std::memset(scores, 0, sizeof(scores));
|
||||
|
||||
Value v = do_evaluate<true>(pos);
|
||||
v = pos.side_to_move() == WHITE ? v : -v; // White's point of view
|
||||
@@ -842,21 +844,21 @@ namespace {
|
||||
<< " | MG EG | MG EG | MG EG \n"
|
||||
<< "----------------+-------------+-------------+-------------\n";
|
||||
|
||||
format_row(ss, "Material", MATERIAL);
|
||||
format_row(ss, "Imbalance", IMBALANCE);
|
||||
format_row(ss, "Pawns", PAWN);
|
||||
format_row(ss, "Knights", KNIGHT);
|
||||
format_row(ss, "Bishops", BISHOP);
|
||||
format_row(ss, "Rooks", ROOK);
|
||||
format_row(ss, "Queens", QUEEN);
|
||||
format_row(ss, "Mobility", MOBILITY);
|
||||
format_row(ss, "King safety", KING);
|
||||
format_row(ss, "Threats", THREAT);
|
||||
format_row(ss, "Passed pawns", PASSED);
|
||||
format_row(ss, "Space", SPACE);
|
||||
print(ss, "Material", MATERIAL);
|
||||
print(ss, "Imbalance", IMBALANCE);
|
||||
print(ss, "Pawns", PAWN);
|
||||
print(ss, "Knights", KNIGHT);
|
||||
print(ss, "Bishops", BISHOP);
|
||||
print(ss, "Rooks", ROOK);
|
||||
print(ss, "Queens", QUEEN);
|
||||
print(ss, "Mobility", MOBILITY);
|
||||
print(ss, "King safety", KING);
|
||||
print(ss, "Threats", THREAT);
|
||||
print(ss, "Passed pawns", PASSED);
|
||||
print(ss, "Space", SPACE);
|
||||
|
||||
ss << "----------------+-------------+-------------+-------------\n";
|
||||
format_row(ss, "Total", TOTAL);
|
||||
print(ss, "Total", TOTAL);
|
||||
|
||||
ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n";
|
||||
|
||||
@@ -872,7 +874,7 @@ namespace Eval {
|
||||
/// of the position always from the point of view of the side to move.
|
||||
|
||||
Value evaluate(const Position& pos) {
|
||||
return do_evaluate<false>(pos) + Tempo;
|
||||
return do_evaluate<false>(pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -885,18 +887,18 @@ namespace Eval {
|
||||
}
|
||||
|
||||
|
||||
/// init() computes evaluation weights from the corresponding UCI parameters
|
||||
/// and setup king tables.
|
||||
/// init() computes evaluation weights, usually at startup
|
||||
|
||||
void init() {
|
||||
|
||||
const double MaxSlope = 30;
|
||||
const double MaxSlope = 7.5;
|
||||
const double Peak = 1280;
|
||||
double t = 0.0;
|
||||
|
||||
for (int t = 0, i = 1; i < 100; ++i)
|
||||
for (int i = 1; i < 400; ++i)
|
||||
{
|
||||
t = int(std::min(Peak, std::min(0.4 * i * i, t + MaxSlope)));
|
||||
KingDanger[i] = apply_weight(make_score(t, 0), Weights[KingSafety]);
|
||||
t = std::min(Peak, std::min(0.025 * i * i, t + MaxSlope));
|
||||
KingDanger[i] = apply_weight(make_score(int(t), 0), Weights[KingSafety]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -20,6 +20,8 @@
|
||||
#ifndef EVALUATE_H_INCLUDED
|
||||
#define EVALUATE_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
class Position;
|
||||
@@ -28,9 +30,9 @@ namespace Eval {
|
||||
|
||||
const Value Tempo = Value(17); // Must be visible to search
|
||||
|
||||
extern void init();
|
||||
extern Value evaluate(const Position& pos);
|
||||
extern std::string trace(const Position& pos);
|
||||
void init();
|
||||
Value evaluate(const Position& pos);
|
||||
std::string trace(const Position& pos);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -23,10 +23,10 @@
|
||||
#include "evaluate.h"
|
||||
#include "position.h"
|
||||
#include "search.h"
|
||||
#include "tbprobe.h"
|
||||
#include "thread.h"
|
||||
#include "tt.h"
|
||||
#include "ucioption.h"
|
||||
#include "uci.h"
|
||||
#include "syzygy/tbprobe.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
@@ -35,12 +35,13 @@ int main(int argc, char* argv[]) {
|
||||
UCI::init(Options);
|
||||
Bitboards::init();
|
||||
Position::init();
|
||||
Bitbases::init_kpk();
|
||||
Bitbases::init();
|
||||
Search::init();
|
||||
Eval::init();
|
||||
Pawns::init();
|
||||
Threads.init();
|
||||
TT.resize(Options["Hash"]);
|
||||
Tablebases::init(Options["SyzygyPath"]);
|
||||
TT.resize(Options["Hash"]);
|
||||
|
||||
UCI::loop(argc, argv);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -17,33 +17,34 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm> // For std::min
|
||||
#include <algorithm> // For std::min
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <cstring> // For std::memset
|
||||
|
||||
#include "material.h"
|
||||
#include "thread.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
|
||||
// Polynomial material balance parameters
|
||||
// Polynomial material imbalance parameters
|
||||
|
||||
// pair pawn knight bishop rook queen
|
||||
const int Linear[6] = { 1852, -162, -1122, -183, 249, -154 };
|
||||
|
||||
const int QuadraticSameSide[][PIECE_TYPE_NB] = {
|
||||
const int QuadraticOurs[][PIECE_TYPE_NB] = {
|
||||
// OUR PIECES
|
||||
// pair pawn knight bishop rook queen
|
||||
{ 0 }, // Bishop pair
|
||||
{ 39, 2 }, // Pawn
|
||||
{ 35, 271, -4 }, // knight OUR PIECES
|
||||
{ 35, 271, -4 }, // Knight OUR PIECES
|
||||
{ 0, 105, 4, 0 }, // Bishop
|
||||
{ -27, -2, 46, 100, -141 }, // Rook
|
||||
{-177, 25, 129, 142, -137, 0 } // Queen
|
||||
};
|
||||
|
||||
const int QuadraticOppositeSide[][PIECE_TYPE_NB] = {
|
||||
const int QuadraticTheirs[][PIECE_TYPE_NB] = {
|
||||
// THEIR PIECES
|
||||
// pair pawn knight bishop rook queen
|
||||
{ 0 }, // Bishop pair
|
||||
@@ -56,7 +57,7 @@ namespace {
|
||||
|
||||
// Endgame evaluation and scaling functions are accessed directly and not through
|
||||
// the function maps because they correspond to more than one material hash key.
|
||||
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
|
||||
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
|
||||
|
||||
Endgame<KBPsK> ScaleKBPsK[] = { Endgame<KBPsK>(WHITE), Endgame<KBPsK>(BLACK) };
|
||||
Endgame<KQKRPs> ScaleKQKRPs[] = { Endgame<KQKRPs>(WHITE), Endgame<KQKRPs>(BLACK) };
|
||||
@@ -104,8 +105,8 @@ namespace {
|
||||
int v = Linear[pt1];
|
||||
|
||||
for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2)
|
||||
v += QuadraticSameSide[pt1][pt2] * pieceCount[Us][pt2]
|
||||
+ QuadraticOppositeSide[pt1][pt2] * pieceCount[Them][pt2];
|
||||
v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2]
|
||||
+ QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2];
|
||||
|
||||
bonus += pieceCount[Us][pt1] * v;
|
||||
}
|
||||
@@ -117,19 +118,16 @@ namespace {
|
||||
|
||||
namespace Material {
|
||||
|
||||
/// Material::probe() takes a position object as input, looks up a MaterialEntry
|
||||
/// object, and returns a pointer to it. If the material configuration is not
|
||||
/// already present in the table, it is computed and stored there, so we don't
|
||||
/// have to recompute everything when the same material configuration occurs again.
|
||||
/// Material::probe() looks up the current position's material configuration in
|
||||
/// the material hash table. It returns a pointer to the Entry if the position
|
||||
/// is found. Otherwise a new Entry is computed and stored there, so we don't
|
||||
/// have to recompute all when the same material configuration occurs again.
|
||||
|
||||
Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
|
||||
Entry* probe(const Position& pos) {
|
||||
|
||||
Key key = pos.material_key();
|
||||
Entry* e = entries[key];
|
||||
Entry* e = pos.this_thread()->materialTable[key];
|
||||
|
||||
// If e->key matches the position's material hash key, it means that we
|
||||
// have analysed this material configuration before, and we can simply
|
||||
// return the information we found the last time instead of recomputing it.
|
||||
if (e->key == key)
|
||||
return e;
|
||||
|
||||
@@ -141,7 +139,7 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
|
||||
// Let's look if we have a specialized evaluation function for this particular
|
||||
// material configuration. Firstly we look for a fixed configuration one, then
|
||||
// for a generic one if the previous search failed.
|
||||
if (endgames.probe(key, e->evaluationFunction))
|
||||
if (pos.this_thread()->endgames.probe(key, e->evaluationFunction))
|
||||
return e;
|
||||
|
||||
if (is_KXK<WHITE>(pos))
|
||||
@@ -156,22 +154,19 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
|
||||
return e;
|
||||
}
|
||||
|
||||
// OK, we didn't find any special evaluation function for the current
|
||||
// material configuration. Is there a suitable scaling function?
|
||||
//
|
||||
// We face problems when there are several conflicting applicable
|
||||
// scaling functions and we need to decide which one to use.
|
||||
// OK, we didn't find any special evaluation function for the current material
|
||||
// configuration. Is there a suitable specialized scaling function?
|
||||
EndgameBase<ScaleFactor>* sf;
|
||||
|
||||
if (endgames.probe(key, sf))
|
||||
if (pos.this_thread()->endgames.probe(key, sf))
|
||||
{
|
||||
e->scalingFunction[sf->color()] = sf;
|
||||
e->scalingFunction[sf->strong_side()] = sf; // Only strong color assigned
|
||||
return e;
|
||||
}
|
||||
|
||||
// Generic scaling functions that refer to more than one material
|
||||
// distribution. They should be probed after the specialized ones.
|
||||
// Note that these ones don't return after setting the function.
|
||||
// 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
|
||||
// case we don't return after setting the function.
|
||||
if (is_KBPsKs<WHITE>(pos))
|
||||
e->scalingFunction[WHITE] = &ScaleKBPsK[WHITE];
|
||||
|
||||
@@ -187,16 +182,18 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
|
||||
Value npm_w = pos.non_pawn_material(WHITE);
|
||||
Value npm_b = pos.non_pawn_material(BLACK);
|
||||
|
||||
if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN))
|
||||
if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board
|
||||
{
|
||||
if (!pos.count<PAWN>(BLACK))
|
||||
{
|
||||
assert(pos.count<PAWN>(WHITE) >= 2);
|
||||
|
||||
e->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
|
||||
}
|
||||
else if (!pos.count<PAWN>(WHITE))
|
||||
{
|
||||
assert(pos.count<PAWN>(BLACK) >= 2);
|
||||
|
||||
e->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
|
||||
}
|
||||
else if (pos.count<PAWN>(WHITE) == 1 && pos.count<PAWN>(BLACK) == 1)
|
||||
@@ -208,14 +205,16 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
|
||||
}
|
||||
}
|
||||
|
||||
// No pawns makes it difficult to win, even with a material advantage. This
|
||||
// catches some trivial draws like KK, KBK and KNK and gives a very drawish
|
||||
// scale factor for cases such as KRKBP and KmmKm (except for KBBKN).
|
||||
// Zero or just one pawn makes it difficult to win, even with a small material
|
||||
// advantage. This catches some trivial draws like KK, KBK and KNK and gives a
|
||||
// drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN).
|
||||
if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg)
|
||||
e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW : npm_b <= BishopValueMg ? 4 : 12);
|
||||
e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW :
|
||||
npm_b <= BishopValueMg ? 4 : 12);
|
||||
|
||||
if (!pos.count<PAWN>(BLACK) && npm_b - npm_w <= BishopValueMg)
|
||||
e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW : npm_w <= BishopValueMg ? 4 : 12);
|
||||
e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW :
|
||||
npm_w <= BishopValueMg ? 4 : 12);
|
||||
|
||||
if (pos.count<PAWN>(WHITE) == 1 && npm_w - npm_b <= BishopValueMg)
|
||||
e->factor[WHITE] = (uint8_t) SCALE_FACTOR_ONEPAWN;
|
||||
@@ -223,25 +222,16 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
|
||||
if (pos.count<PAWN>(BLACK) == 1 && npm_b - npm_w <= BishopValueMg)
|
||||
e->factor[BLACK] = (uint8_t) SCALE_FACTOR_ONEPAWN;
|
||||
|
||||
// Compute the space weight
|
||||
if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg)
|
||||
{
|
||||
int minorPieceCount = pos.count<KNIGHT>(WHITE) + pos.count<BISHOP>(WHITE)
|
||||
+ pos.count<KNIGHT>(BLACK) + pos.count<BISHOP>(BLACK);
|
||||
|
||||
e->spaceWeight = make_score(minorPieceCount * minorPieceCount, 0);
|
||||
}
|
||||
|
||||
// Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
|
||||
// for the bishop pair "extended piece", which allows us to be more flexible
|
||||
// in defining bishop pair bonuses.
|
||||
const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = {
|
||||
const int PieceCount[COLOR_NB][PIECE_TYPE_NB] = {
|
||||
{ pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE),
|
||||
pos.count<BISHOP>(WHITE) , pos.count<ROOK>(WHITE), pos.count<QUEEN >(WHITE) },
|
||||
{ pos.count<BISHOP>(BLACK) > 1, pos.count<PAWN>(BLACK), pos.count<KNIGHT>(BLACK),
|
||||
pos.count<BISHOP>(BLACK) , pos.count<ROOK>(BLACK), pos.count<QUEEN >(BLACK) } };
|
||||
|
||||
e->value = (int16_t)((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16);
|
||||
e->value = int16_t((imbalance<WHITE>(PieceCount) - imbalance<BLACK>(PieceCount)) / 16);
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -28,46 +28,44 @@
|
||||
namespace Material {
|
||||
|
||||
/// Material::Entry contains various information about a material configuration.
|
||||
/// It contains a material balance evaluation, a function pointer to a special
|
||||
/// It contains a material imbalance evaluation, a function pointer to a special
|
||||
/// endgame evaluation function (which in most cases is NULL, meaning that the
|
||||
/// standard evaluation function will be used), and "scale factors".
|
||||
/// standard evaluation function will be used), and scale factors.
|
||||
///
|
||||
/// The scale factors are used to scale the evaluation score up or down.
|
||||
/// For instance, in KRB vs KR endgames, the score is scaled down by a factor
|
||||
/// of 4, which will result in scores of absolute value less than one pawn.
|
||||
/// The scale factors are used to scale the evaluation score up or down. For
|
||||
/// instance, in KRB vs KR endgames, the score is scaled down by a factor of 4,
|
||||
/// which will result in scores of absolute value less than one pawn.
|
||||
|
||||
struct Entry {
|
||||
|
||||
Score material_value() const { return make_score(value, value); }
|
||||
Score space_weight() const { return spaceWeight; }
|
||||
Score imbalance() const { return make_score(value, value); }
|
||||
Phase game_phase() const { return gamePhase; }
|
||||
bool specialized_eval_exists() const { return evaluationFunction != NULL; }
|
||||
Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); }
|
||||
|
||||
// scale_factor takes a position and a color as input, and returns a scale factor
|
||||
// for the given color. We have to provide the position in addition to the color,
|
||||
// because the scale factor need not be a constant: It can also be a function
|
||||
// which should be applied to the position. For instance, in KBP vs K endgames,
|
||||
// a scaling function for draws with rook pawns and wrong-colored bishops.
|
||||
|
||||
// scale_factor takes a position and a color as input and returns a scale factor
|
||||
// for the given color. We have to provide the position in addition to the color
|
||||
// because the scale factor may also be a function which should be applied to
|
||||
// the position. For instance, in KBP vs K endgames, the scaling function looks
|
||||
// for rook pawns and wrong-colored bishops.
|
||||
ScaleFactor scale_factor(const Position& pos, Color c) const {
|
||||
|
||||
return !scalingFunction[c] || (*scalingFunction[c])(pos) == SCALE_FACTOR_NONE
|
||||
? ScaleFactor(factor[c]) : (*scalingFunction[c])(pos);
|
||||
return !scalingFunction[c]
|
||||
|| (*scalingFunction[c])(pos) == SCALE_FACTOR_NONE ? ScaleFactor(factor[c])
|
||||
: (*scalingFunction[c])(pos);
|
||||
}
|
||||
|
||||
Key key;
|
||||
int16_t value;
|
||||
uint8_t factor[COLOR_NB];
|
||||
EndgameBase<Value>* evaluationFunction;
|
||||
EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB];
|
||||
Score spaceWeight;
|
||||
EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each
|
||||
// side (e.g. KPKP, KBPsKs)
|
||||
Phase gamePhase;
|
||||
};
|
||||
|
||||
typedef HashTable<Entry, 8192> Table;
|
||||
|
||||
Entry* probe(const Position& pos, Table& entries, Endgames& endgames);
|
||||
Entry* probe(const Position& pos);
|
||||
|
||||
} // namespace Material
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -27,58 +27,14 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
|
||||
/// Version number. If Version is left empty, then compile date in the format
|
||||
/// DD-MM-YY and show in engine_info.
|
||||
static const string Version = "121014";
|
||||
|
||||
|
||||
/// engine_info() returns the full name of the current Stockfish version. This
|
||||
/// will be either "Stockfish <Tag> DD-MM-YY" (where DD-MM-YY is the date when
|
||||
/// the program was compiled) or "Stockfish <Version>", depending on whether
|
||||
/// Version is empty.
|
||||
|
||||
const string engine_info(bool to_uci) {
|
||||
|
||||
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
|
||||
string month, day, year;
|
||||
stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008"
|
||||
|
||||
ss << "Stockfish " << Version << setfill('0');
|
||||
|
||||
if (Version.empty())
|
||||
{
|
||||
date >> month >> day >> year;
|
||||
ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
|
||||
}
|
||||
|
||||
ss << (Is64Bit ? " 64" : "")
|
||||
<< (HasPext ? " BMI2" : (HasPopCnt ? " SSE4.2" : ""))
|
||||
<< (to_uci ? "\nid author ": " by ")
|
||||
<< "Tord Romstad, Marco Costalba and Joona Kiiski";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
/// 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_c(bool c, bool b) { if (c) dbg_hit_on(b); }
|
||||
void dbg_mean_of(int v) { ++means[0]; means[1] += v; }
|
||||
|
||||
void dbg_print() {
|
||||
|
||||
if (hits[0])
|
||||
cerr << "Total " << hits[0] << " Hits " << hits[1]
|
||||
<< " hit rate (%) " << 100 * hits[1] / hits[0] << endl;
|
||||
|
||||
if (means[0])
|
||||
cerr << "Total " << means[0] << " Mean "
|
||||
<< (double)means[1] / means[0] << endl;
|
||||
}
|
||||
const string Version = "6";
|
||||
|
||||
/// Debug counters
|
||||
int64_t hits[2], means[2];
|
||||
|
||||
/// 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
|
||||
@@ -137,6 +93,53 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/// engine_info() returns the full name of the current Stockfish version. This
|
||||
/// will be either "Stockfish <Tag> DD-MM-YY" (where DD-MM-YY is the date when
|
||||
/// the program was compiled) or "Stockfish <Version>", depending on whether
|
||||
/// Version is empty.
|
||||
|
||||
const string engine_info(bool to_uci) {
|
||||
|
||||
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
|
||||
string month, day, year;
|
||||
stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008"
|
||||
|
||||
ss << "Stockfish " << Version << setfill('0');
|
||||
|
||||
if (Version.empty())
|
||||
{
|
||||
date >> month >> day >> year;
|
||||
ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
|
||||
}
|
||||
|
||||
ss << (Is64Bit ? " 64" : "")
|
||||
<< (HasPext ? " BMI2" : (HasPopCnt ? " POPCNT" : ""))
|
||||
<< (to_uci ? "\nid author ": " by ")
|
||||
<< "Tord Romstad, Marco Costalba and Joona Kiiski";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
/// Debug functions used mainly to collect run-time statistics
|
||||
|
||||
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_mean_of(int v) { ++means[0]; means[1] += v; }
|
||||
|
||||
void dbg_print() {
|
||||
|
||||
if (hits[0])
|
||||
cerr << "Total " << hits[0] << " Hits " << hits[1]
|
||||
<< " hit rate (%) " << 100 * hits[1] / hits[0] << endl;
|
||||
|
||||
if (means[0])
|
||||
cerr << "Total " << means[0] << " Mean "
|
||||
<< (double)means[1] / means[0] << endl;
|
||||
}
|
||||
|
||||
|
||||
/// Used to serialize access to std::cout to avoid multiple threads writing at
|
||||
/// the same time.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -20,21 +20,22 @@
|
||||
#ifndef MISC_H_INCLUDED
|
||||
#define MISC_H_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
extern const std::string engine_info(bool to_uci = false);
|
||||
extern void timed_wait(WaitCondition&, Lock&, int);
|
||||
extern void prefetch(char* addr);
|
||||
extern void start_logger(bool b);
|
||||
const std::string engine_info(bool to_uci = false);
|
||||
void timed_wait(WaitCondition&, Lock&, int);
|
||||
void prefetch(char* addr);
|
||||
void start_logger(bool b);
|
||||
|
||||
extern void dbg_hit_on(bool b);
|
||||
extern void dbg_hit_on_c(bool c, bool b);
|
||||
extern void dbg_mean_of(int v);
|
||||
extern void dbg_print();
|
||||
void dbg_hit_on(bool b);
|
||||
void dbg_hit_on_c(bool c, bool b);
|
||||
void dbg_mean_of(int v);
|
||||
void dbg_print();
|
||||
|
||||
|
||||
namespace Time {
|
||||
@@ -46,7 +47,7 @@ namespace Time {
|
||||
template<class Entry, int Size>
|
||||
struct HashTable {
|
||||
HashTable() : table(Size, Entry()) {}
|
||||
Entry* operator[](Key k) { return &table[(uint32_t)k & (Size - 1)]; }
|
||||
Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; }
|
||||
|
||||
private:
|
||||
std::vector<Entry> table;
|
||||
@@ -59,4 +60,41 @@ std::ostream& operator<<(std::ostream&, SyncCout);
|
||||
#define sync_cout std::cout << IO_LOCK
|
||||
#define sync_endl std::endl << IO_UNLOCK
|
||||
|
||||
|
||||
/// xorshift64star Pseudo-Random Number Generator
|
||||
/// This class is based on original code written and dedicated
|
||||
/// to the public domain by Sebastiano Vigna (2014).
|
||||
/// It has the following characteristics:
|
||||
///
|
||||
/// - Outputs 64-bit numbers
|
||||
/// - Passes Dieharder and SmallCrush test batteries
|
||||
/// - Does not require warm-up, no zeroland to escape
|
||||
/// - Internal state is a single 64-bit integer
|
||||
/// - Period is 2^64 - 1
|
||||
/// - Speed: 1.60 ns/call (Core i7 @3.40GHz)
|
||||
///
|
||||
/// For further analysis see
|
||||
/// <http://vigna.di.unimi.it/ftp/papers/xorshift.pdf>
|
||||
|
||||
class PRNG {
|
||||
|
||||
uint64_t s;
|
||||
|
||||
uint64_t rand64() {
|
||||
|
||||
s ^= s >> 12, s ^= s << 25, s ^= s >> 27;
|
||||
return s * 2685821657736338717LL;
|
||||
}
|
||||
|
||||
public:
|
||||
PRNG(uint64_t seed) : s(seed) { assert(seed); }
|
||||
|
||||
template<typename T> T rand() { return T(rand64()); }
|
||||
|
||||
/// Special generator used to fast init magic numbers.
|
||||
/// Output values only have 1/8th of their bits set on average.
|
||||
template<typename T> T sparse_rand()
|
||||
{ return T(rand64() & rand64() & rand64()); }
|
||||
};
|
||||
|
||||
#endif // #ifndef MISC_H_INCLUDED
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -25,12 +25,12 @@
|
||||
namespace {
|
||||
|
||||
template<CastlingRight Cr, bool Checks, bool Chess960>
|
||||
ExtMove* generate_castling(const Position& pos, ExtMove* mlist, Color us, const CheckInfo* ci) {
|
||||
ExtMove* generate_castling(const Position& pos, ExtMove* moveList, Color us, const CheckInfo* ci) {
|
||||
|
||||
static const bool KingSide = (Cr == WHITE_OO || Cr == BLACK_OO);
|
||||
|
||||
if (pos.castling_impeded(Cr) || !pos.can_castle(Cr))
|
||||
return mlist;
|
||||
return moveList;
|
||||
|
||||
// After castling, the rook and king final positions are the same in Chess960
|
||||
// as they would be in standard chess.
|
||||
@@ -46,59 +46,51 @@ namespace {
|
||||
|
||||
for (Square s = kto; s != kfrom; s += K)
|
||||
if (pos.attackers_to(s) & enemies)
|
||||
return mlist;
|
||||
return moveList;
|
||||
|
||||
// Because we generate only legal castling moves we need to verify that
|
||||
// when moving the castling rook we do not discover some hidden checker.
|
||||
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
|
||||
if (Chess960 && (attacks_bb<ROOK>(kto, pos.pieces() ^ rfrom) & pos.pieces(~us, ROOK, QUEEN)))
|
||||
return mlist;
|
||||
return moveList;
|
||||
|
||||
Move m = make<CASTLING>(kfrom, rfrom);
|
||||
|
||||
if (Checks && !pos.gives_check(m, *ci))
|
||||
return mlist;
|
||||
return moveList;
|
||||
|
||||
(mlist++)->move = m;
|
||||
(moveList++)->move = m;
|
||||
|
||||
return mlist;
|
||||
return moveList;
|
||||
}
|
||||
|
||||
|
||||
template<GenType Type, Square Delta>
|
||||
inline ExtMove* generate_promotions(ExtMove* mlist, Bitboard pawnsOn7,
|
||||
Bitboard target, const CheckInfo* ci) {
|
||||
inline ExtMove* make_promotions(ExtMove* moveList, Square to, const CheckInfo* ci) {
|
||||
|
||||
Bitboard b = shift_bb<Delta>(pawnsOn7) & target;
|
||||
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
(moveList++)->move = make<PROMOTION>(to - Delta, to, QUEEN);
|
||||
|
||||
while (b)
|
||||
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
{
|
||||
Square to = pop_lsb(&b);
|
||||
|
||||
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
(mlist++)->move = make<PROMOTION>(to - Delta, to, QUEEN);
|
||||
|
||||
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
{
|
||||
(mlist++)->move = make<PROMOTION>(to - Delta, to, ROOK);
|
||||
(mlist++)->move = make<PROMOTION>(to - Delta, to, BISHOP);
|
||||
(mlist++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
|
||||
}
|
||||
|
||||
// Knight promotion is the only promotion that can give a direct check
|
||||
// that's not already included in the queen promotion.
|
||||
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq))
|
||||
(mlist++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
|
||||
else
|
||||
(void)ci; // Silence a warning under MSVC
|
||||
(moveList++)->move = make<PROMOTION>(to - Delta, to, ROOK);
|
||||
(moveList++)->move = make<PROMOTION>(to - Delta, to, BISHOP);
|
||||
(moveList++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
|
||||
}
|
||||
|
||||
return mlist;
|
||||
// Knight promotion is the only promotion that can give a direct check
|
||||
// that's not already included in the queen promotion.
|
||||
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq))
|
||||
(moveList++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
|
||||
else
|
||||
(void)ci; // Silence a warning under MSVC
|
||||
|
||||
return moveList;
|
||||
}
|
||||
|
||||
|
||||
template<Color Us, GenType Type>
|
||||
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* mlist,
|
||||
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList,
|
||||
Bitboard target, const CheckInfo* ci) {
|
||||
|
||||
// Compute our parametrized parameters at compile time, named according to
|
||||
@@ -111,7 +103,7 @@ namespace {
|
||||
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
|
||||
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
|
||||
|
||||
Bitboard b1, b2, dc1, dc2, emptySquares;
|
||||
Bitboard emptySquares;
|
||||
|
||||
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
|
||||
Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
|
||||
@@ -124,8 +116,8 @@ namespace {
|
||||
{
|
||||
emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces());
|
||||
|
||||
b1 = shift_bb<Up>(pawnsNotOn7) & emptySquares;
|
||||
b2 = shift_bb<Up>(b1 & TRank3BB) & emptySquares;
|
||||
Bitboard b1 = shift_bb<Up>(pawnsNotOn7) & emptySquares;
|
||||
Bitboard b2 = shift_bb<Up>(b1 & TRank3BB) & emptySquares;
|
||||
|
||||
if (Type == EVASIONS) // Consider only blocking squares
|
||||
{
|
||||
@@ -144,8 +136,8 @@ namespace {
|
||||
// promotion has been already generated amongst the captures.
|
||||
if (pawnsNotOn7 & ci->dcCandidates)
|
||||
{
|
||||
dc1 = shift_bb<Up>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq);
|
||||
dc2 = shift_bb<Up>(dc1 & TRank3BB) & emptySquares;
|
||||
Bitboard dc1 = shift_bb<Up>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq);
|
||||
Bitboard dc2 = shift_bb<Up>(dc1 & TRank3BB) & emptySquares;
|
||||
|
||||
b1 |= dc1;
|
||||
b2 |= dc2;
|
||||
@@ -155,13 +147,13 @@ namespace {
|
||||
while (b1)
|
||||
{
|
||||
Square to = pop_lsb(&b1);
|
||||
(mlist++)->move = make_move(to - Up, to);
|
||||
(moveList++)->move = make_move(to - Up, to);
|
||||
}
|
||||
|
||||
while (b2)
|
||||
{
|
||||
Square to = pop_lsb(&b2);
|
||||
(mlist++)->move = make_move(to - Up - Up, to);
|
||||
(moveList++)->move = make_move(to - Up - Up, to);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,27 +166,36 @@ namespace {
|
||||
if (Type == EVASIONS)
|
||||
emptySquares &= target;
|
||||
|
||||
mlist = generate_promotions<Type, Right>(mlist, pawnsOn7, enemies, ci);
|
||||
mlist = generate_promotions<Type, Left >(mlist, pawnsOn7, enemies, ci);
|
||||
mlist = generate_promotions<Type, Up>(mlist, pawnsOn7, emptySquares, ci);
|
||||
Bitboard b1 = shift_bb<Right>(pawnsOn7) & enemies;
|
||||
Bitboard b2 = shift_bb<Left >(pawnsOn7) & enemies;
|
||||
Bitboard b3 = shift_bb<Up >(pawnsOn7) & emptySquares;
|
||||
|
||||
while (b1)
|
||||
moveList = make_promotions<Type, Right>(moveList, pop_lsb(&b1), ci);
|
||||
|
||||
while (b2)
|
||||
moveList = make_promotions<Type, Left >(moveList, pop_lsb(&b2), ci);
|
||||
|
||||
while (b3)
|
||||
moveList = make_promotions<Type, Up >(moveList, pop_lsb(&b3), ci);
|
||||
}
|
||||
|
||||
// Standard and en-passant captures
|
||||
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
{
|
||||
b1 = shift_bb<Right>(pawnsNotOn7) & enemies;
|
||||
b2 = shift_bb<Left >(pawnsNotOn7) & enemies;
|
||||
Bitboard b1 = shift_bb<Right>(pawnsNotOn7) & enemies;
|
||||
Bitboard b2 = shift_bb<Left >(pawnsNotOn7) & enemies;
|
||||
|
||||
while (b1)
|
||||
{
|
||||
Square to = pop_lsb(&b1);
|
||||
(mlist++)->move = make_move(to - Right, to);
|
||||
(moveList++)->move = make_move(to - Right, to);
|
||||
}
|
||||
|
||||
while (b2)
|
||||
{
|
||||
Square to = pop_lsb(&b2);
|
||||
(mlist++)->move = make_move(to - Left, to);
|
||||
(moveList++)->move = make_move(to - Left, to);
|
||||
}
|
||||
|
||||
if (pos.ep_square() != SQ_NONE)
|
||||
@@ -205,23 +206,23 @@ namespace {
|
||||
// is the double pushed pawn and so is in the target. Otherwise this
|
||||
// is a discovery check and we are forced to do otherwise.
|
||||
if (Type == EVASIONS && !(target & (pos.ep_square() - Up)))
|
||||
return mlist;
|
||||
return moveList;
|
||||
|
||||
b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them);
|
||||
|
||||
assert(b1);
|
||||
|
||||
while (b1)
|
||||
(mlist++)->move = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
|
||||
(moveList++)->move = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
|
||||
}
|
||||
}
|
||||
|
||||
return mlist;
|
||||
return moveList;
|
||||
}
|
||||
|
||||
|
||||
template<PieceType Pt, bool Checks> FORCE_INLINE
|
||||
ExtMove* generate_moves(const Position& pos, ExtMove* mlist, Color us,
|
||||
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us,
|
||||
Bitboard target, const CheckInfo* ci) {
|
||||
|
||||
assert(Pt != KING && Pt != PAWN);
|
||||
@@ -236,7 +237,7 @@ namespace {
|
||||
&& !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt]))
|
||||
continue;
|
||||
|
||||
if (unlikely(ci->dcCandidates) && (ci->dcCandidates & from))
|
||||
if (ci->dcCandidates && (ci->dcCandidates & from))
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -246,51 +247,50 @@ namespace {
|
||||
b &= ci->checkSq[Pt];
|
||||
|
||||
while (b)
|
||||
(mlist++)->move = make_move(from, pop_lsb(&b));
|
||||
(moveList++)->move = make_move(from, pop_lsb(&b));
|
||||
}
|
||||
|
||||
return mlist;
|
||||
return moveList;
|
||||
}
|
||||
|
||||
|
||||
template<Color Us, GenType Type> FORCE_INLINE
|
||||
ExtMove* generate_all(const Position& pos, ExtMove* mlist, Bitboard target,
|
||||
ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target,
|
||||
const CheckInfo* ci = NULL) {
|
||||
|
||||
const bool Checks = Type == QUIET_CHECKS;
|
||||
|
||||
mlist = generate_pawn_moves<Us, Type>(pos, mlist, target, ci);
|
||||
mlist = generate_moves<KNIGHT, Checks>(pos, mlist, Us, target, ci);
|
||||
mlist = generate_moves<BISHOP, Checks>(pos, mlist, Us, target, ci);
|
||||
mlist = generate_moves< ROOK, Checks>(pos, mlist, Us, target, ci);
|
||||
mlist = generate_moves< QUEEN, Checks>(pos, mlist, Us, target, ci);
|
||||
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target, ci);
|
||||
moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target, ci);
|
||||
moveList = generate_moves<BISHOP, Checks>(pos, moveList, Us, target, ci);
|
||||
moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target, ci);
|
||||
moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target, ci);
|
||||
|
||||
if (Type != QUIET_CHECKS && Type != EVASIONS)
|
||||
{
|
||||
Square ksq = pos.king_square(Us);
|
||||
Bitboard b = pos.attacks_from<KING>(ksq) & target;
|
||||
while (b)
|
||||
(mlist++)->move = make_move(ksq, pop_lsb(&b));
|
||||
(moveList++)->move = make_move(ksq, pop_lsb(&b));
|
||||
}
|
||||
|
||||
if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us))
|
||||
{
|
||||
if (pos.is_chess960())
|
||||
{
|
||||
mlist = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, true>(pos, mlist, Us, ci);
|
||||
mlist = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, true>(pos, mlist, Us, ci);
|
||||
moveList = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, true>(pos, moveList, Us, ci);
|
||||
moveList = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, true>(pos, moveList, Us, ci);
|
||||
}
|
||||
else
|
||||
{
|
||||
mlist = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, false>(pos, mlist, Us, ci);
|
||||
mlist = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, false>(pos, mlist, Us, ci);
|
||||
moveList = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, false>(pos, moveList, Us, ci);
|
||||
moveList = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, false>(pos, moveList, Us, ci);
|
||||
}
|
||||
}
|
||||
|
||||
return mlist;
|
||||
return moveList;
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -304,19 +304,19 @@ namespace {
|
||||
/// non-captures. Returns a pointer to the end of the move list.
|
||||
|
||||
template<GenType Type>
|
||||
ExtMove* generate(const Position& pos, ExtMove* mlist) {
|
||||
ExtMove* generate(const Position& pos, ExtMove* moveList) {
|
||||
|
||||
assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS);
|
||||
assert(!pos.checkers());
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
|
||||
Bitboard target = Type == CAPTURES ? pos.pieces(~us)
|
||||
: Type == QUIETS ? ~pos.pieces()
|
||||
: Type == NON_EVASIONS ? ~pos.pieces(us) : 0;
|
||||
Bitboard target = Type == CAPTURES ? pos.pieces(~us)
|
||||
: Type == QUIETS ? ~pos.pieces()
|
||||
: Type == NON_EVASIONS ? ~pos.pieces(us) : 0;
|
||||
|
||||
return us == WHITE ? generate_all<WHITE, Type>(pos, mlist, target)
|
||||
: generate_all<BLACK, Type>(pos, mlist, target);
|
||||
return us == WHITE ? generate_all<WHITE, Type>(pos, moveList, target)
|
||||
: generate_all<BLACK, Type>(pos, moveList, target);
|
||||
}
|
||||
|
||||
// Explicit template instantiations
|
||||
@@ -328,7 +328,7 @@ template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
|
||||
/// generate<QUIET_CHECKS> generates all pseudo-legal non-captures and knight
|
||||
/// underpromotions that give check. Returns a pointer to the end of the move list.
|
||||
template<>
|
||||
ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* mlist) {
|
||||
ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
|
||||
|
||||
assert(!pos.checkers());
|
||||
|
||||
@@ -350,18 +350,18 @@ ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* mlist) {
|
||||
b &= ~PseudoAttacks[QUEEN][ci.ksq];
|
||||
|
||||
while (b)
|
||||
(mlist++)->move = make_move(from, pop_lsb(&b));
|
||||
(moveList++)->move = make_move(from, pop_lsb(&b));
|
||||
}
|
||||
|
||||
return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, mlist, ~pos.pieces(), &ci)
|
||||
: generate_all<BLACK, QUIET_CHECKS>(pos, mlist, ~pos.pieces(), &ci);
|
||||
return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, moveList, ~pos.pieces(), &ci)
|
||||
: generate_all<BLACK, QUIET_CHECKS>(pos, moveList, ~pos.pieces(), &ci);
|
||||
}
|
||||
|
||||
|
||||
/// generate<EVASIONS> generates all pseudo-legal check evasions when the side
|
||||
/// to move is in check. Returns a pointer to the end of the move list.
|
||||
template<>
|
||||
ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* mlist) {
|
||||
ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* moveList) {
|
||||
|
||||
assert(pos.checkers());
|
||||
|
||||
@@ -382,37 +382,37 @@ ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* mlist) {
|
||||
// Generate evasions for king, capture and non capture moves
|
||||
Bitboard b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
|
||||
while (b)
|
||||
(mlist++)->move = make_move(ksq, pop_lsb(&b));
|
||||
(moveList++)->move = make_move(ksq, pop_lsb(&b));
|
||||
|
||||
if (more_than_one(pos.checkers()))
|
||||
return mlist; // Double check, only a king move can save the day
|
||||
return moveList; // Double check, only a king move can save the day
|
||||
|
||||
// Generate blocking evasions or captures of the checking piece
|
||||
Square checksq = lsb(pos.checkers());
|
||||
Bitboard target = between_bb(checksq, ksq) | checksq;
|
||||
|
||||
return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, mlist, target)
|
||||
: generate_all<BLACK, EVASIONS>(pos, mlist, target);
|
||||
return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, moveList, target)
|
||||
: generate_all<BLACK, EVASIONS>(pos, moveList, target);
|
||||
}
|
||||
|
||||
|
||||
/// generate<LEGAL> generates all the legal moves in the given position
|
||||
|
||||
template<>
|
||||
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* mlist) {
|
||||
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
|
||||
|
||||
ExtMove *end, *cur = mlist;
|
||||
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
|
||||
Square ksq = pos.king_square(pos.side_to_move());
|
||||
ExtMove* cur = moveList;
|
||||
|
||||
end = pos.checkers() ? generate<EVASIONS>(pos, mlist)
|
||||
: generate<NON_EVASIONS>(pos, mlist);
|
||||
while (cur != end)
|
||||
moveList = pos.checkers() ? generate<EVASIONS >(pos, moveList)
|
||||
: generate<NON_EVASIONS>(pos, moveList);
|
||||
while (cur != moveList)
|
||||
if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT)
|
||||
&& !pos.legal(cur->move, pinned))
|
||||
cur->move = (--end)->move;
|
||||
cur->move = (--moveList)->move;
|
||||
else
|
||||
++cur;
|
||||
|
||||
return end;
|
||||
return moveList;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
class Position;
|
||||
|
||||
enum GenType {
|
||||
CAPTURES,
|
||||
QUIETS,
|
||||
@@ -31,27 +33,34 @@ enum GenType {
|
||||
LEGAL
|
||||
};
|
||||
|
||||
class Position;
|
||||
struct ExtMove {
|
||||
Move move;
|
||||
Value value;
|
||||
};
|
||||
|
||||
inline bool operator<(const ExtMove& f, const ExtMove& s) {
|
||||
return f.value < s.value;
|
||||
}
|
||||
|
||||
template<GenType>
|
||||
ExtMove* generate(const Position& pos, ExtMove* mlist);
|
||||
ExtMove* generate(const Position& pos, ExtMove* moveList);
|
||||
|
||||
/// The MoveList struct is a simple wrapper around generate(). It sometimes comes
|
||||
/// in handy to use this class instead of the low level generate() function.
|
||||
template<GenType T>
|
||||
struct MoveList {
|
||||
|
||||
explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) { last->move = MOVE_NONE; }
|
||||
explicit MoveList(const Position& pos) : cur(moveList), last(generate<T>(pos, moveList)) { last->move = MOVE_NONE; }
|
||||
void operator++() { ++cur; }
|
||||
Move operator*() const { return cur->move; }
|
||||
size_t size() const { return last - mlist; }
|
||||
size_t size() const { return last - moveList; }
|
||||
bool contains(Move m) const {
|
||||
for (const ExtMove* it(mlist); it != last; ++it) if (it->move == m) return true;
|
||||
for (const ExtMove* it(moveList); it != last; ++it) if (it->move == m) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
ExtMove mlist[MAX_MOVES];
|
||||
ExtMove moveList[MAX_MOVES];
|
||||
ExtMove *cur, *last;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -51,7 +51,7 @@ namespace {
|
||||
|
||||
// Unary predicate used by std::partition to split positive values from remaining
|
||||
// ones so as to sort the two sets separately, with the second sort delayed.
|
||||
inline bool has_positive_value(const ExtMove& ms) { return ms.value > 0; }
|
||||
inline bool has_positive_value(const ExtMove& move) { return move.value > VALUE_ZERO; }
|
||||
|
||||
// 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
|
||||
@@ -61,7 +61,7 @@ namespace {
|
||||
std::swap(*begin, *std::max_element(begin, end));
|
||||
return begin;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
/// Constructors of the MovePicker class. As arguments we pass information
|
||||
@@ -103,15 +103,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
|
||||
stage = QSEARCH_0;
|
||||
|
||||
else if (d > DEPTH_QS_RECAPTURES)
|
||||
{
|
||||
stage = QSEARCH_1;
|
||||
|
||||
// Skip TT move if is not a capture or a promotion. This avoids qsearch
|
||||
// tree explosion due to a possible perpetual check or similar rare cases
|
||||
// when TT table is full.
|
||||
if (ttm && !pos.capture_or_promotion(ttm))
|
||||
ttm = MOVE_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
stage = RECAPTURE;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -40,7 +40,7 @@
|
||||
template<bool Gain, typename T>
|
||||
struct Stats {
|
||||
|
||||
static const Value Max = Value(2000);
|
||||
static const Value Max = Value(250);
|
||||
|
||||
const T* operator[](Piece pc) const { return table[pc]; }
|
||||
void clear() { std::memset(table, 0, sizeof(table)); }
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 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 <cassert>
|
||||
#include <sstream>
|
||||
|
||||
#include "movegen.h"
|
||||
#include "notation.h"
|
||||
#include "position.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static const char* PieceToChar[COLOR_NB] = { " PNBRQK", " pnbrqk" };
|
||||
|
||||
|
||||
/// score_to_uci() converts a value to a string suitable for use with the UCI
|
||||
/// protocol specifications:
|
||||
///
|
||||
/// cp <x> The score from the engine's point of view in centipawns.
|
||||
/// mate <y> Mate in y moves, not plies. If the engine is getting mated
|
||||
/// use negative values for y.
|
||||
|
||||
string score_to_uci(Value v, Value alpha, Value beta) {
|
||||
|
||||
stringstream ss;
|
||||
|
||||
if (abs(v) < VALUE_MATE - MAX_PLY)
|
||||
ss << "cp " << v * 100 / PawnValueEg;
|
||||
else
|
||||
ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
|
||||
|
||||
ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : "");
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
/// move_to_uci() converts a move to a string in coordinate notation
|
||||
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we print
|
||||
/// in the e1g1 notation in normal chess mode, and in e1h1 notation in chess960
|
||||
/// mode. Internally castling moves are always encoded as "king captures rook".
|
||||
|
||||
const string move_to_uci(Move m, bool chess960) {
|
||||
|
||||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
|
||||
if (m == MOVE_NONE)
|
||||
return "(none)";
|
||||
|
||||
if (m == MOVE_NULL)
|
||||
return "0000";
|
||||
|
||||
if (type_of(m) == CASTLING && !chess960)
|
||||
to = make_square(to > from ? FILE_G : FILE_C, rank_of(from));
|
||||
|
||||
string move = to_string(from) + to_string(to);
|
||||
|
||||
if (type_of(m) == PROMOTION)
|
||||
move += PieceToChar[BLACK][promotion_type(m)]; // Lower case
|
||||
|
||||
return move;
|
||||
}
|
||||
|
||||
|
||||
/// move_from_uci() takes a position and a string representing a move in
|
||||
/// simple coordinate notation and returns an equivalent legal Move if any.
|
||||
|
||||
Move move_from_uci(const Position& pos, string& str) {
|
||||
|
||||
if (str.length() == 5) // Junior could send promotion piece in uppercase
|
||||
str[4] = char(tolower(str[4]));
|
||||
|
||||
for (MoveList<LEGAL> it(pos); *it; ++it)
|
||||
if (str == move_to_uci(*it, pos.is_chess960()))
|
||||
return *it;
|
||||
|
||||
return MOVE_NONE;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 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 NOTATION_H_INCLUDED
|
||||
#define NOTATION_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
class Position;
|
||||
|
||||
std::string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE);
|
||||
Move move_from_uci(const Position& pos, std::string& str);
|
||||
const std::string move_to_uci(Move m, bool chess960);
|
||||
|
||||
inline char to_char(File f, bool tolower = true) {
|
||||
return char(f - FILE_A + (tolower ? 'a' : 'A'));
|
||||
}
|
||||
|
||||
inline char to_char(Rank r) {
|
||||
return char(r - RANK_1 + '1');
|
||||
}
|
||||
|
||||
inline const std::string to_string(Square s) {
|
||||
char ch[] = { to_char(file_of(s)), to_char(rank_of(s)), 0 };
|
||||
return ch;
|
||||
}
|
||||
|
||||
#endif // #ifndef NOTATION_H_INCLUDED
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "bitcount.h"
|
||||
#include "pawns.h"
|
||||
#include "position.h"
|
||||
#include "thread.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -49,34 +50,46 @@ namespace {
|
||||
{ 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 bonus by rank
|
||||
const int Connected[RANK_NB] = {0, 6, 15, 10, 57, 75, 135, 258};
|
||||
// Connected pawn bonus by opposed, phalanx flags and rank
|
||||
Score Connected[2][2][RANK_NB];
|
||||
|
||||
// Levers bonus by rank
|
||||
const Score Lever[RANK_NB] = {
|
||||
S( 0, 0), S( 0, 0), S(0, 0), S(0, 0),
|
||||
S(20,20), S(40,40), S(0, 0), S(0, 0) };
|
||||
|
||||
// Bonus for file distance of the two outermost pawns
|
||||
const Score PawnsFileSpan = S(0, 15);
|
||||
|
||||
// Unsupported pawn penalty
|
||||
const Score UnsupportedPawnPenalty = S(20, 10);
|
||||
|
||||
// Weakness of our pawn shelter in front of the king indexed by [rank]
|
||||
const Value ShelterWeakness[RANK_NB] =
|
||||
{ V(100), V(0), V(27), V(73), V(92), V(101), V(101) };
|
||||
// Weakness of our pawn shelter in front of the king by [distance from edge][rank]
|
||||
const Value ShelterWeakness[][RANK_NB] = {
|
||||
{ V(100), V(13), V(24), V(64), V(89), V( 93), V(104) },
|
||||
{ V(110), V( 1), V(29), V(75), V(96), V(102), V(107) },
|
||||
{ V(102), V( 0), V(39), V(74), V(88), V(101), V( 98) },
|
||||
{ V( 88), V( 4), V(33), V(67), V(92), V( 94), V(107) } };
|
||||
|
||||
// Danger of enemy pawns moving toward our king indexed by
|
||||
// [no friendly pawn | pawn unblocked | pawn blocked][rank of enemy pawn]
|
||||
const Value StormDanger[][RANK_NB] = {
|
||||
{ V( 0), V(64), V(128), V(51), V(26) },
|
||||
{ V(26), V(32), V( 96), V(38), V(20) },
|
||||
{ V( 0), V( 0), V(160), V(25), V(13) } };
|
||||
// Danger of enemy pawns moving toward our king by [type][distance from edge][rank]
|
||||
const Value StormDanger[][4][RANK_NB] = {
|
||||
{ { V( 0), V( 63), V( 128), V(43), V(27) },
|
||||
{ V( 0), V( 62), V( 131), V(44), V(26) },
|
||||
{ V( 0), V( 59), V( 121), V(50), V(28) },
|
||||
{ V( 0), V( 62), V( 127), V(54), V(28) } },
|
||||
{ { V(24), V( 40), V( 93), V(42), V(22) },
|
||||
{ V(24), V( 28), V( 101), V(38), V(20) },
|
||||
{ V(24), V( 32), V( 95), V(36), V(23) },
|
||||
{ V(27), V( 24), V( 99), V(36), V(24) } },
|
||||
{ { V( 0), V( 0), V( 81), V(16), V( 6) },
|
||||
{ V( 0), V( 0), V( 165), V(29), V( 9) },
|
||||
{ V( 0), V( 0), V( 163), V(23), V(12) },
|
||||
{ V( 0), V( 0), V( 161), V(28), V(13) } },
|
||||
{ { V( 0), V(-296), V(-299), V(55), V(25) },
|
||||
{ V( 0), V( 67), V( 131), V(46), V(21) },
|
||||
{ V( 0), V( 65), V( 135), V(50), V(31) },
|
||||
{ V( 0), V( 62), V( 128), V(51), V(24) } } };
|
||||
|
||||
// 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.
|
||||
const Value MaxSafetyBonus = V(263);
|
||||
const Value MaxSafetyBonus = V(257);
|
||||
|
||||
#undef S
|
||||
#undef V
|
||||
@@ -89,10 +102,10 @@ namespace {
|
||||
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
|
||||
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
|
||||
|
||||
Bitboard b, p, doubled;
|
||||
Bitboard b, p, doubled, connected;
|
||||
Square s;
|
||||
bool passed, isolated, opposed, connected, backward, unsupported, lever;
|
||||
Score value = SCORE_ZERO;
|
||||
bool passed, isolated, opposed, phalanx, backward, unsupported, lever;
|
||||
Score score = SCORE_ZERO;
|
||||
const Square* pl = pos.list<PAWN>(Us);
|
||||
const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)];
|
||||
|
||||
@@ -111,7 +124,6 @@ namespace {
|
||||
{
|
||||
assert(pos.piece_on(s) == make_piece(Us, PAWN));
|
||||
|
||||
Rank r = rank_of(s), rr = relative_rank(Us, s);
|
||||
File f = file_of(s);
|
||||
|
||||
// This file cannot be semi-open
|
||||
@@ -120,12 +132,10 @@ namespace {
|
||||
// Previous rank
|
||||
p = rank_bb(s - pawn_push(Us));
|
||||
|
||||
// Our rank plus previous one
|
||||
b = rank_bb(s) | p;
|
||||
|
||||
// Flag the pawn as passed, isolated, doubled,
|
||||
// unsupported or connected (but not the backward one).
|
||||
connected = ourPawns & adjacent_files_bb(f) & b;
|
||||
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);
|
||||
@@ -165,56 +175,67 @@ namespace {
|
||||
|
||||
// Score this pawn
|
||||
if (isolated)
|
||||
value -= Isolated[opposed][f];
|
||||
score -= Isolated[opposed][f];
|
||||
|
||||
if (unsupported && !isolated)
|
||||
value -= UnsupportedPawnPenalty;
|
||||
score -= UnsupportedPawnPenalty;
|
||||
|
||||
if (doubled)
|
||||
value -= Doubled[f] / rank_distance(s, lsb(doubled));
|
||||
score -= Doubled[f] / distance<Rank>(s, frontmost_sq(Us, doubled));
|
||||
|
||||
if (backward)
|
||||
value -= Backward[opposed][f];
|
||||
score -= Backward[opposed][f];
|
||||
|
||||
if (connected) {
|
||||
int bonus = Connected[rr];
|
||||
if (ourPawns & adjacent_files_bb(f) & rank_bb(r))
|
||||
bonus += (Connected[rr+1] - Connected[rr]) / 2;
|
||||
value += make_score(bonus / 2, bonus >> opposed);
|
||||
}
|
||||
if (connected)
|
||||
score += Connected[opposed][phalanx][relative_rank(Us, s)];
|
||||
|
||||
if (lever)
|
||||
value += Lever[rr];
|
||||
score += Lever[relative_rank(Us, s)];
|
||||
}
|
||||
|
||||
b = e->semiopenFiles[Us] ^ 0xFF;
|
||||
e->pawnSpan[Us] = b ? int(msb(b) - lsb(b)) : 0;
|
||||
|
||||
// In endgame it's better to have pawns on both wings. So give a bonus according
|
||||
// to file distance between left and right outermost pawns.
|
||||
value += PawnsFileSpan * e->pawnSpan[Us];
|
||||
|
||||
return value;
|
||||
return score;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Pawns {
|
||||
|
||||
/// probe() takes a position as input, computes a Entry object, and returns a
|
||||
/// pointer to it. The result is also stored in a hash table, so we don't have
|
||||
/// to recompute everything when the same pawn structure occurs again.
|
||||
/// Pawns::init() initializes some tables needed by evaluation. Instead of using
|
||||
/// hard-coded tables, when makes sense, we prefer to calculate them with a formula
|
||||
/// to reduce independent parameters and to allow easier tuning and better insight.
|
||||
|
||||
Entry* probe(const Position& pos, Table& entries) {
|
||||
void init()
|
||||
{
|
||||
static const int Seed[RANK_NB] = { 0, 6, 15, 10, 57, 75, 135, 258 };
|
||||
|
||||
for (int opposed = 0; opposed <= 1; ++opposed)
|
||||
for (int phalanx = 0; phalanx <= 1; ++phalanx)
|
||||
for (Rank r = RANK_2; r < RANK_8; ++r)
|
||||
{
|
||||
int bonus = Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0);
|
||||
Connected[opposed][phalanx][r] = make_score(bonus / 2, bonus >> opposed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Pawns::probe() looks up the current position's pawns configuration in
|
||||
/// the pawns hash table. It returns a pointer to the Entry if the position
|
||||
/// is found. Otherwise a new Entry is computed and stored there, so we don't
|
||||
/// have to recompute all when the same pawns configuration occurs again.
|
||||
|
||||
Entry* probe(const Position& pos) {
|
||||
|
||||
Key key = pos.pawn_key();
|
||||
Entry* e = entries[key];
|
||||
Entry* e = pos.this_thread()->pawnsTable[key];
|
||||
|
||||
if (e->key == key)
|
||||
return e;
|
||||
|
||||
e->key = key;
|
||||
e->value = evaluate<WHITE>(pos, e) - evaluate<BLACK>(pos, e);
|
||||
e->score = evaluate<WHITE>(pos, e) - evaluate<BLACK>(pos, e);
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -226,15 +247,16 @@ template<Color Us>
|
||||
Value Entry::shelter_storm(const Position& pos, Square ksq) {
|
||||
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
const Bitboard Edges = (FileABB | FileHBB) & (Rank2BB | Rank3BB);
|
||||
|
||||
enum { NoFriendlyPawn, Unblocked, BlockedByPawn, BlockedByKing };
|
||||
|
||||
Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq));
|
||||
Bitboard ourPawns = b & pos.pieces(Us);
|
||||
Bitboard theirPawns = b & pos.pieces(Them);
|
||||
Value safety = MaxSafetyBonus;
|
||||
File kf = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));
|
||||
File center = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));
|
||||
|
||||
for (File f = kf - File(1); f <= kf + File(1); ++f)
|
||||
for (File f = center - File(1); f <= center + File(1); ++f)
|
||||
{
|
||||
b = ourPawns & file_bb(f);
|
||||
Rank rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
|
||||
@@ -242,14 +264,12 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) {
|
||||
b = theirPawns & file_bb(f);
|
||||
Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
|
||||
|
||||
if ( (Edges & make_square(f, rkThem))
|
||||
&& file_of(ksq) == f
|
||||
&& relative_rank(Us, ksq) == rkThem - 1)
|
||||
safety += 200;
|
||||
else
|
||||
safety -= ShelterWeakness[rkUs]
|
||||
+ StormDanger[rkUs == RANK_1 ? 0 :
|
||||
rkThem != rkUs + 1 ? 1 : 2][rkThem];
|
||||
safety -= ShelterWeakness[std::min(f, FILE_H - f)][rkUs]
|
||||
+ StormDanger
|
||||
[f == file_of(ksq) && rkThem == relative_rank(Us, ksq) + 1 ? BlockedByKing :
|
||||
rkUs == RANK_1 ? NoFriendlyPawn :
|
||||
rkThem == rkUs + 1 ? BlockedByPawn : Unblocked]
|
||||
[std::min(f, FILE_H - f)][rkThem];
|
||||
}
|
||||
|
||||
return safety;
|
||||
@@ -264,14 +284,14 @@ Score Entry::do_king_safety(const Position& pos, Square ksq) {
|
||||
|
||||
kingSquares[Us] = ksq;
|
||||
castlingRights[Us] = pos.can_castle(Us);
|
||||
minKPdistance[Us] = 0;
|
||||
minKingPawnDistance[Us] = 0;
|
||||
|
||||
Bitboard pawns = pos.pieces(Us, PAWN);
|
||||
if (pawns)
|
||||
while (!(DistanceRingsBB[ksq][minKPdistance[Us]++] & pawns)) {}
|
||||
while (!(DistanceRingBB[ksq][minKingPawnDistance[Us]++] & pawns)) {}
|
||||
|
||||
if (relative_rank(Us, ksq) > RANK_4)
|
||||
return make_score(0, -16 * minKPdistance[Us]);
|
||||
return make_score(0, -16 * minKingPawnDistance[Us]);
|
||||
|
||||
Value bonus = shelter_storm<Us>(pos, ksq);
|
||||
|
||||
@@ -282,7 +302,7 @@ Score Entry::do_king_safety(const Position& pos, Square ksq) {
|
||||
if (pos.can_castle(MakeCastling<Us, QUEEN_SIDE>::right))
|
||||
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1)));
|
||||
|
||||
return make_score(bonus, -16 * minKPdistance[Us]);
|
||||
return make_score(bonus, -16 * minKingPawnDistance[Us]);
|
||||
}
|
||||
|
||||
// Explicit template instantiation
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -32,9 +32,10 @@ namespace Pawns {
|
||||
|
||||
struct Entry {
|
||||
|
||||
Score pawns_value() const { return value; }
|
||||
Score pawns_score() const { return score; }
|
||||
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
|
||||
Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
|
||||
int pawn_span(Color c) const { return pawnSpan[c]; }
|
||||
|
||||
int semiopen_file(Color c, File f) const {
|
||||
return semiopenFiles[c] & (1 << f);
|
||||
@@ -44,10 +45,6 @@ struct Entry {
|
||||
return semiopenFiles[c] & (leftSide ? (1 << f) - 1 : ~((1 << (f + 1)) - 1));
|
||||
}
|
||||
|
||||
int pawn_span(Color c) const {
|
||||
return pawnSpan[c];
|
||||
}
|
||||
|
||||
int pawns_on_same_color_squares(Color c, Square s) const {
|
||||
return pawnsOnSquares[c][!!(DarkSquares & s)];
|
||||
}
|
||||
@@ -65,12 +62,12 @@ struct Entry {
|
||||
Value shelter_storm(const Position& pos, Square ksq);
|
||||
|
||||
Key key;
|
||||
Score value;
|
||||
Score score;
|
||||
Bitboard passedPawns[COLOR_NB];
|
||||
Bitboard pawnAttacks[COLOR_NB];
|
||||
Square kingSquares[COLOR_NB];
|
||||
Score kingSafety[COLOR_NB];
|
||||
int minKPdistance[COLOR_NB];
|
||||
int minKingPawnDistance[COLOR_NB];
|
||||
int castlingRights[COLOR_NB];
|
||||
int semiopenFiles[COLOR_NB];
|
||||
int pawnSpan[COLOR_NB];
|
||||
@@ -79,7 +76,8 @@ struct Entry {
|
||||
|
||||
typedef HashTable<Entry, 16384> Table;
|
||||
|
||||
Entry* probe(const Position& pos, Table& entries);
|
||||
void init();
|
||||
Entry* probe(const Position& pos);
|
||||
|
||||
} // namespace Pawns
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -19,31 +19,25 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <cstring> // For std::memset
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "bitcount.h"
|
||||
#include "misc.h"
|
||||
#include "movegen.h"
|
||||
#include "notation.h"
|
||||
#include "position.h"
|
||||
#include "psqtab.h"
|
||||
#include "rkiss.h"
|
||||
#include "thread.h"
|
||||
#include "tt.h"
|
||||
#include "uci.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
static const string PieceToChar(" PNBRQK pnbrqk");
|
||||
|
||||
CACHE_LINE_ALIGNMENT
|
||||
|
||||
Value PieceValue[PHASE_NB][PIECE_NB] = {
|
||||
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
|
||||
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
|
||||
|
||||
static Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
|
||||
|
||||
namespace Zobrist {
|
||||
|
||||
Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
|
||||
@@ -57,6 +51,9 @@ Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion;}
|
||||
|
||||
namespace {
|
||||
|
||||
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
|
||||
// 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.
|
||||
@@ -108,6 +105,30 @@ CheckInfo::CheckInfo(const Position& pos) {
|
||||
}
|
||||
|
||||
|
||||
/// operator<<(Position) returns an ASCII representation of the position
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Position& pos) {
|
||||
|
||||
os << "\n +---+---+---+---+---+---+---+---+\n";
|
||||
|
||||
for (Rank r = RANK_8; r >= RANK_1; --r)
|
||||
{
|
||||
for (File f = FILE_A; f <= FILE_H; ++f)
|
||||
os << " | " << PieceToChar[pos.piece_on(make_square(f, r))];
|
||||
|
||||
os << " |\n +---+---+---+---+---+---+---+---+\n";
|
||||
}
|
||||
|
||||
os << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase
|
||||
<< std::setfill('0') << std::setw(16) << pos.st->key << std::dec << "\nCheckers: ";
|
||||
|
||||
for (Bitboard b = pos.checkers(); b; )
|
||||
os << UCI::square(pop_lsb(&b)) << " ";
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
/// 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:
|
||||
/// Firstly, the white halves of the tables are copied from PSQT[] tables.
|
||||
@@ -116,28 +137,28 @@ CheckInfo::CheckInfo(const Position& pos) {
|
||||
|
||||
void Position::init() {
|
||||
|
||||
RKISS rk;
|
||||
PRNG rng(1070372);
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (PieceType pt = PAWN; pt <= KING; ++pt)
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
Zobrist::psq[c][pt][s] = rk.rand<Key>();
|
||||
Zobrist::psq[c][pt][s] = rng.rand<Key>();
|
||||
|
||||
for (File f = FILE_A; f <= FILE_H; ++f)
|
||||
Zobrist::enpassant[f] = rk.rand<Key>();
|
||||
Zobrist::enpassant[f] = rng.rand<Key>();
|
||||
|
||||
for (int cf = NO_CASTLING; cf <= ANY_CASTLING; ++cf)
|
||||
for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr)
|
||||
{
|
||||
Bitboard b = cf;
|
||||
Bitboard b = cr;
|
||||
while (b)
|
||||
{
|
||||
Key k = Zobrist::castling[1ULL << pop_lsb(&b)];
|
||||
Zobrist::castling[cf] ^= k ? k : rk.rand<Key>();
|
||||
Zobrist::castling[cr] ^= k ? k : rng.rand<Key>();
|
||||
}
|
||||
}
|
||||
|
||||
Zobrist::side = rk.rand<Key>();
|
||||
Zobrist::exclusion = rk.rand<Key>();
|
||||
Zobrist::side = rng.rand<Key>();
|
||||
Zobrist::exclusion = rng.rand<Key>();
|
||||
|
||||
for (PieceType pt = PAWN; pt <= KING; ++pt)
|
||||
{
|
||||
@@ -155,9 +176,8 @@ void Position::init() {
|
||||
}
|
||||
|
||||
|
||||
/// Position::operator=() creates a copy of 'pos'. We want the new born Position
|
||||
/// object to not depend on any external data so we detach state pointer from
|
||||
/// the source one.
|
||||
/// Position::operator=() creates a copy of 'pos' but detaching the state pointer
|
||||
/// from the source to be self-consistent and not depending on any external data.
|
||||
|
||||
Position& Position::operator=(const Position& pos) {
|
||||
|
||||
@@ -342,7 +362,7 @@ void Position::set_castling_right(Color c, Square rfrom) {
|
||||
void Position::set_state(StateInfo* si) const {
|
||||
|
||||
si->key = si->pawnKey = si->materialKey = 0;
|
||||
si->npMaterial[WHITE] = si->npMaterial[BLACK] = VALUE_ZERO;
|
||||
si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO;
|
||||
si->psq = SCORE_ZERO;
|
||||
|
||||
si->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove);
|
||||
@@ -376,7 +396,7 @@ void Position::set_state(StateInfo* si) const {
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (PieceType pt = KNIGHT; pt <= QUEEN; ++pt)
|
||||
si->npMaterial[c] += pieceCount[c][pt] * PieceValue[MG][pt];
|
||||
si->nonPawnMaterial[c] += pieceCount[c][pt] * PieceValue[MG][pt];
|
||||
}
|
||||
|
||||
|
||||
@@ -409,63 +429,37 @@ const string Position::fen() const {
|
||||
ss << (sideToMove == WHITE ? " w " : " b ");
|
||||
|
||||
if (can_castle(WHITE_OO))
|
||||
ss << (chess960 ? to_char(file_of(castling_rook_square(WHITE | KING_SIDE)), false) : 'K');
|
||||
ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | KING_SIDE))) : 'K');
|
||||
|
||||
if (can_castle(WHITE_OOO))
|
||||
ss << (chess960 ? to_char(file_of(castling_rook_square(WHITE | QUEEN_SIDE)), false) : 'Q');
|
||||
ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | QUEEN_SIDE))) : 'Q');
|
||||
|
||||
if (can_castle(BLACK_OO))
|
||||
ss << (chess960 ? to_char(file_of(castling_rook_square(BLACK | KING_SIDE)), true) : 'k');
|
||||
ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | KING_SIDE))) : 'k');
|
||||
|
||||
if (can_castle(BLACK_OOO))
|
||||
ss << (chess960 ? to_char(file_of(castling_rook_square(BLACK | QUEEN_SIDE)), true) : 'q');
|
||||
ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE))) : 'q');
|
||||
|
||||
if (!can_castle(WHITE) && !can_castle(BLACK))
|
||||
ss << '-';
|
||||
|
||||
ss << (ep_square() == SQ_NONE ? " - " : " " + to_string(ep_square()) + " ")
|
||||
ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ")
|
||||
<< st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
/// Position::pretty() returns an ASCII representation of the position
|
||||
|
||||
const string Position::pretty() const {
|
||||
|
||||
std::ostringstream ss;
|
||||
|
||||
ss << "\n +---+---+---+---+---+---+---+---+\n";
|
||||
|
||||
for (Rank r = RANK_8; r >= RANK_1; --r)
|
||||
{
|
||||
for (File f = FILE_A; f <= FILE_H; ++f)
|
||||
ss << " | " << PieceToChar[piece_on(make_square(f, r))];
|
||||
|
||||
ss << " |\n +---+---+---+---+---+---+---+---+\n";
|
||||
}
|
||||
|
||||
ss << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase
|
||||
<< std::setfill('0') << std::setw(16) << st->key << "\nCheckers: ";
|
||||
|
||||
for (Bitboard b = checkers(); b; )
|
||||
ss << to_string(pop_lsb(&b)) << " ";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
/// Position::game_phase() calculates the game phase interpolating total non-pawn
|
||||
/// material between endgame and midgame limits.
|
||||
|
||||
Phase Position::game_phase() const {
|
||||
|
||||
Value npm = st->npMaterial[WHITE] + st->npMaterial[BLACK];
|
||||
Value npm = st->nonPawnMaterial[WHITE] + st->nonPawnMaterial[BLACK];
|
||||
|
||||
npm = std::max(EndgameLimit, std::min(npm, MidgameLimit));
|
||||
|
||||
return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
|
||||
return Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
|
||||
}
|
||||
|
||||
|
||||
@@ -497,16 +491,16 @@ Bitboard Position::check_blockers(Color c, Color kingColor) const {
|
||||
|
||||
|
||||
/// Position::attackers_to() computes a bitboard of all pieces which attack a
|
||||
/// given square. Slider attacks use the occ bitboard to indicate occupancy.
|
||||
/// given square. Slider attacks use the occupied bitboard to indicate occupancy.
|
||||
|
||||
Bitboard Position::attackers_to(Square s, Bitboard occ) const {
|
||||
Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
|
||||
|
||||
return (attacks_from<PAWN>(s, BLACK) & pieces(WHITE, PAWN))
|
||||
| (attacks_from<PAWN>(s, WHITE) & pieces(BLACK, PAWN))
|
||||
| (attacks_from<KNIGHT>(s) & pieces(KNIGHT))
|
||||
| (attacks_bb<ROOK>(s, occ) & pieces(ROOK, QUEEN))
|
||||
| (attacks_bb<BISHOP>(s, occ) & pieces(BISHOP, QUEEN))
|
||||
| (attacks_from<KING>(s) & pieces(KING));
|
||||
return (attacks_from<PAWN>(s, BLACK) & pieces(WHITE, PAWN))
|
||||
| (attacks_from<PAWN>(s, WHITE) & pieces(BLACK, PAWN))
|
||||
| (attacks_from<KNIGHT>(s) & pieces(KNIGHT))
|
||||
| (attacks_bb<ROOK>(s, occupied) & pieces(ROOK, QUEEN))
|
||||
| (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN))
|
||||
| (attacks_from<KING>(s) & pieces(KING));
|
||||
}
|
||||
|
||||
|
||||
@@ -531,15 +525,15 @@ bool Position::legal(Move m, Bitboard pinned) const {
|
||||
Square ksq = king_square(us);
|
||||
Square to = to_sq(m);
|
||||
Square capsq = to - pawn_push(us);
|
||||
Bitboard occ = (pieces() ^ from ^ capsq) | to;
|
||||
Bitboard occupied = (pieces() ^ from ^ capsq) | to;
|
||||
|
||||
assert(to == ep_square());
|
||||
assert(moved_piece(m) == make_piece(us, PAWN));
|
||||
assert(piece_on(capsq) == make_piece(~us, PAWN));
|
||||
assert(piece_on(to) == NO_PIECE);
|
||||
|
||||
return !(attacks_bb< ROOK>(ksq, occ) & pieces(~us, QUEEN, ROOK))
|
||||
&& !(attacks_bb<BISHOP>(ksq, occ) & pieces(~us, QUEEN, BISHOP));
|
||||
return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK))
|
||||
&& !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP));
|
||||
}
|
||||
|
||||
// If the moving piece is a king, check whether the destination
|
||||
@@ -647,7 +641,7 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const {
|
||||
return true;
|
||||
|
||||
// Is there a discovered check?
|
||||
if ( unlikely(ci.dcCandidates)
|
||||
if ( ci.dcCandidates
|
||||
&& (ci.dcCandidates & from)
|
||||
&& !aligned(from, to, ci.ksq))
|
||||
return true;
|
||||
@@ -772,7 +766,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||
st->pawnKey ^= Zobrist::psq[them][PAWN][capsq];
|
||||
}
|
||||
else
|
||||
st->npMaterial[them] -= PieceValue[MG][captured];
|
||||
st->nonPawnMaterial[them] -= PieceValue[MG][captured];
|
||||
|
||||
// Update board and piece lists
|
||||
remove_piece(capsq, them, captured);
|
||||
@@ -842,7 +836,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||
st->psq += psq[us][promotion][to] - psq[us][PAWN][to];
|
||||
|
||||
// Update material
|
||||
st->npMaterial[us] += PieceValue[MG][promotion];
|
||||
st->nonPawnMaterial[us] += PieceValue[MG][promotion];
|
||||
}
|
||||
|
||||
// Update pawn hash key and prefetch access to pawnsTable
|
||||
@@ -876,7 +870,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||
st->checkersBB |= to;
|
||||
|
||||
// Discovered checks
|
||||
if (unlikely(ci.dcCandidates) && (ci.dcCandidates & from))
|
||||
if (ci.dcCandidates && (ci.dcCandidates & from))
|
||||
{
|
||||
if (pt != ROOK)
|
||||
st->checkersBB |= attacks_from<ROOK>(king_square(them)) & pieces(us, QUEEN, ROOK);
|
||||
@@ -1013,7 +1007,7 @@ void Position::undo_null_move() {
|
||||
}
|
||||
|
||||
|
||||
/// Position::key_after() computes the new hash key after the given moven. Needed
|
||||
/// Position::key_after() computes the new hash key after the given move. Needed
|
||||
/// for speculative prefetch. It doesn't recognize special moves like castling,
|
||||
/// en-passant and promotions.
|
||||
|
||||
@@ -1239,8 +1233,8 @@ bool Position::pos_is_ok(int* step) const {
|
||||
if ( st->key != si.key
|
||||
|| st->pawnKey != si.pawnKey
|
||||
|| st->materialKey != si.materialKey
|
||||
|| st->npMaterial[WHITE] != si.npMaterial[WHITE]
|
||||
|| st->npMaterial[BLACK] != si.npMaterial[BLACK]
|
||||
|| st->nonPawnMaterial[WHITE] != si.nonPawnMaterial[WHITE]
|
||||
|| st->nonPawnMaterial[BLACK] != si.nonPawnMaterial[BLACK]
|
||||
|| st->psq != si.psq
|
||||
|| st->checkersBB != si.checkersBB)
|
||||
return false;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -21,17 +21,18 @@
|
||||
#define POSITION_H_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstddef> // For offsetof()
|
||||
#include <string>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "bitcount.h"
|
||||
#include "types.h"
|
||||
|
||||
/// The checkInfo struct is initialized at c'tor time and keeps info used
|
||||
/// to detect if a move gives check.
|
||||
class Position;
|
||||
struct Thread;
|
||||
|
||||
/// CheckInfo struct is initialized at c'tor time and keeps info used to detect
|
||||
/// if a move gives check.
|
||||
|
||||
struct CheckInfo {
|
||||
|
||||
explicit CheckInfo(const Position&);
|
||||
@@ -39,51 +40,61 @@ struct CheckInfo {
|
||||
Bitboard dcCandidates;
|
||||
Bitboard pinned;
|
||||
Bitboard checkSq[PIECE_TYPE_NB];
|
||||
Square ksq;
|
||||
Square ksq;
|
||||
};
|
||||
|
||||
|
||||
/// The StateInfo struct stores information needed to restore a Position
|
||||
/// object to its previous state when we retract a move. Whenever a move
|
||||
/// is made on the board (by calling Position::do_move), a StateInfo
|
||||
/// object must be passed as a parameter.
|
||||
/// StateInfo struct stores information needed to restore a Position object to
|
||||
/// its previous state when we retract a move. Whenever a move is made on the
|
||||
/// board (by calling Position::do_move), a StateInfo object must be passed.
|
||||
|
||||
struct StateInfo {
|
||||
Key pawnKey, materialKey;
|
||||
Value npMaterial[COLOR_NB];
|
||||
int castlingRights, rule50, pliesFromNull;
|
||||
Score psq;
|
||||
|
||||
// Copied when making a move
|
||||
Key pawnKey;
|
||||
Key materialKey;
|
||||
Value nonPawnMaterial[COLOR_NB];
|
||||
int castlingRights;
|
||||
int rule50;
|
||||
int pliesFromNull;
|
||||
Score psq;
|
||||
Square epSquare;
|
||||
|
||||
Key key;
|
||||
Bitboard checkersBB;
|
||||
PieceType capturedType;
|
||||
// Not copied when making a move
|
||||
Key key;
|
||||
Bitboard checkersBB;
|
||||
PieceType capturedType;
|
||||
StateInfo* previous;
|
||||
};
|
||||
|
||||
|
||||
/// When making a move the current StateInfo up to 'key' excluded is copied to
|
||||
/// the new one. Here we calculate the quad words (64bits) needed to be copied.
|
||||
/// 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;
|
||||
|
||||
|
||||
/// The Position class stores the information regarding the board representation
|
||||
/// like pieces, side to move, hash keys, castling info, etc. The most important
|
||||
/// methods are do_move() and undo_move(), used by the search to update node info
|
||||
/// when traversing the search tree.
|
||||
/// Position class stores information regarding the board representation as
|
||||
/// 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
|
||||
/// traversing the search tree.
|
||||
|
||||
class Position {
|
||||
|
||||
friend std::ostream& operator<<(std::ostream&, const Position&);
|
||||
|
||||
Position(const Position&); // Disable the default copy constructor
|
||||
|
||||
public:
|
||||
Position() {}
|
||||
Position(const Position& pos, Thread* t) { *this = pos; thisThread = t; }
|
||||
Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); }
|
||||
Position& operator=(const Position&);
|
||||
static void init();
|
||||
|
||||
// Text input/output
|
||||
Position() {} // To define the global object RootPos
|
||||
Position(const Position& pos, Thread* th) { *this = pos; thisThread = th; }
|
||||
Position(const std::string& f, bool c960, Thread* th) { set(f, c960, th); }
|
||||
Position& operator=(const Position&); // To assign RootPos from UCI
|
||||
|
||||
// FEN string input/output
|
||||
void set(const std::string& fenStr, bool isChess960, Thread* th);
|
||||
const std::string fen() const;
|
||||
const std::string pretty() const;
|
||||
|
||||
// Position representation
|
||||
Bitboard pieces() const;
|
||||
@@ -98,7 +109,6 @@ public:
|
||||
bool empty(Square s) const;
|
||||
template<PieceType Pt> int count(Color c) const;
|
||||
template<PieceType Pt> const Square* list(Color c) const;
|
||||
int total_piece_count() const;
|
||||
|
||||
// Castling
|
||||
int can_castle(Color c) const;
|
||||
@@ -113,7 +123,7 @@ public:
|
||||
|
||||
// Attacks to/from a given square
|
||||
Bitboard attackers_to(Square s) const;
|
||||
Bitboard attackers_to(Square s, Bitboard occ) const;
|
||||
Bitboard attackers_to(Square s, Bitboard occupied) const;
|
||||
Bitboard attacks_from(Piece pc, Square s) const;
|
||||
template<PieceType> Bitboard attacks_from(Square s) const;
|
||||
template<PieceType> Bitboard attacks_from(Square s, Color c) const;
|
||||
@@ -131,7 +141,6 @@ public:
|
||||
// Piece specific
|
||||
bool pawn_passed(Color c, Square s) const;
|
||||
bool pawn_on_7th(Color c) const;
|
||||
bool bishop_pair(Color c) const;
|
||||
bool opposite_bishops() const;
|
||||
|
||||
// Doing and undoing moves
|
||||
@@ -149,12 +158,8 @@ public:
|
||||
Key key() const;
|
||||
Key key_after(Move m) const;
|
||||
Key exclusion_key() const;
|
||||
Key pawn_key() const;
|
||||
Key material_key() const;
|
||||
|
||||
// Incremental piece-square evaluation
|
||||
Score psq_score() const;
|
||||
Value non_pawn_material(Color c) const;
|
||||
Key pawn_key() const;
|
||||
|
||||
// Other properties of the position
|
||||
Color side_to_move() const;
|
||||
@@ -166,6 +171,8 @@ public:
|
||||
void set_nodes_searched(uint64_t n);
|
||||
bool is_draw() const;
|
||||
int rule50_count() const;
|
||||
Score psq_score() const;
|
||||
Value non_pawn_material(Color c) const;
|
||||
|
||||
// Position consistency check, for debugging
|
||||
bool pos_is_ok(int* step = NULL) const;
|
||||
@@ -177,7 +184,7 @@ private:
|
||||
void set_castling_right(Color c, Square rfrom);
|
||||
void set_state(StateInfo* si) const;
|
||||
|
||||
// Helper functions
|
||||
// Other helpers
|
||||
Bitboard check_blockers(Color c, Color kingColor) const;
|
||||
void put_piece(Square s, Color c, PieceType pt);
|
||||
void remove_piece(Square s, Color c, PieceType pt);
|
||||
@@ -185,15 +192,13 @@ private:
|
||||
template<bool Do>
|
||||
void do_castling(Square from, Square& to, Square& rfrom, Square& rto);
|
||||
|
||||
// Board and pieces
|
||||
// Data members
|
||||
Piece board[SQUARE_NB];
|
||||
Bitboard byTypeBB[PIECE_TYPE_NB];
|
||||
Bitboard byColorBB[COLOR_NB];
|
||||
int pieceCount[COLOR_NB][PIECE_TYPE_NB];
|
||||
Square pieceList[COLOR_NB][PIECE_TYPE_NB][16];
|
||||
int index[SQUARE_NB];
|
||||
|
||||
// Other info
|
||||
int castlingRightsMask[SQUARE_NB];
|
||||
Square castlingRookSquare[CASTLING_RIGHT_NB];
|
||||
Bitboard castlingPath[CASTLING_RIGHT_NB];
|
||||
@@ -206,12 +211,12 @@ private:
|
||||
bool chess960;
|
||||
};
|
||||
|
||||
inline uint64_t Position::nodes_searched() const {
|
||||
return nodes;
|
||||
inline Color Position::side_to_move() const {
|
||||
return sideToMove;
|
||||
}
|
||||
|
||||
inline void Position::set_nodes_searched(uint64_t n) {
|
||||
nodes = n;
|
||||
inline bool Position::empty(Square s) const {
|
||||
return board[s] == NO_PIECE;
|
||||
}
|
||||
|
||||
inline Piece Position::piece_on(Square s) const {
|
||||
@@ -222,14 +227,6 @@ inline Piece Position::moved_piece(Move m) const {
|
||||
return board[from_sq(m)];
|
||||
}
|
||||
|
||||
inline bool Position::empty(Square s) const {
|
||||
return board[s] == NO_PIECE;
|
||||
}
|
||||
|
||||
inline Color Position::side_to_move() const {
|
||||
return sideToMove;
|
||||
}
|
||||
|
||||
inline Bitboard Position::pieces() const {
|
||||
return byTypeBB[ALL_PIECES];
|
||||
}
|
||||
@@ -262,14 +259,14 @@ template<PieceType Pt> inline const Square* Position::list(Color c) const {
|
||||
return pieceList[c][Pt];
|
||||
}
|
||||
|
||||
inline Square Position::ep_square() const {
|
||||
return st->epSquare;
|
||||
}
|
||||
|
||||
inline Square Position::king_square(Color c) const {
|
||||
return pieceList[c][KING][0];
|
||||
}
|
||||
|
||||
inline Square Position::ep_square() const {
|
||||
return st->epSquare;
|
||||
}
|
||||
|
||||
inline int Position::can_castle(CastlingRight cr) const {
|
||||
return st->castlingRights & cr;
|
||||
}
|
||||
@@ -288,7 +285,6 @@ inline Square Position::castling_rook_square(CastlingRight cr) const {
|
||||
|
||||
template<PieceType Pt>
|
||||
inline Bitboard Position::attacks_from(Square s) const {
|
||||
|
||||
return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, byTypeBB[ALL_PIECES])
|
||||
: Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s)
|
||||
: StepAttacksBB[Pt][s];
|
||||
@@ -345,7 +341,7 @@ inline Score Position::psq_score() const {
|
||||
}
|
||||
|
||||
inline Value Position::non_pawn_material(Color c) const {
|
||||
return st->npMaterial[c];
|
||||
return st->nonPawnMaterial[c];
|
||||
}
|
||||
|
||||
inline int Position::game_ply() const {
|
||||
@@ -356,23 +352,20 @@ inline int Position::rule50_count() const {
|
||||
return st->rule50;
|
||||
}
|
||||
|
||||
inline int Position::total_piece_count() const {
|
||||
return HasPopCnt ? popcount<Full>(pieces()) : pieceCount[WHITE][ALL_PIECES];
|
||||
inline uint64_t Position::nodes_searched() const {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
inline void Position::set_nodes_searched(uint64_t n) {
|
||||
nodes = n;
|
||||
}
|
||||
|
||||
inline bool Position::opposite_bishops() const {
|
||||
|
||||
return pieceCount[WHITE][BISHOP] == 1
|
||||
&& pieceCount[BLACK][BISHOP] == 1
|
||||
&& opposite_colors(pieceList[WHITE][BISHOP][0], pieceList[BLACK][BISHOP][0]);
|
||||
}
|
||||
|
||||
inline bool Position::bishop_pair(Color c) const {
|
||||
|
||||
return pieceCount[c][BISHOP] >= 2
|
||||
&& opposite_colors(pieceList[c][BISHOP][0], pieceList[c][BISHOP][1]);
|
||||
}
|
||||
|
||||
inline bool Position::pawn_on_7th(Color c) const {
|
||||
return pieces(c, PAWN) & rank_bb(relative_rank(c, RANK_7));
|
||||
}
|
||||
@@ -389,7 +382,7 @@ inline bool Position::capture_or_promotion(Move m) const {
|
||||
|
||||
inline bool Position::capture(Move m) const {
|
||||
|
||||
// Note that castling is encoded as "king captures the rook"
|
||||
// Castling is encoded as "king captures the rook"
|
||||
assert(is_ok(m));
|
||||
return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT;
|
||||
}
|
||||
@@ -410,14 +403,13 @@ inline void Position::put_piece(Square s, Color c, PieceType pt) {
|
||||
byColorBB[c] |= s;
|
||||
index[s] = pieceCount[c][pt]++;
|
||||
pieceList[c][pt][index[s]] = s;
|
||||
if (!HasPopCnt)
|
||||
pieceCount[WHITE][ALL_PIECES]++;
|
||||
pieceCount[c][ALL_PIECES]++;
|
||||
}
|
||||
|
||||
inline void Position::move_piece(Square from, Square to, Color c, PieceType pt) {
|
||||
|
||||
// index[from] is not updated and becomes stale. This works as long
|
||||
// as index[] is accessed just by known occupied squares.
|
||||
// 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;
|
||||
@@ -437,13 +429,12 @@ inline void Position::remove_piece(Square s, Color c, PieceType pt) {
|
||||
byTypeBB[ALL_PIECES] ^= s;
|
||||
byTypeBB[pt] ^= s;
|
||||
byColorBB[c] ^= s;
|
||||
/* board[s] = NO_PIECE; */ // Not needed, will be overwritten by capturing
|
||||
/* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */
|
||||
Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]];
|
||||
index[lastSquare] = index[s];
|
||||
pieceList[c][pt][index[lastSquare]] = lastSquare;
|
||||
pieceList[c][pt][pieceCount[c][pt]] = SQ_NONE;
|
||||
if (!HasPopCnt)
|
||||
pieceCount[WHITE][ALL_PIECES]--;
|
||||
pieceCount[c][ALL_PIECES]--;
|
||||
}
|
||||
|
||||
#endif // #ifndef POSITION_H_INCLUDED
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 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/>.
|
||||
|
||||
This file is based on original code by Heinz van Saanen and is
|
||||
available under 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.
|
||||
*/
|
||||
|
||||
#ifndef RKISS_H_INCLUDED
|
||||
#define RKISS_H_INCLUDED
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/// RKISS is our pseudo random number generator (PRNG) used to compute hash keys.
|
||||
/// George Marsaglia invented the RNG-Kiss-family in the early 90's. This is a
|
||||
/// specific version that Heinz van Saanen derived from some public domain code
|
||||
/// by Bob Jenkins. Following the feature list, as tested by Heinz.
|
||||
///
|
||||
/// - Quite platform independent
|
||||
/// - Passes ALL dieharder tests! Here *nix sys-rand() e.g. fails miserably:-)
|
||||
/// - ~12 times faster than my *nix sys-rand()
|
||||
/// - ~4 times faster than SSE2-version of Mersenne twister
|
||||
/// - Average cycle length: ~2^126
|
||||
/// - 64 bit seed
|
||||
/// - Return doubles with a full 53 bit mantissa
|
||||
/// - Thread safe
|
||||
|
||||
class RKISS {
|
||||
|
||||
uint64_t a, b, c, d;
|
||||
|
||||
uint64_t rotate_L(uint64_t x, unsigned k) const {
|
||||
return (x << k) | (x >> (64 - k));
|
||||
}
|
||||
|
||||
uint64_t rand64() {
|
||||
|
||||
const uint64_t e = a - rotate_L(b, 7);
|
||||
a = b ^ rotate_L(c, 13);
|
||||
b = c + rotate_L(d, 37);
|
||||
c = d + e;
|
||||
return d = e + a;
|
||||
}
|
||||
|
||||
public:
|
||||
RKISS(int seed = 73) {
|
||||
|
||||
a = 0xF1EA5EED, b = c = d = 0xD4E12C77;
|
||||
|
||||
for (int i = 0; i < seed; ++i) // Scramble a few rounds
|
||||
rand64();
|
||||
}
|
||||
|
||||
template<typename T> T rand() { return T(rand64()); }
|
||||
|
||||
/// Special generator used to fast init magic numbers. Here the
|
||||
/// trick is to rotate the randoms of a given quantity 's' known
|
||||
/// to be optimal to quickly find a good magic candidate.
|
||||
template<typename T> T magic_rand(int s) {
|
||||
return rotate_L(rotate_L(rand<T>(), (s >> 0) & 0x3F) & rand<T>()
|
||||
, (s >> 6) & 0x3F) & rand<T>();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // #ifndef RKISS_H_INCLUDED
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -20,7 +20,7 @@
|
||||
#ifndef SEARCH_H_INCLUDED
|
||||
#define SEARCH_H_INCLUDED
|
||||
|
||||
#include <memory>
|
||||
#include <memory> // For std::auto_ptr
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
|
||||
@@ -32,12 +32,13 @@ struct SplitPoint;
|
||||
|
||||
namespace Search {
|
||||
|
||||
/// The Stack struct keeps track of the information we need to remember from
|
||||
/// nodes shallower and deeper in the tree during the search. Each search thread
|
||||
/// has its own array of Stack objects, indexed by the current ply.
|
||||
/// Stack struct keeps track of the information we need to remember from nodes
|
||||
/// shallower and deeper in the tree during the search. Each search thread has
|
||||
/// its own array of Stack objects, indexed by the current ply.
|
||||
|
||||
struct Stack {
|
||||
SplitPoint* splitPoint;
|
||||
Move* pv;
|
||||
int ply;
|
||||
Move currentMove;
|
||||
Move ttMove;
|
||||
@@ -45,49 +46,49 @@ struct Stack {
|
||||
Move killers[2];
|
||||
Depth reduction;
|
||||
Value staticEval;
|
||||
bool skipNullMove;
|
||||
bool skipEarlyPruning;
|
||||
};
|
||||
|
||||
/// RootMove struct is used for moves at the root of the tree. For each root move
|
||||
/// we store a score and a PV (really a refutation in the case of moves which
|
||||
/// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves.
|
||||
|
||||
/// RootMove struct is used for moves at the root of the tree. For each root
|
||||
/// move we store a score, a node count, and a PV (really a refutation in the
|
||||
/// case of moves which fail low). Score is normally set at -VALUE_INFINITE for
|
||||
/// all non-pv moves.
|
||||
struct RootMove {
|
||||
|
||||
RootMove(Move m) : score(-VALUE_INFINITE), prevScore(-VALUE_INFINITE) {
|
||||
pv.push_back(m); pv.push_back(MOVE_NONE);
|
||||
}
|
||||
RootMove(Move m) : score(-VALUE_INFINITE), previousScore(-VALUE_INFINITE), pv(1, m) {}
|
||||
|
||||
bool operator<(const RootMove& m) const { return score > m.score; } // Ascending sort
|
||||
bool operator==(const Move& m) const { return pv[0] == m; }
|
||||
|
||||
void extract_pv_from_tt(Position& pos);
|
||||
void insert_pv_in_tt(Position& pos);
|
||||
Move extract_ponder_from_tt(Position& pos);
|
||||
|
||||
Value score;
|
||||
Value prevScore;
|
||||
Value previousScore;
|
||||
std::vector<Move> pv;
|
||||
};
|
||||
|
||||
typedef std::vector<RootMove> RootMoveVector;
|
||||
|
||||
/// The LimitsType struct stores information sent by GUI about available time
|
||||
/// to search the current move, maximum depth/time, if we are in analysis mode
|
||||
/// or if we have to ponder while it's our opponent's turn to move.
|
||||
/// LimitsType struct stores information sent by GUI about available time to
|
||||
/// search the current move, maximum depth/time, if we are in analysis mode or
|
||||
/// if we have to ponder while it's our opponent's turn to move.
|
||||
|
||||
struct LimitsType {
|
||||
|
||||
LimitsType() { // Using memset on a std::vector is undefined behavior
|
||||
time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = movestogo =
|
||||
depth = nodes = movetime = mate = infinite = ponder = 0;
|
||||
LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC
|
||||
nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = movestogo =
|
||||
depth = movetime = mate = infinite = ponder = 0;
|
||||
}
|
||||
|
||||
bool use_time_management() const {
|
||||
return !(mate | movetime | depth | nodes | infinite);
|
||||
}
|
||||
bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); }
|
||||
|
||||
std::vector<Move> searchmoves;
|
||||
int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder;
|
||||
int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, movetime, mate, infinite, ponder;
|
||||
int64_t nodes;
|
||||
};
|
||||
|
||||
|
||||
/// The SignalsType struct stores volatile flags updated during the search
|
||||
/// typically in an async fashion e.g. to stop the search by the GUI.
|
||||
|
||||
@@ -99,13 +100,13 @@ typedef std::auto_ptr<std::stack<StateInfo> > StateStackPtr;
|
||||
|
||||
extern volatile SignalsType Signals;
|
||||
extern LimitsType Limits;
|
||||
extern std::vector<RootMove> RootMoves;
|
||||
extern RootMoveVector RootMoves;
|
||||
extern Position RootPos;
|
||||
extern Time::point SearchTime;
|
||||
extern StateStackPtr SetupStates;
|
||||
|
||||
extern void init();
|
||||
extern void think();
|
||||
void init();
|
||||
void think();
|
||||
template<bool Root> uint64_t perft(Position& pos, Depth depth);
|
||||
|
||||
} // namespace Search
|
||||
|
||||
1381
DroidFish/jni/stockfish/syzygy/tbcore.cpp
Normal file
1381
DroidFish/jni/stockfish/syzygy/tbcore.cpp
Normal file
File diff suppressed because it is too large
Load Diff
169
DroidFish/jni/stockfish/syzygy/tbcore.h
Normal file
169
DroidFish/jni/stockfish/syzygy/tbcore.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
Copyright (c) 2011-2013 Ronald de Man
|
||||
*/
|
||||
|
||||
#ifndef TBCORE_H
|
||||
#define TBCORE_H
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <pthread.h>
|
||||
#define SEP_CHAR ':'
|
||||
#define FD int
|
||||
#define FD_ERR -1
|
||||
#else
|
||||
#include <windows.h>
|
||||
#define SEP_CHAR ';'
|
||||
#define FD HANDLE
|
||||
#define FD_ERR INVALID_HANDLE_VALUE
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#define LOCK_T pthread_mutex_t
|
||||
#define LOCK_INIT(x) pthread_mutex_init(&(x), NULL)
|
||||
#define LOCK(x) pthread_mutex_lock(&(x))
|
||||
#define UNLOCK(x) pthread_mutex_unlock(&(x))
|
||||
#else
|
||||
#define LOCK_T HANDLE
|
||||
#define LOCK_INIT(x) do { x = CreateMutex(NULL, FALSE, NULL); } while (0)
|
||||
#define LOCK(x) WaitForSingleObject(x, INFINITE)
|
||||
#define UNLOCK(x) ReleaseMutex(x)
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#define BSWAP32(v) __builtin_bswap32(v)
|
||||
#define BSWAP64(v) __builtin_bswap64(v)
|
||||
#else
|
||||
#define BSWAP32(v) _byteswap_ulong(v)
|
||||
#define BSWAP64(v) _byteswap_uint64(v)
|
||||
#endif
|
||||
|
||||
#define WDLSUFFIX ".rtbw"
|
||||
#define DTZSUFFIX ".rtbz"
|
||||
#define WDLDIR "RTBWDIR"
|
||||
#define DTZDIR "RTBZDIR"
|
||||
#define TBPIECES 6
|
||||
|
||||
typedef unsigned long long uint64;
|
||||
typedef unsigned int uint32;
|
||||
typedef unsigned char ubyte;
|
||||
typedef unsigned short ushort;
|
||||
|
||||
const ubyte WDL_MAGIC[4] = { 0x71, 0xe8, 0x23, 0x5d };
|
||||
const ubyte DTZ_MAGIC[4] = { 0xd7, 0x66, 0x0c, 0xa5 };
|
||||
|
||||
#define TBHASHBITS 10
|
||||
|
||||
struct TBHashEntry;
|
||||
|
||||
typedef uint64 base_t;
|
||||
|
||||
struct PairsData {
|
||||
char *indextable;
|
||||
ushort *sizetable;
|
||||
ubyte *data;
|
||||
ushort *offset;
|
||||
ubyte *symlen;
|
||||
ubyte *sympat;
|
||||
int blocksize;
|
||||
int idxbits;
|
||||
int min_len;
|
||||
base_t base[1]; // C++ complains about base[]...
|
||||
};
|
||||
|
||||
struct TBEntry {
|
||||
char *data;
|
||||
uint64 key;
|
||||
uint64 mapping;
|
||||
ubyte ready;
|
||||
ubyte num;
|
||||
ubyte symmetric;
|
||||
ubyte has_pawns;
|
||||
}
|
||||
#ifndef _WIN32
|
||||
__attribute__((__may_alias__))
|
||||
#endif
|
||||
;
|
||||
|
||||
struct TBEntry_piece {
|
||||
char *data;
|
||||
uint64 key;
|
||||
uint64 mapping;
|
||||
ubyte ready;
|
||||
ubyte num;
|
||||
ubyte symmetric;
|
||||
ubyte has_pawns;
|
||||
ubyte enc_type;
|
||||
struct PairsData *precomp[2];
|
||||
int factor[2][TBPIECES];
|
||||
ubyte pieces[2][TBPIECES];
|
||||
ubyte norm[2][TBPIECES];
|
||||
};
|
||||
|
||||
struct TBEntry_pawn {
|
||||
char *data;
|
||||
uint64 key;
|
||||
uint64 mapping;
|
||||
ubyte ready;
|
||||
ubyte num;
|
||||
ubyte symmetric;
|
||||
ubyte has_pawns;
|
||||
ubyte pawns[2];
|
||||
struct {
|
||||
struct PairsData *precomp[2];
|
||||
int factor[2][TBPIECES];
|
||||
ubyte pieces[2][TBPIECES];
|
||||
ubyte norm[2][TBPIECES];
|
||||
} file[4];
|
||||
};
|
||||
|
||||
struct DTZEntry_piece {
|
||||
char *data;
|
||||
uint64 key;
|
||||
uint64 mapping;
|
||||
ubyte ready;
|
||||
ubyte num;
|
||||
ubyte symmetric;
|
||||
ubyte has_pawns;
|
||||
ubyte enc_type;
|
||||
struct PairsData *precomp;
|
||||
int factor[TBPIECES];
|
||||
ubyte pieces[TBPIECES];
|
||||
ubyte norm[TBPIECES];
|
||||
ubyte flags; // accurate, mapped, side
|
||||
ushort map_idx[4];
|
||||
ubyte *map;
|
||||
};
|
||||
|
||||
struct DTZEntry_pawn {
|
||||
char *data;
|
||||
uint64 key;
|
||||
uint64 mapping;
|
||||
ubyte ready;
|
||||
ubyte num;
|
||||
ubyte symmetric;
|
||||
ubyte has_pawns;
|
||||
ubyte pawns[2];
|
||||
struct {
|
||||
struct PairsData *precomp;
|
||||
int factor[TBPIECES];
|
||||
ubyte pieces[TBPIECES];
|
||||
ubyte norm[TBPIECES];
|
||||
} file[4];
|
||||
ubyte flags[4];
|
||||
ushort map_idx[4][4];
|
||||
ubyte *map;
|
||||
};
|
||||
|
||||
struct TBHashEntry {
|
||||
uint64 key;
|
||||
struct TBEntry *ptr;
|
||||
};
|
||||
|
||||
struct DTZTableEntry {
|
||||
uint64 key1;
|
||||
uint64 key2;
|
||||
struct TBEntry *entry;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
831
DroidFish/jni/stockfish/syzygy/tbprobe.cpp
Normal file
831
DroidFish/jni/stockfish/syzygy/tbprobe.cpp
Normal file
@@ -0,0 +1,831 @@
|
||||
/*
|
||||
Copyright (c) 2013 Ronald de Man
|
||||
This file may be redistributed and/or modified without restrictions.
|
||||
|
||||
tbprobe.cpp contains the Stockfish-specific routines of the
|
||||
tablebase probing code. It should be relatively easy to adapt
|
||||
this code to other chess engines.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "../position.h"
|
||||
#include "../movegen.h"
|
||||
#include "../bitboard.h"
|
||||
#include "../search.h"
|
||||
#include "../bitcount.h"
|
||||
|
||||
#include "tbprobe.h"
|
||||
#include "tbcore.h"
|
||||
|
||||
#include "tbcore.cpp"
|
||||
|
||||
namespace Zobrist {
|
||||
extern Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
|
||||
}
|
||||
|
||||
int Tablebases::MaxCardinality = 0;
|
||||
|
||||
// Given a position with 6 or fewer pieces, produce a text string
|
||||
// of the form KQPvKRP, where "KQP" represents the white pieces if
|
||||
// mirror == 0 and the black pieces if mirror == 1.
|
||||
static void prt_str(Position& pos, char *str, int mirror)
|
||||
{
|
||||
Color color;
|
||||
PieceType pt;
|
||||
int i;
|
||||
|
||||
color = !mirror ? WHITE : BLACK;
|
||||
for (pt = KING; pt >= PAWN; --pt)
|
||||
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
|
||||
*str++ = pchr[6 - pt];
|
||||
*str++ = 'v';
|
||||
color = ~color;
|
||||
for (pt = KING; pt >= PAWN; --pt)
|
||||
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
|
||||
*str++ = pchr[6 - pt];
|
||||
*str++ = 0;
|
||||
}
|
||||
|
||||
// Given a position, produce a 64-bit material signature key.
|
||||
// If the engine supports such a key, it should equal the engine's key.
|
||||
static uint64 calc_key(Position& pos, int mirror)
|
||||
{
|
||||
Color color;
|
||||
PieceType pt;
|
||||
int i;
|
||||
uint64 key = 0;
|
||||
|
||||
color = !mirror ? WHITE : BLACK;
|
||||
for (pt = PAWN; pt <= KING; ++pt)
|
||||
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
|
||||
key ^= Zobrist::psq[WHITE][pt][i - 1];
|
||||
color = ~color;
|
||||
for (pt = PAWN; pt <= KING; ++pt)
|
||||
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
|
||||
key ^= Zobrist::psq[BLACK][pt][i - 1];
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
// Produce a 64-bit material key corresponding to the material combination
|
||||
// defined by pcs[16], where pcs[1], ..., pcs[6] is the number of white
|
||||
// pawns, ..., kings and pcs[9], ..., pcs[14] is the number of black
|
||||
// pawns, ..., kings.
|
||||
static uint64 calc_key_from_pcs(int *pcs, int mirror)
|
||||
{
|
||||
int color;
|
||||
PieceType pt;
|
||||
int i;
|
||||
uint64 key = 0;
|
||||
|
||||
color = !mirror ? 0 : 8;
|
||||
for (pt = PAWN; pt <= KING; ++pt)
|
||||
for (i = 0; i < pcs[color + pt]; i++)
|
||||
key ^= Zobrist::psq[WHITE][pt][i];
|
||||
color ^= 8;
|
||||
for (pt = PAWN; pt <= KING; ++pt)
|
||||
for (i = 0; i < pcs[color + pt]; i++)
|
||||
key ^= Zobrist::psq[BLACK][pt][i];
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
bool is_little_endian() {
|
||||
union {
|
||||
int i;
|
||||
char c[sizeof(int)];
|
||||
} x;
|
||||
x.i = 1;
|
||||
return x.c[0] == 1;
|
||||
}
|
||||
|
||||
static ubyte decompress_pairs(struct PairsData *d, uint64 idx)
|
||||
{
|
||||
static const bool isLittleEndian = is_little_endian();
|
||||
return isLittleEndian ? decompress_pairs<true >(d, idx)
|
||||
: decompress_pairs<false>(d, idx);
|
||||
}
|
||||
|
||||
// probe_wdl_table and probe_dtz_table require similar adaptations.
|
||||
static int probe_wdl_table(Position& pos, int *success)
|
||||
{
|
||||
struct TBEntry *ptr;
|
||||
struct TBHashEntry *ptr2;
|
||||
uint64 idx;
|
||||
uint64 key;
|
||||
int i;
|
||||
ubyte res;
|
||||
int p[TBPIECES];
|
||||
|
||||
// Obtain the position's material signature key.
|
||||
key = pos.material_key();
|
||||
|
||||
// Test for KvK.
|
||||
if (key == (Zobrist::psq[WHITE][KING][0] ^ Zobrist::psq[BLACK][KING][0]))
|
||||
return 0;
|
||||
|
||||
ptr2 = TB_hash[key >> (64 - TBHASHBITS)];
|
||||
for (i = 0; i < HSHMAX; i++)
|
||||
if (ptr2[i].key == key) break;
|
||||
if (i == HSHMAX) {
|
||||
*success = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ptr = ptr2[i].ptr;
|
||||
if (!ptr->ready) {
|
||||
LOCK(TB_mutex);
|
||||
if (!ptr->ready) {
|
||||
char str[16];
|
||||
prt_str(pos, str, ptr->key != key);
|
||||
if (!init_table_wdl(ptr, str)) {
|
||||
ptr2[i].key = 0ULL;
|
||||
*success = 0;
|
||||
UNLOCK(TB_mutex);
|
||||
return 0;
|
||||
}
|
||||
// Memory barrier to ensure ptr->ready = 1 is not reordered.
|
||||
#ifdef _MSC_VER
|
||||
_ReadWriteBarrier();
|
||||
#else
|
||||
__asm__ __volatile__ ("" ::: "memory");
|
||||
#endif
|
||||
ptr->ready = 1;
|
||||
}
|
||||
UNLOCK(TB_mutex);
|
||||
}
|
||||
|
||||
int bside, mirror, cmirror;
|
||||
if (!ptr->symmetric) {
|
||||
if (key != ptr->key) {
|
||||
cmirror = 8;
|
||||
mirror = 0x38;
|
||||
bside = (pos.side_to_move() == WHITE);
|
||||
} else {
|
||||
cmirror = mirror = 0;
|
||||
bside = !(pos.side_to_move() == WHITE);
|
||||
}
|
||||
} else {
|
||||
cmirror = pos.side_to_move() == WHITE ? 0 : 8;
|
||||
mirror = pos.side_to_move() == WHITE ? 0 : 0x38;
|
||||
bside = 0;
|
||||
}
|
||||
|
||||
// p[i] is to contain the square 0-63 (A1-H8) for a piece of type
|
||||
// pc[i] ^ cmirror, where 1 = white pawn, ..., 14 = black king.
|
||||
// Pieces of the same type are guaranteed to be consecutive.
|
||||
if (!ptr->has_pawns) {
|
||||
struct TBEntry_piece *entry = (struct TBEntry_piece *)ptr;
|
||||
ubyte *pc = entry->pieces[bside];
|
||||
for (i = 0; i < entry->num;) {
|
||||
Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
|
||||
(PieceType)(pc[i] & 0x07));
|
||||
do {
|
||||
p[i++] = pop_lsb(&bb);
|
||||
} while (bb);
|
||||
}
|
||||
idx = encode_piece(entry, entry->norm[bside], p, entry->factor[bside]);
|
||||
res = decompress_pairs(entry->precomp[bside], idx);
|
||||
} else {
|
||||
struct TBEntry_pawn *entry = (struct TBEntry_pawn *)ptr;
|
||||
int k = entry->file[0].pieces[0][0] ^ cmirror;
|
||||
Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07));
|
||||
i = 0;
|
||||
do {
|
||||
p[i++] = pop_lsb(&bb) ^ mirror;
|
||||
} while (bb);
|
||||
int f = pawn_file(entry, p);
|
||||
ubyte *pc = entry->file[f].pieces[bside];
|
||||
for (; i < entry->num;) {
|
||||
bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
|
||||
(PieceType)(pc[i] & 0x07));
|
||||
do {
|
||||
p[i++] = pop_lsb(&bb) ^ mirror;
|
||||
} while (bb);
|
||||
}
|
||||
idx = encode_pawn(entry, entry->file[f].norm[bside], p, entry->file[f].factor[bside]);
|
||||
res = decompress_pairs(entry->file[f].precomp[bside], idx);
|
||||
}
|
||||
|
||||
return ((int)res) - 2;
|
||||
}
|
||||
|
||||
static int probe_dtz_table(Position& pos, int wdl, int *success)
|
||||
{
|
||||
struct TBEntry *ptr;
|
||||
uint64 idx;
|
||||
int i, res;
|
||||
int p[TBPIECES];
|
||||
|
||||
// Obtain the position's material signature key.
|
||||
uint64 key = pos.material_key();
|
||||
|
||||
if (DTZ_table[0].key1 != key && DTZ_table[0].key2 != key) {
|
||||
for (i = 1; i < DTZ_ENTRIES; i++)
|
||||
if (DTZ_table[i].key1 == key) break;
|
||||
if (i < DTZ_ENTRIES) {
|
||||
struct DTZTableEntry table_entry = DTZ_table[i];
|
||||
for (; i > 0; i--)
|
||||
DTZ_table[i] = DTZ_table[i - 1];
|
||||
DTZ_table[0] = table_entry;
|
||||
} else {
|
||||
struct TBHashEntry *ptr2 = TB_hash[key >> (64 - TBHASHBITS)];
|
||||
for (i = 0; i < HSHMAX; i++)
|
||||
if (ptr2[i].key == key) break;
|
||||
if (i == HSHMAX) {
|
||||
*success = 0;
|
||||
return 0;
|
||||
}
|
||||
ptr = ptr2[i].ptr;
|
||||
char str[16];
|
||||
int mirror = (ptr->key != key);
|
||||
prt_str(pos, str, mirror);
|
||||
if (DTZ_table[DTZ_ENTRIES - 1].entry)
|
||||
free_dtz_entry(DTZ_table[DTZ_ENTRIES-1].entry);
|
||||
for (i = DTZ_ENTRIES - 1; i > 0; i--)
|
||||
DTZ_table[i] = DTZ_table[i - 1];
|
||||
load_dtz_table(str, calc_key(pos, mirror), calc_key(pos, !mirror));
|
||||
}
|
||||
}
|
||||
|
||||
ptr = DTZ_table[0].entry;
|
||||
if (!ptr) {
|
||||
*success = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bside, mirror, cmirror;
|
||||
if (!ptr->symmetric) {
|
||||
if (key != ptr->key) {
|
||||
cmirror = 8;
|
||||
mirror = 0x38;
|
||||
bside = (pos.side_to_move() == WHITE);
|
||||
} else {
|
||||
cmirror = mirror = 0;
|
||||
bside = !(pos.side_to_move() == WHITE);
|
||||
}
|
||||
} else {
|
||||
cmirror = pos.side_to_move() == WHITE ? 0 : 8;
|
||||
mirror = pos.side_to_move() == WHITE ? 0 : 0x38;
|
||||
bside = 0;
|
||||
}
|
||||
|
||||
if (!ptr->has_pawns) {
|
||||
struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr;
|
||||
if ((entry->flags & 1) != bside && !entry->symmetric) {
|
||||
*success = -1;
|
||||
return 0;
|
||||
}
|
||||
ubyte *pc = entry->pieces;
|
||||
for (i = 0; i < entry->num;) {
|
||||
Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
|
||||
(PieceType)(pc[i] & 0x07));
|
||||
do {
|
||||
p[i++] = pop_lsb(&bb);
|
||||
} while (bb);
|
||||
}
|
||||
idx = encode_piece((struct TBEntry_piece *)entry, entry->norm, p, entry->factor);
|
||||
res = decompress_pairs(entry->precomp, idx);
|
||||
|
||||
if (entry->flags & 2)
|
||||
res = entry->map[entry->map_idx[wdl_to_map[wdl + 2]] + res];
|
||||
|
||||
if (!(entry->flags & pa_flags[wdl + 2]) || (wdl & 1))
|
||||
res *= 2;
|
||||
} else {
|
||||
struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr;
|
||||
int k = entry->file[0].pieces[0] ^ cmirror;
|
||||
Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07));
|
||||
i = 0;
|
||||
do {
|
||||
p[i++] = pop_lsb(&bb) ^ mirror;
|
||||
} while (bb);
|
||||
int f = pawn_file((struct TBEntry_pawn *)entry, p);
|
||||
if ((entry->flags[f] & 1) != bside) {
|
||||
*success = -1;
|
||||
return 0;
|
||||
}
|
||||
ubyte *pc = entry->file[f].pieces;
|
||||
for (; i < entry->num;) {
|
||||
bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
|
||||
(PieceType)(pc[i] & 0x07));
|
||||
do {
|
||||
p[i++] = pop_lsb(&bb) ^ mirror;
|
||||
} while (bb);
|
||||
}
|
||||
idx = encode_pawn((struct TBEntry_pawn *)entry, entry->file[f].norm, p, entry->file[f].factor);
|
||||
res = decompress_pairs(entry->file[f].precomp, idx);
|
||||
|
||||
if (entry->flags[f] & 2)
|
||||
res = entry->map[entry->map_idx[f][wdl_to_map[wdl + 2]] + res];
|
||||
|
||||
if (!(entry->flags[f] & pa_flags[wdl + 2]) || (wdl & 1))
|
||||
res *= 2;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Add underpromotion captures to list of captures.
|
||||
static ExtMove *add_underprom_caps(Position& pos, ExtMove *stack, ExtMove *end)
|
||||
{
|
||||
ExtMove *moves, *extra = end;
|
||||
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
Move move = moves->move;
|
||||
if (type_of(move) == PROMOTION && !pos.empty(to_sq(move))) {
|
||||
(*extra++).move = (Move)(move - (1 << 12));
|
||||
(*extra++).move = (Move)(move - (2 << 12));
|
||||
(*extra++).move = (Move)(move - (3 << 12));
|
||||
}
|
||||
}
|
||||
|
||||
return extra;
|
||||
}
|
||||
|
||||
static int probe_ab(Position& pos, int alpha, int beta, int *success)
|
||||
{
|
||||
int v;
|
||||
ExtMove stack[64];
|
||||
ExtMove *moves, *end;
|
||||
StateInfo st;
|
||||
|
||||
// Generate (at least) all legal non-ep captures including (under)promotions.
|
||||
// It is OK to generate more, as long as they are filtered out below.
|
||||
if (!pos.checkers()) {
|
||||
end = generate<CAPTURES>(pos, stack);
|
||||
// Since underpromotion captures are not included, we need to add them.
|
||||
end = add_underprom_caps(pos, stack, end);
|
||||
} else
|
||||
end = generate<EVASIONS>(pos, stack);
|
||||
|
||||
CheckInfo ci(pos);
|
||||
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
Move capture = moves->move;
|
||||
if (!pos.capture(capture) || type_of(capture) == ENPASSANT
|
||||
|| !pos.legal(capture, ci.pinned))
|
||||
continue;
|
||||
pos.do_move(capture, st, ci, pos.gives_check(capture, ci));
|
||||
v = -probe_ab(pos, -beta, -alpha, success);
|
||||
pos.undo_move(capture);
|
||||
if (*success == 0) return 0;
|
||||
if (v > alpha) {
|
||||
if (v >= beta) {
|
||||
*success = 2;
|
||||
return v;
|
||||
}
|
||||
alpha = v;
|
||||
}
|
||||
}
|
||||
|
||||
v = probe_wdl_table(pos, success);
|
||||
if (*success == 0) return 0;
|
||||
if (alpha >= v) {
|
||||
*success = 1 + (alpha > 0);
|
||||
return alpha;
|
||||
} else {
|
||||
*success = 1;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
// Probe the WDL table for a particular position.
|
||||
// If *success != 0, the probe was successful.
|
||||
// The return value is from the point of view of the side to move:
|
||||
// -2 : loss
|
||||
// -1 : loss, but draw under 50-move rule
|
||||
// 0 : draw
|
||||
// 1 : win, but draw under 50-move rule
|
||||
// 2 : win
|
||||
int Tablebases::probe_wdl(Position& pos, int *success)
|
||||
{
|
||||
int v;
|
||||
|
||||
*success = 1;
|
||||
v = probe_ab(pos, -2, 2, success);
|
||||
|
||||
// If en passant is not possible, we are done.
|
||||
if (pos.ep_square() == SQ_NONE)
|
||||
return v;
|
||||
if (!(*success)) return 0;
|
||||
|
||||
// Now handle en passant.
|
||||
int v1 = -3;
|
||||
// Generate (at least) all legal en passant captures.
|
||||
ExtMove stack[192];
|
||||
ExtMove *moves, *end;
|
||||
StateInfo st;
|
||||
|
||||
if (!pos.checkers())
|
||||
end = generate<CAPTURES>(pos, stack);
|
||||
else
|
||||
end = generate<EVASIONS>(pos, stack);
|
||||
|
||||
CheckInfo ci(pos);
|
||||
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
Move capture = moves->move;
|
||||
if (type_of(capture) != ENPASSANT
|
||||
|| !pos.legal(capture, ci.pinned))
|
||||
continue;
|
||||
pos.do_move(capture, st, ci, pos.gives_check(capture, ci));
|
||||
int v0 = -probe_ab(pos, -2, 2, success);
|
||||
pos.undo_move(capture);
|
||||
if (*success == 0) return 0;
|
||||
if (v0 > v1) v1 = v0;
|
||||
}
|
||||
if (v1 > -3) {
|
||||
if (v1 >= v) v = v1;
|
||||
else if (v == 0) {
|
||||
// Check whether there is at least one legal non-ep move.
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
Move capture = moves->move;
|
||||
if (type_of(capture) == ENPASSANT) continue;
|
||||
if (pos.legal(capture, ci.pinned)) break;
|
||||
}
|
||||
if (moves == end && !pos.checkers()) {
|
||||
end = generate<QUIETS>(pos, end);
|
||||
for (; moves < end; moves++) {
|
||||
Move move = moves->move;
|
||||
if (pos.legal(move, ci.pinned))
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If not, then we are forced to play the losing ep capture.
|
||||
if (moves == end)
|
||||
v = v1;
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
// This routine treats a position with en passant captures as one without.
|
||||
static int probe_dtz_no_ep(Position& pos, int *success)
|
||||
{
|
||||
int wdl, dtz;
|
||||
|
||||
wdl = probe_ab(pos, -2, 2, success);
|
||||
if (*success == 0) return 0;
|
||||
|
||||
if (wdl == 0) return 0;
|
||||
|
||||
if (*success == 2)
|
||||
return wdl == 2 ? 1 : 101;
|
||||
|
||||
ExtMove stack[192];
|
||||
ExtMove *moves, *end = NULL;
|
||||
StateInfo st;
|
||||
CheckInfo ci(pos);
|
||||
|
||||
if (wdl > 0) {
|
||||
// Generate at least all legal non-capturing pawn moves
|
||||
// including non-capturing promotions.
|
||||
if (!pos.checkers())
|
||||
end = generate<NON_EVASIONS>(pos, stack);
|
||||
else
|
||||
end = generate<EVASIONS>(pos, stack);
|
||||
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
Move move = moves->move;
|
||||
if (type_of(pos.moved_piece(move)) != PAWN || pos.capture(move)
|
||||
|| !pos.legal(move, ci.pinned))
|
||||
continue;
|
||||
pos.do_move(move, st, ci, pos.gives_check(move, ci));
|
||||
int v = -probe_ab(pos, -2, -wdl + 1, success);
|
||||
pos.undo_move(move);
|
||||
if (*success == 0) return 0;
|
||||
if (v == wdl)
|
||||
return v == 2 ? 1 : 101;
|
||||
}
|
||||
}
|
||||
|
||||
dtz = 1 + probe_dtz_table(pos, wdl, success);
|
||||
if (*success >= 0) {
|
||||
if (wdl & 1) dtz += 100;
|
||||
return wdl >= 0 ? dtz : -dtz;
|
||||
}
|
||||
|
||||
if (wdl > 0) {
|
||||
int best = 0xffff;
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
Move move = moves->move;
|
||||
if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN
|
||||
|| !pos.legal(move, ci.pinned))
|
||||
continue;
|
||||
pos.do_move(move, st, ci, pos.gives_check(move, ci));
|
||||
int v = -Tablebases::probe_dtz(pos, success);
|
||||
pos.undo_move(move);
|
||||
if (*success == 0) return 0;
|
||||
if (v > 0 && v + 1 < best)
|
||||
best = v + 1;
|
||||
}
|
||||
return best;
|
||||
} else {
|
||||
int best = -1;
|
||||
if (!pos.checkers())
|
||||
end = generate<NON_EVASIONS>(pos, stack);
|
||||
else
|
||||
end = generate<EVASIONS>(pos, stack);
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
int v;
|
||||
Move move = moves->move;
|
||||
if (!pos.legal(move, ci.pinned))
|
||||
continue;
|
||||
pos.do_move(move, st, ci, pos.gives_check(move, ci));
|
||||
if (st.rule50 == 0) {
|
||||
if (wdl == -2) v = -1;
|
||||
else {
|
||||
v = probe_ab(pos, 1, 2, success);
|
||||
v = (v == 2) ? 0 : -101;
|
||||
}
|
||||
} else {
|
||||
v = -Tablebases::probe_dtz(pos, success) - 1;
|
||||
}
|
||||
pos.undo_move(move);
|
||||
if (*success == 0) return 0;
|
||||
if (v < best)
|
||||
best = v;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
}
|
||||
|
||||
static int wdl_to_dtz[] = {
|
||||
-1, -101, 0, 101, 1
|
||||
};
|
||||
|
||||
// Probe the DTZ table for a particular position.
|
||||
// If *success != 0, the probe was successful.
|
||||
// The return value is from the point of view of the side to move:
|
||||
// n < -100 : loss, but draw under 50-move rule
|
||||
// -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0)
|
||||
// 0 : draw
|
||||
// 1 < n <= 100 : win in n ply (assuming 50-move counter == 0)
|
||||
// 100 < n : win, but draw under 50-move rule
|
||||
//
|
||||
// The return value n can be off by 1: a return value -n can mean a loss
|
||||
// in n+1 ply and a return value +n can mean a win in n+1 ply. This
|
||||
// cannot happen for tables with positions exactly on the "edge" of
|
||||
// the 50-move rule.
|
||||
//
|
||||
// This implies that if dtz > 0 is returned, the position is certainly
|
||||
// a win if dtz + 50-move-counter <= 99. Care must be taken that the engine
|
||||
// picks moves that preserve dtz + 50-move-counter <= 99.
|
||||
//
|
||||
// If n = 100 immediately after a capture or pawn move, then the position
|
||||
// is also certainly a win, and during the whole phase until the next
|
||||
// capture or pawn move, the inequality to be preserved is
|
||||
// dtz + 50-movecounter <= 100.
|
||||
//
|
||||
// In short, if a move is available resulting in dtz + 50-move-counter <= 99,
|
||||
// then do not accept moves leading to dtz + 50-move-counter == 100.
|
||||
//
|
||||
int Tablebases::probe_dtz(Position& pos, int *success)
|
||||
{
|
||||
*success = 1;
|
||||
int v = probe_dtz_no_ep(pos, success);
|
||||
|
||||
if (pos.ep_square() == SQ_NONE)
|
||||
return v;
|
||||
if (*success == 0) return 0;
|
||||
|
||||
// Now handle en passant.
|
||||
int v1 = -3;
|
||||
|
||||
ExtMove stack[192];
|
||||
ExtMove *moves, *end;
|
||||
StateInfo st;
|
||||
|
||||
if (!pos.checkers())
|
||||
end = generate<CAPTURES>(pos, stack);
|
||||
else
|
||||
end = generate<EVASIONS>(pos, stack);
|
||||
CheckInfo ci(pos);
|
||||
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
Move capture = moves->move;
|
||||
if (type_of(capture) != ENPASSANT
|
||||
|| !pos.legal(capture, ci.pinned))
|
||||
continue;
|
||||
pos.do_move(capture, st, ci, pos.gives_check(capture, ci));
|
||||
int v0 = -probe_ab(pos, -2, 2, success);
|
||||
pos.undo_move(capture);
|
||||
if (*success == 0) return 0;
|
||||
if (v0 > v1) v1 = v0;
|
||||
}
|
||||
if (v1 > -3) {
|
||||
v1 = wdl_to_dtz[v1 + 2];
|
||||
if (v < -100) {
|
||||
if (v1 >= 0)
|
||||
v = v1;
|
||||
} else if (v < 0) {
|
||||
if (v1 >= 0 || v1 < 100)
|
||||
v = v1;
|
||||
} else if (v > 100) {
|
||||
if (v1 > 0)
|
||||
v = v1;
|
||||
} else if (v > 0) {
|
||||
if (v1 == 1)
|
||||
v = v1;
|
||||
} else if (v1 >= 0) {
|
||||
v = v1;
|
||||
} else {
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
Move move = moves->move;
|
||||
if (type_of(move) == ENPASSANT) continue;
|
||||
if (pos.legal(move, ci.pinned)) break;
|
||||
}
|
||||
if (moves == end && !pos.checkers()) {
|
||||
end = generate<QUIETS>(pos, end);
|
||||
for (; moves < end; moves++) {
|
||||
Move move = moves->move;
|
||||
if (pos.legal(move, ci.pinned))
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (moves == end)
|
||||
v = v1;
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
// Check whether there has been at least one repetition of positions
|
||||
// since the last capture or pawn move.
|
||||
static int has_repeated(StateInfo *st)
|
||||
{
|
||||
while (1) {
|
||||
int i = 4, e = std::min(st->rule50, st->pliesFromNull);
|
||||
if (e < i)
|
||||
return 0;
|
||||
StateInfo *stp = st->previous->previous;
|
||||
do {
|
||||
stp = stp->previous->previous;
|
||||
if (stp->key == st->key)
|
||||
return 1;
|
||||
i += 2;
|
||||
} while (i <= e);
|
||||
st = st->previous;
|
||||
}
|
||||
}
|
||||
|
||||
static Value wdl_to_Value[5] = {
|
||||
-VALUE_MATE + MAX_PLY + 1,
|
||||
VALUE_DRAW - 2,
|
||||
VALUE_DRAW,
|
||||
VALUE_DRAW + 2,
|
||||
VALUE_MATE - MAX_PLY - 1
|
||||
};
|
||||
|
||||
// Use the DTZ tables to filter out moves that don't preserve the win or draw.
|
||||
// If the position is lost, but DTZ is fairly high, only keep moves that
|
||||
// maximise DTZ.
|
||||
//
|
||||
// A return value false indicates that not all probes were successful and that
|
||||
// no moves were filtered out.
|
||||
bool Tablebases::root_probe(Position& pos, Search::RootMoveVector& rootMoves, Value& score)
|
||||
{
|
||||
int success;
|
||||
|
||||
int dtz = probe_dtz(pos, &success);
|
||||
if (!success) return false;
|
||||
|
||||
StateInfo st;
|
||||
CheckInfo ci(pos);
|
||||
|
||||
// Probe each move.
|
||||
for (size_t i = 0; i < rootMoves.size(); i++) {
|
||||
Move move = rootMoves[i].pv[0];
|
||||
pos.do_move(move, st, ci, pos.gives_check(move, ci));
|
||||
int v = 0;
|
||||
if (pos.checkers() && dtz > 0) {
|
||||
ExtMove s[192];
|
||||
if (generate<LEGAL>(pos, s) == s)
|
||||
v = 1;
|
||||
}
|
||||
if (!v) {
|
||||
if (st.rule50 != 0) {
|
||||
v = -Tablebases::probe_dtz(pos, &success);
|
||||
if (v > 0) v++;
|
||||
else if (v < 0) v--;
|
||||
} else {
|
||||
v = -Tablebases::probe_wdl(pos, &success);
|
||||
v = wdl_to_dtz[v + 2];
|
||||
}
|
||||
}
|
||||
pos.undo_move(move);
|
||||
if (!success) return false;
|
||||
rootMoves[i].score = (Value)v;
|
||||
}
|
||||
|
||||
// Obtain 50-move counter for the root position.
|
||||
// In Stockfish there seems to be no clean way, so we do it like this:
|
||||
int cnt50 = st.previous->rule50;
|
||||
|
||||
// Use 50-move counter to determine whether the root position is
|
||||
// won, lost or drawn.
|
||||
int wdl = 0;
|
||||
if (dtz > 0)
|
||||
wdl = (dtz + cnt50 <= 100) ? 2 : 1;
|
||||
else if (dtz < 0)
|
||||
wdl = (-dtz + cnt50 <= 100) ? -2 : -1;
|
||||
|
||||
// Determine the score to report to the user.
|
||||
score = wdl_to_Value[wdl + 2];
|
||||
// If the position is winning or losing, but too few moves left, adjust the
|
||||
// score to show how close it is to winning or losing.
|
||||
// NOTE: int(PawnValueEg) is used as scaling factor in score_to_uci().
|
||||
if (wdl == 1 && dtz <= 100)
|
||||
score = (Value)(((200 - dtz - cnt50) * int(PawnValueEg)) / 200);
|
||||
else if (wdl == -1 && dtz >= -100)
|
||||
score = -(Value)(((200 + dtz - cnt50) * int(PawnValueEg)) / 200);
|
||||
|
||||
// Now be a bit smart about filtering out moves.
|
||||
size_t j = 0;
|
||||
if (dtz > 0) { // winning (or 50-move rule draw)
|
||||
int best = 0xffff;
|
||||
for (size_t i = 0; i < rootMoves.size(); i++) {
|
||||
int v = rootMoves[i].score;
|
||||
if (v > 0 && v < best)
|
||||
best = v;
|
||||
}
|
||||
int max = best;
|
||||
// If the current phase has not seen repetitions, then try all moves
|
||||
// that stay safely within the 50-move budget, if there are any.
|
||||
if (!has_repeated(st.previous) && best + cnt50 <= 99)
|
||||
max = 99 - cnt50;
|
||||
for (size_t i = 0; i < rootMoves.size(); i++) {
|
||||
int v = rootMoves[i].score;
|
||||
if (v > 0 && v <= max)
|
||||
rootMoves[j++] = rootMoves[i];
|
||||
}
|
||||
} else if (dtz < 0) { // losing (or 50-move rule draw)
|
||||
int best = 0;
|
||||
for (size_t i = 0; i < rootMoves.size(); i++) {
|
||||
int v = rootMoves[i].score;
|
||||
if (v < best)
|
||||
best = v;
|
||||
}
|
||||
// Try all moves, unless we approach or have a 50-move rule draw.
|
||||
if (-best * 2 + cnt50 < 100)
|
||||
return true;
|
||||
for (size_t i = 0; i < rootMoves.size(); i++) {
|
||||
if (rootMoves[i].score == best)
|
||||
rootMoves[j++] = rootMoves[i];
|
||||
}
|
||||
} else { // drawing
|
||||
// Try all moves that preserve the draw.
|
||||
for (size_t i = 0; i < rootMoves.size(); i++) {
|
||||
if (rootMoves[i].score == 0)
|
||||
rootMoves[j++] = rootMoves[i];
|
||||
}
|
||||
}
|
||||
rootMoves.resize(j, Search::RootMove(MOVE_NONE));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use the WDL tables to filter out moves that don't preserve the win or draw.
|
||||
// This is a fallback for the case that some or all DTZ tables are missing.
|
||||
//
|
||||
// A return value false indicates that not all probes were successful and that
|
||||
// no moves were filtered out.
|
||||
bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoveVector& rootMoves, Value& score)
|
||||
{
|
||||
int success;
|
||||
|
||||
int wdl = Tablebases::probe_wdl(pos, &success);
|
||||
if (!success) return false;
|
||||
score = wdl_to_Value[wdl + 2];
|
||||
|
||||
StateInfo st;
|
||||
CheckInfo ci(pos);
|
||||
|
||||
int best = -2;
|
||||
|
||||
// Probe each move.
|
||||
for (size_t i = 0; i < rootMoves.size(); i++) {
|
||||
Move move = rootMoves[i].pv[0];
|
||||
pos.do_move(move, st, ci, pos.gives_check(move, ci));
|
||||
int v = -Tablebases::probe_wdl(pos, &success);
|
||||
pos.undo_move(move);
|
||||
if (!success) return false;
|
||||
rootMoves[i].score = (Value)v;
|
||||
if (v > best)
|
||||
best = v;
|
||||
}
|
||||
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < rootMoves.size(); i++) {
|
||||
if (rootMoves[i].score == best)
|
||||
rootMoves[j++] = rootMoves[i];
|
||||
}
|
||||
rootMoves.resize(j, Search::RootMove(MOVE_NONE));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
18
DroidFish/jni/stockfish/syzygy/tbprobe.h
Normal file
18
DroidFish/jni/stockfish/syzygy/tbprobe.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef TBPROBE_H
|
||||
#define TBPROBE_H
|
||||
|
||||
#include "../search.h"
|
||||
|
||||
namespace Tablebases {
|
||||
|
||||
extern int MaxCardinality;
|
||||
|
||||
void init(const std::string& path);
|
||||
int probe_wdl(Position& pos, int *success);
|
||||
int probe_dtz(Position& pos, int *success);
|
||||
bool root_probe(Position& pos, Search::RootMoveVector& rootMoves, Value& score);
|
||||
bool root_probe_wdl(Position& pos, Search::RootMoveVector& rootMoves, Value& score);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2011-2013 Ronald de Man
|
||||
*/
|
||||
|
||||
#ifndef TBCORE_H
|
||||
#define TBCORE_H
|
||||
|
||||
#ifndef __WIN32__
|
||||
#define SEP_CHAR ':'
|
||||
#define FD int
|
||||
#define FD_ERR -1
|
||||
#else
|
||||
#include <windows.h>
|
||||
#define SEP_CHAR ';'
|
||||
#define FD HANDLE
|
||||
#define FD_ERR INVALID_HANDLE_VALUE
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <atomic>
|
||||
|
||||
#define WDLSUFFIX ".rtbw"
|
||||
#define DTZSUFFIX ".rtbz"
|
||||
#define TBPIECES 6
|
||||
|
||||
#define WDL_MAGIC 0x5d23e871
|
||||
#define DTZ_MAGIC 0xa50c66d7
|
||||
|
||||
#define TBHASHBITS 11
|
||||
|
||||
typedef unsigned char ubyte;
|
||||
typedef unsigned short ushort;
|
||||
|
||||
struct TBHashEntry;
|
||||
|
||||
typedef uint64_t base_t;
|
||||
|
||||
struct PairsData {
|
||||
char *indextable;
|
||||
ushort *sizetable;
|
||||
ubyte *data;
|
||||
ushort *offset;
|
||||
ubyte *symlen;
|
||||
ubyte *sympat;
|
||||
int blocksize;
|
||||
int idxbits;
|
||||
int min_len;
|
||||
base_t base[1]; // C++ complains about base[]...
|
||||
};
|
||||
|
||||
struct TBEntry {
|
||||
char *data;
|
||||
uint64_t key;
|
||||
uint64_t mapping;
|
||||
std::atomic<ubyte> ready;
|
||||
ubyte num;
|
||||
ubyte symmetric;
|
||||
ubyte has_pawns;
|
||||
} __attribute__((__may_alias__));
|
||||
|
||||
struct TBEntry_piece {
|
||||
char *data;
|
||||
uint64_t key;
|
||||
uint64_t mapping;
|
||||
std::atomic<ubyte> ready;
|
||||
ubyte num;
|
||||
ubyte symmetric;
|
||||
ubyte has_pawns;
|
||||
ubyte enc_type;
|
||||
struct PairsData *precomp[2];
|
||||
int factor[2][TBPIECES];
|
||||
ubyte pieces[2][TBPIECES];
|
||||
ubyte norm[2][TBPIECES];
|
||||
};
|
||||
|
||||
struct TBEntry_pawn {
|
||||
char *data;
|
||||
uint64_t key;
|
||||
uint64_t mapping;
|
||||
std::atomic<ubyte> ready;
|
||||
ubyte num;
|
||||
ubyte symmetric;
|
||||
ubyte has_pawns;
|
||||
ubyte pawns[2];
|
||||
struct {
|
||||
struct PairsData *precomp[2];
|
||||
int factor[2][TBPIECES];
|
||||
ubyte pieces[2][TBPIECES];
|
||||
ubyte norm[2][TBPIECES];
|
||||
} file[4];
|
||||
};
|
||||
|
||||
struct DTZEntry_piece {
|
||||
char *data;
|
||||
uint64_t key;
|
||||
uint64_t mapping;
|
||||
std::atomic<ubyte> ready;
|
||||
ubyte num;
|
||||
ubyte symmetric;
|
||||
ubyte has_pawns;
|
||||
ubyte enc_type;
|
||||
struct PairsData *precomp;
|
||||
int factor[TBPIECES];
|
||||
ubyte pieces[TBPIECES];
|
||||
ubyte norm[TBPIECES];
|
||||
ubyte flags; // accurate, mapped, side
|
||||
ushort map_idx[4];
|
||||
ubyte *map;
|
||||
};
|
||||
|
||||
struct DTZEntry_pawn {
|
||||
char *data;
|
||||
uint64_t key;
|
||||
uint64_t mapping;
|
||||
std::atomic<ubyte> ready;
|
||||
ubyte num;
|
||||
ubyte symmetric;
|
||||
ubyte has_pawns;
|
||||
ubyte pawns[2];
|
||||
struct {
|
||||
struct PairsData *precomp;
|
||||
int factor[TBPIECES];
|
||||
ubyte pieces[TBPIECES];
|
||||
ubyte norm[TBPIECES];
|
||||
} file[4];
|
||||
ubyte flags[4];
|
||||
ushort map_idx[4][4];
|
||||
ubyte *map;
|
||||
};
|
||||
|
||||
struct TBHashEntry {
|
||||
uint64_t key;
|
||||
struct TBEntry *ptr;
|
||||
};
|
||||
|
||||
struct DTZTableEntry {
|
||||
uint64_t key1;
|
||||
uint64_t key2;
|
||||
std::atomic<TBEntry*> entry;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,819 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013 Ronald de Man
|
||||
This file may be redistributed and/or modified without restrictions.
|
||||
|
||||
tbprobe.cpp contains the Stockfish-specific routines of the
|
||||
tablebase probing code. It should be relatively easy to adapt
|
||||
this code to other chess engines.
|
||||
*/
|
||||
|
||||
#include "position.h"
|
||||
#include "movegen.h"
|
||||
#include "bitboard.h"
|
||||
#include "search.h"
|
||||
#include "bitcount.h"
|
||||
|
||||
#include "tbprobe.h"
|
||||
#include "tbcore.h"
|
||||
|
||||
#include "tbcore.cpp"
|
||||
|
||||
namespace Zobrist {
|
||||
extern Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
|
||||
}
|
||||
|
||||
int Tablebases::TBLargest = 0;
|
||||
|
||||
// Given a position with 6 or fewer pieces, produce a text string
|
||||
// of the form KQPvKRP, where "KQP" represents the white pieces if
|
||||
// mirror == false and the black pieces if mirror == true.
|
||||
static void prt_str(Position& pos, char *str, bool mirror)
|
||||
{
|
||||
Color color;
|
||||
PieceType pt;
|
||||
int i;
|
||||
|
||||
color = !mirror ? WHITE : BLACK;
|
||||
for (pt = KING; pt >= PAWN; --pt)
|
||||
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
|
||||
*str++ = pchr[6 - pt];
|
||||
*str++ = 'v';
|
||||
color = ~color;
|
||||
for (pt = KING; pt >= PAWN; --pt)
|
||||
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
|
||||
*str++ = pchr[6 - pt];
|
||||
*str++ = 0;
|
||||
}
|
||||
|
||||
// Given a position, produce a 64-bit material signature key.
|
||||
// If the engine supports such a key, it should equal the engine's key.
|
||||
static uint64_t calc_key(const Position& pos, bool mirror)
|
||||
{
|
||||
Color color;
|
||||
PieceType pt;
|
||||
int i;
|
||||
uint64_t key = 0;
|
||||
|
||||
color = !mirror ? WHITE : BLACK;
|
||||
for (pt = PAWN; pt <= KING; ++pt)
|
||||
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
|
||||
key ^= Zobrist::psq[WHITE][pt][i - 1];
|
||||
color = ~color;
|
||||
for (pt = PAWN; pt <= KING; ++pt)
|
||||
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
|
||||
key ^= Zobrist::psq[BLACK][pt][i - 1];
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
// Produce a 64-bit material key corresponding to the material combination
|
||||
// defined by pcs[16], where pcs[1], ..., pcs[6] is the number of white
|
||||
// pawns, ..., kings and pcs[9], ..., pcs[14] is the number of black
|
||||
// pawns, ..., kings.
|
||||
static uint64_t calc_key_from_pcs(const int *pcs, bool mirror)
|
||||
{
|
||||
int color;
|
||||
PieceType pt;
|
||||
int i;
|
||||
uint64_t key = 0;
|
||||
|
||||
color = !mirror ? 0 : 8;
|
||||
for (pt = PAWN; pt <= KING; ++pt)
|
||||
for (i = 0; i < pcs[color + pt]; i++)
|
||||
key ^= Zobrist::psq[WHITE][pt][i];
|
||||
color ^= 8;
|
||||
for (pt = PAWN; pt <= KING; ++pt)
|
||||
for (i = 0; i < pcs[color + pt]; i++)
|
||||
key ^= Zobrist::psq[BLACK][pt][i];
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
// probe_wdl_table and probe_dtz_table require similar adaptations.
|
||||
static int probe_wdl_table(Position& pos, int *success)
|
||||
{
|
||||
struct TBEntry *ptr;
|
||||
struct TBHashEntry *ptr2;
|
||||
uint64_t idx;
|
||||
uint64_t key;
|
||||
int i;
|
||||
ubyte res;
|
||||
int p[TBPIECES];
|
||||
|
||||
// Obtain the position's material signature key.
|
||||
key = pos.material_key();
|
||||
|
||||
// Test for KvK.
|
||||
if (key == (Zobrist::psq[WHITE][KING][0] ^ Zobrist::psq[BLACK][KING][0]))
|
||||
return 0;
|
||||
|
||||
ptr2 = WDL_hash[key >> (64 - TBHASHBITS)];
|
||||
for (i = 0; i < HSHMAX; i++)
|
||||
if (ptr2[i].key == key) break;
|
||||
if (i == HSHMAX) {
|
||||
*success = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ptr = ptr2[i].ptr;
|
||||
ubyte ready = ptr->ready.load(std::memory_order_relaxed);
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
if (!ready) {
|
||||
std::lock_guard<std::mutex> L(TB_mutex);
|
||||
ready = ptr->ready.load(std::memory_order_relaxed);
|
||||
if (!ready) {
|
||||
char str[16];
|
||||
prt_str(pos, str, ptr->key != key);
|
||||
if (!init_table_wdl(ptr, str)) {
|
||||
ptr2[i].key = 0ULL;
|
||||
*success = 0;
|
||||
return 0;
|
||||
}
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
ptr->ready.store(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
int bside, mirror, cmirror;
|
||||
if (!ptr->symmetric) {
|
||||
if (key != ptr->key) {
|
||||
cmirror = 8;
|
||||
mirror = 0x38;
|
||||
bside = (pos.side_to_move() == WHITE);
|
||||
} else {
|
||||
cmirror = mirror = 0;
|
||||
bside = !(pos.side_to_move() == WHITE);
|
||||
}
|
||||
} else {
|
||||
cmirror = pos.side_to_move() == WHITE ? 0 : 8;
|
||||
mirror = pos.side_to_move() == WHITE ? 0 : 0x38;
|
||||
bside = 0;
|
||||
}
|
||||
|
||||
// p[i] is to contain the square 0-63 (A1-H8) for a piece of type
|
||||
// pc[i] ^ cmirror, where 1 = white pawn, ..., 14 = black king.
|
||||
// Pieces of the same type are guaranteed to be consecutive.
|
||||
if (!ptr->has_pawns) {
|
||||
struct TBEntry_piece *entry = (struct TBEntry_piece *)ptr;
|
||||
ubyte *pc = entry->pieces[bside];
|
||||
for (i = 0; i < entry->num;) {
|
||||
Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
|
||||
(PieceType)(pc[i] & 0x07));
|
||||
do {
|
||||
p[i++] = pop_lsb(&bb);
|
||||
} while (bb);
|
||||
}
|
||||
idx = encode_piece(entry, entry->norm[bside], p, entry->factor[bside]);
|
||||
res = decompress_pairs(entry->precomp[bside], idx);
|
||||
} else {
|
||||
struct TBEntry_pawn *entry = (struct TBEntry_pawn *)ptr;
|
||||
int k = entry->file[0].pieces[0][0] ^ cmirror;
|
||||
Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07));
|
||||
i = 0;
|
||||
do {
|
||||
p[i++] = pop_lsb(&bb) ^ mirror;
|
||||
} while (bb);
|
||||
int f = pawn_file(entry, p);
|
||||
ubyte *pc = entry->file[f].pieces[bside];
|
||||
for (; i < entry->num;) {
|
||||
bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
|
||||
(PieceType)(pc[i] & 0x07));
|
||||
do {
|
||||
p[i++] = pop_lsb(&bb) ^ mirror;
|
||||
} while (bb);
|
||||
}
|
||||
idx = encode_pawn(entry, entry->file[f].norm[bside], p, entry->file[f].factor[bside]);
|
||||
res = decompress_pairs(entry->file[f].precomp[bside], idx);
|
||||
}
|
||||
|
||||
return ((int)res) - 2;
|
||||
}
|
||||
|
||||
static int probe_dtz_table(Position& pos, int wdl, int *success)
|
||||
{
|
||||
uint64_t idx;
|
||||
int i, res;
|
||||
int p[TBPIECES];
|
||||
|
||||
// Obtain the position's material signature key.
|
||||
uint64_t key = calc_key(pos, false);
|
||||
|
||||
DTZTableEntry* dtzTabEnt;
|
||||
{
|
||||
dtzTabEnt = DTZ_hash[key >> (64 - TBHASHBITS)];
|
||||
for (i = 0; i < HSHMAX; i++)
|
||||
if (dtzTabEnt[i].key1 == key) break;
|
||||
if (i == HSHMAX) {
|
||||
uint64_t key2 = calc_key(pos, true);
|
||||
dtzTabEnt = DTZ_hash[key2 >> (64 - TBHASHBITS)];
|
||||
for (i = 0; i < HSHMAX; i++)
|
||||
if (dtzTabEnt[i].key2 == key) break;
|
||||
}
|
||||
if (i == HSHMAX) {
|
||||
*success = 0;
|
||||
return 0;
|
||||
}
|
||||
dtzTabEnt += i;
|
||||
}
|
||||
|
||||
TBEntry* ptr = dtzTabEnt->entry.load(std::memory_order_relaxed);
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
if (!ptr) {
|
||||
std::lock_guard<std::mutex> L(TB_mutex);
|
||||
ptr = dtzTabEnt->entry.load(std::memory_order_relaxed);
|
||||
if (!ptr) {
|
||||
struct TBHashEntry *ptr2 = WDL_hash[key >> (64 - TBHASHBITS)];
|
||||
for (i = 0; i < HSHMAX; i++)
|
||||
if (ptr2[i].key == key) break;
|
||||
if (i == HSHMAX) {
|
||||
*success = 0;
|
||||
return 0;
|
||||
}
|
||||
char str[16];
|
||||
bool mirror = (ptr2[i].ptr->key != key);
|
||||
prt_str(pos, str, mirror);
|
||||
ptr = load_dtz_table(str, calc_key(pos, mirror), calc_key(pos, !mirror));
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
dtzTabEnt->entry.store(ptr, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ptr) {
|
||||
*success = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bside, mirror, cmirror;
|
||||
if (!ptr->symmetric) {
|
||||
if (key != ptr->key) {
|
||||
cmirror = 8;
|
||||
mirror = 0x38;
|
||||
bside = (pos.side_to_move() == WHITE);
|
||||
} else {
|
||||
cmirror = mirror = 0;
|
||||
bside = !(pos.side_to_move() == WHITE);
|
||||
}
|
||||
} else {
|
||||
cmirror = pos.side_to_move() == WHITE ? 0 : 8;
|
||||
mirror = pos.side_to_move() == WHITE ? 0 : 0x38;
|
||||
bside = 0;
|
||||
}
|
||||
|
||||
if (!ptr->has_pawns) {
|
||||
struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr;
|
||||
if ((entry->flags & 1) != bside && !entry->symmetric) {
|
||||
*success = -1;
|
||||
return 0;
|
||||
}
|
||||
ubyte *pc = entry->pieces;
|
||||
for (i = 0; i < entry->num;) {
|
||||
Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
|
||||
(PieceType)(pc[i] & 0x07));
|
||||
do {
|
||||
p[i++] = pop_lsb(&bb);
|
||||
} while (bb);
|
||||
}
|
||||
idx = encode_piece((struct TBEntry_piece *)entry, entry->norm, p, entry->factor);
|
||||
res = decompress_pairs(entry->precomp, idx);
|
||||
|
||||
if (entry->flags & 2)
|
||||
res = entry->map[entry->map_idx[wdl_to_map[wdl + 2]] + res];
|
||||
|
||||
if (!(entry->flags & pa_flags[wdl + 2]) || (wdl & 1))
|
||||
res *= 2;
|
||||
} else {
|
||||
struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr;
|
||||
int k = entry->file[0].pieces[0] ^ cmirror;
|
||||
Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07));
|
||||
i = 0;
|
||||
do {
|
||||
p[i++] = pop_lsb(&bb) ^ mirror;
|
||||
} while (bb);
|
||||
int f = pawn_file((struct TBEntry_pawn *)entry, p);
|
||||
if ((entry->flags[f] & 1) != bside) {
|
||||
*success = -1;
|
||||
return 0;
|
||||
}
|
||||
ubyte *pc = entry->file[f].pieces;
|
||||
for (; i < entry->num;) {
|
||||
bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
|
||||
(PieceType)(pc[i] & 0x07));
|
||||
do {
|
||||
p[i++] = pop_lsb(&bb) ^ mirror;
|
||||
} while (bb);
|
||||
}
|
||||
idx = encode_pawn((struct TBEntry_pawn *)entry, entry->file[f].norm, p, entry->file[f].factor);
|
||||
res = decompress_pairs(entry->file[f].precomp, idx);
|
||||
|
||||
if (entry->flags[f] & 2)
|
||||
res = entry->map[entry->map_idx[f][wdl_to_map[wdl + 2]] + res];
|
||||
|
||||
if (!(entry->flags[f] & pa_flags[wdl + 2]) || (wdl & 1))
|
||||
res *= 2;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Add underpromotion captures to list of captures.
|
||||
static ExtMove *add_underprom_caps(Position& pos, ExtMove *stack, ExtMove *end)
|
||||
{
|
||||
ExtMove *moves, *extra = end;
|
||||
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
Move move = moves->move;
|
||||
if (type_of(move) == PROMOTION && !pos.empty(to_sq(move))) {
|
||||
(*extra++).move = (Move)(move - (1 << 12));
|
||||
(*extra++).move = (Move)(move - (2 << 12));
|
||||
(*extra++).move = (Move)(move - (3 << 12));
|
||||
}
|
||||
}
|
||||
|
||||
return extra;
|
||||
}
|
||||
|
||||
static int probe_ab(Position& pos, int alpha, int beta, int *success)
|
||||
{
|
||||
int v;
|
||||
ExtMove stack[64];
|
||||
ExtMove *moves, *end;
|
||||
StateInfo st;
|
||||
|
||||
// Generate (at least) all legal non-ep captures including (under)promotions.
|
||||
// It is OK to generate more, as long as they are filtered out below.
|
||||
if (!pos.checkers()) {
|
||||
end = generate<CAPTURES>(pos, stack);
|
||||
// Since underpromotion captures are not included, we need to add them.
|
||||
end = add_underprom_caps(pos, stack, end);
|
||||
} else
|
||||
end = generate<EVASIONS>(pos, stack);
|
||||
|
||||
CheckInfo ci(pos);
|
||||
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
Move capture = moves->move;
|
||||
if (!pos.capture(capture) || type_of(capture) == ENPASSANT
|
||||
|| !pos.legal(capture, ci.pinned))
|
||||
continue;
|
||||
pos.do_move(capture, st, ci, pos.gives_check(capture, ci));
|
||||
v = -probe_ab(pos, -beta, -alpha, success);
|
||||
pos.undo_move(capture);
|
||||
if (*success == 0) return 0;
|
||||
if (v > alpha) {
|
||||
if (v >= beta) {
|
||||
*success = 2;
|
||||
return v;
|
||||
}
|
||||
alpha = v;
|
||||
}
|
||||
}
|
||||
|
||||
v = probe_wdl_table(pos, success);
|
||||
if (*success == 0) return 0;
|
||||
if (alpha >= v) {
|
||||
*success = 1 + (alpha > 0);
|
||||
return alpha;
|
||||
} else {
|
||||
*success = 1;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
// Probe the WDL table for a particular position.
|
||||
// If *success != 0, the probe was successful.
|
||||
// The return value is from the point of view of the side to move:
|
||||
// -2 : loss
|
||||
// -1 : loss, but draw under 50-move rule
|
||||
// 0 : draw
|
||||
// 1 : win, but draw under 50-move rule
|
||||
// 2 : win
|
||||
int Tablebases::probe_wdl(Position& pos, int *success)
|
||||
{
|
||||
int v;
|
||||
|
||||
*success = 1;
|
||||
v = probe_ab(pos, -2, 2, success);
|
||||
|
||||
// If en passant is not possible, we are done.
|
||||
if (pos.ep_square() == SQ_NONE)
|
||||
return v;
|
||||
if (!(*success)) return 0;
|
||||
|
||||
// Now handle en passant.
|
||||
int v1 = -3;
|
||||
// Generate (at least) all legal en passant captures.
|
||||
ExtMove stack[192];
|
||||
ExtMove *moves, *end;
|
||||
StateInfo st;
|
||||
|
||||
if (!pos.checkers())
|
||||
end = generate<CAPTURES>(pos, stack);
|
||||
else
|
||||
end = generate<EVASIONS>(pos, stack);
|
||||
|
||||
CheckInfo ci(pos);
|
||||
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
Move capture = moves->move;
|
||||
if (type_of(capture) != ENPASSANT
|
||||
|| !pos.legal(capture, ci.pinned))
|
||||
continue;
|
||||
pos.do_move(capture, st, ci, pos.gives_check(capture, ci));
|
||||
int v0 = -probe_ab(pos, -2, 2, success);
|
||||
pos.undo_move(capture);
|
||||
if (*success == 0) return 0;
|
||||
if (v0 > v1) v1 = v0;
|
||||
}
|
||||
if (v1 > -3) {
|
||||
if (v1 >= v) v = v1;
|
||||
else if (v == 0) {
|
||||
// Check whether there is at least one legal non-ep move.
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
Move capture = moves->move;
|
||||
if (type_of(capture) == ENPASSANT) continue;
|
||||
if (pos.legal(capture, ci.pinned)) break;
|
||||
}
|
||||
if (moves == end && !pos.checkers()) {
|
||||
end = generate<QUIETS>(pos, end);
|
||||
for (; moves < end; moves++) {
|
||||
Move move = moves->move;
|
||||
if (pos.legal(move, ci.pinned))
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If not, then we are forced to play the losing ep capture.
|
||||
if (moves == end)
|
||||
v = v1;
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
// This routine treats a position with en passant captures as one without.
|
||||
static int probe_dtz_no_ep(Position& pos, int *success)
|
||||
{
|
||||
int wdl, dtz;
|
||||
|
||||
wdl = probe_ab(pos, -2, 2, success);
|
||||
if (*success == 0) return 0;
|
||||
|
||||
if (wdl == 0) return 0;
|
||||
|
||||
if (*success == 2)
|
||||
return wdl == 2 ? 1 : 101;
|
||||
|
||||
ExtMove stack[192];
|
||||
ExtMove *moves, *end = NULL;
|
||||
StateInfo st;
|
||||
CheckInfo ci(pos);
|
||||
|
||||
if (wdl > 0) {
|
||||
// Generate at least all legal non-capturing pawn moves
|
||||
// including non-capturing promotions.
|
||||
if (!pos.checkers())
|
||||
end = generate<NON_EVASIONS>(pos, stack);
|
||||
else
|
||||
end = generate<EVASIONS>(pos, stack);
|
||||
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
Move move = moves->move;
|
||||
if (type_of(pos.moved_piece(move)) != PAWN || pos.capture(move)
|
||||
|| !pos.legal(move, ci.pinned))
|
||||
continue;
|
||||
pos.do_move(move, st, ci, pos.gives_check(move, ci));
|
||||
int v = -probe_ab(pos, -2, -wdl + 1, success);
|
||||
pos.undo_move(move);
|
||||
if (*success == 0) return 0;
|
||||
if (v == wdl)
|
||||
return v == 2 ? 1 : 101;
|
||||
}
|
||||
}
|
||||
|
||||
dtz = 1 + probe_dtz_table(pos, wdl, success);
|
||||
if (*success >= 0) {
|
||||
if (wdl & 1) dtz += 100;
|
||||
return wdl >= 0 ? dtz : -dtz;
|
||||
}
|
||||
|
||||
if (wdl > 0) {
|
||||
int best = 0xffff;
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
Move move = moves->move;
|
||||
if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN
|
||||
|| !pos.legal(move, ci.pinned))
|
||||
continue;
|
||||
pos.do_move(move, st, ci, pos.gives_check(move, ci));
|
||||
int v = -Tablebases::probe_dtz(pos, success);
|
||||
pos.undo_move(move);
|
||||
if (*success == 0) return 0;
|
||||
if (v > 0 && v + 1 < best)
|
||||
best = v + 1;
|
||||
}
|
||||
return best;
|
||||
} else {
|
||||
int best = -1;
|
||||
if (!pos.checkers())
|
||||
end = generate<NON_EVASIONS>(pos, stack);
|
||||
else
|
||||
end = generate<EVASIONS>(pos, stack);
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
int v;
|
||||
Move move = moves->move;
|
||||
if (!pos.legal(move, ci.pinned))
|
||||
continue;
|
||||
pos.do_move(move, st, ci, pos.gives_check(move, ci));
|
||||
if (st.rule50 == 0) {
|
||||
if (wdl == -2) v = -1;
|
||||
else {
|
||||
v = probe_ab(pos, 1, 2, success);
|
||||
v = (v == 2) ? 0 : -101;
|
||||
}
|
||||
} else {
|
||||
v = -Tablebases::probe_dtz(pos, success) - 1;
|
||||
}
|
||||
pos.undo_move(move);
|
||||
if (*success == 0) return 0;
|
||||
if (v < best)
|
||||
best = v;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
}
|
||||
|
||||
static int wdl_to_dtz[] = {
|
||||
-1, -101, 0, 101, 1
|
||||
};
|
||||
|
||||
// Probe the DTZ table for a particular position.
|
||||
// If *success != 0, the probe was successful.
|
||||
// The return value is from the point of view of the side to move:
|
||||
// n < -100 : loss, but draw under 50-move rule
|
||||
// -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0)
|
||||
// 0 : draw
|
||||
// 1 < n <= 100 : win in n ply (assuming 50-move counter == 0)
|
||||
// 100 < n : win, but draw under 50-move rule
|
||||
//
|
||||
// The return value n can be off by 1: a return value -n can mean a loss
|
||||
// in n+1 ply and a return value +n can mean a win in n+1 ply. This
|
||||
// cannot happen for tables with positions exactly on the "edge" of
|
||||
// the 50-move rule.
|
||||
//
|
||||
// This implies that if dtz > 0 is returned, the position is certainly
|
||||
// a win if dtz + 50-move-counter <= 99. Care must be taken that the engine
|
||||
// picks moves that preserve dtz + 50-move-counter <= 99.
|
||||
//
|
||||
// If n = 100 immediately after a capture or pawn move, then the position
|
||||
// is also certainly a win, and during the whole phase until the next
|
||||
// capture or pawn move, the inequality to be preserved is
|
||||
// dtz + 50-movecounter <= 100.
|
||||
//
|
||||
// In short, if a move is available resulting in dtz + 50-move-counter <= 99,
|
||||
// then do not accept moves leading to dtz + 50-move-counter == 100.
|
||||
//
|
||||
int Tablebases::probe_dtz(Position& pos, int *success)
|
||||
{
|
||||
*success = 1;
|
||||
int v = probe_dtz_no_ep(pos, success);
|
||||
|
||||
if (pos.ep_square() == SQ_NONE)
|
||||
return v;
|
||||
if (*success == 0) return 0;
|
||||
|
||||
// Now handle en passant.
|
||||
int v1 = -3;
|
||||
|
||||
ExtMove stack[192];
|
||||
ExtMove *moves, *end;
|
||||
StateInfo st;
|
||||
|
||||
if (!pos.checkers())
|
||||
end = generate<CAPTURES>(pos, stack);
|
||||
else
|
||||
end = generate<EVASIONS>(pos, stack);
|
||||
CheckInfo ci(pos);
|
||||
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
Move capture = moves->move;
|
||||
if (type_of(capture) != ENPASSANT
|
||||
|| !pos.legal(capture, ci.pinned))
|
||||
continue;
|
||||
pos.do_move(capture, st, ci, pos.gives_check(capture, ci));
|
||||
int v0 = -probe_ab(pos, -2, 2, success);
|
||||
pos.undo_move(capture);
|
||||
if (*success == 0) return 0;
|
||||
if (v0 > v1) v1 = v0;
|
||||
}
|
||||
if (v1 > -3) {
|
||||
v1 = wdl_to_dtz[v1 + 2];
|
||||
if (v < -100) {
|
||||
if (v1 >= 0)
|
||||
v = v1;
|
||||
} else if (v < 0) {
|
||||
if (v1 >= 0 || v1 < 100)
|
||||
v = v1;
|
||||
} else if (v > 100) {
|
||||
if (v1 > 0)
|
||||
v = v1;
|
||||
} else if (v > 0) {
|
||||
if (v1 == 1)
|
||||
v = v1;
|
||||
} else if (v1 >= 0) {
|
||||
v = v1;
|
||||
} else {
|
||||
for (moves = stack; moves < end; moves++) {
|
||||
Move move = moves->move;
|
||||
if (type_of(move) == ENPASSANT) continue;
|
||||
if (pos.legal(move, ci.pinned)) break;
|
||||
}
|
||||
if (moves == end && !pos.checkers()) {
|
||||
end = generate<QUIETS>(pos, end);
|
||||
for (; moves < end; moves++) {
|
||||
Move move = moves->move;
|
||||
if (pos.legal(move, ci.pinned))
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (moves == end)
|
||||
v = v1;
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
// Check whether there has been at least one repetition of positions
|
||||
// since the last capture or pawn move.
|
||||
static int has_repeated(StateInfo *st)
|
||||
{
|
||||
while (1) {
|
||||
int i = 4, e = std::min(st->rule50, st->pliesFromNull);
|
||||
if (e < i)
|
||||
return 0;
|
||||
StateInfo *stp = st->previous->previous;
|
||||
do {
|
||||
stp = stp->previous->previous;
|
||||
if (stp->key == st->key)
|
||||
return 1;
|
||||
i += 2;
|
||||
} while (i <= e);
|
||||
st = st->previous;
|
||||
}
|
||||
}
|
||||
|
||||
static Value wdl_to_Value[5] = {
|
||||
-VALUE_MATE + MAX_PLY + 1,
|
||||
VALUE_DRAW - 2,
|
||||
VALUE_DRAW,
|
||||
VALUE_DRAW + 2,
|
||||
VALUE_MATE - MAX_PLY - 1
|
||||
};
|
||||
|
||||
// Use the DTZ tables to filter out moves that don't preserve the win or draw.
|
||||
// If the position is lost, but DTZ is fairly high, only keep moves that
|
||||
// maximise DTZ.
|
||||
//
|
||||
// A return value false indicates that not all probes were successful and that
|
||||
// no moves were filtered out.
|
||||
bool Tablebases::root_probe(Position& pos, Value& TBScore)
|
||||
{
|
||||
int success;
|
||||
|
||||
int dtz = probe_dtz(pos, &success);
|
||||
if (!success) return false;
|
||||
|
||||
StateInfo st;
|
||||
CheckInfo ci(pos);
|
||||
|
||||
// Probe each move.
|
||||
for (size_t i = 0; i < Search::RootMoves.size(); i++) {
|
||||
Move move = Search::RootMoves[i].pv[0];
|
||||
pos.do_move(move, st, ci, pos.gives_check(move, ci));
|
||||
int v = 0;
|
||||
if (pos.checkers() && dtz > 0) {
|
||||
ExtMove s[192];
|
||||
if (generate<LEGAL>(pos, s) == s)
|
||||
v = 1;
|
||||
}
|
||||
if (!v) {
|
||||
if (st.rule50 != 0) {
|
||||
v = -Tablebases::probe_dtz(pos, &success);
|
||||
if (v > 0) v++;
|
||||
else if (v < 0) v--;
|
||||
} else {
|
||||
v = -Tablebases::probe_wdl(pos, &success);
|
||||
v = wdl_to_dtz[v + 2];
|
||||
}
|
||||
}
|
||||
pos.undo_move(move);
|
||||
if (!success) return false;
|
||||
Search::RootMoves[i].score = (Value)v;
|
||||
}
|
||||
|
||||
// Obtain 50-move counter for the root position.
|
||||
// In Stockfish there seems to be no clean way, so we do it like this:
|
||||
int cnt50 = st.previous->rule50;
|
||||
|
||||
// Use 50-move counter to determine whether the root position is
|
||||
// won, lost or drawn.
|
||||
int wdl = 0;
|
||||
if (dtz > 0)
|
||||
wdl = (dtz + cnt50 <= 100) ? 2 : 1;
|
||||
else if (dtz < 0)
|
||||
wdl = (-dtz + cnt50 <= 100) ? -2 : -1;
|
||||
|
||||
// Determine the score to report to the user.
|
||||
TBScore = wdl_to_Value[wdl + 2];
|
||||
// If the position is winning or losing, but too few moves left, adjust the
|
||||
// score to show how close it is to winning or losing.
|
||||
// NOTE: int(PawnValueEg) is used as scaling factor in score_to_uci().
|
||||
if (wdl == 1 && dtz <= 100)
|
||||
TBScore = (Value)(((200 - dtz - cnt50) * int(PawnValueEg)) / 200);
|
||||
else if (wdl == -1 && dtz >= -100)
|
||||
TBScore = -(Value)(((200 + dtz - cnt50) * int(PawnValueEg)) / 200);
|
||||
|
||||
// Now be a bit smart about filtering out moves.
|
||||
size_t j = 0;
|
||||
if (dtz > 0) { // winning (or 50-move rule draw)
|
||||
int best = 0xffff;
|
||||
for (size_t i = 0; i < Search::RootMoves.size(); i++) {
|
||||
int v = Search::RootMoves[i].score;
|
||||
if (v > 0 && v < best)
|
||||
best = v;
|
||||
}
|
||||
int max = best;
|
||||
// If the current phase has not seen repetitions, then try all moves
|
||||
// that stay safely within the 50-move budget, if there are any.
|
||||
if (!has_repeated(st.previous) && best + cnt50 <= 99)
|
||||
max = 99 - cnt50;
|
||||
for (size_t i = 0; i < Search::RootMoves.size(); i++) {
|
||||
int v = Search::RootMoves[i].score;
|
||||
if (v > 0 && v <= max)
|
||||
Search::RootMoves[j++] = Search::RootMoves[i];
|
||||
}
|
||||
} else if (dtz < 0) { // losing (or 50-move rule draw)
|
||||
int best = 0;
|
||||
for (size_t i = 0; i < Search::RootMoves.size(); i++) {
|
||||
int v = Search::RootMoves[i].score;
|
||||
if (v < best)
|
||||
best = v;
|
||||
}
|
||||
// Try all moves, unless we approach or have a 50-move rule draw.
|
||||
if (-best * 2 + cnt50 < 100)
|
||||
return true;
|
||||
for (size_t i = 0; i < Search::RootMoves.size(); i++) {
|
||||
if (Search::RootMoves[i].score == best)
|
||||
Search::RootMoves[j++] = Search::RootMoves[i];
|
||||
}
|
||||
} else { // drawing
|
||||
// Try all moves that preserve the draw.
|
||||
for (size_t i = 0; i < Search::RootMoves.size(); i++) {
|
||||
if (Search::RootMoves[i].score == 0)
|
||||
Search::RootMoves[j++] = Search::RootMoves[i];
|
||||
}
|
||||
}
|
||||
Search::RootMoves.resize(j, Search::RootMove(MOVE_NONE));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use the WDL tables to filter out moves that don't preserve the win or draw.
|
||||
// This is a fallback for the case that some or all DTZ tables are missing.
|
||||
//
|
||||
// A return value false indicates that not all probes were successful and that
|
||||
// no moves were filtered out.
|
||||
bool Tablebases::root_probe_wdl(Position& pos, Value& TBScore)
|
||||
{
|
||||
int success;
|
||||
|
||||
int wdl = Tablebases::probe_wdl(pos, &success);
|
||||
if (!success) return false;
|
||||
TBScore = wdl_to_Value[wdl + 2];
|
||||
|
||||
StateInfo st;
|
||||
CheckInfo ci(pos);
|
||||
|
||||
int best = -2;
|
||||
|
||||
// Probe each move.
|
||||
for (size_t i = 0; i < Search::RootMoves.size(); i++) {
|
||||
Move move = Search::RootMoves[i].pv[0];
|
||||
pos.do_move(move, st, ci, pos.gives_check(move, ci));
|
||||
int v = -Tablebases::probe_wdl(pos, &success);
|
||||
pos.undo_move(move);
|
||||
if (!success) return false;
|
||||
Search::RootMoves[i].score = (Value)v;
|
||||
if (v > best)
|
||||
best = v;
|
||||
}
|
||||
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < Search::RootMoves.size(); i++) {
|
||||
if (Search::RootMoves[i].score == best)
|
||||
Search::RootMoves[j++] = Search::RootMoves[i];
|
||||
}
|
||||
Search::RootMoves.resize(j, Search::RootMove(MOVE_NONE));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#ifndef TBPROBE_H
|
||||
#define TBPROBE_H
|
||||
|
||||
namespace Tablebases {
|
||||
|
||||
extern int TBLargest;
|
||||
|
||||
void init(const std::string& path);
|
||||
int probe_wdl(Position& pos, int *success);
|
||||
int probe_dtz(Position& pos, int *success);
|
||||
bool root_probe(Position& pos, Value& TBScore);
|
||||
bool root_probe_wdl(Position& pos, Value& TBScore);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "movegen.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
#include "ucioption.h"
|
||||
#include "uci.h"
|
||||
|
||||
using namespace Search;
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace {
|
||||
|
||||
|
||||
// Helpers to launch a thread after creation and joining before delete. Must be
|
||||
// outside Thread c'tor and d'tor because the object will 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.
|
||||
|
||||
template<typename T> T* new_thread() {
|
||||
@@ -50,7 +50,11 @@ namespace {
|
||||
}
|
||||
|
||||
void delete_thread(ThreadBase* th) {
|
||||
|
||||
th->mutex.lock();
|
||||
th->exit = true; // Search must be already finished
|
||||
th->mutex.unlock();
|
||||
|
||||
th->notify_one();
|
||||
thread_join(th->handle); // Wait for thread termination
|
||||
delete th;
|
||||
@@ -59,7 +63,7 @@ namespace {
|
||||
}
|
||||
|
||||
|
||||
// notify_one() wakes up the thread when there is some work to do
|
||||
// ThreadBase::notify_one() wakes up the thread when there is some work to do
|
||||
|
||||
void ThreadBase::notify_one() {
|
||||
|
||||
@@ -69,20 +73,20 @@ void ThreadBase::notify_one() {
|
||||
}
|
||||
|
||||
|
||||
// wait_for() set the thread to sleep until condition 'b' turns true
|
||||
// ThreadBase::wait_for() set the thread to sleep until 'condition' turns true
|
||||
|
||||
void ThreadBase::wait_for(volatile const bool& b) {
|
||||
void ThreadBase::wait_for(volatile const bool& condition) {
|
||||
|
||||
mutex.lock();
|
||||
while (!b) sleepCondition.wait(mutex);
|
||||
while (!condition) sleepCondition.wait(mutex);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
// Thread c'tor just inits data and does not launch any execution thread.
|
||||
// Such a thread will only be started when c'tor returns.
|
||||
// Thread c'tor makes some init but does not launch any execution thread that
|
||||
// will be started only when c'tor returns.
|
||||
|
||||
Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC
|
||||
Thread::Thread() /* : splitPoints() */ { // Initialization of non POD broken in MSVC
|
||||
|
||||
searching = false;
|
||||
maxPly = splitPointsSize = 0;
|
||||
@@ -92,7 +96,7 @@ Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC
|
||||
}
|
||||
|
||||
|
||||
// cutoff_occurred() checks whether a beta cutoff has occurred in the
|
||||
// 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 {
|
||||
@@ -127,145 +131,25 @@ bool Thread::available_to(const Thread* master) const {
|
||||
}
|
||||
|
||||
|
||||
// TimerThread::idle_loop() is where the timer thread waits msec milliseconds
|
||||
// and then calls check_time(). If msec is 0 thread sleeps until it's woken up.
|
||||
|
||||
void TimerThread::idle_loop() {
|
||||
|
||||
while (!exit)
|
||||
{
|
||||
mutex.lock();
|
||||
|
||||
if (!exit)
|
||||
sleepCondition.wait_for(mutex, run ? Resolution : INT_MAX);
|
||||
|
||||
mutex.unlock();
|
||||
|
||||
if (run)
|
||||
check_time();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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.
|
||||
|
||||
void MainThread::idle_loop() {
|
||||
|
||||
while (true)
|
||||
{
|
||||
mutex.lock();
|
||||
|
||||
thinking = false;
|
||||
|
||||
while (!thinking && !exit)
|
||||
{
|
||||
Threads.sleepCondition.notify_one(); // Wake up the UI thread if needed
|
||||
sleepCondition.wait(mutex);
|
||||
}
|
||||
|
||||
mutex.unlock();
|
||||
|
||||
if (exit)
|
||||
return;
|
||||
|
||||
searching = true;
|
||||
|
||||
Search::think();
|
||||
|
||||
assert(searching);
|
||||
|
||||
searching = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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 static
|
||||
// object and we need a fully initialized engine at this point due to allocation
|
||||
// of Endgames in Thread c'tor.
|
||||
|
||||
void ThreadPool::init() {
|
||||
|
||||
timer = new_thread<TimerThread>();
|
||||
push_back(new_thread<MainThread>());
|
||||
read_uci_options();
|
||||
}
|
||||
|
||||
|
||||
// exit() cleanly terminates the threads before the program exits. Cannot be done in
|
||||
// d'tor because we have to terminate the threads before to free ThreadPool object.
|
||||
|
||||
void ThreadPool::exit() {
|
||||
|
||||
delete_thread(timer); // As first because check_time() accesses threads data
|
||||
|
||||
for (iterator it = begin(); it != end(); ++it)
|
||||
delete_thread(*it);
|
||||
}
|
||||
|
||||
|
||||
// read_uci_options() updates internal threads parameters from the corresponding
|
||||
// UCI options and creates/destroys threads to match the requested number. Thread
|
||||
// objects are dynamically allocated to avoid creating all possible threads
|
||||
// in advance (which include pawns and material tables), even if only a few
|
||||
// are to be used.
|
||||
|
||||
void ThreadPool::read_uci_options() {
|
||||
|
||||
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
|
||||
size_t requested = Options["Threads"];
|
||||
|
||||
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)
|
||||
push_back(new_thread<Thread>());
|
||||
|
||||
while (size() > requested)
|
||||
{
|
||||
delete_thread(back());
|
||||
pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// available_slave() tries to find an idle thread which is available as a slave
|
||||
// for the thread 'master'.
|
||||
|
||||
Thread* ThreadPool::available_slave(const Thread* master) const {
|
||||
|
||||
for (const_iterator it = begin(); it != end(); ++it)
|
||||
if ((*it)->available_to(master))
|
||||
return *it;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// split() does the actual work of distributing the work at a node between
|
||||
// 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
|
||||
// told that they have been assigned work. This will cause them to instantly
|
||||
// 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, const Stack* ss, Value alpha, Value beta, Value* bestValue,
|
||||
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(pos.pos_is_ok());
|
||||
assert(searching);
|
||||
assert(-VALUE_INFINITE < *bestValue && *bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE);
|
||||
assert(depth >= Threads.minimumSplitDepth);
|
||||
assert(searching);
|
||||
assert(splitPointsSize < MAX_SPLITPOINTS_PER_THREAD);
|
||||
|
||||
// Pick the next available split point from the split point stack
|
||||
// Pick and init the next available split point
|
||||
SplitPoint& sp = splitPoints[splitPointsSize];
|
||||
|
||||
sp.masterThread = this;
|
||||
@@ -296,7 +180,9 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
|
||||
activeSplitPoint = &sp;
|
||||
activePosition = NULL;
|
||||
|
||||
for (Thread* slave; (slave = Threads.available_slave(this)) != NULL; )
|
||||
Thread* slave;
|
||||
|
||||
while ((slave = Threads.available_slave(this)) != NULL)
|
||||
{
|
||||
sp.slavesMask.set(slave->idx);
|
||||
slave->activeSplitPoint = &sp;
|
||||
@@ -320,8 +206,8 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
|
||||
assert(!activePosition);
|
||||
|
||||
// We have returned from the idle loop, which means that all threads are
|
||||
// finished. Note that setting 'searching' and decreasing splitPointsSize is
|
||||
// done under lock protection to avoid a race with Thread::available_to().
|
||||
// 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();
|
||||
|
||||
@@ -337,22 +223,142 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
|
||||
Threads.mutex.unlock();
|
||||
}
|
||||
|
||||
// wait_for_think_finished() waits for main thread to go to sleep then returns
|
||||
|
||||
void ThreadPool::wait_for_think_finished() {
|
||||
// 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.
|
||||
|
||||
MainThread* t = main();
|
||||
t->mutex.lock();
|
||||
while (t->thinking) sleepCondition.wait(t->mutex);
|
||||
t->mutex.unlock();
|
||||
void TimerThread::idle_loop() {
|
||||
|
||||
while (!exit)
|
||||
{
|
||||
mutex.lock();
|
||||
|
||||
if (!exit)
|
||||
sleepCondition.wait_for(mutex, run ? Resolution : INT_MAX);
|
||||
|
||||
mutex.unlock();
|
||||
|
||||
if (run)
|
||||
check_time();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// start_thinking() wakes up the main thread sleeping in MainThread::idle_loop()
|
||||
// so to start a new search, then returns immediately.
|
||||
// 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.
|
||||
|
||||
void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, StateStackPtr& states) {
|
||||
void MainThread::idle_loop() {
|
||||
|
||||
while (!exit)
|
||||
{
|
||||
mutex.lock();
|
||||
|
||||
thinking = false;
|
||||
|
||||
while (!thinking && !exit)
|
||||
{
|
||||
Threads.sleepCondition.notify_one(); // Wake up the UI thread if needed
|
||||
sleepCondition.wait(mutex);
|
||||
}
|
||||
|
||||
mutex.unlock();
|
||||
|
||||
if (!exit)
|
||||
{
|
||||
searching = true;
|
||||
|
||||
Search::think();
|
||||
|
||||
assert(searching);
|
||||
|
||||
searching = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
// static object and we need a fully initialized engine at this point due to
|
||||
// allocation of Endgames in Thread c'tor.
|
||||
|
||||
void ThreadPool::init() {
|
||||
|
||||
timer = new_thread<TimerThread>();
|
||||
push_back(new_thread<MainThread>());
|
||||
read_uci_options();
|
||||
}
|
||||
|
||||
|
||||
// ThreadPool::exit() terminates the threads before the program exits. Cannot be
|
||||
// done in d'tor because threads must be terminated before freeing us.
|
||||
|
||||
void ThreadPool::exit() {
|
||||
|
||||
delete_thread(timer); // As first because check_time() accesses threads data
|
||||
|
||||
for (iterator it = begin(); it != end(); ++it)
|
||||
delete_thread(*it);
|
||||
}
|
||||
|
||||
|
||||
// ThreadPool::read_uci_options() updates internal threads parameters from the
|
||||
// corresponding UCI options and creates/destroys threads to match the requested
|
||||
// number. Thread objects are dynamically allocated to avoid creating all possible
|
||||
// threads in advance (which include pawns and material tables), even if only a
|
||||
// few are to be used.
|
||||
|
||||
void ThreadPool::read_uci_options() {
|
||||
|
||||
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
|
||||
size_t requested = Options["Threads"];
|
||||
|
||||
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)
|
||||
push_back(new_thread<Thread>());
|
||||
|
||||
while (size() > requested)
|
||||
{
|
||||
delete_thread(back());
|
||||
pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ThreadPool::available_slave() tries to find an idle thread which is available
|
||||
// as a slave for the thread 'master'.
|
||||
|
||||
Thread* ThreadPool::available_slave(const Thread* master) const {
|
||||
|
||||
for (const_iterator it = begin(); it != end(); ++it)
|
||||
if ((*it)->available_to(master))
|
||||
return *it;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
// ThreadPool::start_thinking() wakes up the main thread sleeping in
|
||||
// MainThread::idle_loop() and starts a new search, then returns immediately.
|
||||
|
||||
void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
|
||||
StateStackPtr& states) {
|
||||
wait_for_think_finished();
|
||||
|
||||
SearchTime = Time::now(); // As early as possible
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -29,9 +29,14 @@
|
||||
#include "position.h"
|
||||
#include "search.h"
|
||||
|
||||
struct Thread;
|
||||
|
||||
const int 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); }
|
||||
@@ -57,13 +62,15 @@ private:
|
||||
WaitCondition c;
|
||||
};
|
||||
|
||||
struct Thread;
|
||||
|
||||
/// 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;
|
||||
const Search::Stack* ss;
|
||||
Search::Stack* ss;
|
||||
Thread* masterThread;
|
||||
Depth depth;
|
||||
Value beta;
|
||||
@@ -74,7 +81,7 @@ struct SplitPoint {
|
||||
MovePicker* movePicker;
|
||||
SplitPoint* parentSplitPoint;
|
||||
|
||||
// Shared data
|
||||
// Shared variable data
|
||||
Mutex mutex;
|
||||
std::bitset<MAX_THREADS> slavesMask;
|
||||
volatile bool allSlavesSearching;
|
||||
@@ -117,13 +124,13 @@ struct Thread : public ThreadBase {
|
||||
bool cutoff_occurred() const;
|
||||
bool available_to(const Thread* master) const;
|
||||
|
||||
void split(Position& pos, const Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove,
|
||||
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;
|
||||
Material::Table materialTable;
|
||||
Endgames endgames;
|
||||
Pawns::Table pawnsTable;
|
||||
Position* activePosition;
|
||||
size_t idx;
|
||||
int maxPly;
|
||||
@@ -143,10 +150,13 @@ struct MainThread : public Thread {
|
||||
};
|
||||
|
||||
struct TimerThread : public ThreadBase {
|
||||
|
||||
static const int Resolution = 5; // Millisec between two check_time() calls
|
||||
|
||||
TimerThread() : run(false) {}
|
||||
virtual void idle_loop();
|
||||
|
||||
bool run;
|
||||
static const int Resolution = 5; // msec between two check_time() calls
|
||||
};
|
||||
|
||||
|
||||
@@ -156,10 +166,10 @@ struct TimerThread : public ThreadBase {
|
||||
|
||||
struct ThreadPool : public std::vector<Thread*> {
|
||||
|
||||
void init(); // No c'tor and d'tor, threads rely on globals that should
|
||||
void exit(); // be initialized and are valid during the whole thread lifetime.
|
||||
void init(); // No c'tor and d'tor, threads rely on globals that should be
|
||||
void exit(); // initialized and are valid during the whole thread lifetime.
|
||||
|
||||
MainThread* main() { return static_cast<MainThread*>((*this)[0]); }
|
||||
MainThread* main() { return static_cast<MainThread*>(at(0)); }
|
||||
void read_uci_options();
|
||||
Thread* available_slave(const Thread* master) const;
|
||||
void wait_for_think_finished();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include "search.h"
|
||||
#include "timeman.h"
|
||||
#include "ucioption.h"
|
||||
#include "uci.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -33,10 +33,6 @@ namespace {
|
||||
const double MaxRatio = 7.0; // When in trouble, we can step over reserved time with this ratio
|
||||
const double StealRatio = 0.33; // However we must not steal time from remaining moves over this ratio
|
||||
|
||||
const double xscale = 9.3;
|
||||
const double xshift = 59.8;
|
||||
const double skewfactor = 0.172;
|
||||
|
||||
|
||||
// move_importance() is a skew-logistic function based on naive statistical
|
||||
// analysis of "how many games are still undecided after n half-moves". Game
|
||||
@@ -45,79 +41,76 @@ namespace {
|
||||
|
||||
double move_importance(int ply) {
|
||||
|
||||
return pow((1 + exp((ply - xshift) / xscale)), -skewfactor) + DBL_MIN; // Ensure non-zero
|
||||
const double XScale = 9.3;
|
||||
const double XShift = 59.8;
|
||||
const double Skew = 0.172;
|
||||
|
||||
return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero
|
||||
}
|
||||
|
||||
template<TimeType T>
|
||||
int remaining(int myTime, int movesToGo, int currentPly, int slowMover)
|
||||
int remaining(int myTime, int movesToGo, int ply, int slowMover)
|
||||
{
|
||||
const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
|
||||
const double TStealRatio = (T == OptimumTime ? 0 : StealRatio);
|
||||
|
||||
double thisMoveImportance = (move_importance(currentPly) * slowMover) / 100;
|
||||
double moveImportance = (move_importance(ply) * slowMover) / 100;
|
||||
double otherMovesImportance = 0;
|
||||
|
||||
for (int i = 1; i < movesToGo; ++i)
|
||||
otherMovesImportance += move_importance(currentPly + 2 * i);
|
||||
otherMovesImportance += move_importance(ply + 2 * i);
|
||||
|
||||
double ratio1 = (TMaxRatio * thisMoveImportance) / (TMaxRatio * thisMoveImportance + otherMovesImportance);
|
||||
double ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / (thisMoveImportance + otherMovesImportance);
|
||||
double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance);
|
||||
double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance);
|
||||
|
||||
return int(myTime * std::min(ratio1, ratio2));
|
||||
return int(myTime * std::min(ratio1, ratio2)); // Intel C++ asks an explicit cast
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color us)
|
||||
/// init() is called at the beginning of the search and calculates the allowed
|
||||
/// thinking time out of the time control and current game ply. We support four
|
||||
/// different kinds of time controls, passed in 'limits':
|
||||
///
|
||||
/// inc == 0 && movestogo == 0 means: x basetime [sudden death!]
|
||||
/// inc == 0 && movestogo != 0 means: x moves in y minutes
|
||||
/// inc > 0 && movestogo == 0 means: x basetime + 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)
|
||||
{
|
||||
/* We support four different kinds of time controls:
|
||||
|
||||
increment == 0 && movesToGo == 0 means: x basetime [sudden death!]
|
||||
increment == 0 && movesToGo != 0 means: x moves in y minutes
|
||||
increment > 0 && movesToGo == 0 means: x basetime + z increment
|
||||
increment > 0 && movesToGo != 0 means: x moves in y minutes + z increment
|
||||
|
||||
Time management is adjusted by following parameters:
|
||||
|
||||
emergencyMoveHorizon: Be prepared to always play at least this many moves
|
||||
emergencyBaseTime : Always attempt to keep at least this much time (in ms) at clock
|
||||
emergencyMoveTime : Plus attempt to keep at least this much time for each remaining emergency move
|
||||
minThinkingTime : No matter what, use at least this much thinking before doing the move
|
||||
*/
|
||||
|
||||
int hypMTG, hypMyTime, t1, t2;
|
||||
|
||||
// Read uci parameters
|
||||
int moveOverhead = Options["Move Overhead"];
|
||||
int minThinkingTime = Options["Minimum Thinking Time"];
|
||||
int moveOverhead = Options["Move Overhead"];
|
||||
int slowMover = Options["Slow Mover"];
|
||||
|
||||
// Initialize unstablePvFactor to 1 and search times to maximum values
|
||||
unstablePvFactor = 1;
|
||||
optimumSearchTime = maximumSearchTime = std::max(limits.time[us], minThinkingTime);
|
||||
|
||||
// We calculate optimum time usage for different hypothetical "moves to go"-values and choose the
|
||||
// minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values.
|
||||
for (hypMTG = 1; hypMTG <= (limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon); ++hypMTG)
|
||||
const int MaxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon;
|
||||
|
||||
// We calculate optimum time usage for different hypothetical "moves to go"-values
|
||||
// and choose the minimum of calculated search time values. Usually the greatest
|
||||
// hypMTG gives the minimum values.
|
||||
for (int hypMTG = 1; hypMTG <= MaxMTG; ++hypMTG)
|
||||
{
|
||||
// Calculate thinking time for hypothetical "moves to go"-value
|
||||
hypMyTime = limits.time[us]
|
||||
+ limits.inc[us] * (hypMTG - 1)
|
||||
- moveOverhead * (2 + std::min(hypMTG, 40));
|
||||
int hypMyTime = limits.time[us]
|
||||
+ limits.inc[us] * (hypMTG - 1)
|
||||
- moveOverhead * (2 + std::min(hypMTG, 40));
|
||||
|
||||
hypMyTime = std::max(hypMyTime, 0);
|
||||
|
||||
t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, currentPly, slowMover);
|
||||
t2 = minThinkingTime + remaining<MaxTime>(hypMyTime, hypMTG, currentPly, slowMover);
|
||||
int t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, ply, slowMover);
|
||||
int t2 = minThinkingTime + remaining<MaxTime >(hypMyTime, hypMTG, ply, slowMover);
|
||||
|
||||
optimumSearchTime = std::min(optimumSearchTime, t1);
|
||||
maximumSearchTime = std::min(maximumSearchTime, t2);
|
||||
optimumSearchTime = std::min(t1, optimumSearchTime);
|
||||
maximumSearchTime = std::min(t2, maximumSearchTime);
|
||||
}
|
||||
|
||||
if (Options["Ponder"])
|
||||
optimumSearchTime += optimumSearchTime / 4;
|
||||
|
||||
// Make sure that maxSearchTime is not over absoluteMaxSearchTime
|
||||
optimumSearchTime = std::min(optimumSearchTime, maximumSearchTime);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
class TimeManager {
|
||||
public:
|
||||
void init(const Search::LimitsType& limits, int currentPly, Color us);
|
||||
void init(const Search::LimitsType& limits, Color us, int ply);
|
||||
void pv_instability(double bestMoveChanges) { unstablePvFactor = 1 + bestMoveChanges; }
|
||||
int available_time() const { return int(optimumSearchTime * unstablePvFactor * 0.71); }
|
||||
int maximum_time() const { return maximumSearchTime; }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -17,7 +17,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <cstring> // For std::memset
|
||||
#include <iostream>
|
||||
|
||||
#include "bitboard.h"
|
||||
@@ -28,11 +28,13 @@ TranspositionTable TT; // Our global transposition table
|
||||
|
||||
/// TranspositionTable::resize() sets the size of the transposition table,
|
||||
/// measured in megabytes. Transposition table consists of a power of 2 number
|
||||
/// of clusters and each cluster consists of TTClusterSize number of TTEntry.
|
||||
/// of clusters and each cluster consists of ClusterSize number of TTEntry.
|
||||
|
||||
void TranspositionTable::resize(size_t mbSize) {
|
||||
|
||||
size_t newClusterCount = size_t(1) << msb((mbSize * 1024 * 1024) / sizeof(TTCluster));
|
||||
assert(sizeof(Cluster) == CacheLineSize / 2);
|
||||
|
||||
size_t newClusterCount = size_t(1) << msb((mbSize * 1024 * 1024) / sizeof(Cluster));
|
||||
|
||||
if (newClusterCount == clusterCount)
|
||||
return;
|
||||
@@ -40,7 +42,7 @@ void TranspositionTable::resize(size_t mbSize) {
|
||||
clusterCount = newClusterCount;
|
||||
|
||||
free(mem);
|
||||
mem = calloc(clusterCount * sizeof(TTCluster) + CACHE_LINE_SIZE - 1, 1);
|
||||
mem = calloc(clusterCount * sizeof(Cluster) + CacheLineSize - 1, 1);
|
||||
|
||||
if (!mem)
|
||||
{
|
||||
@@ -49,72 +51,48 @@ void TranspositionTable::resize(size_t mbSize) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
table = (TTCluster*)((uintptr_t(mem) + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1));
|
||||
table = (Cluster*)((uintptr_t(mem) + CacheLineSize - 1) & ~(CacheLineSize - 1));
|
||||
}
|
||||
|
||||
|
||||
/// TranspositionTable::clear() overwrites the entire transposition table
|
||||
/// with zeroes. It is called whenever the table is resized, or when the
|
||||
/// with zeros. It is called whenever the table is resized, or when the
|
||||
/// user asks the program to clear the table (from the UCI interface).
|
||||
|
||||
void TranspositionTable::clear() {
|
||||
|
||||
std::memset(table, 0, clusterCount * sizeof(TTCluster));
|
||||
std::memset(table, 0, clusterCount * sizeof(Cluster));
|
||||
}
|
||||
|
||||
|
||||
/// TranspositionTable::probe() looks up the current position in the
|
||||
/// transposition table. Returns a pointer to the TTEntry or NULL if
|
||||
/// position is not found.
|
||||
/// 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.
|
||||
/// 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
|
||||
/// TTEntry t2 if t1 is from the current search and t2 is from a previous search,
|
||||
/// or if the depth of t1 is bigger than the depth of t2.
|
||||
|
||||
const TTEntry* TranspositionTable::probe(const Key key) const {
|
||||
TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
|
||||
|
||||
TTEntry* tte = first_entry(key);
|
||||
uint16_t key16 = key >> 48;
|
||||
TTEntry* const tte = first_entry(key);
|
||||
const uint16_t key16 = key >> 48; // Use the high 16 bits as key inside the cluster
|
||||
|
||||
for (unsigned i = 0; i < TTClusterSize; ++i, ++tte)
|
||||
if (tte->key16 == key16)
|
||||
for (int i = 0; i < ClusterSize; ++i)
|
||||
if (!tte[i].key16 || tte[i].key16 == key16)
|
||||
{
|
||||
tte->genBound8 = generation | tte->bound(); // Refresh
|
||||
return tte;
|
||||
if (tte[i].key16)
|
||||
tte[i].genBound8 = uint8_t(generation8 | tte[i].bound()); // Refresh
|
||||
|
||||
return found = (bool)tte[i].key16, &tte[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/// TranspositionTable::store() writes a new entry containing position key and
|
||||
/// valuable information of current position. The lowest order bits of position
|
||||
/// key are used to decide in which cluster the position will be placed.
|
||||
/// When a new entry is written and there are no empty entries available in the
|
||||
/// cluster, it replaces the least valuable of the entries. A TTEntry t1 is considered
|
||||
/// to be more valuable than a TTEntry t2 if t1 is from the current search and t2
|
||||
/// is from a previous search, or if the depth of t1 is bigger than the depth of t2.
|
||||
|
||||
void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV) {
|
||||
|
||||
TTEntry *tte, *replace;
|
||||
uint16_t key16 = key >> 48; // Use the high 16 bits as key inside the cluster
|
||||
|
||||
tte = replace = first_entry(key);
|
||||
|
||||
for (unsigned i = 0; i < TTClusterSize; ++i, ++tte)
|
||||
{
|
||||
if (!tte->key16 || tte->key16 == key16) // Empty or overwrite old
|
||||
{
|
||||
if (!m)
|
||||
m = tte->move(); // Preserve any existing ttMove
|
||||
|
||||
replace = tte;
|
||||
break;
|
||||
}
|
||||
|
||||
// Implement replace strategy
|
||||
if ( (( tte->genBound8 & 0xFC) == generation || tte->bound() == BOUND_EXACT)
|
||||
- ((replace->genBound8 & 0xFC) == generation)
|
||||
- (tte->depth8 < replace->depth8) < 0)
|
||||
replace = tte;
|
||||
}
|
||||
|
||||
replace->save(key16, v, b, d, m, generation, statV);
|
||||
// Find an entry to be replaced according to the replacement strategy
|
||||
TTEntry* replace = tte;
|
||||
for (int i = 1; i < ClusterSize; ++i)
|
||||
if ( (( tte[i].genBound8 & 0xFC) == generation8 || tte[i].bound() == BOUND_EXACT)
|
||||
- ((replace->genBound8 & 0xFC) == generation8)
|
||||
- (tte[i].depth8 < replace->depth8) < 0)
|
||||
replace = &tte[i];
|
||||
|
||||
return found = false, replace;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "misc.h"
|
||||
#include "types.h"
|
||||
|
||||
/// The TTEntry is the 10 bytes transposition table entry, defined as below:
|
||||
/// TTEntry struct is the 10 bytes transposition table entry, defined as below:
|
||||
///
|
||||
/// key 16 bit
|
||||
/// move 16 bit
|
||||
@@ -35,80 +35,72 @@
|
||||
|
||||
struct TTEntry {
|
||||
|
||||
Move move() const { return (Move )move16; }
|
||||
Value value() const { return (Value)value16; }
|
||||
Value eval_value() const { return (Value)evalValue; }
|
||||
Depth depth() const { return (Depth)depth8; }
|
||||
Bound bound() const { return (Bound)(genBound8 & 0x3); }
|
||||
Move move() const { return (Move )move16; }
|
||||
Value value() const { return (Value)value16; }
|
||||
Value eval() const { return (Value)eval16; }
|
||||
Depth depth() const { return (Depth)depth8; }
|
||||
Bound bound() const { return (Bound)(genBound8 & 0x3); }
|
||||
|
||||
private:
|
||||
friend class TranspositionTable;
|
||||
void save(Key k, Value v, Bound b, Depth d, Move m, Value ev, uint8_t g) {
|
||||
|
||||
void save(uint16_t k, Value v, Bound b, Depth d, Move m, uint8_t g, Value ev) {
|
||||
if (m || (k >> 48) != key16) // Preserve any existing move for the same position
|
||||
move16 = (uint16_t)m;
|
||||
|
||||
key16 = (uint16_t)k;
|
||||
move16 = (uint16_t)m;
|
||||
key16 = (uint16_t)(k >> 48);
|
||||
value16 = (int16_t)v;
|
||||
evalValue = (int16_t)ev;
|
||||
eval16 = (int16_t)ev;
|
||||
genBound8 = (uint8_t)(g | b);
|
||||
depth8 = (int8_t)d;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class TranspositionTable;
|
||||
|
||||
uint16_t key16;
|
||||
uint16_t move16;
|
||||
int16_t value16;
|
||||
int16_t evalValue;
|
||||
int16_t eval16;
|
||||
uint8_t genBound8;
|
||||
int8_t depth8;
|
||||
};
|
||||
|
||||
/// TTCluster is a 32 bytes cluster of TT entries consisting of:
|
||||
///
|
||||
/// 3 x TTEntry (3 x 10 bytes)
|
||||
/// padding (2 bytes)
|
||||
|
||||
const unsigned TTClusterSize = 3;
|
||||
|
||||
struct TTCluster {
|
||||
TTEntry entry[TTClusterSize];
|
||||
char padding[2];
|
||||
};
|
||||
|
||||
/// A TranspositionTable consists of a power of 2 number of clusters and each
|
||||
/// cluster consists of TTClusterSize number of TTEntry. Each non-empty entry
|
||||
/// cluster consists of ClusterSize number of TTEntry. Each non-empty entry
|
||||
/// contains information of exactly one position. The size of a cluster should
|
||||
/// not be bigger than a cache line size. In case it is less, it should be padded
|
||||
/// to guarantee always aligned accesses.
|
||||
|
||||
class TranspositionTable {
|
||||
|
||||
static const int CacheLineSize = 64;
|
||||
static const int ClusterSize = 3;
|
||||
|
||||
struct Cluster {
|
||||
TTEntry entry[ClusterSize];
|
||||
char padding[2]; // Align to the cache line size
|
||||
};
|
||||
|
||||
public:
|
||||
~TranspositionTable() { free(mem); }
|
||||
void new_search() { generation += 4; } // Lower 2 bits are used by Bound
|
||||
|
||||
const TTEntry* probe(const Key key) const;
|
||||
TTEntry* first_entry(const Key key) const;
|
||||
void new_search() { generation8 += 4; } // Lower 2 bits are used by Bound
|
||||
uint8_t generation() const { return generation8; }
|
||||
TTEntry* probe(const Key key, bool& found) const;
|
||||
void resize(size_t mbSize);
|
||||
void clear();
|
||||
void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV);
|
||||
|
||||
// The lowest order bits of the key are used to get the index of the cluster
|
||||
TTEntry* first_entry(const Key key) const {
|
||||
return &table[(size_t)key & (clusterCount - 1)].entry[0];
|
||||
}
|
||||
|
||||
private:
|
||||
size_t clusterCount;
|
||||
TTCluster* table;
|
||||
Cluster* table;
|
||||
void* mem;
|
||||
uint8_t generation; // Size must be not bigger than TTEntry::genBound8
|
||||
uint8_t generation8; // Size must be not bigger than TTEntry::genBound8
|
||||
};
|
||||
|
||||
extern TranspositionTable TT;
|
||||
|
||||
|
||||
/// TranspositionTable::first_entry() returns a pointer to the first entry of
|
||||
/// a cluster given a position. The lowest order bits of the key are used to
|
||||
/// get the index of the cluster inside the table.
|
||||
|
||||
inline TTEntry* TranspositionTable::first_entry(const Key key) const {
|
||||
|
||||
return &table[(size_t)key & (clusterCount - 1)].entry[0];
|
||||
}
|
||||
|
||||
#endif // #ifndef TT_H_INCLUDED
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -20,20 +20,19 @@
|
||||
#ifndef TYPES_H_INCLUDED
|
||||
#define TYPES_H_INCLUDED
|
||||
|
||||
/// For Linux and OSX configuration is done automatically using Makefile. To get
|
||||
/// started type 'make help'.
|
||||
/// When compiling with provided Makefile (e.g. for Linux and OSX), configuration
|
||||
/// is done automatically. To get started type 'make help'.
|
||||
///
|
||||
/// For Windows, part of the configuration is detected automatically, but some
|
||||
/// switches need to be set manually:
|
||||
/// When Makefile is not used (e.g. with Microsoft Visual Studio) some switches
|
||||
/// need to be set manually:
|
||||
///
|
||||
/// -DNDEBUG | Disable debugging mode. Always use this.
|
||||
/// -DNDEBUG | Disable debugging mode. Always use this for release.
|
||||
///
|
||||
/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want
|
||||
/// | the executable to run on some very old machines.
|
||||
/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. You may need this to
|
||||
/// | run on some very old machines.
|
||||
///
|
||||
/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works
|
||||
/// | only in 64-bit mode. For compiling requires hardware with
|
||||
/// | popcnt support.
|
||||
/// | only in 64-bit mode and requires hardware with popcnt support.
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
@@ -42,33 +41,33 @@
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#define unlikely(x) (x) // For code annotation purposes
|
||||
/// Predefined macros hell:
|
||||
///
|
||||
/// __GNUC__ Compiler is gcc, Clang or Intel on Linux
|
||||
/// __INTEL_COMPILER Compiler is Intel
|
||||
/// _MSC_VER Compiler is MSVC or Intel on Windows
|
||||
/// _WIN32 Building on Windows (any)
|
||||
/// _WIN64 Building on Windows 64 bit
|
||||
|
||||
#if defined(_WIN64) && !defined(IS_64BIT)
|
||||
#if defined(_WIN64) && !defined(IS_64BIT) // Last condition means Makefile is not used
|
||||
# include <intrin.h> // MSVC popcnt and bsfq instrinsics
|
||||
# define IS_64BIT
|
||||
# define USE_BSFQ
|
||||
#endif
|
||||
|
||||
#if defined(USE_POPCNT) && defined(_MSC_VER) && defined(__INTEL_COMPILER)
|
||||
#if defined(USE_POPCNT) && defined(__INTEL_COMPILER) && defined(_MSC_VER)
|
||||
# include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic
|
||||
#endif
|
||||
|
||||
#if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER))
|
||||
# include <xmmintrin.h> // Intel and Microsoft header for _mm_prefetch()
|
||||
#endif
|
||||
|
||||
#if defined(USE_PEXT)
|
||||
# include <immintrin.h> // Header for _pext_u64() intrinsic
|
||||
# define pext(b, m) _pext_u64(b, m)
|
||||
#else
|
||||
//# define _pext_u64(b, m) (0)
|
||||
#endif
|
||||
|
||||
# if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER))
|
||||
# include <xmmintrin.h> // Intel and Microsoft header for _mm_prefetch()
|
||||
# endif
|
||||
|
||||
#define CACHE_LINE_SIZE 64
|
||||
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
|
||||
# define CACHE_LINE_ALIGNMENT __declspec(align(CACHE_LINE_SIZE))
|
||||
#else
|
||||
# define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(CACHE_LINE_SIZE)))
|
||||
# define pext(b, m) (0)
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@@ -100,9 +99,8 @@ const bool Is64Bit = false;
|
||||
typedef uint64_t Key;
|
||||
typedef uint64_t Bitboard;
|
||||
|
||||
const int MAX_MOVES = 256;
|
||||
const int MAX_PLY = 120;
|
||||
const int MAX_PLY_PLUS_6 = MAX_PLY + 6;
|
||||
const int MAX_MOVES = 256;
|
||||
const int MAX_PLY = 128;
|
||||
|
||||
/// A move needs 16 bits to be stored
|
||||
///
|
||||
@@ -139,9 +137,9 @@ enum CastlingSide {
|
||||
enum CastlingRight {
|
||||
NO_CASTLING,
|
||||
WHITE_OO,
|
||||
WHITE_OOO = WHITE_OO << 1,
|
||||
BLACK_OO = WHITE_OO << 2,
|
||||
BLACK_OOO = WHITE_OO << 3,
|
||||
WHITE_OOO = WHITE_OO << 1,
|
||||
BLACK_OO = WHITE_OO << 2,
|
||||
BLACK_OOO = WHITE_OO << 3,
|
||||
ANY_CASTLING = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO,
|
||||
CASTLING_RIGHT_NB = 16
|
||||
};
|
||||
@@ -218,7 +216,8 @@ enum Depth {
|
||||
DEPTH_QS_NO_CHECKS = -1,
|
||||
DEPTH_QS_RECAPTURES = -5,
|
||||
|
||||
DEPTH_NONE = -6
|
||||
DEPTH_NONE = -6,
|
||||
DEPTH_MAX = MAX_PLY
|
||||
};
|
||||
|
||||
enum Square {
|
||||
@@ -256,9 +255,9 @@ enum Rank {
|
||||
};
|
||||
|
||||
|
||||
/// The Score enum stores a middlegame and an endgame value in a single integer
|
||||
/// (enum). The least significant 16 bits are used to store the endgame value
|
||||
/// and the upper 16 bits are used to store the middlegame value. The compiler
|
||||
/// 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
|
||||
/// the upper 16 bits are used to store the middlegame value. The compiler
|
||||
/// is free to choose the enum type as long as it can store the data, so we
|
||||
/// ensure that Score is an integer type by assigning some big int values.
|
||||
enum Score {
|
||||
@@ -267,36 +266,41 @@ enum Score {
|
||||
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
|
||||
};
|
||||
|
||||
inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
|
||||
inline Score make_score(int mg, int eg) {
|
||||
return Score((mg << 16) + eg);
|
||||
}
|
||||
|
||||
/// Extracting the signed lower and upper 16 bits is not so trivial because
|
||||
/// according to the standard a simple cast to short is implementation defined
|
||||
/// and so is a right shift of a signed integer.
|
||||
inline Value mg_value(Score s) {
|
||||
return Value(((s + 0x8000) & ~0xffff) / 0x10000);
|
||||
|
||||
union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s + 0x8000) >> 16) };
|
||||
return Value(mg.s);
|
||||
}
|
||||
|
||||
inline Value eg_value(Score s) {
|
||||
return Value((int)(unsigned(s) & 0x7FFFU) - (int)(unsigned(s) & 0x8000U));
|
||||
|
||||
union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s)) };
|
||||
return Value(eg.s);
|
||||
}
|
||||
|
||||
#define ENABLE_BASE_OPERATORS_ON(T) \
|
||||
inline T operator+(const T d1, const T d2) { return T(int(d1) + int(d2)); } \
|
||||
inline T operator-(const T d1, const T d2) { return T(int(d1) - int(d2)); } \
|
||||
inline T operator*(int i, const T d) { return T(i * int(d)); } \
|
||||
inline T operator*(const T d, int i) { return T(int(d) * i); } \
|
||||
inline T operator-(const T d) { return T(-int(d)); } \
|
||||
inline T& operator+=(T& d1, const T d2) { return d1 = d1 + d2; } \
|
||||
inline T& operator-=(T& d1, const T d2) { return d1 = d1 - d2; } \
|
||||
#define ENABLE_BASE_OPERATORS_ON(T) \
|
||||
inline T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \
|
||||
inline T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \
|
||||
inline T operator*(int i, T d) { return T(i * int(d)); } \
|
||||
inline T operator*(T d, int i) { return T(int(d) * i); } \
|
||||
inline T operator-(T d) { return T(-int(d)); } \
|
||||
inline T& operator+=(T& d1, T d2) { return d1 = d1 + d2; } \
|
||||
inline T& operator-=(T& d1, T d2) { return d1 = d1 - d2; } \
|
||||
inline T& operator*=(T& d, int i) { return d = T(int(d) * i); }
|
||||
|
||||
ENABLE_BASE_OPERATORS_ON(Score)
|
||||
|
||||
#define ENABLE_FULL_OPERATORS_ON(T) \
|
||||
ENABLE_BASE_OPERATORS_ON(T) \
|
||||
inline T& operator++(T& d) { return d = T(int(d) + 1); } \
|
||||
inline T& operator--(T& d) { return d = T(int(d) - 1); } \
|
||||
inline T operator/(const T d, int i) { return T(int(d) / i); } \
|
||||
#define ENABLE_FULL_OPERATORS_ON(T) \
|
||||
ENABLE_BASE_OPERATORS_ON(T) \
|
||||
inline T& operator++(T& d) { return d = T(int(d) + 1); } \
|
||||
inline T& operator--(T& d) { return d = T(int(d) - 1); } \
|
||||
inline T operator/(T d, int i) { return T(int(d) / i); } \
|
||||
inline int operator/(T d1, T d2) { return int(d1) / int(d2); } \
|
||||
inline T& operator/=(T& d, int i) { return d = T(int(d) / i); }
|
||||
|
||||
ENABLE_FULL_OPERATORS_ON(Value)
|
||||
@@ -308,6 +312,8 @@ ENABLE_FULL_OPERATORS_ON(Square)
|
||||
ENABLE_FULL_OPERATORS_ON(File)
|
||||
ENABLE_FULL_OPERATORS_ON(Rank)
|
||||
|
||||
ENABLE_BASE_OPERATORS_ON(Score)
|
||||
|
||||
#undef ENABLE_FULL_OPERATORS_ON
|
||||
#undef ENABLE_BASE_OPERATORS_ON
|
||||
|
||||
@@ -326,19 +332,8 @@ inline Score operator/(Score s, int i) {
|
||||
return make_score(mg_value(s) / i, eg_value(s) / i);
|
||||
}
|
||||
|
||||
CACHE_LINE_ALIGNMENT
|
||||
|
||||
extern Value PieceValue[PHASE_NB][PIECE_NB];
|
||||
|
||||
struct ExtMove {
|
||||
Move move;
|
||||
Value value;
|
||||
};
|
||||
|
||||
inline bool operator<(const ExtMove& f, const ExtMove& s) {
|
||||
return f.value < s.value;
|
||||
}
|
||||
|
||||
inline Color operator~(Color c) {
|
||||
return Color(c ^ BLACK);
|
||||
}
|
||||
@@ -435,7 +430,7 @@ inline Move make(Square from, Square to, PieceType pt = KNIGHT) {
|
||||
}
|
||||
|
||||
inline bool is_ok(Move m) {
|
||||
return from_sq(m) != to_sq(m); // Catches also MOVE_NULL and MOVE_NONE
|
||||
return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE
|
||||
}
|
||||
|
||||
#endif // #ifndef TYPES_H_INCLUDED
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -17,18 +17,17 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "evaluate.h"
|
||||
#include "notation.h"
|
||||
#include "movegen.h"
|
||||
#include "position.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
#include "tt.h"
|
||||
#include "ucioption.h"
|
||||
#include "uci.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -39,9 +38,9 @@ namespace {
|
||||
// FEN string of the initial position, normal chess
|
||||
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
|
||||
// Keep a track of the position keys along the setup moves (from the start position
|
||||
// to the position just before the search starts). This is needed by the repetition
|
||||
// draw detection code.
|
||||
// Stack to keep track of the position states along the setup moves (from the
|
||||
// start position to the position just before the search starts). Needed by
|
||||
// 'draw by repetition' detection.
|
||||
Search::StateStackPtr SetupStates;
|
||||
|
||||
|
||||
@@ -72,7 +71,7 @@ namespace {
|
||||
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>());
|
||||
|
||||
// Parse move list (if any)
|
||||
while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE)
|
||||
while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE)
|
||||
{
|
||||
SetupStates->push(StateInfo());
|
||||
pos.do_move(m, SetupStates->top());
|
||||
@@ -105,7 +104,7 @@ namespace {
|
||||
|
||||
|
||||
// go() is called when engine receives the "go" UCI command. The function sets
|
||||
// the thinking time and other parameters from the input string, and starts
|
||||
// the thinking time and other parameters from the input string, then starts
|
||||
// the search.
|
||||
|
||||
void go(const Position& pos, istringstream& is) {
|
||||
@@ -114,10 +113,9 @@ namespace {
|
||||
string token;
|
||||
|
||||
while (is >> token)
|
||||
{
|
||||
if (token == "searchmoves")
|
||||
while (is >> token)
|
||||
limits.searchmoves.push_back(move_from_uci(pos, token));
|
||||
limits.searchmoves.push_back(UCI::to_move(pos, token));
|
||||
|
||||
else if (token == "wtime") is >> limits.time[WHITE];
|
||||
else if (token == "btime") is >> limits.time[BLACK];
|
||||
@@ -130,7 +128,6 @@ namespace {
|
||||
else if (token == "mate") is >> limits.mate;
|
||||
else if (token == "infinite") limits.infinite = true;
|
||||
else if (token == "ponder") limits.ponder = true;
|
||||
}
|
||||
|
||||
Threads.start_thinking(pos, limits, SetupStates);
|
||||
}
|
||||
@@ -138,10 +135,11 @@ namespace {
|
||||
} // namespace
|
||||
|
||||
|
||||
/// Wait for a command from the user, parse this text string as an UCI command,
|
||||
/// and call the appropriate functions. Also intercepts EOF from stdin to ensure
|
||||
/// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI
|
||||
/// commands, the function also supports a few debug commands.
|
||||
/// UCI::loop() waits for a command from stdin, parses it and calls the appropriate
|
||||
/// function. Also intercepts EOF from stdin to ensure gracefully exiting if the
|
||||
/// GUI dies unexpectedly. When called with some command line arguments, e.g. to
|
||||
/// run 'bench', once the command is executed the function returns immediately.
|
||||
/// In addition to the UCI ones, also some additional debug commands are supported.
|
||||
|
||||
void UCI::loop(int argc, char* argv[]) {
|
||||
|
||||
@@ -152,28 +150,45 @@ void UCI::loop(int argc, char* argv[]) {
|
||||
cmd += std::string(argv[i]) + " ";
|
||||
|
||||
do {
|
||||
if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input
|
||||
if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF
|
||||
cmd = "quit";
|
||||
|
||||
istringstream is(cmd);
|
||||
|
||||
token.clear(); // getline() could return empty or blank line
|
||||
is >> skipws >> token;
|
||||
|
||||
if (token == "quit" || token == "stop" || token == "ponderhit")
|
||||
// The GUI sends 'ponderhit' to tell us to ponder on the same move the
|
||||
// opponent has played. In case Signals.stopOnPonderhit is set we are
|
||||
// waiting for 'ponderhit' to stop the search (for instance because we
|
||||
// already ran out of time), otherwise we should continue searching but
|
||||
// switching from pondering to normal search.
|
||||
if ( token == "quit"
|
||||
|| token == "stop"
|
||||
|| (token == "ponderhit" && Search::Signals.stopOnPonderhit))
|
||||
{
|
||||
// The GUI sends 'ponderhit' to tell us to ponder on the same move the
|
||||
// opponent has played. In case Signals.stopOnPonderhit is set we are
|
||||
// waiting for 'ponderhit' to stop the search (for instance because we
|
||||
// already ran out of time), otherwise we should continue searching but
|
||||
// switch from pondering to normal search.
|
||||
if (token != "ponderhit" || Search::Signals.stopOnPonderhit)
|
||||
{
|
||||
Search::Signals.stop = true;
|
||||
Threads.main()->notify_one(); // Could be sleeping
|
||||
}
|
||||
else
|
||||
Search::Limits.ponder = false;
|
||||
Search::Signals.stop = true;
|
||||
Threads.main()->notify_one(); // Could be sleeping
|
||||
}
|
||||
else if (token == "ponderhit")
|
||||
Search::Limits.ponder = false; // Switch to normal search
|
||||
|
||||
else if (token == "uci")
|
||||
sync_cout << "id name " << engine_info(true)
|
||||
<< "\n" << Options
|
||||
<< "\nuciok" << 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 == "position") position(pos, is);
|
||||
else if (token == "setoption") setoption(is);
|
||||
|
||||
// Additional custom non-UCI commands, useful for debugging
|
||||
else if (token == "flip") pos.flip();
|
||||
else if (token == "bench") benchmark(pos, is);
|
||||
else if (token == "d") sync_cout << pos << sync_endl;
|
||||
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
|
||||
else if (token == "perft")
|
||||
{
|
||||
int depth;
|
||||
@@ -181,31 +196,10 @@ void UCI::loop(int argc, char* argv[]) {
|
||||
|
||||
is >> depth;
|
||||
ss << Options["Hash"] << " "
|
||||
<< Options["Threads"] << " " << depth << " current " << token;
|
||||
<< Options["Threads"] << " " << depth << " current perft";
|
||||
|
||||
benchmark(pos, ss);
|
||||
}
|
||||
else if (token == "key")
|
||||
sync_cout << hex << uppercase << setfill('0')
|
||||
<< "position key: " << setw(16) << pos.key()
|
||||
<< "\nmaterial key: " << setw(16) << pos.material_key()
|
||||
<< "\npawn key: " << setw(16) << pos.pawn_key()
|
||||
<< dec << nouppercase << setfill(' ') << sync_endl;
|
||||
|
||||
else if (token == "uci")
|
||||
sync_cout << "id name " << engine_info(true)
|
||||
<< "\n" << Options
|
||||
<< "\nuciok" << sync_endl;
|
||||
|
||||
else if (token == "ucinewgame") TT.clear();
|
||||
else if (token == "go") go(pos, is);
|
||||
else if (token == "position") position(pos, is);
|
||||
else if (token == "setoption") setoption(is);
|
||||
else if (token == "flip") pos.flip();
|
||||
else if (token == "bench") benchmark(pos, is);
|
||||
else if (token == "d") sync_cout << pos.pretty() << sync_endl;
|
||||
else if (token == "isready") sync_cout << "readyok" << sync_endl;
|
||||
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
|
||||
else
|
||||
sync_cout << "Unknown command: " << cmd << sync_endl;
|
||||
|
||||
@@ -213,3 +207,76 @@ void UCI::loop(int argc, char* argv[]) {
|
||||
|
||||
Threads.wait_for_think_finished(); // Cannot quit whilst the search is running
|
||||
}
|
||||
|
||||
|
||||
/// UCI::value() converts a Value to a string suitable for use with the UCI
|
||||
/// protocol specification:
|
||||
///
|
||||
/// cp <x> The score from the engine's point of view in centipawns.
|
||||
/// mate <y> Mate in y moves, not plies. If the engine is getting mated
|
||||
/// use negative values for y.
|
||||
|
||||
string UCI::value(Value v) {
|
||||
|
||||
stringstream ss;
|
||||
|
||||
if (abs(v) < VALUE_MATE - MAX_PLY)
|
||||
ss << "cp " << v * 100 / PawnValueEg;
|
||||
else
|
||||
ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
/// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.)
|
||||
|
||||
std::string UCI::square(Square s) {
|
||||
|
||||
char sq[] = { char('a' + file_of(s)), char('1' + rank_of(s)), 0 }; // NULL terminated
|
||||
return sq;
|
||||
}
|
||||
|
||||
|
||||
/// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q).
|
||||
/// The only special case is castling, where we print in the e1g1 notation in
|
||||
/// normal chess mode, and in e1h1 notation in chess960 mode. Internally all
|
||||
/// castling moves are always encoded as 'king captures rook'.
|
||||
|
||||
string UCI::move(Move m, bool chess960) {
|
||||
|
||||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
|
||||
if (m == MOVE_NONE)
|
||||
return "(none)";
|
||||
|
||||
if (m == MOVE_NULL)
|
||||
return "0000";
|
||||
|
||||
if (type_of(m) == CASTLING && !chess960)
|
||||
to = make_square(to > from ? FILE_G : FILE_C, rank_of(from));
|
||||
|
||||
string move = UCI::square(from) + UCI::square(to);
|
||||
|
||||
if (type_of(m) == PROMOTION)
|
||||
move += " pnbrqk"[promotion_type(m)];
|
||||
|
||||
return move;
|
||||
}
|
||||
|
||||
|
||||
/// UCI::to_move() converts a string representing a move in coordinate notation
|
||||
/// (g1f3, a7a8q) to the corresponding legal Move, if any.
|
||||
|
||||
Move UCI::to_move(const Position& pos, string& str) {
|
||||
|
||||
if (str.length() == 5) // Junior could send promotion piece in uppercase
|
||||
str[4] = char(tolower(str[4]));
|
||||
|
||||
for (MoveList<LEGAL> it(pos); *it; ++it)
|
||||
if (str == UCI::move(*it, pos.is_chess960()))
|
||||
return *it;
|
||||
|
||||
return MOVE_NONE;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -23,6 +23,10 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
class Position;
|
||||
|
||||
namespace UCI {
|
||||
|
||||
class Option;
|
||||
@@ -46,8 +50,8 @@ public:
|
||||
Option(const char* v, OnChange = NULL);
|
||||
Option(int v, int min, int max, OnChange = NULL);
|
||||
|
||||
Option& operator=(const std::string& v);
|
||||
void operator<<(const Option& o);
|
||||
Option& operator=(const std::string&);
|
||||
void operator<<(const Option&);
|
||||
operator int() const;
|
||||
operator std::string() const;
|
||||
|
||||
@@ -62,6 +66,10 @@ private:
|
||||
|
||||
void init(OptionsMap&);
|
||||
void loop(int argc, char* argv[]);
|
||||
std::string value(Value v);
|
||||
std::string square(Square s);
|
||||
std::string move(Move m, bool chess960);
|
||||
Move to_move(const Position& pos, std::string& str);
|
||||
|
||||
} // namespace UCI
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
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
|
||||
@@ -22,12 +22,11 @@
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
#include "evaluate.h"
|
||||
#include "misc.h"
|
||||
#include "thread.h"
|
||||
#include "tt.h"
|
||||
#include "ucioption.h"
|
||||
#include "tbprobe.h"
|
||||
#include "uci.h"
|
||||
#include "syzygy/tbprobe.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
@@ -36,11 +35,10 @@ UCI::OptionsMap Options; // Global object
|
||||
namespace UCI {
|
||||
|
||||
/// 'On change' actions, triggered by an option's value change
|
||||
void on_logger(const Option& o) { start_logger(o); }
|
||||
void on_eval(const Option&) { Eval::init(); }
|
||||
void on_threads(const Option&) { Threads.read_uci_options(); }
|
||||
void on_hash_size(const Option& o) { TT.resize(o); }
|
||||
void on_clear_hash(const Option&) { TT.clear(); }
|
||||
void on_hash_size(const Option& o) { TT.resize(o); }
|
||||
void on_logger(const Option& o) { start_logger(o); }
|
||||
void on_threads(const Option&) { Threads.read_uci_options(); }
|
||||
void on_tb_path(const Option& o) { Tablebases::init(o); }
|
||||
|
||||
|
||||
@@ -56,11 +54,13 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const
|
||||
|
||||
void init(OptionsMap& o) {
|
||||
|
||||
const int MaxHashMB = Is64Bit ? 1024 * 1024 : 2048;
|
||||
|
||||
o["Write Debug Log"] << Option(false, on_logger);
|
||||
o["Contempt"] << Option(0, -100, 100);
|
||||
o["Min Split Depth"] << Option(0, 0, 12, on_threads);
|
||||
o["Threads"] << Option(1, 1, MAX_THREADS, on_threads);
|
||||
o["Hash"] << Option(16, 1, 1024 * 1024, on_hash_size);
|
||||
o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size);
|
||||
o["Clear Hash"] << Option(on_clear_hash);
|
||||
o["Ponder"] << Option(true);
|
||||
o["MultiPV"] << Option(1, 1, 500);
|
||||
|
||||
Reference in New Issue
Block a user