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

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

View File

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