mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-02-07 04:39:13 +01:00
DroidFish: Updated stockfish engine to git version from 2016-05-21.
This commit is contained in:
parent
db860811ca
commit
9395f8c658
|
@ -144,10 +144,12 @@ void benchmark(const Position& current, istream& is) {
|
|||
|
||||
uint64_t nodes = 0;
|
||||
TimePoint elapsed = now();
|
||||
Position pos;
|
||||
|
||||
for (size_t i = 0; i < fens.size(); ++i)
|
||||
{
|
||||
Position pos(fens[i], Options["UCI_Chess960"], Threads.main());
|
||||
StateListPtr states(new std::deque<StateInfo>(1));
|
||||
pos.set(fens[i], Options["UCI_Chess960"], &states->back(), Threads.main());
|
||||
|
||||
cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl;
|
||||
|
||||
|
@ -156,9 +158,8 @@ void benchmark(const Position& current, istream& is) {
|
|||
|
||||
else
|
||||
{
|
||||
Search::StateStackPtr st;
|
||||
limits.startTime = now();
|
||||
Threads.start_thinking(pos, limits, st);
|
||||
Threads.start_thinking(pos, states, limits);
|
||||
Threads.main()->wait_for_search_finished();
|
||||
nodes += Threads.nodes_searched();
|
||||
}
|
||||
|
@ -166,7 +167,7 @@ void benchmark(const Position& current, istream& is) {
|
|||
|
||||
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
|
||||
|
||||
dbg_print(); // Just before to exit
|
||||
dbg_print(); // Just before exiting
|
||||
|
||||
cerr << "\n==========================="
|
||||
<< "\nTotal time (ms) : " << elapsed
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "bitcount.h"
|
||||
#include "misc.h"
|
||||
|
||||
uint8_t PopCnt16[1 << 16];
|
||||
int SquareDistance[SQUARE_NB][SQUARE_NB];
|
||||
|
||||
Bitboard RookMasks [SQUARE_NB];
|
||||
|
@ -74,18 +74,30 @@ namespace {
|
|||
return Is64Bit ? (b * DeBruijn64) >> 58
|
||||
: ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn32) >> 26;
|
||||
}
|
||||
|
||||
|
||||
// popcount16() counts the non-zero bits using SWAR-Popcount algorithm
|
||||
|
||||
unsigned popcount16(unsigned u) {
|
||||
u -= (u >> 1) & 0x5555U;
|
||||
u = ((u >> 2) & 0x3333U) + (u & 0x3333U);
|
||||
u = ((u >> 4) + u) & 0x0F0FU;
|
||||
return (u * 0x0101U) >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef USE_BSFQ
|
||||
#ifdef NO_BSF
|
||||
|
||||
/// Software fall-back of lsb() and msb() for CPU lacking hardware support
|
||||
|
||||
Square lsb(Bitboard b) {
|
||||
assert(b);
|
||||
return BSFTable[bsf_index(b)];
|
||||
}
|
||||
|
||||
Square msb(Bitboard b) {
|
||||
|
||||
assert(b);
|
||||
unsigned b32;
|
||||
int result = 0;
|
||||
|
||||
|
@ -112,7 +124,7 @@ Square msb(Bitboard b) {
|
|||
return Square(result + MSBTable[b32]);
|
||||
}
|
||||
|
||||
#endif // ifndef USE_BSFQ
|
||||
#endif // ifdef NO_BSF
|
||||
|
||||
|
||||
/// Bitboards::pretty() returns an ASCII representation of a bitboard suitable
|
||||
|
@ -139,6 +151,9 @@ const std::string Bitboards::pretty(Bitboard b) {
|
|||
|
||||
void Bitboards::init() {
|
||||
|
||||
for (unsigned i = 0; i < (1 << 16); ++i)
|
||||
PopCnt16[i] = (uint8_t) popcount16(i);
|
||||
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
{
|
||||
SquareBB[s] = 1ULL << s;
|
||||
|
@ -263,7 +278,7 @@ namespace {
|
|||
// the number of 1s of the mask. Hence we deduce the size of the shift to
|
||||
// apply to the 64 or 32 bits word to get the index.
|
||||
masks[s] = sliding_attack(deltas, s, 0) & ~edges;
|
||||
shifts[s] = (Is64Bit ? 64 : 32) - popcount<Max15>(masks[s]);
|
||||
shifts[s] = (Is64Bit ? 64 : 32) - popcount(masks[s]);
|
||||
|
||||
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and
|
||||
// store the corresponding sliding attack bitboard in reference[].
|
||||
|
@ -294,7 +309,7 @@ namespace {
|
|||
do {
|
||||
do
|
||||
magics[s] = rng.sparse_rand<Bitboard>();
|
||||
while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6);
|
||||
while (popcount((magics[s] * masks[s]) >> 56) < 6);
|
||||
|
||||
// A good magic must map every possible occupancy to an index that
|
||||
// looks up the correct sliding attack in the attacks[s] database.
|
||||
|
|
|
@ -61,16 +61,6 @@ const Bitboard Rank8BB = Rank1BB << (8 * 7);
|
|||
|
||||
extern int SquareDistance[SQUARE_NB][SQUARE_NB];
|
||||
|
||||
extern Bitboard RookMasks [SQUARE_NB];
|
||||
extern Bitboard RookMagics [SQUARE_NB];
|
||||
extern Bitboard* RookAttacks[SQUARE_NB];
|
||||
extern unsigned RookShifts [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];
|
||||
extern Bitboard RankBB[RANK_NB];
|
||||
|
@ -225,6 +215,13 @@ template<> inline int distance<Rank>(Square x, Square y) { return distance(rank_
|
|||
template<PieceType Pt>
|
||||
inline unsigned magic_index(Square s, Bitboard occupied) {
|
||||
|
||||
extern Bitboard RookMasks[SQUARE_NB];
|
||||
extern Bitboard RookMagics[SQUARE_NB];
|
||||
extern unsigned RookShifts[SQUARE_NB];
|
||||
extern Bitboard BishopMasks[SQUARE_NB];
|
||||
extern Bitboard BishopMagics[SQUARE_NB];
|
||||
extern unsigned BishopShifts[SQUARE_NB];
|
||||
|
||||
Bitboard* const Masks = Pt == ROOK ? RookMasks : BishopMasks;
|
||||
Bitboard* const Magics = Pt == ROOK ? RookMagics : BishopMagics;
|
||||
unsigned* const Shifts = Pt == ROOK ? RookShifts : BishopShifts;
|
||||
|
@ -242,6 +239,10 @@ inline unsigned magic_index(Square s, Bitboard occupied) {
|
|||
|
||||
template<PieceType Pt>
|
||||
inline Bitboard attacks_bb(Square s, Bitboard occupied) {
|
||||
|
||||
extern Bitboard* RookAttacks[SQUARE_NB];
|
||||
extern Bitboard* BishopAttacks[SQUARE_NB];
|
||||
|
||||
return (Pt == ROOK ? RookAttacks : BishopAttacks)[s][magic_index<Pt>(s, occupied)];
|
||||
}
|
||||
|
||||
|
@ -257,56 +258,61 @@ inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) {
|
|||
}
|
||||
|
||||
|
||||
/// popcount() counts the number of non-zero bits in a bitboard
|
||||
|
||||
inline int popcount(Bitboard b) {
|
||||
|
||||
#ifndef USE_POPCNT
|
||||
|
||||
extern uint8_t PopCnt16[1 << 16];
|
||||
union { Bitboard bb; uint16_t u[4]; } v = { b };
|
||||
return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]];
|
||||
|
||||
#elif defined(_MSC_VER) || defined(__INTEL_COMPILER)
|
||||
|
||||
return (int)_mm_popcnt_u64(b);
|
||||
|
||||
#else // Assumed gcc or compatible compiler
|
||||
|
||||
return __builtin_popcountll(b);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// lsb() and msb() return the least/most significant bit in a non-zero bitboard
|
||||
|
||||
#ifdef USE_BSFQ
|
||||
|
||||
# if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
|
||||
#if defined(__GNUC__)
|
||||
|
||||
inline Square lsb(Bitboard b) {
|
||||
assert(b);
|
||||
return Square(__builtin_ctzll(b));
|
||||
}
|
||||
|
||||
inline Square msb(Bitboard b) {
|
||||
assert(b);
|
||||
return Square(63 - __builtin_clzll(b));
|
||||
}
|
||||
|
||||
#elif defined(_WIN64) && defined(_MSC_VER)
|
||||
|
||||
inline Square lsb(Bitboard b) {
|
||||
assert(b);
|
||||
unsigned long idx;
|
||||
_BitScanForward64(&idx, b);
|
||||
return (Square) idx;
|
||||
}
|
||||
|
||||
inline Square msb(Bitboard b) {
|
||||
assert(b);
|
||||
unsigned long idx;
|
||||
_BitScanReverse64(&idx, b);
|
||||
return (Square) idx;
|
||||
}
|
||||
|
||||
# elif defined(__arm__)
|
||||
#else
|
||||
|
||||
inline int lsb32(uint32_t v) {
|
||||
__asm__("rbit %0, %1" : "=r"(v) : "r"(v));
|
||||
return __builtin_clz(v);
|
||||
}
|
||||
|
||||
inline Square msb(Bitboard b) {
|
||||
return (Square) (63 - __builtin_clzll(b));
|
||||
}
|
||||
|
||||
inline Square lsb(Bitboard b) {
|
||||
return (Square) (uint32_t(b) ? lsb32(uint32_t(b)) : 32 + lsb32(uint32_t(b >> 32)));
|
||||
}
|
||||
|
||||
# else // Assumed gcc or compatible compiler
|
||||
|
||||
inline Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen
|
||||
Bitboard idx;
|
||||
__asm__("bsfq %1, %0": "=r"(idx): "rm"(b) );
|
||||
return (Square) idx;
|
||||
}
|
||||
|
||||
inline Square msb(Bitboard b) {
|
||||
Bitboard idx;
|
||||
__asm__("bsrq %1, %0": "=r"(idx): "rm"(b) );
|
||||
return (Square) idx;
|
||||
}
|
||||
|
||||
# endif
|
||||
|
||||
#else // ifdef(USE_BSFQ)
|
||||
#define NO_BSF // Fallback on software implementation for other cases
|
||||
|
||||
Square lsb(Bitboard b);
|
||||
Square msb(Bitboard b);
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include <cassert>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "bitcount.h"
|
||||
#include "endgame.h"
|
||||
#include "movegen.h"
|
||||
|
||||
|
@ -100,7 +99,8 @@ namespace {
|
|||
string fen = sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/8/8/"
|
||||
+ sides[1] + char(8 - sides[1].length() + '0') + " w - - 0 10";
|
||||
|
||||
return Position(fen, false, nullptr).material_key();
|
||||
StateInfo st;
|
||||
return Position().set(fen, false, &st, nullptr).material_key();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "bitcount.h"
|
||||
#include "bitboard.h"
|
||||
#include "evaluate.h"
|
||||
#include "material.h"
|
||||
#include "pawns.h"
|
||||
|
@ -33,7 +33,7 @@ namespace {
|
|||
|
||||
namespace Trace {
|
||||
|
||||
enum Term { // First 8 entries are for PieceType
|
||||
enum Term { // The first 8 entries are for PieceType
|
||||
MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERM_NB
|
||||
};
|
||||
|
||||
|
@ -89,7 +89,7 @@ namespace {
|
|||
// which attack a square in the kingRing of the enemy king.
|
||||
int kingAttackersCount[COLOR_NB];
|
||||
|
||||
// kingAttackersWeight[color] is the sum of the "weight" of the pieces of the
|
||||
// kingAttackersWeight[color] is the sum of the "weights" 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 elements in the
|
||||
// KingAttackWeights array.
|
||||
|
@ -107,18 +107,6 @@ namespace {
|
|||
Pawns::Entry* pi;
|
||||
};
|
||||
|
||||
|
||||
// Evaluation weights, indexed by the corresponding evaluation term
|
||||
enum { PawnStructure, PassedPawns, Space, KingSafety };
|
||||
|
||||
const struct Weight { int mg, eg; } Weights[] = {
|
||||
{214, 203}, {193, 262}, {47, 0}, {330, 0} };
|
||||
|
||||
Score operator*(Score s, const Weight& w) {
|
||||
return make_score(mg_value(s) * w.mg / 256, eg_value(s) * w.eg / 256);
|
||||
}
|
||||
|
||||
|
||||
#define V(v) Value(v)
|
||||
#define S(mg, eg) make_score(mg, eg)
|
||||
|
||||
|
@ -144,61 +132,64 @@ namespace {
|
|||
// Outpost[knight/bishop][supported by pawn] contains bonuses for knights and
|
||||
// bishops outposts, bigger if outpost piece is supported by a pawn.
|
||||
const Score Outpost[][2] = {
|
||||
{ S(42,11), S(63,17) }, // Knights
|
||||
{ S(18, 5), S(27, 8) } // Bishops
|
||||
{ S(43,11), S(65,20) }, // Knights
|
||||
{ S(20, 3), S(29, 8) } // Bishops
|
||||
};
|
||||
|
||||
// ReachableOutpost[knight/bishop][supported by pawn] contains bonuses for
|
||||
// knights and bishops which can reach an outpost square in one move, bigger
|
||||
// if outpost square is supported by a pawn.
|
||||
const Score ReachableOutpost[][2] = {
|
||||
{ S(21, 5), S(31, 8) }, // Knights
|
||||
{ S( 8, 2), S(13, 4) } // Bishops
|
||||
{ S(21, 5), S(35, 8) }, // Knights
|
||||
{ S( 8, 0), S(14, 4) } // Bishops
|
||||
};
|
||||
|
||||
// RookOnFile[semiopen/open] contains bonuses for each rook when there is no
|
||||
// friendly pawn on the rook file.
|
||||
const Score RookOnFile[2] = { S(19, 10), S(43, 21) };
|
||||
const Score RookOnFile[2] = { S(20, 7), S(45, 20) };
|
||||
|
||||
// ThreatBySafePawn[PieceType] contains bonuses according to which piece
|
||||
// type is attacked by a pawn which is protected or not attacked.
|
||||
// type is attacked by a pawn which is protected or is not attacked.
|
||||
const Score ThreatBySafePawn[PIECE_TYPE_NB] = {
|
||||
S(0, 0), S(0, 0), S(176, 139), S(131, 127), S(217, 218), S(203, 215) };
|
||||
|
||||
|
||||
// Threat[by minor/by rook][attacked PieceType] contains
|
||||
// bonuses according to which piece type attacks which one.
|
||||
// Attacks on lesser pieces which are pawn defended are not considered.
|
||||
// Attacks on lesser pieces which are pawn-defended are not considered.
|
||||
const Score Threat[][PIECE_TYPE_NB] = {
|
||||
{ S(0, 0), S(0, 33), S(45, 43), S(46, 47), S(72,107), S(48,118) }, // by Minor
|
||||
{ S(0, 0), S(0, 25), S(40, 62), S(40, 59), S( 0, 34), S(35, 48) } // by Rook
|
||||
};
|
||||
|
||||
// ThreatByKing[on one/on many] contains bonuses for King attacks on
|
||||
// pawns or pieces which are not pawn defended.
|
||||
// pawns or pieces which are not pawn-defended.
|
||||
const Score ThreatByKing[2] = { S(3, 62), S(9, 138) };
|
||||
|
||||
// Passed[mg/eg][Rank] contains midgame and endgame bonuses for passed pawns.
|
||||
// We don't use a Score because we process the two components independently.
|
||||
const Value Passed[][RANK_NB] = {
|
||||
{ V(0), V( 1), V(34), V(90), V(214), V(328) },
|
||||
{ V(7), V(14), V(37), V(63), V(134), V(189) }
|
||||
{ V(5), V( 5), V(31), V(73), V(166), V(252) },
|
||||
{ V(7), V(14), V(38), V(73), V(166), V(252) }
|
||||
};
|
||||
|
||||
// PassedFile[File] contains a bonus according to the file of a passed pawn
|
||||
const Score PassedFile[FILE_NB] = {
|
||||
S( 12, 10), S( 3, 10), S( 1, -8), S(-27,-12),
|
||||
S(-27,-12), S( 1, -8), S( 3, 10), S( 12, 10)
|
||||
S( 9, 10), S( 2, 10), S( 1, -8), S(-20,-12),
|
||||
S(-20,-12), S( 1, -8), S( 2, 10), S( 9, 10)
|
||||
};
|
||||
|
||||
// Assorted bonuses and penalties used by evaluation
|
||||
const Score MinorBehindPawn = S(16, 0);
|
||||
const Score BishopPawns = S( 8, 12);
|
||||
const Score RookOnPawn = S( 7, 27);
|
||||
const Score RookOnPawn = S( 8, 24);
|
||||
const Score TrappedRook = S(92, 0);
|
||||
const Score Checked = S(20, 20);
|
||||
const Score ThreatByHangingPawn = S(70, 63);
|
||||
const Score Hanging = S(48, 28);
|
||||
const Score ThreatByPawnPush = S(31, 19);
|
||||
const Score SafeCheck = S(20, 20);
|
||||
const Score OtherCheck = S(10, 10);
|
||||
const Score ThreatByHangingPawn = S(71, 61);
|
||||
const Score LooseEnemies = S( 0, 25);
|
||||
const Score WeakQueen = S(35, 0);
|
||||
const Score Hanging = S(48, 27);
|
||||
const Score ThreatByPawnPush = S(38, 22);
|
||||
const Score Unstoppable = S( 0, 20);
|
||||
|
||||
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
|
||||
|
@ -220,13 +211,13 @@ namespace {
|
|||
|
||||
// Penalties for enemy's safe checks
|
||||
const int QueenContactCheck = 89;
|
||||
const int QueenCheck = 50;
|
||||
const int QueenCheck = 52;
|
||||
const int RookCheck = 45;
|
||||
const int BishopCheck = 6;
|
||||
const int KnightCheck = 14;
|
||||
const int BishopCheck = 5;
|
||||
const int KnightCheck = 17;
|
||||
|
||||
|
||||
// eval_init() initializes king and attack bitboards for given color
|
||||
// eval_init() initializes king and attack bitboards for a given color
|
||||
// adding pawn attacks. To be done at the beginning of the evaluation.
|
||||
|
||||
template<Color Us>
|
||||
|
@ -245,7 +236,7 @@ namespace {
|
|||
{
|
||||
ei.kingRing[Them] = b | shift_bb<Down>(b);
|
||||
b &= ei.attackedBy[Us][PAWN];
|
||||
ei.kingAttackersCount[Us] = b ? popcount<Max15>(b) : 0;
|
||||
ei.kingAttackersCount[Us] = popcount(b);
|
||||
ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0;
|
||||
}
|
||||
else
|
||||
|
@ -287,9 +278,7 @@ namespace {
|
|||
{
|
||||
ei.kingAttackersCount[Us]++;
|
||||
ei.kingAttackersWeight[Us] += KingAttackWeights[Pt];
|
||||
bb = b & ei.attackedBy[Them][KING];
|
||||
if (bb)
|
||||
ei.kingAdjacentZoneAttacksCount[Us] += popcount<Max15>(bb);
|
||||
ei.kingAdjacentZoneAttacksCount[Us] += popcount(b & ei.attackedBy[Them][KING]);
|
||||
}
|
||||
|
||||
if (Pt == QUEEN)
|
||||
|
@ -297,7 +286,7 @@ namespace {
|
|||
| ei.attackedBy[Them][BISHOP]
|
||||
| ei.attackedBy[Them][ROOK]);
|
||||
|
||||
int mob = popcount<Pt == QUEEN ? Full : Max15>(b & mobilityArea[Us]);
|
||||
int mob = popcount(b & mobilityArea[Us]);
|
||||
|
||||
mobility[Us] += MobilityBonus[Pt][mob];
|
||||
|
||||
|
@ -319,7 +308,7 @@ namespace {
|
|||
&& (pos.pieces(PAWN) & (s + pawn_push(Us))))
|
||||
score += MinorBehindPawn;
|
||||
|
||||
// Penalty for pawns on same color square of bishop
|
||||
// Penalty for pawns on the same color square as the bishop
|
||||
if (Pt == BISHOP)
|
||||
score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s);
|
||||
|
||||
|
@ -342,17 +331,13 @@ namespace {
|
|||
{
|
||||
// Bonus for aligning with enemy pawns on the same rank/file
|
||||
if (relative_rank(Us, s) >= RANK_5)
|
||||
{
|
||||
Bitboard alignedPawns = pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s];
|
||||
if (alignedPawns)
|
||||
score += RookOnPawn * popcount<Max15>(alignedPawns);
|
||||
}
|
||||
score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]);
|
||||
|
||||
// Bonus when on an open or semi-open file
|
||||
if (ei.pi->semiopen_file(Us, file_of(s)))
|
||||
score += RookOnFile[!!ei.pi->semiopen_file(Them, file_of(s))];
|
||||
|
||||
// Penalize when trapped by the king, even more if king cannot castle
|
||||
// Penalize when trapped by the king, even more if the king cannot castle
|
||||
else if (mob <= 3)
|
||||
{
|
||||
Square ksq = pos.square<KING>(Us);
|
||||
|
@ -368,7 +353,7 @@ namespace {
|
|||
if (DoTrace)
|
||||
Trace::add(Pt, Us, score);
|
||||
|
||||
// Recursively call evaluate_pieces() of next piece type until KING excluded
|
||||
// Recursively call evaluate_pieces() of next piece type until KING is excluded
|
||||
return score - evaluate_pieces<DoTrace, Them, NextPt>(pos, ei, mobility, mobilityArea);
|
||||
}
|
||||
|
||||
|
@ -383,9 +368,10 @@ namespace {
|
|||
template<Color Us, bool DoTrace>
|
||||
Score evaluate_king(const Position& pos, const EvalInfo& ei) {
|
||||
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
const Square Up = (Us == WHITE ? DELTA_N : DELTA_S);
|
||||
|
||||
Bitboard undefended, b, b1, b2, safe;
|
||||
Bitboard undefended, b, b1, b2, safe, other;
|
||||
int attackUnits;
|
||||
const Square ksq = pos.square<KING>(Us);
|
||||
|
||||
|
@ -395,14 +381,17 @@ namespace {
|
|||
// Main king safety evaluation
|
||||
if (ei.kingAttackersCount[Them])
|
||||
{
|
||||
// Find the attacked squares around the king which have no defenders
|
||||
// apart from the king itself.
|
||||
// Find the attacked squares which are defended only by the king...
|
||||
undefended = ei.attackedBy[Them][ALL_PIECES]
|
||||
& ei.attackedBy[Us][KING]
|
||||
& ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
|
||||
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
|
||||
| ei.attackedBy[Us][QUEEN]);
|
||||
|
||||
// ... and those which are not defended at all in the larger king ring
|
||||
b = ei.attackedBy[Them][ALL_PIECES] & ~ei.attackedBy[Us][ALL_PIECES]
|
||||
& ei.kingRing[Us] & ~pos.pieces(Them);
|
||||
|
||||
// Initialize the 'attackUnits' variable, which is used later on as an
|
||||
// index into the KingDanger[] array. The initial value is based on the
|
||||
// number and types of the enemy's attacking pieces, the number of
|
||||
|
@ -410,8 +399,8 @@ namespace {
|
|||
// the pawn shelter (current 'score' value).
|
||||
attackUnits = std::min(72, ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them])
|
||||
+ 9 * ei.kingAdjacentZoneAttacksCount[Them]
|
||||
+ 27 * popcount<Max15>(undefended)
|
||||
+ 11 * !!ei.pinnedPieces[Us]
|
||||
+ 27 * popcount(undefended)
|
||||
+ 11 * (popcount(b) + !!ei.pinnedPieces[Us])
|
||||
- 64 * !pos.count<QUEEN>(Them)
|
||||
- mg_value(score) / 8;
|
||||
|
||||
|
@ -425,50 +414,48 @@ namespace {
|
|||
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]
|
||||
| ei.attackedBy[Them][KING];
|
||||
|
||||
if (b)
|
||||
attackUnits += QueenContactCheck * popcount<Max15>(b);
|
||||
attackUnits += QueenContactCheck * popcount(b);
|
||||
}
|
||||
|
||||
// Analyse the enemy's safe distance checks for sliders and knights
|
||||
safe = ~(ei.attackedBy[Us][ALL_PIECES] | pos.pieces(Them));
|
||||
// Analyse the safe enemy's checks which are possible on next move...
|
||||
safe = ~(ei.attackedBy[Us][ALL_PIECES] | pos.pieces(Them));
|
||||
|
||||
b1 = pos.attacks_from<ROOK >(ksq) & safe;
|
||||
b2 = pos.attacks_from<BISHOP>(ksq) & safe;
|
||||
// ... and some other potential checks, only requiring the square to be
|
||||
// safe from pawn-attacks, and not being occupied by a blocked pawn.
|
||||
other = ~( ei.attackedBy[Us][PAWN]
|
||||
| (pos.pieces(Them, PAWN) & shift_bb<Up>(pos.pieces(PAWN))));
|
||||
|
||||
b1 = pos.attacks_from<ROOK >(ksq);
|
||||
b2 = pos.attacks_from<BISHOP>(ksq);
|
||||
|
||||
// Enemy queen safe checks
|
||||
b = (b1 | b2) & ei.attackedBy[Them][QUEEN];
|
||||
if (b)
|
||||
{
|
||||
attackUnits += QueenCheck * popcount<Max15>(b);
|
||||
score -= Checked;
|
||||
}
|
||||
if ((b1 | b2) & ei.attackedBy[Them][QUEEN] & safe)
|
||||
attackUnits += QueenCheck, score -= SafeCheck;
|
||||
|
||||
// Enemy rooks safe checks
|
||||
b = b1 & ei.attackedBy[Them][ROOK];
|
||||
if (b)
|
||||
{
|
||||
attackUnits += RookCheck * popcount<Max15>(b);
|
||||
score -= Checked;
|
||||
}
|
||||
// Enemy rooks safe and other checks
|
||||
if (b1 & ei.attackedBy[Them][ROOK] & safe)
|
||||
attackUnits += RookCheck, score -= SafeCheck;
|
||||
|
||||
// Enemy bishops safe checks
|
||||
b = b2 & ei.attackedBy[Them][BISHOP];
|
||||
if (b)
|
||||
{
|
||||
attackUnits += BishopCheck * popcount<Max15>(b);
|
||||
score -= Checked;
|
||||
}
|
||||
else if (b1 & ei.attackedBy[Them][ROOK] & other)
|
||||
score -= OtherCheck;
|
||||
|
||||
// Enemy knights safe checks
|
||||
b = pos.attacks_from<KNIGHT>(ksq) & ei.attackedBy[Them][KNIGHT] & safe;
|
||||
if (b)
|
||||
{
|
||||
attackUnits += KnightCheck * popcount<Max15>(b);
|
||||
score -= Checked;
|
||||
}
|
||||
// Enemy bishops safe and other checks
|
||||
if (b2 & ei.attackedBy[Them][BISHOP] & safe)
|
||||
attackUnits += BishopCheck, score -= SafeCheck;
|
||||
|
||||
else if (b2 & ei.attackedBy[Them][BISHOP] & other)
|
||||
score -= OtherCheck;
|
||||
|
||||
// Enemy knights safe and other checks
|
||||
b = pos.attacks_from<KNIGHT>(ksq) & ei.attackedBy[Them][KNIGHT];
|
||||
if (b & safe)
|
||||
attackUnits += KnightCheck, score -= SafeCheck;
|
||||
|
||||
else if (b & other)
|
||||
score -= OtherCheck;
|
||||
|
||||
// Finally, extract the king danger score from the KingDanger[]
|
||||
// array and subtract the score from evaluation.
|
||||
// array and subtract the score from the evaluation.
|
||||
score -= KingDanger[std::max(std::min(attackUnits, 399), 0)];
|
||||
}
|
||||
|
||||
|
@ -479,8 +466,8 @@ namespace {
|
|||
}
|
||||
|
||||
|
||||
// evaluate_threats() assigns bonuses according to the type of attacking piece
|
||||
// and the type of attacked one.
|
||||
// evaluate_threats() assigns bonuses according to the types of the attacking
|
||||
// and the attacked pieces.
|
||||
|
||||
template<Color Us, bool DoTrace>
|
||||
Score evaluate_threats(const Position& pos, const EvalInfo& ei) {
|
||||
|
@ -497,6 +484,18 @@ namespace {
|
|||
Bitboard b, weak, defended, safeThreats;
|
||||
Score score = SCORE_ZERO;
|
||||
|
||||
// Small bonus if the opponent has loose pawns or pieces
|
||||
if ( (pos.pieces(Them) ^ pos.pieces(Them, QUEEN, KING))
|
||||
& ~(ei.attackedBy[Us][ALL_PIECES] | ei.attackedBy[Them][ALL_PIECES]))
|
||||
score += LooseEnemies;
|
||||
|
||||
// Bonus for pin or discovered attack on the opponent queen
|
||||
if ( pos.count<QUEEN>(Them) == 1
|
||||
&& pos.slider_blockers(pos.pieces(),
|
||||
pos.pieces(Us, ROOK, BISHOP),
|
||||
pos.square<QUEEN>(Them)))
|
||||
score += WeakQueen;
|
||||
|
||||
// Non-pawn enemies attacked by a pawn
|
||||
weak = (pos.pieces(Them) ^ pos.pieces(Them, PAWN)) & ei.attackedBy[Us][PAWN];
|
||||
|
||||
|
@ -533,9 +532,7 @@ namespace {
|
|||
while (b)
|
||||
score += Threat[Rook ][type_of(pos.piece_on(pop_lsb(&b)))];
|
||||
|
||||
b = weak & ~ei.attackedBy[Them][ALL_PIECES];
|
||||
if (b)
|
||||
score += Hanging * popcount<Max15>(b);
|
||||
score += Hanging * popcount(weak & ~ei.attackedBy[Them][ALL_PIECES]);
|
||||
|
||||
b = weak & ei.attackedBy[Us][KING];
|
||||
if (b)
|
||||
|
@ -554,8 +551,7 @@ namespace {
|
|||
& pos.pieces(Them)
|
||||
& ~ei.attackedBy[Us][PAWN];
|
||||
|
||||
if (b)
|
||||
score += ThreatByPawnPush * popcount<Max15>(b);
|
||||
score += ThreatByPawnPush * popcount(b);
|
||||
|
||||
if (DoTrace)
|
||||
Trace::add(THREAT, Us, score);
|
||||
|
@ -619,7 +615,7 @@ namespace {
|
|||
// assign a smaller bonus if the block square isn't attacked.
|
||||
int k = !unsafeSquares ? 18 : !(unsafeSquares & blockSq) ? 8 : 0;
|
||||
|
||||
// If the path to queen is fully defended, assign a big bonus.
|
||||
// If the path to the queen is fully defended, assign a big bonus.
|
||||
// Otherwise assign a smaller bonus if the block square is defended.
|
||||
if (defendedSquares == squaresToQueen)
|
||||
k += 6;
|
||||
|
@ -630,20 +626,17 @@ namespace {
|
|||
mbonus += k * rr, ebonus += k * rr;
|
||||
}
|
||||
else if (pos.pieces(Us) & blockSq)
|
||||
mbonus += rr * 3 + r * 2 + 3, ebonus += rr + r * 2;
|
||||
mbonus += rr + r * 2, ebonus += rr + r * 2;
|
||||
} // rr != 0
|
||||
|
||||
if (pos.count<PAWN>(Us) < pos.count<PAWN>(Them))
|
||||
ebonus += ebonus / 4;
|
||||
|
||||
score += make_score(mbonus, ebonus) + PassedFile[file_of(s)];
|
||||
}
|
||||
|
||||
if (DoTrace)
|
||||
Trace::add(PASSED, Us, score * Weights[PassedPawns]);
|
||||
Trace::add(PASSED, Us, score);
|
||||
|
||||
// Add the scores to the middlegame and endgame eval
|
||||
return score * Weights[PassedPawns];
|
||||
return score;
|
||||
}
|
||||
|
||||
|
||||
|
@ -678,24 +671,25 @@ namespace {
|
|||
assert(unsigned(safe >> (Us == WHITE ? 32 : 0)) == 0);
|
||||
|
||||
// ...count safe + (behind & safe) with a single popcount
|
||||
int bonus = popcount<Full>((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe));
|
||||
int bonus = popcount((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);
|
||||
return make_score(bonus * weight * weight * 2 / 11, 0);
|
||||
}
|
||||
|
||||
|
||||
// evaluate_initiative() computes the initiative correction value for the
|
||||
// position, i.e. second order bonus/malus based on the known attacking/defending
|
||||
// position, i.e., second order bonus/malus based on the known attacking/defending
|
||||
// status of the players.
|
||||
Score evaluate_initiative(const Position& pos, int asymmetry, Value eg) {
|
||||
|
||||
int kingDistance = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
|
||||
int kingDistance = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
|
||||
- distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
|
||||
int pawns = pos.count<PAWN>(WHITE) + pos.count<PAWN>(BLACK);
|
||||
|
||||
// Compute the initiative bonus for the attacking side
|
||||
int initiative = 8 * (pawns + asymmetry + kingDistance - 15);
|
||||
int initiative = 8 * (asymmetry + kingDistance - 15) + 12 * pawns;
|
||||
|
||||
// Now apply the bonus: note that we find the attacking side by extracting
|
||||
// the sign of the endgame value, and that we carefully cap the bonus so
|
||||
|
@ -707,9 +701,9 @@ namespace {
|
|||
|
||||
|
||||
// evaluate_scale_factor() computes the scale factor for the winning side
|
||||
ScaleFactor evaluate_scale_factor(const Position& pos, const EvalInfo& ei, Score score) {
|
||||
ScaleFactor evaluate_scale_factor(const Position& pos, const EvalInfo& ei, Value eg) {
|
||||
|
||||
Color strongSide = eg_value(score) > VALUE_DRAW ? WHITE : BLACK;
|
||||
Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK;
|
||||
ScaleFactor sf = ei.me->scale_factor(pos, strongSide);
|
||||
|
||||
// If we don't already have an unusual scale factor, check for certain
|
||||
|
@ -720,7 +714,7 @@ namespace {
|
|||
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.
|
||||
// is almost a draw, in case of KBP vs KB, it is even more a draw.
|
||||
if ( pos.non_pawn_material(WHITE) == BishopValueMg
|
||||
&& pos.non_pawn_material(BLACK) == BishopValueMg)
|
||||
sf = more_than_one(pos.pieces(PAWN)) ? ScaleFactor(31) : ScaleFactor(9);
|
||||
|
@ -732,7 +726,7 @@ namespace {
|
|||
}
|
||||
// Endings where weaker side can place his king in front of the opponent's
|
||||
// pawns are drawish.
|
||||
else if ( abs(eg_value(score)) <= BishopValueEg
|
||||
else if ( abs(eg) <= BishopValueEg
|
||||
&& ei.pi->pawn_span(strongSide) <= 1
|
||||
&& !pos.pawn_passed(~strongSide, pos.square<KING>(~strongSide)))
|
||||
sf = ei.pi->pawn_span(strongSide) ? ScaleFactor(51) : ScaleFactor(37);
|
||||
|
@ -771,7 +765,7 @@ Value Eval::evaluate(const Position& pos) {
|
|||
|
||||
// Probe the pawn hash table
|
||||
ei.pi = Pawns::probe(pos);
|
||||
score += ei.pi->pawns_score() * Weights[PawnStructure];
|
||||
score += ei.pi->pawns_score();
|
||||
|
||||
// Initialize attack and king safety bitboards
|
||||
ei.attackedBy[WHITE][ALL_PIECES] = ei.attackedBy[BLACK][ALL_PIECES] = 0;
|
||||
|
@ -821,14 +815,14 @@ Value Eval::evaluate(const Position& pos) {
|
|||
|
||||
// Evaluate space for both sides, only during opening
|
||||
if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >= 12222)
|
||||
score += ( evaluate_space<WHITE>(pos, ei)
|
||||
- evaluate_space<BLACK>(pos, ei)) * Weights[Space];
|
||||
score += evaluate_space<WHITE>(pos, ei)
|
||||
- evaluate_space<BLACK>(pos, ei);
|
||||
|
||||
// Evaluate position potential for the winning side
|
||||
score += evaluate_initiative(pos, ei.pi->pawn_asymmetry(), eg_value(score));
|
||||
|
||||
// Evaluate scale factor for the winning side
|
||||
ScaleFactor sf = evaluate_scale_factor(pos, ei, score);
|
||||
ScaleFactor sf = evaluate_scale_factor(pos, ei, eg_value(score));
|
||||
|
||||
// Interpolate between a middlegame and a (scaled by 'sf') endgame score
|
||||
Value v = mg_value(score) * int(ei.me->game_phase())
|
||||
|
@ -841,10 +835,10 @@ Value Eval::evaluate(const Position& pos) {
|
|||
{
|
||||
Trace::add(MATERIAL, pos.psq_score());
|
||||
Trace::add(IMBALANCE, ei.me->imbalance());
|
||||
Trace::add(PAWN, ei.pi->pawns_score() * Weights[PawnStructure]);
|
||||
Trace::add(PAWN, ei.pi->pawns_score());
|
||||
Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]);
|
||||
Trace::add(SPACE, evaluate_space<WHITE>(pos, ei) * Weights[Space]
|
||||
, evaluate_space<BLACK>(pos, ei) * Weights[Space]);
|
||||
Trace::add(SPACE, evaluate_space<WHITE>(pos, ei)
|
||||
, evaluate_space<BLACK>(pos, ei));
|
||||
Trace::add(TOTAL, score);
|
||||
}
|
||||
|
||||
|
@ -897,13 +891,13 @@ std::string Eval::trace(const Position& pos) {
|
|||
|
||||
void Eval::init() {
|
||||
|
||||
const int MaxSlope = 8700;
|
||||
const int Peak = 1280000;
|
||||
const int MaxSlope = 322;
|
||||
const int Peak = 47410;
|
||||
int t = 0;
|
||||
|
||||
for (int i = 0; i < 400; ++i)
|
||||
{
|
||||
t = std::min(Peak, std::min(i * i * 27, t + MaxSlope));
|
||||
KingDanger[i] = make_score(t / 1000, 0) * Weights[KingSafety];
|
||||
t = std::min(Peak, std::min(i * i - 16, t + MaxSlope));
|
||||
KingDanger[i] = make_score(t * 268 / 7700, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace {
|
|||
|
||||
/// Version number. If Version is left empty, then compile date in the format
|
||||
/// DD-MM-YY and show in engine_info.
|
||||
const string Version = "7";
|
||||
const string Version = "2016-05-21";
|
||||
|
||||
/// 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
|
||||
|
|
|
@ -239,7 +239,7 @@ namespace {
|
|||
&& !(PseudoAttacks[Pt][from] & target & ci->checkSquares[Pt]))
|
||||
continue;
|
||||
|
||||
if (ci->dcCandidates && (ci->dcCandidates & from))
|
||||
if (ci->dcCandidates & from)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
namespace {
|
||||
|
||||
enum Stages {
|
||||
MAIN_SEARCH, GOOD_CAPTURES, KILLERS, GOOD_QUIETS, BAD_QUIETS, BAD_CAPTURES,
|
||||
MAIN_SEARCH, GOOD_CAPTURES, KILLERS, QUIET, BAD_CAPTURES,
|
||||
EVASION, ALL_EVASIONS,
|
||||
QSEARCH_WITH_CHECKS, QCAPTURES_1, CHECKS,
|
||||
QSEARCH_WITHOUT_CHECKS, QCAPTURES_2,
|
||||
|
@ -51,7 +51,7 @@ namespace {
|
|||
|
||||
// pick_best() finds the best move in the range (begin, end) and moves it to
|
||||
// the front. It's faster than sorting all the moves in advance when there
|
||||
// are few moves e.g. the possible captures.
|
||||
// are few moves, e.g., the possible captures.
|
||||
Move pick_best(ExtMove* begin, ExtMove* end)
|
||||
{
|
||||
std::swap(*begin, *std::max_element(begin, end));
|
||||
|
@ -64,23 +64,24 @@ namespace {
|
|||
/// Constructors of the MovePicker class. As arguments we pass information
|
||||
/// to help it to return the (presumably) good moves first, to decide which
|
||||
/// moves to return (in the quiescence search, for instance, we only want to
|
||||
/// search captures, promotions and some checks) and how important good move
|
||||
/// search captures, promotions, and some checks) and how important good move
|
||||
/// ordering is at the current node.
|
||||
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h,
|
||||
const CounterMovesStats& cmh, Move cm, Search::Stack* s)
|
||||
: pos(p), history(h), counterMovesHistory(&cmh), ss(s), countermove(cm), depth(d) {
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, Search::Stack* s)
|
||||
: pos(p), ss(s), depth(d) {
|
||||
|
||||
assert(d > DEPTH_ZERO);
|
||||
|
||||
Square prevSq = to_sq((ss-1)->currentMove);
|
||||
countermove = pos.this_thread()->counterMoves[pos.piece_on(prevSq)][prevSq];
|
||||
|
||||
stage = pos.checkers() ? EVASION : MAIN_SEARCH;
|
||||
ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
|
||||
endMoves += (ttMove != MOVE_NONE);
|
||||
}
|
||||
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
|
||||
const HistoryStats& h, Square s)
|
||||
: pos(p), history(h), counterMovesHistory(nullptr) {
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, Square s)
|
||||
: pos(p) {
|
||||
|
||||
assert(d <= DEPTH_ZERO);
|
||||
|
||||
|
@ -104,8 +105,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
|
|||
endMoves += (ttMove != MOVE_NONE);
|
||||
}
|
||||
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, Value th)
|
||||
: pos(p), history(h), counterMovesHistory(nullptr), threshold(th) {
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Value th)
|
||||
: pos(p), threshold(th) {
|
||||
|
||||
assert(!pos.checkers());
|
||||
|
||||
|
@ -127,9 +128,9 @@ template<>
|
|||
void MovePicker::score<CAPTURES>() {
|
||||
// Winning and equal captures in the main search are ordered by MVV, preferring
|
||||
// captures near our home rank. Surprisingly, this appears to perform slightly
|
||||
// better than SEE based move ordering: exchanging big pieces before capturing
|
||||
// better than SEE-based move ordering: exchanging big pieces before capturing
|
||||
// a hanging piece probably helps to reduce the subtree size.
|
||||
// In main search we want to push captures with negative SEE values to the
|
||||
// In the main search we want to push captures with negative SEE values to the
|
||||
// badCaptures[] array, but instead of doing it now we delay until the move
|
||||
// has been picked up, saving some SEE calls in case we get a cutoff.
|
||||
for (auto& m : *this)
|
||||
|
@ -140,16 +141,25 @@ void MovePicker::score<CAPTURES>() {
|
|||
template<>
|
||||
void MovePicker::score<QUIETS>() {
|
||||
|
||||
const HistoryStats& history = pos.this_thread()->history;
|
||||
|
||||
const CounterMoveStats* cm = (ss-1)->counterMoves;
|
||||
const CounterMoveStats* fm = (ss-2)->counterMoves;
|
||||
const CounterMoveStats* f2 = (ss-4)->counterMoves;
|
||||
|
||||
for (auto& m : *this)
|
||||
m.value = history[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*counterMovesHistory)[pos.moved_piece(m)][to_sq(m)];
|
||||
m.value = history[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (cm ? (*cm)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO)
|
||||
+ (fm ? (*fm)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO)
|
||||
+ (f2 ? (*f2)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO);
|
||||
}
|
||||
|
||||
template<>
|
||||
void MovePicker::score<EVASIONS>() {
|
||||
// Try winning and equal captures captures ordered by MVV/LVA, then non-captures
|
||||
// ordered by history value, then bad-captures and quiet moves with a negative
|
||||
// SEE ordered by SEE value.
|
||||
// Try winning and equal captures ordered by MVV/LVA, then non-captures ordered
|
||||
// by history value, then bad captures and quiet moves with a negative SEE ordered
|
||||
// by SEE value.
|
||||
const HistoryStats& history = pos.this_thread()->history;
|
||||
Value see;
|
||||
|
||||
for (auto& m : *this)
|
||||
|
@ -164,7 +174,7 @@ void MovePicker::score<EVASIONS>() {
|
|||
}
|
||||
|
||||
|
||||
/// generate_next_stage() generates, scores and sorts the next bunch of moves,
|
||||
/// generate_next_stage() generates, scores, and sorts the next bunch of moves
|
||||
/// when there are no more moves to try for the current stage.
|
||||
|
||||
void MovePicker::generate_next_stage() {
|
||||
|
@ -189,17 +199,15 @@ void MovePicker::generate_next_stage() {
|
|||
endMoves = cur + 2 + (countermove != killers[0] && countermove != killers[1]);
|
||||
break;
|
||||
|
||||
case GOOD_QUIETS:
|
||||
endQuiets = endMoves = generate<QUIETS>(pos, moves);
|
||||
case QUIET:
|
||||
endMoves = generate<QUIETS>(pos, moves);
|
||||
score<QUIETS>();
|
||||
endMoves = std::partition(cur, endMoves, [](const ExtMove& m) { return m.value > VALUE_ZERO; });
|
||||
insertion_sort(cur, endMoves);
|
||||
break;
|
||||
|
||||
case BAD_QUIETS:
|
||||
cur = endMoves;
|
||||
endMoves = endQuiets;
|
||||
if (depth >= 3 * ONE_PLY)
|
||||
if (depth < 3 * ONE_PLY)
|
||||
{
|
||||
ExtMove* goodQuiet = std::partition(cur, endMoves, [](const ExtMove& m)
|
||||
{ return m.value > VALUE_ZERO; });
|
||||
insertion_sort(cur, goodQuiet);
|
||||
} else
|
||||
insertion_sort(cur, endMoves);
|
||||
break;
|
||||
|
||||
|
@ -272,7 +280,7 @@ Move MovePicker::next_move() {
|
|||
return move;
|
||||
break;
|
||||
|
||||
case GOOD_QUIETS: case BAD_QUIETS:
|
||||
case QUIET:
|
||||
move = *cur++;
|
||||
if ( move != ttMove
|
||||
&& move != killers[0]
|
||||
|
|
|
@ -46,29 +46,25 @@ struct Stats {
|
|||
T* operator[](Piece pc) { return table[pc]; }
|
||||
void clear() { std::memset(table, 0, sizeof(table)); }
|
||||
|
||||
void update(Piece pc, Square to, Move m) {
|
||||
|
||||
if (m != table[pc][to])
|
||||
table[pc][to] = m;
|
||||
}
|
||||
void update(Piece pc, Square to, Move m) { table[pc][to] = m; }
|
||||
|
||||
void update(Piece pc, Square to, Value v) {
|
||||
|
||||
if (abs(int(v)) >= 324)
|
||||
return;
|
||||
|
||||
table[pc][to] -= table[pc][to] * abs(int(v)) / (CM ? 512 : 324);
|
||||
table[pc][to] += int(v) * (CM ? 64 : 32);
|
||||
table[pc][to] -= table[pc][to] * abs(int(v)) / (CM ? 936 : 324);
|
||||
table[pc][to] += int(v) * 32;
|
||||
}
|
||||
|
||||
private:
|
||||
T table[PIECE_NB][SQUARE_NB];
|
||||
};
|
||||
|
||||
typedef Stats<Move> MovesStats;
|
||||
typedef Stats<Move> MoveStats;
|
||||
typedef Stats<Value, false> HistoryStats;
|
||||
typedef Stats<Value, true> CounterMovesStats;
|
||||
typedef Stats<CounterMovesStats> CounterMovesHistoryStats;
|
||||
typedef Stats<Value, true> CounterMoveStats;
|
||||
typedef Stats<CounterMoveStats> CounterMoveHistoryStats;
|
||||
|
||||
|
||||
/// MovePicker class is used to pick one pseudo legal move at a time from the
|
||||
|
@ -83,9 +79,9 @@ public:
|
|||
MovePicker(const MovePicker&) = delete;
|
||||
MovePicker& operator=(const MovePicker&) = delete;
|
||||
|
||||
MovePicker(const Position&, Move, Depth, const HistoryStats&, Square);
|
||||
MovePicker(const Position&, Move, const HistoryStats&, Value);
|
||||
MovePicker(const Position&, Move, Depth, const HistoryStats&, const CounterMovesStats&, Move, Search::Stack*);
|
||||
MovePicker(const Position&, Move, Value);
|
||||
MovePicker(const Position&, Move, Depth, Square);
|
||||
MovePicker(const Position&, Move, Depth, Search::Stack*);
|
||||
|
||||
Move next_move();
|
||||
|
||||
|
@ -96,9 +92,7 @@ private:
|
|||
ExtMove* end() { return endMoves; }
|
||||
|
||||
const Position& pos;
|
||||
const HistoryStats& history;
|
||||
const CounterMovesStats* counterMovesHistory;
|
||||
Search::Stack* ss;
|
||||
const Search::Stack* ss;
|
||||
Move countermove;
|
||||
Depth depth;
|
||||
Move ttMove;
|
||||
|
@ -106,7 +100,7 @@ private:
|
|||
Square recaptureSquare;
|
||||
Value threshold;
|
||||
int stage;
|
||||
ExtMove *endQuiets, *endBadCaptures = moves + MAX_MOVES - 1;
|
||||
ExtMove* endBadCaptures = moves + MAX_MOVES - 1;
|
||||
ExtMove moves[MAX_MOVES], *cur = moves, *endMoves = moves;
|
||||
};
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include <cassert>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "bitcount.h"
|
||||
#include "pawns.h"
|
||||
#include "position.h"
|
||||
#include "thread.h"
|
||||
|
@ -32,34 +31,26 @@ namespace {
|
|||
#define V Value
|
||||
#define S(mg, eg) make_score(mg, eg)
|
||||
|
||||
// Isolated pawn penalty by opposed flag and file
|
||||
const Score Isolated[2][FILE_NB] = {
|
||||
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52),
|
||||
S(60, 52), S(60, 52), S(54, 52), S(37, 45) },
|
||||
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35),
|
||||
S(40, 35), S(40, 35), S(36, 35), S(25, 30) } };
|
||||
// Isolated pawn penalty by opposed flag
|
||||
const Score Isolated[2] = { S(45, 40), S(30, 27) };
|
||||
|
||||
// Backward pawn penalty by opposed flag
|
||||
const Score Backward[2] = { S(67, 42), S(49, 24) };
|
||||
|
||||
// Unsupported pawn penalty, for pawns which are neither isolated or backward
|
||||
const Score Unsupported = S(20, 10);
|
||||
const Score Backward[2] = { S(56, 33), S(41, 19) };
|
||||
|
||||
// Unsupported pawn penalty for pawns which are neither isolated or backward,
|
||||
// by number of pawns it supports [less than 2 / exactly 2].
|
||||
const Score Unsupported[2] = { S(17, 8), S(21, 12) };
|
||||
|
||||
// Connected pawn bonus by opposed, phalanx, twice supported and rank
|
||||
Score Connected[2][2][2][RANK_NB];
|
||||
|
||||
// Doubled pawn penalty by file
|
||||
const Score Doubled[FILE_NB] = {
|
||||
S(13, 43), S(20, 48), S(23, 48), S(23, 48),
|
||||
S(23, 48), S(23, 48), S(20, 48), S(13, 43) };
|
||||
|
||||
// Doubled pawn penalty
|
||||
const Score Doubled = S(18,38);
|
||||
|
||||
// Lever 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) };
|
||||
|
||||
// Center bind bonus, when two pawns controls the same central square
|
||||
const Score CenterBind = S(16, 0);
|
||||
S(17, 16), S(33, 32), S(0, 0), S(0, 0) };
|
||||
|
||||
// Weakness of our pawn shelter in front of the king by [distance from edge][rank]
|
||||
const Value ShelterWeakness[][RANK_NB] = {
|
||||
|
@ -102,13 +93,9 @@ namespace {
|
|||
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
|
||||
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
|
||||
|
||||
const Bitboard CenterBindMask =
|
||||
Us == WHITE ? (FileDBB | FileEBB) & (Rank5BB | Rank6BB | Rank7BB)
|
||||
: (FileDBB | FileEBB) & (Rank4BB | Rank3BB | Rank2BB);
|
||||
|
||||
Bitboard b, neighbours, doubled, supported, phalanx;
|
||||
Bitboard b, neighbours, stoppers, doubled, supported, phalanx;
|
||||
Square s;
|
||||
bool passed, isolated, opposed, backward, lever, connected;
|
||||
bool opposed, lever, connected, backward;
|
||||
Score score = SCORE_ZERO;
|
||||
const Square* pl = pos.squares<PAWN>(Us);
|
||||
const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)];
|
||||
|
@ -120,7 +107,7 @@ namespace {
|
|||
e->kingSquares[Us] = SQ_NONE;
|
||||
e->semiopenFiles[Us] = 0xFF;
|
||||
e->pawnAttacks[Us] = shift_bb<Right>(ourPawns) | shift_bb<Left>(ourPawns);
|
||||
e->pawnsOnSquares[Us][BLACK] = popcount<Max15>(ourPawns & DarkSquares);
|
||||
e->pawnsOnSquares[Us][BLACK] = popcount(ourPawns & DarkSquares);
|
||||
e->pawnsOnSquares[Us][WHITE] = pos.count<PAWN>(Us) - e->pawnsOnSquares[Us][BLACK];
|
||||
|
||||
// Loop through all pawns of the current color and score each pawn
|
||||
|
@ -134,61 +121,53 @@ namespace {
|
|||
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
|
||||
|
||||
// Flag the pawn
|
||||
neighbours = ourPawns & adjacent_files_bb(f);
|
||||
doubled = ourPawns & forward_bb(Us, s);
|
||||
opposed = theirPawns & forward_bb(Us, s);
|
||||
passed = !(theirPawns & passed_pawn_mask(Us, s));
|
||||
lever = theirPawns & pawnAttacksBB[s];
|
||||
phalanx = neighbours & rank_bb(s);
|
||||
supported = neighbours & rank_bb(s - Up);
|
||||
connected = supported | phalanx;
|
||||
isolated = !neighbours;
|
||||
opposed = theirPawns & forward_bb(Us, s);
|
||||
stoppers = theirPawns & passed_pawn_mask(Us, s);
|
||||
lever = theirPawns & pawnAttacksBB[s];
|
||||
doubled = ourPawns & (s + Up);
|
||||
neighbours = ourPawns & adjacent_files_bb(f);
|
||||
phalanx = neighbours & rank_bb(s);
|
||||
supported = neighbours & rank_bb(s - Up);
|
||||
connected = supported | phalanx;
|
||||
|
||||
// Test for backward pawn.
|
||||
// If the pawn is passed, isolated, lever or connected it cannot be
|
||||
// backward. If there are friendly pawns behind on adjacent files
|
||||
// or if it is sufficiently advanced, it cannot be backward either.
|
||||
if ( (passed | isolated | lever | connected)
|
||||
|| (ourPawns & pawn_attack_span(Them, s))
|
||||
|| (relative_rank(Us, s) >= RANK_5))
|
||||
// A pawn is backward when it is behind all pawns of the same color on the
|
||||
// adjacent files and cannot be safely advanced.
|
||||
if (!neighbours || lever || relative_rank(Us, s) >= RANK_5)
|
||||
backward = false;
|
||||
else
|
||||
{
|
||||
// We now know there are no friendly pawns beside or behind this
|
||||
// pawn on adjacent files. We now check whether the pawn is
|
||||
// backward by looking in the forward direction on the adjacent
|
||||
// files, and picking the closest pawn there.
|
||||
b = pawn_attack_span(Us, s) & (ourPawns | theirPawns);
|
||||
b = pawn_attack_span(Us, s) & rank_bb(backmost_sq(Us, b));
|
||||
// Find the backmost rank with neighbours or stoppers
|
||||
b = rank_bb(backmost_sq(Us, neighbours | stoppers));
|
||||
|
||||
// If we have an enemy pawn in the same or next rank, the pawn is
|
||||
// backward because it cannot advance without being captured.
|
||||
backward = (b | shift_bb<Up>(b)) & theirPawns;
|
||||
// The pawn is backward when it cannot safely progress to that rank:
|
||||
// either there is a stopper in the way on this rank, or there is a
|
||||
// stopper on adjacent file which controls the way to that rank.
|
||||
backward = (b | shift_bb<Up>(b & adjacent_files_bb(f))) & stoppers;
|
||||
|
||||
assert(!backward || !(pawn_attack_span(Them, s + Up) & neighbours));
|
||||
}
|
||||
|
||||
assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns));
|
||||
|
||||
// Passed pawns will be properly scored in evaluation because we need
|
||||
// full attack info to evaluate them. Only the frontmost passed
|
||||
// pawn on each file is considered a true passed pawn.
|
||||
if (passed && !doubled)
|
||||
if (!(stoppers | doubled)) // FIXME this is just doubled by adjacent pawn
|
||||
e->passedPawns[Us] |= s;
|
||||
|
||||
// Score this pawn
|
||||
if (isolated)
|
||||
score -= Isolated[opposed][f];
|
||||
if (!neighbours)
|
||||
score -= Isolated[opposed];
|
||||
|
||||
else if (backward)
|
||||
score -= Backward[opposed];
|
||||
|
||||
else if (!supported)
|
||||
score -= Unsupported;
|
||||
score -= Unsupported[more_than_one(neighbours & pawnAttacksBB[s])];
|
||||
|
||||
if (connected)
|
||||
score += Connected[opposed][!!phalanx][more_than_one(supported)][relative_rank(Us, s)];
|
||||
|
||||
if (doubled)
|
||||
score -= Doubled[f] / distance<Rank>(s, frontmost_sq(Us, doubled));
|
||||
score -= Doubled;
|
||||
|
||||
if (lever)
|
||||
score += Lever[relative_rank(Us, s)];
|
||||
|
@ -197,9 +176,6 @@ namespace {
|
|||
b = e->semiopenFiles[Us] ^ 0xFF;
|
||||
e->pawnSpan[Us] = b ? int(msb(b) - lsb(b)) : 0;
|
||||
|
||||
b = shift_bb<Right>(ourPawns) & shift_bb<Left>(ourPawns) & CenterBindMask;
|
||||
score += CenterBind * popcount<Max15>(b);
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
|
@ -213,7 +189,7 @@ namespace Pawns {
|
|||
|
||||
void init()
|
||||
{
|
||||
static const int Seed[RANK_NB] = { 0, 6, 15, 10, 57, 75, 135, 258 };
|
||||
static const int Seed[RANK_NB] = { 0, 8, 19, 13, 71, 94, 169, 324 };
|
||||
|
||||
for (int opposed = 0; opposed <= 1; ++opposed)
|
||||
for (int phalanx = 0; phalanx <= 1; ++phalanx)
|
||||
|
@ -222,7 +198,7 @@ void init()
|
|||
{
|
||||
int v = (Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0)) >> opposed;
|
||||
v += (apex ? v / 2 : 0);
|
||||
Connected[opposed][phalanx][apex][r] = make_score(3 * v / 2, v);
|
||||
Connected[opposed][phalanx][apex][r] = make_score(v, v * 5 / 8);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,7 +218,7 @@ Entry* probe(const Position& pos) {
|
|||
|
||||
e->key = key;
|
||||
e->score = evaluate<WHITE>(pos, e) - evaluate<BLACK>(pos, e);
|
||||
e->asymmetry = popcount<Max15>(e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK]);
|
||||
e->asymmetry = popcount(e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK]);
|
||||
return e;
|
||||
}
|
||||
|
||||
|
@ -297,9 +273,6 @@ Score Entry::do_king_safety(const Position& pos, Square ksq) {
|
|||
if (pawns)
|
||||
while (!(DistanceRingBB[ksq][minKingPawnDistance++] & pawns)) {}
|
||||
|
||||
if (relative_rank(Us, ksq) > RANK_4)
|
||||
return make_score(0, -16 * minKingPawnDistance);
|
||||
|
||||
Value bonus = shelter_storm<Us>(pos, ksq);
|
||||
|
||||
// If we can castle use the bonus after the castling if it is bigger
|
||||
|
|
|
@ -53,7 +53,7 @@ struct Entry {
|
|||
}
|
||||
|
||||
template<Color Us>
|
||||
Score king_safety(const Position& pos, Square ksq) {
|
||||
Score king_safety(const Position& pos, Square ksq) {
|
||||
return kingSquares[Us] == ksq && castlingRights[Us] == pos.can_castle(Us)
|
||||
? kingSafety[Us] : (kingSafety[Us] = do_king_safety<Us>(pos, ksq));
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "bitcount.h"
|
||||
#include "bitboard.h"
|
||||
#include "misc.h"
|
||||
#include "movegen.h"
|
||||
#include "position.h"
|
||||
|
@ -34,10 +34,6 @@
|
|||
|
||||
using std::string;
|
||||
|
||||
Value PieceValue[PHASE_NB][PIECE_NB] = {
|
||||
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
|
||||
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
|
||||
|
||||
namespace Zobrist {
|
||||
|
||||
Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
|
||||
|
@ -159,42 +155,11 @@ void Position::init() {
|
|||
}
|
||||
|
||||
|
||||
/// 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) {
|
||||
|
||||
std::memcpy(this, &pos, sizeof(Position));
|
||||
std::memcpy(&startState, st, sizeof(StateInfo));
|
||||
st = &startState;
|
||||
nodes = 0;
|
||||
|
||||
assert(pos_is_ok());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/// Position::clear() erases the position object to a pristine state, with an
|
||||
/// empty board, white to move, and no castling rights.
|
||||
|
||||
void Position::clear() {
|
||||
|
||||
std::memset(this, 0, sizeof(Position));
|
||||
startState.epSquare = SQ_NONE;
|
||||
st = &startState;
|
||||
|
||||
for (int i = 0; i < PIECE_TYPE_NB; ++i)
|
||||
for (int j = 0; j < 16; ++j)
|
||||
pieceList[WHITE][i][j] = pieceList[BLACK][i][j] = SQ_NONE;
|
||||
}
|
||||
|
||||
|
||||
/// Position::set() initializes the position object with the given FEN string.
|
||||
/// This function is not very robust - make sure that input FENs are correct,
|
||||
/// this is assumed to be the responsibility of the GUI.
|
||||
|
||||
void Position::set(const string& fenStr, bool isChess960, Thread* th) {
|
||||
Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Thread* th) {
|
||||
/*
|
||||
A FEN string defines a particular position using only the ASCII character set.
|
||||
|
||||
|
@ -234,7 +199,11 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
|
|||
Square sq = SQ_A8;
|
||||
std::istringstream ss(fenStr);
|
||||
|
||||
clear();
|
||||
std::memset(this, 0, sizeof(Position));
|
||||
std::memset(si, 0, sizeof(StateInfo));
|
||||
std::fill_n(&pieceList[0][0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE);
|
||||
st = si;
|
||||
|
||||
ss >> std::noskipws;
|
||||
|
||||
// 1. Piece placement
|
||||
|
@ -295,6 +264,8 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
|
|||
if (!(attackers_to(st->epSquare) & pieces(sideToMove, PAWN)))
|
||||
st->epSquare = SQ_NONE;
|
||||
}
|
||||
else
|
||||
st->epSquare = SQ_NONE;
|
||||
|
||||
// 5-6. Halfmove clock and fullmove number
|
||||
ss >> std::skipws >> st->rule50 >> gamePly;
|
||||
|
@ -308,6 +279,8 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
|
|||
set_state(st);
|
||||
|
||||
assert(pos_is_ok());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
@ -447,28 +420,27 @@ Phase Position::game_phase() const {
|
|||
}
|
||||
|
||||
|
||||
/// Position::check_blockers() returns a bitboard of all the pieces with color
|
||||
/// 'c' that are blocking check on the king with color 'kingColor'. A piece
|
||||
/// blocks a check if removing that piece from the board would result in a
|
||||
/// position where the king is in check. A check blocking piece can be either a
|
||||
/// pinned or a discovered check piece, according if its color 'c' is the same
|
||||
/// or the opposite of 'kingColor'.
|
||||
/// Position::slider_blockers() returns a bitboard of all the pieces in 'target' that
|
||||
/// are blocking attacks on the square 's' from 'sliders'. A piece blocks a slider
|
||||
/// if removing that piece from the board would result in a position where square 's'
|
||||
/// is attacked. For example, a king-attack blocking piece can be either a pinned or
|
||||
/// a discovered check piece, according if its color is the opposite or the same of
|
||||
/// the color of the slider.
|
||||
|
||||
Bitboard Position::check_blockers(Color c, Color kingColor) const {
|
||||
Bitboard Position::slider_blockers(Bitboard target, Bitboard sliders, Square s) const {
|
||||
|
||||
Bitboard b, pinners, result = 0;
|
||||
Square ksq = square<KING>(kingColor);
|
||||
|
||||
// Pinners are sliders that give check when a pinned piece is removed
|
||||
pinners = ( (pieces( ROOK, QUEEN) & PseudoAttacks[ROOK ][ksq])
|
||||
| (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq])) & pieces(~kingColor);
|
||||
// Pinners are sliders that attack 's' when a pinned piece is removed
|
||||
pinners = ( (PseudoAttacks[ROOK ][s] & pieces(QUEEN, ROOK))
|
||||
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
|
||||
|
||||
while (pinners)
|
||||
{
|
||||
b = between_bb(ksq, pop_lsb(&pinners)) & pieces();
|
||||
b = between_bb(s, pop_lsb(&pinners)) & pieces();
|
||||
|
||||
if (!more_than_one(b))
|
||||
result |= b & pieces(c);
|
||||
result |= b & target;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -528,8 +500,7 @@ bool Position::legal(Move m, Bitboard pinned) const {
|
|||
|
||||
// A non-king move is legal if and only if it is not pinned or it
|
||||
// is moving along the ray towards or away from the king.
|
||||
return !pinned
|
||||
|| !(pinned & from)
|
||||
return !(pinned & from)
|
||||
|| aligned(from, to_sq(m), square<KING>(us));
|
||||
}
|
||||
|
||||
|
@ -622,8 +593,7 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const {
|
|||
return true;
|
||||
|
||||
// Is there a discovered check?
|
||||
if ( ci.dcCandidates
|
||||
&& (ci.dcCandidates & from)
|
||||
if ( (ci.dcCandidates & from)
|
||||
&& !aligned(from, to, ci.ksq))
|
||||
return true;
|
||||
|
||||
|
@ -1112,7 +1082,7 @@ void Position::flip() {
|
|||
std::getline(ss, token); // Half and full moves
|
||||
f += token;
|
||||
|
||||
set(f, is_chess960(), this_thread());
|
||||
set(f, is_chess960(), st, this_thread());
|
||||
|
||||
assert(pos_is_ok());
|
||||
}
|
||||
|
@ -1170,7 +1140,7 @@ bool Position::pos_is_ok(int* failedStep) const {
|
|||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (PieceType pt = PAWN; pt <= KING; ++pt)
|
||||
{
|
||||
if (pieceCount[c][pt] != popcount<Full>(pieces(c, pt)))
|
||||
if (pieceCount[c][pt] != popcount(pieces(c, pt)))
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < pieceCount[c][pt]; ++i)
|
||||
|
|
|
@ -23,7 +23,10 @@
|
|||
|
||||
#include <cassert>
|
||||
#include <cstddef> // For offsetof()
|
||||
#include <deque>
|
||||
#include <memory> // For std::unique_ptr
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "types.h"
|
||||
|
@ -75,6 +78,9 @@ struct StateInfo {
|
|||
StateInfo* previous;
|
||||
};
|
||||
|
||||
// In a std::deque references to elements are unaffected upon resizing
|
||||
typedef std::unique_ptr<std::deque<StateInfo>> StateListPtr;
|
||||
|
||||
|
||||
/// Position class stores information regarding the board representation as
|
||||
/// pieces, side to move, hash keys, castling info, etc. Important methods are
|
||||
|
@ -86,14 +92,12 @@ class Position {
|
|||
public:
|
||||
static void init();
|
||||
|
||||
Position() = default; // To define the global object RootPos
|
||||
Position() = default;
|
||||
Position(const Position&) = delete;
|
||||
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
|
||||
Position& operator=(const Position&) = delete;
|
||||
|
||||
// FEN string input/output
|
||||
void set(const std::string& fenStr, bool isChess960, Thread* th);
|
||||
Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th);
|
||||
const std::string fen() const;
|
||||
|
||||
// Position representation
|
||||
|
@ -127,6 +131,7 @@ public:
|
|||
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;
|
||||
Bitboard slider_blockers(Bitboard target, Bitboard sliders, Square s) const;
|
||||
|
||||
// Properties of moves
|
||||
bool legal(Move m, Bitboard pinned) const;
|
||||
|
@ -178,12 +183,10 @@ public:
|
|||
|
||||
private:
|
||||
// Initialization helpers (used while setting up a position)
|
||||
void clear();
|
||||
void set_castling_right(Color c, Square rfrom);
|
||||
void set_state(StateInfo* si) const;
|
||||
|
||||
// Other helpers
|
||||
Bitboard check_blockers(Color c, Color kingColor) const;
|
||||
void put_piece(Color c, PieceType pt, Square s);
|
||||
void remove_piece(Color c, PieceType pt, Square s);
|
||||
void move_piece(Color c, PieceType pt, Square from, Square to);
|
||||
|
@ -200,7 +203,6 @@ private:
|
|||
int castlingRightsMask[SQUARE_NB];
|
||||
Square castlingRookSquare[CASTLING_RIGHT_NB];
|
||||
Bitboard castlingPath[CASTLING_RIGHT_NB];
|
||||
StateInfo startState;
|
||||
uint64_t nodes;
|
||||
int gamePly;
|
||||
Color sideToMove;
|
||||
|
@ -309,11 +311,11 @@ inline Bitboard Position::checkers() const {
|
|||
}
|
||||
|
||||
inline Bitboard Position::discovered_check_candidates() const {
|
||||
return check_blockers(sideToMove, ~sideToMove);
|
||||
return slider_blockers(pieces(sideToMove), pieces(sideToMove), square<KING>(~sideToMove));
|
||||
}
|
||||
|
||||
inline Bitboard Position::pinned_pieces(Color c) const {
|
||||
return check_blockers(c, c);
|
||||
return slider_blockers(pieces(c), pieces(~c), square<KING>(c));
|
||||
}
|
||||
|
||||
inline bool Position::pawn_passed(Color c, Square s) const {
|
||||
|
|
|
@ -18,8 +18,14 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
Value PieceValue[PHASE_NB][PIECE_NB] = {
|
||||
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
|
||||
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
|
||||
|
||||
namespace PSQT {
|
||||
|
||||
#define S(mg, eg) make_score(mg, eg)
|
||||
|
@ -32,13 +38,12 @@ const Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
|
|||
{ },
|
||||
{ // Pawn
|
||||
{ S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) },
|
||||
{ S(-19, 5), S( 1,-4), S( 7, 8), S( 3,-2) },
|
||||
{ S(-26,-6), S( -7,-5), S( 19, 5), S(24, 4) },
|
||||
{ S(-25, 1), S(-14, 3), S( 16,-8), S(31,-3) },
|
||||
{ S(-14, 6), S( 0, 9), S( -1, 7), S(17,-6) },
|
||||
{ S(-14, 6), S(-13,-5), S(-10, 2), S(-6, 4) },
|
||||
{ S(-12, 1), S( 15,-9), S( -8, 1), S(-4,18) },
|
||||
{ S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) }
|
||||
{ S(-16, 7), S( 1,-4), S( 7, 8), S( 3,-2) },
|
||||
{ S(-23,-4), S( -7,-5), S( 19, 5), S(24, 4) },
|
||||
{ S(-22, 3), S(-14, 3), S( 20,-8), S(35,-3) },
|
||||
{ S(-11, 8), S( 0, 9), S( 3, 7), S(21,-6) },
|
||||
{ S(-11, 8), S(-13,-5), S( -6, 2), S(-2, 4) },
|
||||
{ S( -9, 3), S( 15,-9), S( -8, 1), S(-4,18) }
|
||||
},
|
||||
{ // Knight
|
||||
{ S(-143, -97), S(-96,-82), S(-80,-46), S(-73,-14) },
|
||||
|
@ -96,7 +101,7 @@ const Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
|
|||
|
||||
Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
|
||||
|
||||
// init() initializes piece square tables: the white halves of the tables are
|
||||
// init() initializes piece-square tables: the white halves of the tables are
|
||||
// copied from Bonus[] adding the piece value, then the black halves of the
|
||||
// tables are initialized by flipping and changing the sign of the white scores.
|
||||
void init() {
|
||||
|
@ -110,8 +115,9 @@ void init() {
|
|||
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
{
|
||||
int edgeDistance = file_of(s) < FILE_E ? file_of(s) : FILE_H - file_of(s);
|
||||
psq[BLACK][pt][~s] = -(psq[WHITE][pt][s] = v + Bonus[pt][rank_of(s)][edgeDistance]);
|
||||
File f = std::min(file_of(s), FILE_H - file_of(s));
|
||||
psq[WHITE][pt][ s] = v + Bonus[pt][rank_of(s)][f];
|
||||
psq[BLACK][pt][~s] = -psq[WHITE][pt][s];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,6 @@ namespace Search {
|
|||
|
||||
SignalsType Signals;
|
||||
LimitsType Limits;
|
||||
StateStackPtr SetupStates;
|
||||
}
|
||||
|
||||
namespace Tablebases {
|
||||
|
@ -61,8 +60,8 @@ using namespace Search;
|
|||
|
||||
namespace {
|
||||
|
||||
// Different node types, used as template parameter
|
||||
enum NodeType { Root, PV, NonPV };
|
||||
// Different node types, used as a template parameter
|
||||
enum NodeType { NonPV, PV };
|
||||
|
||||
// Razoring and futility margin based on depth
|
||||
const int razor_margin[4] = { 483, 570, 603, 554 };
|
||||
|
@ -76,7 +75,7 @@ namespace {
|
|||
return Reductions[PvNode][i][std::min(d, 63 * ONE_PLY)][std::min(mn, 63)];
|
||||
}
|
||||
|
||||
// Skill struct is used to implement strength limiting
|
||||
// Skill structure is used to implement strength limit
|
||||
struct Skill {
|
||||
Skill(int l) : level(l) {}
|
||||
bool enabled() const { return level < 20; }
|
||||
|
@ -88,8 +87,8 @@ namespace {
|
|||
Move best = MOVE_NONE;
|
||||
};
|
||||
|
||||
// EasyMoveManager struct is used to detect a so called 'easy move'; when PV is
|
||||
// stable across multiple search iterations we can fast return the best move.
|
||||
// EasyMoveManager structure is used to detect an 'easy move'. When the PV is
|
||||
// stable across multiple search iterations, we can quickly return the best move.
|
||||
struct EasyMoveManager {
|
||||
|
||||
void clear() {
|
||||
|
@ -106,7 +105,7 @@ namespace {
|
|||
|
||||
assert(newPv.size() >= 3);
|
||||
|
||||
// Keep track of how many times in a row 3rd ply remains stable
|
||||
// Keep track of how many times in a row the 3rd ply remains stable
|
||||
stableCnt = (newPv[2] == pv[2]) ? stableCnt + 1 : 0;
|
||||
|
||||
if (!std::equal(newPv.begin(), newPv.begin() + 3, pv))
|
||||
|
@ -127,9 +126,38 @@ namespace {
|
|||
Move pv[3];
|
||||
};
|
||||
|
||||
// Set of rows with half bits set to 1 and half to 0. It is used to allocate
|
||||
// the search depths across the threads.
|
||||
typedef std::vector<int> Row;
|
||||
|
||||
const Row HalfDensity[] = {
|
||||
{0, 1},
|
||||
{1, 0},
|
||||
{0, 0, 1, 1},
|
||||
{0, 1, 1, 0},
|
||||
{1, 1, 0, 0},
|
||||
{1, 0, 0, 1},
|
||||
{0, 0, 0, 1, 1, 1},
|
||||
{0, 0, 1, 1, 1, 0},
|
||||
{0, 1, 1, 1, 0, 0},
|
||||
{1, 1, 1, 0, 0, 0},
|
||||
{1, 1, 0, 0, 0, 1},
|
||||
{1, 0, 0, 0, 1, 1},
|
||||
{0, 0, 0, 0, 1, 1, 1, 1},
|
||||
{0, 0, 0, 1, 1, 1, 1, 0},
|
||||
{0, 0, 1, 1, 1, 1, 0 ,0},
|
||||
{0, 1, 1, 1, 1, 0, 0 ,0},
|
||||
{1, 1, 1, 1, 0, 0, 0 ,0},
|
||||
{1, 1, 1, 0, 0, 0, 0 ,1},
|
||||
{1, 1, 0, 0, 0, 0, 1 ,1},
|
||||
{1, 0, 0, 0, 0, 1, 1 ,1},
|
||||
};
|
||||
|
||||
const size_t HalfDensitySize = std::extent<decltype(HalfDensity)>::value;
|
||||
|
||||
EasyMoveManager EasyMove;
|
||||
Value DrawValue[COLOR_NB];
|
||||
CounterMovesHistoryStats CounterMovesHistory;
|
||||
CounterMoveHistoryStats CounterMoveHistory;
|
||||
|
||||
template <NodeType NT>
|
||||
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode);
|
||||
|
@ -150,22 +178,21 @@ namespace {
|
|||
|
||||
void Search::init() {
|
||||
|
||||
const double K[][2] = {{ 0.799, 2.281 }, { 0.484, 3.023 }};
|
||||
for (int imp = 0; imp <= 1; ++imp)
|
||||
for (int d = 1; d < 64; ++d)
|
||||
for (int mc = 1; mc < 64; ++mc)
|
||||
{
|
||||
double r = log(d) * log(mc) / 2;
|
||||
if (r < 0.80)
|
||||
continue;
|
||||
|
||||
for (int pv = 0; pv <= 1; ++pv)
|
||||
for (int imp = 0; imp <= 1; ++imp)
|
||||
for (int d = 1; d < 64; ++d)
|
||||
for (int mc = 1; mc < 64; ++mc)
|
||||
{
|
||||
double r = K[pv][0] + log(d) * log(mc) / K[pv][1];
|
||||
Reductions[NonPV][imp][d][mc] = int(round(r)) * ONE_PLY;
|
||||
Reductions[PV][imp][d][mc] = std::max(Reductions[NonPV][imp][d][mc] - ONE_PLY, DEPTH_ZERO);
|
||||
|
||||
if (r >= 1.5)
|
||||
Reductions[pv][imp][d][mc] = int(r) * ONE_PLY;
|
||||
|
||||
// Increase reduction when eval is not improving
|
||||
if (!pv && !imp && Reductions[pv][imp][d][mc] >= 2 * ONE_PLY)
|
||||
Reductions[pv][imp][d][mc] += ONE_PLY;
|
||||
}
|
||||
// Increase reduction for non-PV nodes when eval is not improving
|
||||
if (!imp && Reductions[NonPV][imp][d][mc] >= 2 * ONE_PLY)
|
||||
Reductions[NonPV][imp][d][mc] += ONE_PLY;
|
||||
}
|
||||
|
||||
for (int d = 0; d < 16; ++d)
|
||||
{
|
||||
|
@ -175,23 +202,25 @@ void Search::init() {
|
|||
}
|
||||
|
||||
|
||||
/// Search::clear() resets to zero search state, to obtain reproducible results
|
||||
/// Search::clear() resets search state to zero, to obtain reproducible results
|
||||
|
||||
void Search::clear() {
|
||||
|
||||
TT.clear();
|
||||
CounterMovesHistory.clear();
|
||||
CounterMoveHistory.clear();
|
||||
|
||||
for (Thread* th : Threads)
|
||||
{
|
||||
th->history.clear();
|
||||
th->counterMoves.clear();
|
||||
}
|
||||
|
||||
Threads.main()->previousScore = VALUE_INFINITE;
|
||||
}
|
||||
|
||||
|
||||
/// Search::perft() is our utility to verify move generation. All the leaf nodes
|
||||
/// up to the given depth are generated and counted and the sum returned.
|
||||
/// up to the given depth are generated and counted, and the sum is returned.
|
||||
template<bool Root>
|
||||
uint64_t Search::perft(Position& pos, Depth depth) {
|
||||
|
||||
|
@ -221,8 +250,7 @@ template uint64_t Search::perft<true>(Position&, Depth);
|
|||
|
||||
|
||||
/// MainThread::search() is called by the main thread when the program receives
|
||||
/// the UCI 'go' command. It searches from root position and at the end prints
|
||||
/// the "bestmove" to output.
|
||||
/// the UCI 'go' command. It searches from the root position and outputs the "bestmove".
|
||||
|
||||
void MainThread::search() {
|
||||
|
||||
|
@ -255,11 +283,12 @@ void MainThread::search() {
|
|||
}
|
||||
else
|
||||
{
|
||||
if (TB::Cardinality >= rootPos.count<ALL_PIECES>(WHITE)
|
||||
+ rootPos.count<ALL_PIECES>(BLACK))
|
||||
if ( TB::Cardinality >= rootPos.count<ALL_PIECES>(WHITE)
|
||||
+ rootPos.count<ALL_PIECES>(BLACK)
|
||||
&& !rootPos.can_castle(ANY_CASTLING))
|
||||
{
|
||||
// If the current root position is in the tablebases then RootMoves
|
||||
// contains only moves that preserve the draw or win.
|
||||
// If the current root position is in the tablebases, then RootMoves
|
||||
// contains only moves that preserve the draw or the win.
|
||||
TB::RootInTB = Tablebases::root_probe(rootPos, rootMoves, TB::Score);
|
||||
|
||||
if (TB::RootInTB)
|
||||
|
@ -267,7 +296,7 @@ void MainThread::search() {
|
|||
|
||||
else // If DTZ tables are missing, use WDL tables as a fallback
|
||||
{
|
||||
// Filter out moves that do not preserve a draw or win
|
||||
// Filter out moves that do not preserve the draw or the win.
|
||||
TB::RootInTB = Tablebases::root_probe_wdl(rootPos, rootMoves, TB::Score);
|
||||
|
||||
// Only probe during search if winning
|
||||
|
@ -287,22 +316,14 @@ void MainThread::search() {
|
|||
}
|
||||
|
||||
for (Thread* th : Threads)
|
||||
{
|
||||
th->maxPly = 0;
|
||||
th->rootDepth = DEPTH_ZERO;
|
||||
if (th != this)
|
||||
{
|
||||
th->rootPos = Position(rootPos, th);
|
||||
th->rootMoves = rootMoves;
|
||||
th->start_searching();
|
||||
}
|
||||
}
|
||||
|
||||
Thread::search(); // Let's start searching!
|
||||
}
|
||||
|
||||
// When playing in 'nodes as time' mode, subtract the searched nodes from
|
||||
// the available ones before to exit.
|
||||
// the available ones before exiting.
|
||||
if (Limits.npmsec)
|
||||
Time.availableNodes += Limits.inc[us] - Threads.nodes_searched();
|
||||
|
||||
|
@ -329,7 +350,9 @@ void MainThread::search() {
|
|||
Thread* bestThread = this;
|
||||
if ( !this->easyMovePlayed
|
||||
&& Options["MultiPV"] == 1
|
||||
&& !Skill(Options["Skill Level"]).enabled())
|
||||
&& !Limits.depth
|
||||
&& !Skill(Options["Skill Level"]).enabled()
|
||||
&& rootMoves[0].pv[0] != MOVE_NONE)
|
||||
{
|
||||
for (Thread* th : Threads)
|
||||
if ( th->completedDepth > bestThread->completedDepth
|
||||
|
@ -337,6 +360,8 @@ void MainThread::search() {
|
|||
bestThread = th;
|
||||
}
|
||||
|
||||
previousScore = bestThread->rootMoves[0].score;
|
||||
|
||||
// Send new PV when needed
|
||||
if (bestThread != this)
|
||||
sync_cout << UCI::pv(bestThread->rootPos, bestThread->completedDepth, -VALUE_INFINITE, VALUE_INFINITE) << sync_endl;
|
||||
|
@ -352,16 +377,16 @@ void MainThread::search() {
|
|||
|
||||
// Thread::search() is the main iterative deepening loop. It calls search()
|
||||
// repeatedly with increasing depth until the allocated thinking time has been
|
||||
// consumed, user stops the search, or the maximum search depth is reached.
|
||||
// consumed, the user stops the search, or the maximum search depth is reached.
|
||||
|
||||
void Thread::search() {
|
||||
|
||||
Stack stack[MAX_PLY+4], *ss = stack+2; // To allow referencing (ss-2) and (ss+2)
|
||||
Stack stack[MAX_PLY+7], *ss = stack+5; // To allow referencing (ss-5) and (ss+2)
|
||||
Value bestValue, alpha, beta, delta;
|
||||
Move easyMove = MOVE_NONE;
|
||||
MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr);
|
||||
|
||||
std::memset(ss-2, 0, 5 * sizeof(Stack));
|
||||
std::memset(ss-5, 0, 8 * sizeof(Stack));
|
||||
|
||||
bestValue = delta = alpha = -VALUE_INFINITE;
|
||||
beta = VALUE_INFINITE;
|
||||
|
@ -386,31 +411,16 @@ void Thread::search() {
|
|||
|
||||
multiPV = std::min(multiPV, rootMoves.size());
|
||||
|
||||
// Iterative deepening loop until requested to stop or target depth reached
|
||||
while (++rootDepth < DEPTH_MAX && !Signals.stop && (!Limits.depth || rootDepth <= Limits.depth))
|
||||
// Iterative deepening loop until requested to stop or the target depth is reached.
|
||||
while (++rootDepth < DEPTH_MAX && !Signals.stop && (!Limits.depth || Threads.main()->rootDepth <= Limits.depth))
|
||||
{
|
||||
// Set up the new depth for the helper threads skipping in average each
|
||||
// 2nd ply (using a half density map similar to a Hadamard matrix).
|
||||
// Set up the new depths for the helper threads skipping on average every
|
||||
// 2nd ply (using a half-density matrix).
|
||||
if (!mainThread)
|
||||
{
|
||||
int d = rootDepth + rootPos.game_ply();
|
||||
|
||||
if (idx <= 6 || idx > 24)
|
||||
{
|
||||
if (((d + idx) >> (msb(idx + 1) - 1)) % 2)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Table of values of 6 bits with 3 of them set
|
||||
static const int HalfDensityMap[] = {
|
||||
0x07, 0x0b, 0x0d, 0x0e, 0x13, 0x16, 0x19, 0x1a, 0x1c,
|
||||
0x23, 0x25, 0x26, 0x29, 0x2c, 0x31, 0x32, 0x34, 0x38
|
||||
};
|
||||
|
||||
if ((HalfDensityMap[idx - 7] >> (d % 6)) & 1)
|
||||
continue;
|
||||
}
|
||||
const Row& row = HalfDensity[(idx - 1) % HalfDensitySize];
|
||||
if (row[(rootDepth + rootPos.game_ply()) % row.size()])
|
||||
continue;
|
||||
}
|
||||
|
||||
// Age out PV variability metric
|
||||
|
@ -438,7 +448,7 @@ void Thread::search() {
|
|||
// high/low anymore.
|
||||
while (true)
|
||||
{
|
||||
bestValue = ::search<Root>(rootPos, ss, alpha, beta, rootDepth, false);
|
||||
bestValue = ::search<PV>(rootPos, ss, alpha, beta, rootDepth, false);
|
||||
|
||||
// Bring the best move to the front. It is critical that sorting
|
||||
// is done with a stable algorithm because all the values but the
|
||||
|
@ -448,14 +458,14 @@ void Thread::search() {
|
|||
// search the already searched PV lines are preserved.
|
||||
std::stable_sort(rootMoves.begin() + PVIdx, rootMoves.end());
|
||||
|
||||
// Write PV back to transposition table in case the relevant
|
||||
// Write PV back to the transposition table in case the relevant
|
||||
// entries have been overwritten during the search.
|
||||
for (size_t i = 0; i <= PVIdx; ++i)
|
||||
rootMoves[i].insert_pv_in_tt(rootPos);
|
||||
|
||||
// If search has been stopped break immediately. Sorting and
|
||||
// If search has been stopped, break immediately. Sorting and
|
||||
// writing PV back to TT is safe because RootMoves is still
|
||||
// valid, although it refers to previous iteration.
|
||||
// valid, although it refers to the previous iteration.
|
||||
if (Signals.stop)
|
||||
break;
|
||||
|
||||
|
@ -497,7 +507,7 @@ void Thread::search() {
|
|||
std::stable_sort(rootMoves.begin(), rootMoves.begin() + PVIdx + 1);
|
||||
|
||||
if (!mainThread)
|
||||
break;
|
||||
continue;
|
||||
|
||||
if (Signals.stop)
|
||||
sync_cout << "info nodes " << Threads.nodes_searched()
|
||||
|
@ -528,18 +538,22 @@ void Thread::search() {
|
|||
{
|
||||
if (!Signals.stop && !Signals.stopOnPonderhit)
|
||||
{
|
||||
// Take some extra time if the best move has changed
|
||||
if (rootDepth > 4 * ONE_PLY && multiPV == 1)
|
||||
Time.pv_instability(mainThread->bestMoveChanges);
|
||||
|
||||
// Stop the search if only one legal move is available or all
|
||||
// of the available time has been used or we matched an easyMove
|
||||
// Stop the search if only one legal move is available, or if all
|
||||
// of the available time has been used, or if we matched an easyMove
|
||||
// from the previous search and just did a fast verification.
|
||||
const int F[] = { mainThread->failedLow,
|
||||
bestValue - mainThread->previousScore };
|
||||
|
||||
int improvingFactor = std::max(229, std::min(715, 357 + 119 * F[0] - 6 * F[1]));
|
||||
double unstablePvFactor = 1 + mainThread->bestMoveChanges;
|
||||
|
||||
bool doEasyMove = rootMoves[0].pv[0] == easyMove
|
||||
&& mainThread->bestMoveChanges < 0.03
|
||||
&& Time.elapsed() > Time.optimum() * 5 / 42;
|
||||
|
||||
if ( rootMoves.size() == 1
|
||||
|| Time.elapsed() > Time.available() * (mainThread->failedLow ? 641 : 315) / 640
|
||||
|| (mainThread->easyMovePlayed = ( rootMoves[0].pv[0] == easyMove
|
||||
&& mainThread->bestMoveChanges < 0.03
|
||||
&& Time.elapsed() > Time.available() / 8)))
|
||||
|| Time.elapsed() > Time.optimum() * unstablePvFactor * improvingFactor / 628
|
||||
|| (mainThread->easyMovePlayed = doEasyMove))
|
||||
{
|
||||
// If we are allowed to ponder do not stop the search now but
|
||||
// keep pondering until the GUI sends "ponderhit" or "stop".
|
||||
|
@ -579,8 +593,8 @@ namespace {
|
|||
template <NodeType NT>
|
||||
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) {
|
||||
|
||||
const bool RootNode = NT == Root;
|
||||
const bool PvNode = NT == PV || NT == Root;
|
||||
const bool PvNode = NT == PV;
|
||||
const bool rootNode = PvNode && (ss-1)->ply == 0;
|
||||
|
||||
assert(-VALUE_INFINITE <= alpha && alpha < beta && beta <= VALUE_INFINITE);
|
||||
assert(PvNode || (alpha == beta - 1));
|
||||
|
@ -595,6 +609,7 @@ namespace {
|
|||
Value bestValue, value, ttValue, eval, nullValue, futilityValue;
|
||||
bool ttHit, inCheck, givesCheck, singularExtensionNode, improving;
|
||||
bool captureOrPromotion, doFullDepthSearch;
|
||||
Piece moved_piece;
|
||||
int moveCount, quietCount;
|
||||
|
||||
// Step 1. Initialize node
|
||||
|
@ -604,7 +619,7 @@ namespace {
|
|||
bestValue = -VALUE_INFINITE;
|
||||
ss->ply = (ss-1)->ply + 1;
|
||||
|
||||
// Check for available remaining time
|
||||
// Check for the available remaining time
|
||||
if (thisThread->resetCalls.load(std::memory_order_relaxed))
|
||||
{
|
||||
thisThread->resetCalls = false;
|
||||
|
@ -622,7 +637,7 @@ namespace {
|
|||
if (PvNode && thisThread->maxPly < ss->ply)
|
||||
thisThread->maxPly = ss->ply;
|
||||
|
||||
if (!RootNode)
|
||||
if (!rootNode)
|
||||
{
|
||||
// Step 2. Check for aborted search and immediate draw
|
||||
if (Signals.stop.load(std::memory_order_relaxed) || pos.is_draw() || ss->ply >= MAX_PLY)
|
||||
|
@ -644,6 +659,7 @@ namespace {
|
|||
assert(0 <= ss->ply && ss->ply < MAX_PLY);
|
||||
|
||||
ss->currentMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
|
||||
ss->counterMoves = nullptr;
|
||||
(ss+1)->skipEarlyPruning = false;
|
||||
(ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
|
||||
|
||||
|
@ -654,7 +670,7 @@ namespace {
|
|||
posKey = excludedMove ? pos.exclusion_key() : pos.key();
|
||||
tte = TT.probe(posKey, ttHit);
|
||||
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
|
||||
ttMove = RootNode ? thisThread->rootMoves[thisThread->PVIdx].pv[0]
|
||||
ttMove = rootNode ? thisThread->rootMoves[thisThread->PVIdx].pv[0]
|
||||
: ttHit ? tte->move() : MOVE_NONE;
|
||||
|
||||
// At non-PV nodes we check for an early TT cutoff
|
||||
|
@ -675,13 +691,14 @@ namespace {
|
|||
}
|
||||
|
||||
// Step 4a. Tablebase probe
|
||||
if (!RootNode && TB::Cardinality)
|
||||
if (!rootNode && TB::Cardinality)
|
||||
{
|
||||
int piecesCnt = pos.count<ALL_PIECES>(WHITE) + pos.count<ALL_PIECES>(BLACK);
|
||||
|
||||
if ( piecesCnt <= TB::Cardinality
|
||||
&& (piecesCnt < TB::Cardinality || depth >= TB::ProbeDepth)
|
||||
&& pos.rule50_count() == 0)
|
||||
&& pos.rule50_count() == 0
|
||||
&& !pos.can_castle(ANY_CASTLING))
|
||||
{
|
||||
int found, v = Tablebases::probe_wdl(pos, &found);
|
||||
|
||||
|
@ -752,7 +769,7 @@ namespace {
|
|||
}
|
||||
|
||||
// Step 7. Futility pruning: child node (skipped when in check)
|
||||
if ( !RootNode
|
||||
if ( !rootNode
|
||||
&& depth < 7 * ONE_PLY
|
||||
&& eval - futility_margin(depth) >= beta
|
||||
&& eval < VALUE_KNOWN_WIN // Do not return unproven wins
|
||||
|
@ -766,6 +783,7 @@ namespace {
|
|||
&& pos.non_pawn_material(pos.side_to_move()))
|
||||
{
|
||||
ss->currentMove = MOVE_NULL;
|
||||
ss->counterMoves = nullptr;
|
||||
|
||||
assert(eval - beta >= 0);
|
||||
|
||||
|
@ -814,13 +832,14 @@ namespace {
|
|||
assert((ss-1)->currentMove != MOVE_NONE);
|
||||
assert((ss-1)->currentMove != MOVE_NULL);
|
||||
|
||||
MovePicker mp(pos, ttMove, thisThread->history, PieceValue[MG][pos.captured_piece_type()]);
|
||||
MovePicker mp(pos, ttMove, PieceValue[MG][pos.captured_piece_type()]);
|
||||
CheckInfo ci(pos);
|
||||
|
||||
while ((move = mp.next_move()) != MOVE_NONE)
|
||||
if (pos.legal(move, ci.pinned))
|
||||
{
|
||||
ss->currentMove = move;
|
||||
ss->counterMoves = &CounterMoveHistory[pos.moved_piece(move)][to_sq(move)];
|
||||
pos.do_move(move, st, pos.gives_check(move, ci));
|
||||
value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode);
|
||||
pos.undo_move(move);
|
||||
|
@ -836,7 +855,7 @@ namespace {
|
|||
{
|
||||
Depth d = depth - 2 * ONE_PLY - (PvNode ? DEPTH_ZERO : depth / 4);
|
||||
ss->skipEarlyPruning = true;
|
||||
search<PvNode ? PV : NonPV>(pos, ss, alpha, beta, d, true);
|
||||
search<NT>(pos, ss, alpha, beta, d, true);
|
||||
ss->skipEarlyPruning = false;
|
||||
|
||||
tte = TT.probe(posKey, ttHit);
|
||||
|
@ -846,17 +865,18 @@ namespace {
|
|||
moves_loop: // When in check search starts from here
|
||||
|
||||
Square prevSq = to_sq((ss-1)->currentMove);
|
||||
Move cm = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq];
|
||||
const CounterMovesStats& cmh = CounterMovesHistory[pos.piece_on(prevSq)][prevSq];
|
||||
const CounterMoveStats* cmh = (ss-1)->counterMoves;
|
||||
const CounterMoveStats* fmh = (ss-2)->counterMoves;
|
||||
const CounterMoveStats* fmh2 = (ss-4)->counterMoves;
|
||||
|
||||
MovePicker mp(pos, ttMove, depth, thisThread->history, cmh, cm, ss);
|
||||
MovePicker mp(pos, ttMove, depth, ss);
|
||||
CheckInfo ci(pos);
|
||||
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
|
||||
improving = ss->staticEval >= (ss-2)->staticEval
|
||||
|| ss->staticEval == VALUE_NONE
|
||||
||(ss-2)->staticEval == VALUE_NONE;
|
||||
|
||||
singularExtensionNode = !RootNode
|
||||
singularExtensionNode = !rootNode
|
||||
&& depth >= 8 * ONE_PLY
|
||||
&& ttMove != MOVE_NONE
|
||||
/* && ttValue != VALUE_NONE Already implicit in the next condition */
|
||||
|
@ -877,13 +897,13 @@ moves_loop: // When in check search starts from here
|
|||
// At root obey the "searchmoves" option and skip moves not listed in Root
|
||||
// Move List. As a consequence any illegal move is also skipped. In MultiPV
|
||||
// mode we also skip PV moves which have been already searched.
|
||||
if (RootNode && !std::count(thisThread->rootMoves.begin() + thisThread->PVIdx,
|
||||
if (rootNode && !std::count(thisThread->rootMoves.begin() + thisThread->PVIdx,
|
||||
thisThread->rootMoves.end(), move))
|
||||
continue;
|
||||
|
||||
ss->moveCount = ++moveCount;
|
||||
|
||||
if (RootNode && thisThread == Threads.main() && Time.elapsed() > 3000)
|
||||
if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000)
|
||||
sync_cout << "info depth " << depth / ONE_PLY
|
||||
<< " currmove " << UCI::move(move, pos.is_chess960())
|
||||
<< " currmovenumber " << moveCount + thisThread->PVIdx << sync_endl;
|
||||
|
@ -893,6 +913,7 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
extension = DEPTH_ZERO;
|
||||
captureOrPromotion = pos.capture_or_promotion(move);
|
||||
moved_piece = pos.moved_piece(move);
|
||||
|
||||
givesCheck = type_of(move) == NORMAL && !ci.dcCandidates
|
||||
? ci.checkSquares[type_of(pos.piece_on(from_sq(move)))] & to_sq(move)
|
||||
|
@ -927,7 +948,7 @@ moves_loop: // When in check search starts from here
|
|||
newDepth = depth - ONE_PLY + extension;
|
||||
|
||||
// Step 13. Pruning at shallow depth
|
||||
if ( !RootNode
|
||||
if ( !rootNode
|
||||
&& !captureOrPromotion
|
||||
&& !inCheck
|
||||
&& !givesCheck
|
||||
|
@ -939,14 +960,15 @@ moves_loop: // When in check search starts from here
|
|||
&& moveCount >= FutilityMoveCounts[improving][depth])
|
||||
continue;
|
||||
|
||||
// History based pruning
|
||||
// Countermoves based pruning
|
||||
if ( depth <= 4 * ONE_PLY
|
||||
&& move != ss->killers[0]
|
||||
&& thisThread->history[pos.moved_piece(move)][to_sq(move)] < VALUE_ZERO
|
||||
&& cmh[pos.moved_piece(move)][to_sq(move)] < VALUE_ZERO)
|
||||
&& (!cmh || (*cmh )[moved_piece][to_sq(move)] < VALUE_ZERO)
|
||||
&& (!fmh || (*fmh )[moved_piece][to_sq(move)] < VALUE_ZERO)
|
||||
&& (!fmh2 || (*fmh2)[moved_piece][to_sq(move)] < VALUE_ZERO || (cmh && fmh)))
|
||||
continue;
|
||||
|
||||
predictedDepth = newDepth - reduction<PvNode>(improving, depth, moveCount);
|
||||
predictedDepth = std::max(newDepth - reduction<PvNode>(improving, depth, moveCount), DEPTH_ZERO);
|
||||
|
||||
// Futility pruning: parent node
|
||||
if (predictedDepth < 7 * ONE_PLY)
|
||||
|
@ -969,13 +991,14 @@ moves_loop: // When in check search starts from here
|
|||
prefetch(TT.first_entry(pos.key_after(move)));
|
||||
|
||||
// Check for legality just before making the move
|
||||
if (!RootNode && !pos.legal(move, ci.pinned))
|
||||
if (!rootNode && !pos.legal(move, ci.pinned))
|
||||
{
|
||||
ss->moveCount = --moveCount;
|
||||
continue;
|
||||
}
|
||||
|
||||
ss->currentMove = move;
|
||||
ss->counterMoves = &CounterMoveHistory[moved_piece][to_sq(move)];
|
||||
|
||||
// Step 14. Make the move
|
||||
pos.do_move(move, st, givesCheck);
|
||||
|
@ -987,19 +1010,23 @@ moves_loop: // When in check search starts from here
|
|||
&& !captureOrPromotion)
|
||||
{
|
||||
Depth r = reduction<PvNode>(improving, depth, moveCount);
|
||||
Value val = thisThread->history[moved_piece][to_sq(move)]
|
||||
+ (cmh ? (*cmh )[moved_piece][to_sq(move)] : VALUE_ZERO)
|
||||
+ (fmh ? (*fmh )[moved_piece][to_sq(move)] : VALUE_ZERO)
|
||||
+ (fmh2 ? (*fmh2)[moved_piece][to_sq(move)] : VALUE_ZERO);
|
||||
|
||||
// Increase reduction for cut nodes and moves with a bad history
|
||||
if ( (!PvNode && cutNode)
|
||||
|| ( thisThread->history[pos.piece_on(to_sq(move))][to_sq(move)] < VALUE_ZERO
|
||||
&& cmh[pos.piece_on(to_sq(move))][to_sq(move)] <= VALUE_ZERO))
|
||||
// Increase reduction for cut nodes
|
||||
if (!PvNode && cutNode)
|
||||
r += ONE_PLY;
|
||||
|
||||
// Decrease reduction for moves with a good history
|
||||
if ( thisThread->history[pos.piece_on(to_sq(move))][to_sq(move)] > VALUE_ZERO
|
||||
&& cmh[pos.piece_on(to_sq(move))][to_sq(move)] > VALUE_ZERO)
|
||||
r = std::max(DEPTH_ZERO, r - ONE_PLY);
|
||||
// Decrease/increase reduction for moves with a good/bad history
|
||||
int rHist = (val - 10000) / 20000;
|
||||
r = std::max(DEPTH_ZERO, r - rHist * ONE_PLY);
|
||||
|
||||
// Decrease reduction for moves that escape a capture
|
||||
// Decrease reduction for moves that escape a capture. Filter out
|
||||
// castling moves, because they are coded as "king captures rook" and
|
||||
// hence break make_move(). Also use see() instead of see_sign(),
|
||||
// because the destination square is empty.
|
||||
if ( r
|
||||
&& type_of(move) == NORMAL
|
||||
&& type_of(pos.piece_on(to_sq(move))) != PAWN
|
||||
|
@ -1015,7 +1042,7 @@ moves_loop: // When in check search starts from here
|
|||
else
|
||||
doFullDepthSearch = !PvNode || moveCount > 1;
|
||||
|
||||
// Step 16. Full depth search, when LMR is skipped or fails high
|
||||
// Step 16. Full depth search when LMR is skipped or fails high
|
||||
if (doFullDepthSearch)
|
||||
value = newDepth < ONE_PLY ?
|
||||
givesCheck ? -qsearch<NonPV, true>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
|
||||
|
@ -1024,8 +1051,8 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
// For PV nodes only, do a full PV search on the first move or after a fail
|
||||
// high (in the latter case search only if value < beta), otherwise let the
|
||||
// parent node fail low with value <= alpha and to try another move.
|
||||
if (PvNode && (moveCount == 1 || (value > alpha && (RootNode || value < beta))))
|
||||
// parent node fail low with value <= alpha and try another move.
|
||||
if (PvNode && (moveCount == 1 || (value > alpha && (rootNode || value < beta))))
|
||||
{
|
||||
(ss+1)->pv = pv;
|
||||
(ss+1)->pv[0] = MOVE_NONE;
|
||||
|
@ -1041,14 +1068,14 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
|
||||
|
||||
// Step 18. Check for new best move
|
||||
// Step 18. Check for a new best move
|
||||
// Finished searching the move. If a stop occurred, the return value of
|
||||
// the search cannot be trusted, and we return immediately without
|
||||
// updating best move, PV and TT.
|
||||
if (Signals.stop.load(std::memory_order_relaxed))
|
||||
return VALUE_ZERO;
|
||||
|
||||
if (RootNode)
|
||||
if (rootNode)
|
||||
{
|
||||
RootMove& rm = *std::find(thisThread->rootMoves.begin(),
|
||||
thisThread->rootMoves.end(), move);
|
||||
|
@ -1092,7 +1119,7 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
bestMove = move;
|
||||
|
||||
if (PvNode && !RootNode) // Update pv even in fail-high case
|
||||
if (PvNode && !rootNode) // Update pv even in fail-high case
|
||||
update_pv(ss->pv, move, (ss+1)->pv);
|
||||
|
||||
if (PvNode && value < beta) // Update alpha! Always alpha < beta
|
||||
|
@ -1109,7 +1136,7 @@ moves_loop: // When in check search starts from here
|
|||
quietsSearched[quietCount++] = move;
|
||||
}
|
||||
|
||||
// Following condition would detect a stop only after move loop has been
|
||||
// The following condition would detect a stop only after move loop has been
|
||||
// completed. But in this case bestValue is valid because we have fully
|
||||
// searched our subtree, and we can anyhow save the result in TT.
|
||||
/*
|
||||
|
@ -1119,7 +1146,7 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
// Step 20. Check for mate and stalemate
|
||||
// All legal moves have been searched and if there are no legal moves, it
|
||||
// must be mate or stalemate. If we are in a singular extension search then
|
||||
// must be a mate or a stalemate. If we are in a singular extension search then
|
||||
// return a fail low score.
|
||||
if (!moveCount)
|
||||
bestValue = excludedMove ? alpha
|
||||
|
@ -1134,13 +1161,17 @@ moves_loop: // When in check search starts from here
|
|||
&& !bestMove
|
||||
&& !inCheck
|
||||
&& !pos.captured_piece_type()
|
||||
&& is_ok((ss - 1)->currentMove)
|
||||
&& is_ok((ss - 2)->currentMove))
|
||||
&& is_ok((ss-1)->currentMove))
|
||||
{
|
||||
Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY) + depth / ONE_PLY - 1);
|
||||
Square prevPrevSq = to_sq((ss - 2)->currentMove);
|
||||
CounterMovesStats& prevCmh = CounterMovesHistory[pos.piece_on(prevPrevSq)][prevPrevSq];
|
||||
prevCmh.update(pos.piece_on(prevSq), prevSq, bonus);
|
||||
if ((ss-2)->counterMoves)
|
||||
(ss-2)->counterMoves->update(pos.piece_on(prevSq), prevSq, bonus);
|
||||
|
||||
if ((ss-3)->counterMoves)
|
||||
(ss-3)->counterMoves->update(pos.piece_on(prevSq), prevSq, bonus);
|
||||
|
||||
if ((ss-5)->counterMoves)
|
||||
(ss-5)->counterMoves->update(pos.piece_on(prevSq), prevSq, bonus);
|
||||
}
|
||||
|
||||
tte->save(posKey, value_to_tt(bestValue, ss->ply),
|
||||
|
@ -1163,7 +1194,6 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
const bool PvNode = NT == PV;
|
||||
|
||||
assert(NT == PV || NT == NonPV);
|
||||
assert(InCheck == !!pos.checkers());
|
||||
assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE);
|
||||
assert(PvNode || (alpha == beta - 1));
|
||||
|
@ -1262,7 +1292,7 @@ moves_loop: // When in check search starts from here
|
|||
// to search the moves. Because the depth is <= 0 here, only captures,
|
||||
// queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will
|
||||
// be generated.
|
||||
MovePicker mp(pos, ttMove, depth, pos.this_thread()->history, to_sq((ss-1)->currentMove));
|
||||
MovePicker mp(pos, ttMove, depth, to_sq((ss-1)->currentMove));
|
||||
CheckInfo ci(pos);
|
||||
|
||||
// Loop through the moves until no moves remain or a beta cutoff occurs
|
||||
|
@ -1325,7 +1355,7 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
|
||||
|
||||
// Check for new best move
|
||||
// Check for a new best move
|
||||
if (value > bestValue)
|
||||
{
|
||||
bestValue = value;
|
||||
|
@ -1401,8 +1431,8 @@ moves_loop: // When in check search starts from here
|
|||
}
|
||||
|
||||
|
||||
// update_stats() updates killers, history, countermove and countermove
|
||||
// history when a new quiet best move is found.
|
||||
// update_stats() updates killers, history, countermove and countermove plus
|
||||
// follow-up move history when a new quiet best move is found.
|
||||
|
||||
void update_stats(const Position& pos, Stack* ss, Move move,
|
||||
Depth depth, Move* quiets, int quietsCnt) {
|
||||
|
@ -1416,34 +1446,51 @@ moves_loop: // When in check search starts from here
|
|||
Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY) + depth / ONE_PLY - 1);
|
||||
|
||||
Square prevSq = to_sq((ss-1)->currentMove);
|
||||
CounterMovesStats& cmh = CounterMovesHistory[pos.piece_on(prevSq)][prevSq];
|
||||
CounterMoveStats* cmh = (ss-1)->counterMoves;
|
||||
CounterMoveStats* fmh = (ss-2)->counterMoves;
|
||||
CounterMoveStats* fmh2 = (ss-4)->counterMoves;
|
||||
Thread* thisThread = pos.this_thread();
|
||||
|
||||
thisThread->history.update(pos.moved_piece(move), to_sq(move), bonus);
|
||||
|
||||
if (is_ok((ss-1)->currentMove))
|
||||
if (cmh)
|
||||
{
|
||||
thisThread->counterMoves.update(pos.piece_on(prevSq), prevSq, move);
|
||||
cmh.update(pos.moved_piece(move), to_sq(move), bonus);
|
||||
cmh->update(pos.moved_piece(move), to_sq(move), bonus);
|
||||
}
|
||||
|
||||
if (fmh)
|
||||
fmh->update(pos.moved_piece(move), to_sq(move), bonus);
|
||||
|
||||
if (fmh2)
|
||||
fmh2->update(pos.moved_piece(move), to_sq(move), bonus);
|
||||
|
||||
// Decrease all the other played quiet moves
|
||||
for (int i = 0; i < quietsCnt; ++i)
|
||||
{
|
||||
thisThread->history.update(pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus);
|
||||
|
||||
if (is_ok((ss-1)->currentMove))
|
||||
cmh.update(pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus);
|
||||
if (cmh)
|
||||
cmh->update(pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus);
|
||||
|
||||
if (fmh)
|
||||
fmh->update(pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus);
|
||||
|
||||
if (fmh2)
|
||||
fmh2->update(pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus);
|
||||
}
|
||||
|
||||
// Extra penalty for a quiet TT move in previous ply when it gets refuted
|
||||
if ( (ss-1)->moveCount == 1
|
||||
&& !pos.captured_piece_type()
|
||||
&& is_ok((ss-2)->currentMove))
|
||||
if ((ss-1)->moveCount == 1 && !pos.captured_piece_type())
|
||||
{
|
||||
Square prevPrevSq = to_sq((ss-2)->currentMove);
|
||||
CounterMovesStats& prevCmh = CounterMovesHistory[pos.piece_on(prevPrevSq)][prevPrevSq];
|
||||
prevCmh.update(pos.piece_on(prevSq), prevSq, -bonus - 2 * (depth + 1) / ONE_PLY);
|
||||
if ((ss-2)->counterMoves)
|
||||
(ss-2)->counterMoves->update(pos.piece_on(prevSq), prevSq, -bonus - 2 * (depth + 1) / ONE_PLY);
|
||||
|
||||
if ((ss-3)->counterMoves)
|
||||
(ss-3)->counterMoves->update(pos.piece_on(prevSq), prevSq, -bonus - 2 * (depth + 1) / ONE_PLY);
|
||||
|
||||
if ((ss-5)->counterMoves)
|
||||
(ss-5)->counterMoves->update(pos.piece_on(prevSq), prevSq, -bonus - 2 * (depth + 1) / ONE_PLY);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1453,7 +1500,7 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
Move Skill::pick_best(size_t multiPV) {
|
||||
|
||||
const Search::RootMoveVector& rootMoves = Threads.main()->rootMoves;
|
||||
const RootMoves& rootMoves = Threads.main()->rootMoves;
|
||||
static PRNG rng(now()); // PRNG sequence should be non-deterministic
|
||||
|
||||
// RootMoves are already sorted by score in descending order
|
||||
|
@ -1463,8 +1510,8 @@ moves_loop: // When in check search starts from here
|
|||
int maxScore = -VALUE_INFINITE;
|
||||
|
||||
// Choose best move. For each move score we add two terms, both dependent on
|
||||
// weakness. One deterministic and bigger for weaker levels, and one random,
|
||||
// then we choose the move with the resulting highest score.
|
||||
// weakness. One is deterministic and bigger for weaker levels, and one is
|
||||
// random. Then we choose the move with the resulting highest score.
|
||||
for (size_t i = 0; i < multiPV; ++i)
|
||||
{
|
||||
// This is our magic formula
|
||||
|
@ -1518,7 +1565,7 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) {
|
|||
|
||||
std::stringstream ss;
|
||||
int elapsed = Time.elapsed() + 1;
|
||||
const Search::RootMoveVector& rootMoves = pos.this_thread()->rootMoves;
|
||||
const RootMoves& rootMoves = pos.this_thread()->rootMoves;
|
||||
size_t PVIdx = pos.this_thread()->PVIdx;
|
||||
size_t multiPV = std::min((size_t)Options["MultiPV"], rootMoves.size());
|
||||
uint64_t nodes_searched = Threads.nodes_searched();
|
||||
|
@ -1594,7 +1641,7 @@ void RootMove::insert_pv_in_tt(Position& pos) {
|
|||
|
||||
|
||||
/// RootMove::extract_ponder_from_tt() is called in case we have no ponder move
|
||||
/// before exiting the search, for instance in case we stop the search during a
|
||||
/// before exiting the search, for instance, in case we stop the search during a
|
||||
/// fail high at root. We try hard to have a ponder move to return to the GUI,
|
||||
/// otherwise in case of 'ponder on' we have nothing to think on.
|
||||
|
||||
|
|
|
@ -22,14 +22,15 @@
|
|||
#define SEARCH_H_INCLUDED
|
||||
|
||||
#include <atomic>
|
||||
#include <memory> // For std::unique_ptr
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
|
||||
#include "misc.h"
|
||||
#include "position.h"
|
||||
#include "types.h"
|
||||
|
||||
template<typename T, bool CM> struct Stats;
|
||||
typedef Stats<Value, true> CounterMoveStats;
|
||||
|
||||
namespace Search {
|
||||
|
||||
/// Stack struct keeps track of the information we need to remember from nodes
|
||||
|
@ -45,6 +46,7 @@ struct Stack {
|
|||
Value staticEval;
|
||||
bool skipEarlyPruning;
|
||||
int moveCount;
|
||||
CounterMoveStats* counterMoves;
|
||||
};
|
||||
|
||||
/// RootMove struct is used for moves at the root of the tree. For each root move
|
||||
|
@ -65,7 +67,7 @@ struct RootMove {
|
|||
std::vector<Move> pv;
|
||||
};
|
||||
|
||||
typedef std::vector<RootMove> RootMoveVector;
|
||||
typedef std::vector<RootMove> RootMoves;
|
||||
|
||||
/// 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
|
||||
|
@ -74,8 +76,8 @@ typedef std::vector<RootMove> RootMoveVector;
|
|||
struct LimitsType {
|
||||
|
||||
LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC
|
||||
nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movestogo =
|
||||
depth = movetime = mate = infinite = ponder = 0;
|
||||
nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] =
|
||||
npmsec = movestogo = depth = movetime = mate = infinite = ponder = 0;
|
||||
}
|
||||
|
||||
bool use_time_management() const {
|
||||
|
@ -95,11 +97,8 @@ struct SignalsType {
|
|||
std::atomic_bool stop, stopOnPonderhit;
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<std::stack<StateInfo>> StateStackPtr;
|
||||
|
||||
extern SignalsType Signals;
|
||||
extern LimitsType Limits;
|
||||
extern StateStackPtr SetupStates;
|
||||
|
||||
void init();
|
||||
void clear();
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
This file may be redistributed and/or modified without restrictions.
|
||||
|
||||
tbcore.c contains engine-independent routines of the tablebase probing code.
|
||||
This file should not need to much adaptation to add tablebase probing to
|
||||
This file should not need too much adaptation to add tablebase probing to
|
||||
a particular engine, provided the engine is written in C or C++.
|
||||
*/
|
||||
|
||||
|
@ -343,30 +343,31 @@ void Tablebases::init(const std::string& path)
|
|||
init_tb(str);
|
||||
}
|
||||
|
||||
|
||||
if (sizeof(char*) >= 8) {
|
||||
for (i = 1; i < 6; i++)
|
||||
for (j = i; j < 6; j++)
|
||||
for (k = i; k < 6; k++)
|
||||
for (l = (i == k) ? j : k; l < 6; l++) {
|
||||
sprintf(str, "K%c%cvK%c%c", pchr[i], pchr[j], pchr[k], pchr[l]);
|
||||
init_tb(str);
|
||||
}
|
||||
|
||||
for (i = 1; i < 6; i++)
|
||||
for (j = i; j < 6; j++)
|
||||
for (k = j; k < 6; k++)
|
||||
for (l = 1; l < 6; l++) {
|
||||
sprintf(str, "K%c%c%cvK%c", pchr[i], pchr[j], pchr[k], pchr[l]);
|
||||
init_tb(str);
|
||||
}
|
||||
|
||||
for (i = 1; i < 6; i++)
|
||||
for (j = i; j < 6; j++)
|
||||
for (k = j; k < 6; k++)
|
||||
for (l = k; l < 6; l++) {
|
||||
sprintf(str, "K%c%c%c%cvK", pchr[i], pchr[j], pchr[k], pchr[l]);
|
||||
init_tb(str);
|
||||
}
|
||||
for (i = 1; i < 6; i++)
|
||||
for (j = i; j < 6; j++)
|
||||
for (k = i; k < 6; k++)
|
||||
for (l = (i == k) ? j : k; l < 6; l++) {
|
||||
sprintf(str, "K%c%cvK%c%c", pchr[i], pchr[j], pchr[k], pchr[l]);
|
||||
init_tb(str);
|
||||
}
|
||||
|
||||
for (i = 1; i < 6; i++)
|
||||
for (j = i; j < 6; j++)
|
||||
for (k = j; k < 6; k++)
|
||||
for (l = 1; l < 6; l++) {
|
||||
sprintf(str, "K%c%c%cvK%c", pchr[i], pchr[j], pchr[k], pchr[l]);
|
||||
init_tb(str);
|
||||
}
|
||||
|
||||
for (i = 1; i < 6; i++)
|
||||
for (j = i; j < 6; j++)
|
||||
for (k = j; k < 6; k++)
|
||||
for (l = k; l < 6; l++) {
|
||||
sprintf(str, "K%c%c%c%cvK", pchr[i], pchr[j], pchr[k], pchr[l]);
|
||||
init_tb(str);
|
||||
}
|
||||
}
|
||||
|
||||
printf("info string Found %d tablebases.\n", TBnum_piece + TBnum_pawn);
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include "../movegen.h"
|
||||
#include "../bitboard.h"
|
||||
#include "../search.h"
|
||||
#include "../bitcount.h"
|
||||
|
||||
#include "tbprobe.h"
|
||||
#include "tbcore.h"
|
||||
|
@ -39,12 +38,12 @@ static void prt_str(Position& pos, char *str, int mirror)
|
|||
|
||||
color = !mirror ? WHITE : BLACK;
|
||||
for (pt = KING; pt >= PAWN; --pt)
|
||||
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
|
||||
for (i = popcount(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--)
|
||||
for (i = popcount(pos.pieces(color, pt)); i > 0; i--)
|
||||
*str++ = pchr[6 - pt];
|
||||
*str++ = 0;
|
||||
}
|
||||
|
@ -60,11 +59,11 @@ static uint64 calc_key(Position& pos, int mirror)
|
|||
|
||||
color = !mirror ? WHITE : BLACK;
|
||||
for (pt = PAWN; pt <= KING; ++pt)
|
||||
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
|
||||
for (i = popcount(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--)
|
||||
for (i = popcount(pos.pieces(color, pt)); i > 0; i--)
|
||||
key ^= Zobrist::psq[BLACK][pt][i - 1];
|
||||
|
||||
return key;
|
||||
|
@ -689,7 +688,7 @@ static Value wdl_to_Value[5] = {
|
|||
//
|
||||
// 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)
|
||||
bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves, Value& score)
|
||||
{
|
||||
int success;
|
||||
|
||||
|
@ -796,7 +795,7 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoveVector& rootMoves, Va
|
|||
//
|
||||
// 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)
|
||||
bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Value& score)
|
||||
{
|
||||
int success;
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ 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);
|
||||
bool root_probe(Position& pos, Search::RootMoves& rootMoves, Value& score);
|
||||
bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Value& score);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -26,11 +26,9 @@
|
|||
#include "thread.h"
|
||||
#include "uci.h"
|
||||
|
||||
using namespace Search;
|
||||
|
||||
ThreadPool Threads; // Global object
|
||||
|
||||
/// Thread constructor launch the thread and then wait until it goes to sleep
|
||||
/// Thread constructor launches the thread and then waits until it goes to sleep
|
||||
/// in idle_loop().
|
||||
|
||||
Thread::Thread() {
|
||||
|
@ -48,7 +46,7 @@ Thread::Thread() {
|
|||
}
|
||||
|
||||
|
||||
/// Thread destructor wait for thread termination before returning
|
||||
/// Thread destructor waits for thread termination before returning
|
||||
|
||||
Thread::~Thread() {
|
||||
|
||||
|
@ -60,7 +58,8 @@ Thread::~Thread() {
|
|||
}
|
||||
|
||||
|
||||
/// Thread::wait_for_search_finished() wait on sleep condition until not searching
|
||||
/// Thread::wait_for_search_finished() waits on sleep condition
|
||||
/// until not searching
|
||||
|
||||
void Thread::wait_for_search_finished() {
|
||||
|
||||
|
@ -69,7 +68,7 @@ void Thread::wait_for_search_finished() {
|
|||
}
|
||||
|
||||
|
||||
/// Thread::wait() wait on sleep condition until condition is true
|
||||
/// Thread::wait() waits on sleep condition until condition is true
|
||||
|
||||
void Thread::wait(std::atomic_bool& condition) {
|
||||
|
||||
|
@ -78,7 +77,7 @@ void Thread::wait(std::atomic_bool& condition) {
|
|||
}
|
||||
|
||||
|
||||
/// Thread::start_searching() wake up the thread that will start the search
|
||||
/// Thread::start_searching() wakes up the thread that will start the search
|
||||
|
||||
void Thread::start_searching(bool resume) {
|
||||
|
||||
|
@ -115,7 +114,7 @@ void Thread::idle_loop() {
|
|||
}
|
||||
|
||||
|
||||
/// ThreadPool::init() create and launch requested threads, that will go
|
||||
/// ThreadPool::init() creates and launches requested threads that will go
|
||||
/// immediately to sleep. We cannot use a constructor because Threads is a
|
||||
/// static object and we need a fully initialized engine at this point due to
|
||||
/// allocation of Endgames in the Thread constructor.
|
||||
|
@ -127,9 +126,9 @@ void ThreadPool::init() {
|
|||
}
|
||||
|
||||
|
||||
/// ThreadPool::exit() terminate threads before the program exits. Cannot be
|
||||
/// ThreadPool::exit() terminates threads before the program exits. Cannot be
|
||||
/// done in destructor because threads must be terminated before deleting any
|
||||
/// static objects, so while still in main().
|
||||
/// static objects while still in main().
|
||||
|
||||
void ThreadPool::exit() {
|
||||
|
||||
|
@ -156,7 +155,7 @@ void ThreadPool::read_uci_options() {
|
|||
}
|
||||
|
||||
|
||||
/// ThreadPool::nodes_searched() return the number of nodes searched
|
||||
/// ThreadPool::nodes_searched() returns the number of nodes searched
|
||||
|
||||
int64_t ThreadPool::nodes_searched() {
|
||||
|
||||
|
@ -167,29 +166,41 @@ int64_t ThreadPool::nodes_searched() {
|
|||
}
|
||||
|
||||
|
||||
/// ThreadPool::start_thinking() wake up the main thread sleeping in idle_loop()
|
||||
/// and start a new search, then return immediately.
|
||||
/// ThreadPool::start_thinking() wakes up the main thread sleeping in idle_loop()
|
||||
/// and starts a new search, then returns immediately.
|
||||
|
||||
void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
|
||||
StateStackPtr& states) {
|
||||
void ThreadPool::start_thinking(const Position& pos, StateListPtr& states,
|
||||
const Search::LimitsType& limits) {
|
||||
|
||||
main()->wait_for_search_finished();
|
||||
|
||||
Signals.stopOnPonderhit = Signals.stop = false;
|
||||
|
||||
main()->rootMoves.clear();
|
||||
main()->rootPos = pos;
|
||||
Limits = limits;
|
||||
if (states.get()) // If we don't set a new position, preserve current state
|
||||
{
|
||||
SetupStates = std::move(states); // Ownership transfer here
|
||||
assert(!states.get());
|
||||
}
|
||||
Search::Signals.stopOnPonderhit = Search::Signals.stop = false;
|
||||
Search::Limits = limits;
|
||||
Search::RootMoves rootMoves;
|
||||
|
||||
for (const auto& m : MoveList<LEGAL>(pos))
|
||||
if ( limits.searchmoves.empty()
|
||||
|| std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m))
|
||||
main()->rootMoves.push_back(RootMove(m));
|
||||
rootMoves.push_back(Search::RootMove(m));
|
||||
|
||||
// After ownership transfer 'states' becomes empty, so if we stop the search
|
||||
// and call 'go' again without setting a new position states.get() == NULL.
|
||||
assert(states.get() || setupStates.get());
|
||||
|
||||
if (states.get())
|
||||
setupStates = std::move(states); // Ownership transfer, states is now empty
|
||||
|
||||
StateInfo tmp = setupStates->back();
|
||||
|
||||
for (Thread* th : Threads)
|
||||
{
|
||||
th->maxPly = 0;
|
||||
th->rootDepth = DEPTH_ZERO;
|
||||
th->rootMoves = rootMoves;
|
||||
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
|
||||
}
|
||||
|
||||
setupStates->back() = tmp; // Restore st->previous, cleared by Position::set()
|
||||
|
||||
main()->start_searching();
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
#include "thread_win32.h"
|
||||
|
||||
|
||||
/// Thread struct keeps together all the thread related stuff. We also use
|
||||
/// Thread struct keeps together all the thread-related stuff. We also use
|
||||
/// per-thread pawn and material hash tables so that once we get a pointer to an
|
||||
/// entry its life time is unlimited and we don't have to care about someone
|
||||
/// changing the entry under our feet.
|
||||
|
@ -64,10 +64,10 @@ public:
|
|||
int maxPly, callsCnt;
|
||||
|
||||
Position rootPos;
|
||||
Search::RootMoveVector rootMoves;
|
||||
Search::RootMoves rootMoves;
|
||||
Depth rootDepth;
|
||||
HistoryStats history;
|
||||
MovesStats counterMoves;
|
||||
MoveStats counterMoves;
|
||||
Depth completedDepth;
|
||||
std::atomic_bool resetCalls;
|
||||
};
|
||||
|
@ -80,10 +80,11 @@ struct MainThread : public Thread {
|
|||
|
||||
bool easyMovePlayed, failedLow;
|
||||
double bestMoveChanges;
|
||||
Value previousScore;
|
||||
};
|
||||
|
||||
|
||||
/// ThreadPool struct handles all the threads related stuff like init, starting,
|
||||
/// ThreadPool struct handles all the threads-related stuff like init, starting,
|
||||
/// parking and, most importantly, launching a thread. All the access to threads
|
||||
/// data is done through this class.
|
||||
|
||||
|
@ -93,9 +94,12 @@ struct ThreadPool : public std::vector<Thread*> {
|
|||
void exit(); // be initialized and valid during the whole thread lifetime.
|
||||
|
||||
MainThread* main() { return static_cast<MainThread*>(at(0)); }
|
||||
void start_thinking(const Position&, const Search::LimitsType&, Search::StateStackPtr&);
|
||||
void start_thinking(const Position&, StateListPtr&, const Search::LimitsType&);
|
||||
void read_uci_options();
|
||||
int64_t nodes_searched();
|
||||
|
||||
private:
|
||||
StateListPtr setupStates;
|
||||
};
|
||||
|
||||
extern ThreadPool Threads;
|
||||
|
|
|
@ -25,11 +25,11 @@
|
|||
/// relies on libwinpthread. Currently libwinpthread implements mutexes directly
|
||||
/// on top of Windows semaphores. Semaphores, being kernel objects, require kernel
|
||||
/// mode transition in order to lock or unlock, which is very slow compared to
|
||||
/// interlocked operations (about 30% slower on bench test). To workaround this
|
||||
/// interlocked operations (about 30% slower on bench test). To work around this
|
||||
/// issue, we define our wrappers to the low level Win32 calls. We use critical
|
||||
/// sections to support Windows XP and older versions. Unfortunately, cond_wait()
|
||||
/// is racy between unlock() and WaitForSingleObject() but they have the same
|
||||
/// speed performance of SRW locks.
|
||||
/// speed performance as the SRW locks.
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
|
|
@ -33,20 +33,20 @@ namespace {
|
|||
enum TimeType { OptimumTime, MaxTime };
|
||||
|
||||
const int MoveHorizon = 50; // Plan time management at most this many moves ahead
|
||||
const double MaxRatio = 6.93; // When in trouble, we can step over reserved time with this ratio
|
||||
const double StealRatio = 0.36; // However we must not steal time from remaining moves over this ratio
|
||||
const double MaxRatio = 7.09; // When in trouble, we can step over reserved time with this ratio
|
||||
const double StealRatio = 0.35; // However we must not steal time from remaining moves over this ratio
|
||||
|
||||
|
||||
// move_importance() is a skew-logistic function based on naive statistical
|
||||
// analysis of "how many games are still undecided after n half-moves". Game
|
||||
// is considered "undecided" as long as neither side has >275cp advantage.
|
||||
// Data was extracted from CCRL game database with some simple filtering criteria.
|
||||
// Data was extracted from the CCRL game database with some simple filtering criteria.
|
||||
|
||||
double move_importance(int ply) {
|
||||
|
||||
const double XScale = 8.27;
|
||||
const double XShift = 59.;
|
||||
const double Skew = 0.179;
|
||||
const double XScale = 7.64;
|
||||
const double XShift = 58.4;
|
||||
const double Skew = 0.183;
|
||||
|
||||
return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ namespace {
|
|||
double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance);
|
||||
double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance);
|
||||
|
||||
return int(myTime * std::min(ratio1, ratio2)); // Intel C++ asks an explicit cast
|
||||
return int(myTime * std::min(ratio1, ratio2)); // Intel C++ asks for an explicit cast
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -91,7 +91,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
|
|||
// If we have to play in 'nodes as time' mode, then convert from time
|
||||
// to nodes, and use resulting values in time management formulas.
|
||||
// WARNING: Given npms (nodes per millisecond) must be much lower then
|
||||
// real engine speed to avoid time losses.
|
||||
// the real engine speed to avoid time losses.
|
||||
if (npmsec)
|
||||
{
|
||||
if (!availableNodes) // Only once at game start
|
||||
|
@ -104,7 +104,6 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
|
|||
}
|
||||
|
||||
startTime = limits.startTime;
|
||||
unstablePvFactor = 1;
|
||||
optimumTime = maximumTime = std::max(limits.time[us], minThinkingTime);
|
||||
|
||||
const int MaxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon;
|
||||
|
|
|
@ -31,8 +31,7 @@
|
|||
class TimeManagement {
|
||||
public:
|
||||
void init(Search::LimitsType& limits, Color us, int ply);
|
||||
void pv_instability(double bestMoveChanges) { unstablePvFactor = 1 + bestMoveChanges; }
|
||||
int available() const { return int(optimumTime * unstablePvFactor * 1.016); }
|
||||
int optimum() const { return optimumTime; }
|
||||
int maximum() const { return maximumTime; }
|
||||
int elapsed() const { return int(Search::Limits.npmsec ? Threads.nodes_searched() : now() - startTime); }
|
||||
|
||||
|
@ -42,7 +41,6 @@ private:
|
|||
TimePoint startTime;
|
||||
int optimumTime;
|
||||
int maximumTime;
|
||||
double unstablePvFactor;
|
||||
};
|
||||
|
||||
extern TimeManagement Time;
|
||||
|
|
|
@ -50,7 +50,7 @@ struct TTEntry {
|
|||
|
||||
// Don't overwrite more valuable entries
|
||||
if ( (k >> 48) != key16
|
||||
|| d > depth8 - 2
|
||||
|| d > depth8 - 4
|
||||
/* || g != (genBound8 & 0xFC) // Matching non-zero keys are already refreshed by probe() */
|
||||
|| b == BOUND_EXACT)
|
||||
{
|
||||
|
|
|
@ -60,13 +60,12 @@
|
|||
/// _WIN64 Building on Windows 64 bit
|
||||
|
||||
#if defined(_WIN64) && defined(_MSC_VER) // No Makefile used
|
||||
# include <intrin.h> // MSVC popcnt and bsfq instrinsics
|
||||
# include <intrin.h> // Microsoft header for _BitScanForward64()
|
||||
# define IS_64BIT
|
||||
# define USE_BSFQ
|
||||
#endif
|
||||
|
||||
#if defined(USE_POPCNT) && defined(__INTEL_COMPILER) && defined(_MSC_VER)
|
||||
# include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic
|
||||
#if defined(USE_POPCNT) && (defined(__INTEL_COMPILER) || defined(_MSC_VER))
|
||||
# include <nmmintrin.h> // Intel and Microsoft header for _mm_popcnt_u64()
|
||||
#endif
|
||||
|
||||
#if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER))
|
||||
|
@ -185,10 +184,10 @@ enum Value : int {
|
|||
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY,
|
||||
|
||||
PawnValueMg = 198, PawnValueEg = 258,
|
||||
KnightValueMg = 817, KnightValueEg = 846,
|
||||
BishopValueMg = 836, BishopValueEg = 857,
|
||||
RookValueMg = 1270, RookValueEg = 1281,
|
||||
QueenValueMg = 2521, QueenValueEg = 2558,
|
||||
KnightValueMg = 817, KnightValueEg = 896,
|
||||
BishopValueMg = 836, BishopValueEg = 907,
|
||||
RookValueMg = 1270, RookValueEg = 1356,
|
||||
QueenValueMg = 2521, QueenValueEg = 2658,
|
||||
|
||||
MidgameLimit = 15581, EndgameLimit = 3998
|
||||
};
|
||||
|
@ -355,7 +354,7 @@ inline Piece make_piece(Color c, PieceType pt) {
|
|||
return Piece((c << 3) | pt);
|
||||
}
|
||||
|
||||
inline PieceType type_of(Piece pc) {
|
||||
inline PieceType type_of(Piece pc) {
|
||||
return PieceType(pc & 7);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,10 +39,10 @@ namespace {
|
|||
// FEN string of the initial position, normal chess
|
||||
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
|
||||
// Stack to keep track of the position states along the setup moves (from the
|
||||
// A list to keep track of the position states along the setup moves (from the
|
||||
// start position to the position just before the search starts). Needed by
|
||||
// 'draw by repetition' detection.
|
||||
Search::StateStackPtr SetupStates;
|
||||
StateListPtr States(new std::deque<StateInfo>(1));
|
||||
|
||||
|
||||
// position() is called when engine receives the "position" UCI command.
|
||||
|
@ -68,14 +68,14 @@ namespace {
|
|||
else
|
||||
return;
|
||||
|
||||
pos.set(fen, Options["UCI_Chess960"], Threads.main());
|
||||
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>);
|
||||
States = StateListPtr(new std::deque<StateInfo>(1));
|
||||
pos.set(fen, Options["UCI_Chess960"], &States->back(), Threads.main());
|
||||
|
||||
// Parse move list (if any)
|
||||
while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE)
|
||||
{
|
||||
SetupStates->push(StateInfo());
|
||||
pos.do_move(m, SetupStates->top(), pos.gives_check(m, CheckInfo(pos)));
|
||||
States->push_back(StateInfo());
|
||||
pos.do_move(m, States->back(), pos.gives_check(m, CheckInfo(pos)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ namespace {
|
|||
else if (token == "infinite") limits.infinite = 1;
|
||||
else if (token == "ponder") limits.ponder = 1;
|
||||
|
||||
Threads.start_thinking(pos, limits, SetupStates);
|
||||
Threads.start_thinking(pos, States, limits);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -146,9 +146,11 @@ namespace {
|
|||
|
||||
void UCI::loop(int argc, char* argv[]) {
|
||||
|
||||
Position pos(StartFEN, false, Threads.main()); // The root position
|
||||
Position pos;
|
||||
string token, cmd;
|
||||
|
||||
pos.set(StartFEN, false, &States->back(), Threads.main());
|
||||
|
||||
for (int i = 1; i < argc; ++i)
|
||||
cmd += std::string(argv[i]) + " ";
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ void init(OptionsMap& o) {
|
|||
o["Skill Level"] << Option(20, 0, 20);
|
||||
o["Move Overhead"] << Option(30, 0, 5000);
|
||||
o["Minimum Thinking Time"] << Option(20, 0, 5000);
|
||||
o["Slow Mover"] << Option(84, 10, 1000);
|
||||
o["Slow Mover"] << Option(89, 10, 1000);
|
||||
o["nodestime"] << Option(0, 0, 10000);
|
||||
o["UCI_Chess960"] << Option(false);
|
||||
o["SyzygyPath"] << Option("<empty>", on_tb_path);
|
||||
|
|
Loading…
Reference in New Issue
Block a user