DroidFish: Updated Stockfish engine to version 5.

This commit is contained in:
Peter Osterlund
2014-05-31 12:23:03 +00:00
parent 93d521c75e
commit 5d723926b0
41 changed files with 2187 additions and 2592 deletions

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -23,6 +23,7 @@
#include <vector> #include <vector>
#include "misc.h" #include "misc.h"
#include "notation.h"
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "thread.h" #include "thread.h"
@@ -66,10 +67,10 @@ static const char* Defaults[] = {
/// benchmark() runs a simple benchmark by letting Stockfish analyze a set /// benchmark() runs a simple benchmark by letting Stockfish analyze a set
/// of positions for a given limit each. There are five parameters; the /// of positions for a given limit each. There are five parameters: the
/// transposition table size, the number of search threads that should /// transposition table size, the number of search threads that should
/// be used, the limit value spent for each position (optional, default is /// be used, the limit value spent for each position (optional, default is
/// depth 12), an optional file name where to look for positions in fen /// depth 13), an optional file name where to look for positions in FEN
/// format (defaults are the positions defined above) and the type of the /// format (defaults are the positions defined above) and the type of the
/// limit value: depth (default), time in secs or number of nodes. /// limit value: depth (default), time in secs or number of nodes.
@@ -126,7 +127,7 @@ void benchmark(const Position& current, istream& is) {
file.close(); file.close();
} }
int64_t nodes = 0; uint64_t nodes = 0;
Search::StateStackPtr st; Search::StateStackPtr st;
Time::point elapsed = Time::now(); Time::point elapsed = Time::now();
@@ -136,21 +137,33 @@ void benchmark(const Position& current, istream& is) {
cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl; cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl;
if (limitType == "perft") if (limitType == "divide")
for (MoveList<LEGAL> it(pos); *it; ++it)
{ {
size_t cnt = Search::perft(pos, limits.depth * ONE_PLY); StateInfo si;
pos.do_move(*it, si);
uint64_t cnt = limits.depth > 1 ? Search::perft(pos, (limits.depth - 1) * ONE_PLY) : 1;
pos.undo_move(*it);
cerr << move_to_uci(*it, pos.is_chess960()) << ": " << cnt << endl;
nodes += cnt;
}
else if (limitType == "perft")
{
uint64_t cnt = Search::perft(pos, limits.depth * ONE_PLY);
cerr << "\nPerft " << limits.depth << " leaf nodes: " << cnt << endl; cerr << "\nPerft " << limits.depth << " leaf nodes: " << cnt << endl;
nodes += cnt; nodes += cnt;
} }
else else
{ {
Threads.start_thinking(pos, limits, vector<Move>(), st); Threads.start_thinking(pos, limits, st);
Threads.wait_for_think_finished(); Threads.wait_for_think_finished();
nodes += Search::RootPos.nodes_searched(); nodes += Search::RootPos.nodes_searched();
} }
} }
elapsed = Time::now() - elapsed + 1; // Assure positive to avoid a 'divide by zero' elapsed = Time::now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
dbg_print(); // Just before to exit
cerr << "\n===========================" cerr << "\n==========================="
<< "\nTotal time (ms) : " << elapsed << "\nTotal time (ms) : " << elapsed

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -25,15 +25,15 @@
namespace { namespace {
// The possible pawns squares are 24, the first 4 files and ranks from 2 to 7 // There are 24 possible pawn squares: the first 4 files and ranks from 2 to 7
const unsigned IndexMax = 2*24*64*64; // stm * psq * wksq * bksq = 196608 const unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608
// Each uint32_t stores results of 32 positions, one per bit // Each uint32_t stores results of 32 positions, one per bit
uint32_t KPKBitbase[IndexMax / 32]; uint32_t KPKBitbase[MAX_INDEX / 32];
// A KPK bitbase index is an integer in [0, IndexMax] range // A KPK bitbase index is an integer in [0, IndexMax] range
// //
// Information is mapped in a way that minimizes number of iterations: // Information is mapped in a way that minimizes the number of iterations:
// //
// bit 0- 5: white king square (from SQ_A1 to SQ_H8) // bit 0- 5: white king square (from SQ_A1 to SQ_H8)
// bit 6-11: black king square (from SQ_A1 to SQ_H8) // bit 6-11: black king square (from SQ_A1 to SQ_H8)
@@ -84,20 +84,20 @@ void Bitbases::init_kpk() {
unsigned idx, repeat = 1; unsigned idx, repeat = 1;
std::vector<KPKPosition> db; std::vector<KPKPosition> db;
db.reserve(IndexMax); db.reserve(MAX_INDEX);
// Initialize db with known win / draw positions // Initialize db with known win / draw positions
for (idx = 0; idx < IndexMax; ++idx) for (idx = 0; idx < MAX_INDEX; ++idx)
db.push_back(KPKPosition(idx)); db.push_back(KPKPosition(idx));
// Iterate through the positions until no more of the unknown positions can be // Iterate through the positions until none of the unknown positions can be
// changed to either wins or draws (15 cycles needed). // changed to either wins or draws (15 cycles needed).
while (repeat) while (repeat)
for (repeat = idx = 0; idx < IndexMax; ++idx) for (repeat = idx = 0; idx < MAX_INDEX; ++idx)
repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN); repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN);
// Map 32 results into one KPKBitbase[] entry // Map 32 results into one KPKBitbase[] entry
for (idx = 0; idx < IndexMax; ++idx) for (idx = 0; idx < MAX_INDEX; ++idx)
if (db[idx] == WIN) if (db[idx] == WIN)
KPKBitbase[idx / 32] |= 1 << (idx & 0x1F); KPKBitbase[idx / 32] |= 1 << (idx & 0x1F);
} }
@@ -110,24 +110,26 @@ namespace {
wksq = Square((idx >> 0) & 0x3F); wksq = Square((idx >> 0) & 0x3F);
bksq = Square((idx >> 6) & 0x3F); bksq = Square((idx >> 6) & 0x3F);
us = Color ((idx >> 12) & 0x01); us = Color ((idx >> 12) & 0x01);
psq = File ((idx >> 13) & 0x03) | Rank(RANK_7 - (idx >> 15)); psq = make_square(File((idx >> 13) & 0x03), Rank(RANK_7 - (idx >> 15)));
result = UNKNOWN; result = UNKNOWN;
// Check if two pieces are on the same square or if a king can be captured // Check if two pieces are on the same square or if a king can be captured
if ( square_distance(wksq, bksq) <= 1 || wksq == psq || bksq == psq if ( square_distance(wksq, bksq) <= 1
|| wksq == psq
|| bksq == psq
|| (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq))) || (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq)))
result = INVALID; result = INVALID;
else if (us == WHITE) else if (us == WHITE)
{ {
// Immediate win if pawn can be promoted without getting captured // Immediate win if a pawn can be promoted without getting captured
if ( rank_of(psq) == RANK_7 if ( rank_of(psq) == RANK_7
&& wksq != psq + DELTA_N && wksq != psq + DELTA_N
&& ( square_distance(bksq, psq + DELTA_N) > 1 && ( square_distance(bksq, psq + DELTA_N) > 1
||(StepAttacksBB[KING][wksq] & (psq + DELTA_N)))) ||(StepAttacksBB[KING][wksq] & (psq + DELTA_N))))
result = WIN; result = WIN;
} }
// Immediate draw if is stalemate or king captures undefended pawn // Immediate draw if it is a stalemate or a king captures undefended pawn
else if ( !(StepAttacksBB[KING][bksq] & ~(StepAttacksBB[KING][wksq] | StepAttacksBB[PAWN][psq])) else if ( !(StepAttacksBB[KING][bksq] & ~(StepAttacksBB[KING][wksq] | StepAttacksBB[PAWN][psq]))
|| (StepAttacksBB[KING][bksq] & psq & ~StepAttacksBB[KING][wksq])) || (StepAttacksBB[KING][bksq] & psq & ~StepAttacksBB[KING][wksq]))
result = DRAW; result = DRAW;
@@ -138,13 +140,13 @@ namespace {
// White to Move: If one move leads to a position classified as WIN, the result // White to Move: If one move leads to a position classified as WIN, the result
// of the current position is WIN. If all moves lead to positions classified // of the current position is WIN. If all moves lead to positions classified
// as DRAW, the current position is classified DRAW otherwise the current // as DRAW, the current position is classified as DRAW, otherwise the current
// position is classified as UNKNOWN. // position is classified as UNKNOWN.
// //
// Black to Move: If one move leads to a position classified as DRAW, the result // Black to Move: If one move leads to a position classified as DRAW, the result
// of the current position is DRAW. If all moves lead to positions classified // of the current position is DRAW. If all moves lead to positions classified
// as WIN, the position is classified WIN otherwise the current position is // as WIN, the position is classified as WIN, otherwise the current position is
// classified UNKNOWN. // classified as UNKNOWN.
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,12 +18,10 @@
*/ */
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring> // For memset
#include <iostream>
#include "bitboard.h" #include "bitboard.h"
#include "bitcount.h" #include "bitcount.h"
#include "misc.h"
#include "rkiss.h" #include "rkiss.h"
CACHE_LINE_ALIGNMENT CACHE_LINE_ALIGNMENT
@@ -81,8 +79,8 @@ namespace {
} }
} }
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard. /// lsb()/msb() finds the least/most significant bit in a non-zero bitboard.
/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard. /// pop_lsb() finds and clears the least significant bit in a non-zero bitboard.
#ifndef USE_BSFQ #ifndef USE_BSFQ
@@ -120,55 +118,47 @@ Square msb(Bitboard b) {
result += 8; result += 8;
} }
return (Square)(result + MS1BTable[b32]); return Square(result + MS1BTable[b32]);
} }
#endif // ifndef USE_BSFQ #endif // ifndef USE_BSFQ
/// Bitboards::print() prints a bitboard in an easily readable format to the /// Bitboards::pretty() returns an ASCII representation of a bitboard to be
/// standard output. This is sometimes useful for debugging. /// printed to standard output. This is sometimes useful for debugging.
void Bitboards::print(Bitboard b) { const std::string Bitboards::pretty(Bitboard b) {
sync_cout; std::string s = "+---+---+---+---+---+---+---+---+\n";
for (Rank rank = RANK_8; rank >= RANK_1; --rank) for (Rank r = RANK_8; r >= RANK_1; --r)
{ {
std::cout << "+---+---+---+---+---+---+---+---+" << '\n'; for (File f = FILE_A; f <= FILE_H; ++f)
s.append(b & make_square(f, r) ? "| X " : "| ");
for (File file = FILE_A; file <= FILE_H; ++file) s.append("|\n+---+---+---+---+---+---+---+---+\n");
std::cout << "| " << (b & (file | rank) ? "X " : " ");
std::cout << "|\n";
} }
std::cout << "+---+---+---+---+---+---+---+---+" << sync_endl;
return s;
} }
/// Bitboards::init() initializes various bitboard arrays. It is called during /// Bitboards::init() initializes various bitboard tables. It is called at
/// program initialization. /// startup and relies on global objects to be already zero-initialized.
void Bitboards::init() { void Bitboards::init() {
for (int k = 0, i = 0; i < 8; ++i)
while (k < (2 << i))
MS1BTable[k++] = i;
for (int i = 0; i < 64; ++i)
BSFTable[bsf_index(1ULL << i)] = Square(i);
for (Square s = SQ_A1; s <= SQ_H8; ++s) for (Square s = SQ_A1; s <= SQ_H8; ++s)
SquareBB[s] = 1ULL << s; BSFTable[bsf_index(SquareBB[s] = 1ULL << s)] = s;
FileBB[FILE_A] = FileABB; for (Bitboard b = 1; b < 256; ++b)
RankBB[RANK_1] = Rank1BB; MS1BTable[b] = more_than_one(b) ? MS1BTable[b - 1] : lsb(b);
for (int i = 1; i < 8; ++i) for (File f = FILE_A; f <= FILE_H; ++f)
{ FileBB[f] = f > FILE_A ? FileBB[f - 1] << 1 : FileABB;
FileBB[i] = FileBB[i - 1] << 1;
RankBB[i] = RankBB[i - 1] << 8; for (Rank r = RANK_1; r <= RANK_8; ++r)
} RankBB[r] = r > RANK_1 ? RankBB[r - 1] << 8 : Rank1BB;
for (File f = FILE_A; f <= FILE_H; ++f) for (File f = FILE_A; f <= FILE_H; ++f)
AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0); AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
@@ -186,9 +176,9 @@ void Bitboards::init() {
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
if (s1 != s2)
{ {
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2)); SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
if (s1 != s2)
DistanceRingsBB[s1][SquareDistance[s1][s2] - 1] |= s2; DistanceRingsBB[s1][SquareDistance[s1][s2] - 1] |= s2;
} }
@@ -198,9 +188,9 @@ void Bitboards::init() {
for (Color c = WHITE; c <= BLACK; ++c) for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt = PAWN; pt <= KING; ++pt) for (PieceType pt = PAWN; pt <= KING; ++pt)
for (Square s = SQ_A1; s <= SQ_H8; ++s) for (Square s = SQ_A1; s <= SQ_H8; ++s)
for (int k = 0; steps[pt][k]; ++k) for (int i = 0; steps[pt][i]; ++i)
{ {
Square to = s + Square(c == WHITE ? steps[pt][k] : -steps[pt][k]); Square to = s + Square(c == WHITE ? steps[pt][i] : -steps[pt][i]);
if (is_ok(to) && square_distance(s, to) < 3) if (is_ok(to) && square_distance(s, to) < 3)
StepAttacksBB[make_piece(c, pt)][s] |= to; StepAttacksBB[make_piece(c, pt)][s] |= to;
@@ -212,23 +202,22 @@ void Bitboards::init() {
init_magics(RTable, RAttacks, RMagics, RMasks, RShifts, RDeltas, magic_index<ROOK>); init_magics(RTable, RAttacks, RMagics, RMasks, RShifts, RDeltas, magic_index<ROOK>);
init_magics(BTable, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index<BISHOP>); init_magics(BTable, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index<BISHOP>);
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
PseudoAttacks[QUEEN][s] = PseudoAttacks[BISHOP][s] = attacks_bb<BISHOP>(s, 0);
PseudoAttacks[QUEEN][s] |= PseudoAttacks[ ROOK][s] = attacks_bb< ROOK>(s, 0);
}
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
if (PseudoAttacks[QUEEN][s1] & s2)
{ {
Square delta = (s2 - s1) / square_distance(s1, s2); PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb<BISHOP>(s1, 0);
PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0);
for (Square s = s1 + delta; s != s2; s += delta) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
BetweenBB[s1][s2] |= s; {
Piece pc = (PseudoAttacks[BISHOP][s1] & s2) ? W_BISHOP :
(PseudoAttacks[ROOK][s1] & s2) ? W_ROOK : NO_PIECE;
PieceType pt = (PseudoAttacks[BISHOP][s1] & s2) ? BISHOP : ROOK; if (pc == NO_PIECE)
LineBB[s1][s2] = (PseudoAttacks[pt][s1] & PseudoAttacks[pt][s2]) | s1 | s2; continue;
LineBB[s1][s2] = (attacks_bb(pc, s1, 0) & attacks_bb(pc, s2, 0)) | s1 | s2;
BetweenBB[s1][s2] = attacks_bb(pc, s1, SquareBB[s2]) & attacks_bb(pc, s2, SquareBB[s1]);
}
} }
} }
@@ -254,20 +243,6 @@ namespace {
} }
Bitboard pick_random(RKISS& rk, int booster) {
// Values s1 and s2 are used to rotate the candidate magic of a
// quantity known to be the optimal to quickly find the magics.
int s1 = booster & 63, s2 = (booster >> 6) & 63;
Bitboard m = rk.rand<Bitboard>();
m = (m >> s1) | (m << (64 - s1));
m &= rk.rand<Bitboard>();
m = (m >> s2) | (m << (64 - s2));
return m & rk.rand<Bitboard>();
}
// init_magics() computes all rook and bishop attacks at startup. Magic // init_magics() computes all rook and bishop attacks at startup. Magic
// bitboards are used to look up attacks of sliding pieces. As a reference see // bitboards are used to look up attacks of sliding pieces. As a reference see
// chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we // chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
@@ -276,8 +251,9 @@ namespace {
void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[], void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[],
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) { Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) {
int MagicBoosters[][8] = { { 3191, 2184, 1310, 3618, 2091, 1308, 2452, 3996 }, int MagicBoosters[][8] = { { 969, 1976, 2850, 542, 2069, 2852, 1708, 164 },
{ 1059, 3608, 605, 3234, 3326, 38, 2029, 3043 } }; { 3101, 552, 3555, 926, 834, 26, 2131, 1117 } };
RKISS rk; RKISS rk;
Bitboard occupancy[4096], reference[4096], edges, b; Bitboard occupancy[4096], reference[4096], edges, b;
int i, size, booster; int i, size, booster;
@@ -303,7 +279,12 @@ namespace {
b = size = 0; b = size = 0;
do { do {
occupancy[size] = b; occupancy[size] = b;
reference[size++] = sliding_attack(deltas, s, b); reference[size] = sliding_attack(deltas, s, b);
if (HasPext)
attacks[s][_pext_u64(b, masks[s])] = reference[size];
size++;
b = (b - masks[s]) & masks[s]; b = (b - masks[s]) & masks[s];
} while (b); } while (b);
@@ -312,12 +293,15 @@ namespace {
if (s < SQ_H8) if (s < SQ_H8)
attacks[s + 1] = attacks[s] + size; attacks[s + 1] = attacks[s] + size;
if (HasPext)
continue;
booster = MagicBoosters[Is64Bit][rank_of(s)]; booster = MagicBoosters[Is64Bit][rank_of(s)];
// Find a magic for square 's' picking up an (almost) random number // Find a magic for square 's' picking up an (almost) random number
// until we find the one that passes the verification test. // until we find the one that passes the verification test.
do { do {
do magics[s] = pick_random(rk, booster); do magics[s] = rk.magic_rand<Bitboard>(booster);
while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6); while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6);
std::memset(attacks[s], 0, size * sizeof(Bitboard)); std::memset(attacks[s], 0, size * sizeof(Bitboard));
@@ -333,11 +317,11 @@ namespace {
if (attack && attack != reference[i]) if (attack && attack != reference[i])
break; break;
assert(reference[i] != 0); assert(reference[i]);
attack = reference[i]; attack = reference[i];
} }
} while (i != size); } while (i < size);
} }
} }
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -26,7 +26,7 @@
namespace Bitboards { namespace Bitboards {
void init(); void init();
void print(Bitboard b); const std::string pretty(Bitboard b);
} }
@@ -177,7 +177,7 @@ inline Bitboard in_front_bb(Color c, Rank r) {
/// between_bb() returns a bitboard representing all squares between two squares. /// between_bb() returns a bitboard representing all squares between two squares.
/// For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with the bits for /// For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with the bits for
/// square d5 and e6 set. If s1 and s2 are not on the same line, file or diagonal, /// square d5 and e6 set. If s1 and s2 are not on the same rank, file or diagonal,
/// 0 is returned. /// 0 is returned.
inline Bitboard between_bb(Square s1, Square s2) { inline Bitboard between_bb(Square s1, Square s2) {
@@ -241,6 +241,9 @@ FORCE_INLINE unsigned magic_index(Square s, Bitboard occ) {
Bitboard* const Magics = Pt == ROOK ? RMagics : BMagics; Bitboard* const Magics = Pt == ROOK ? RMagics : BMagics;
unsigned* const Shifts = Pt == ROOK ? RShifts : BShifts; unsigned* const Shifts = Pt == ROOK ? RShifts : BShifts;
if (HasPext)
return unsigned(_pext_u64(occ, Masks[s]));
if (Is64Bit) if (Is64Bit)
return unsigned(((occ & Masks[s]) * Magics[s]) >> Shifts[s]); return unsigned(((occ & Masks[s]) * Magics[s]) >> Shifts[s]);
@@ -254,24 +257,34 @@ inline Bitboard attacks_bb(Square s, Bitboard occ) {
return (Pt == ROOK ? RAttacks : BAttacks)[s][magic_index<Pt>(s, occ)]; return (Pt == ROOK ? RAttacks : BAttacks)[s][magic_index<Pt>(s, occ)];
} }
inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occ) {
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard. switch (type_of(pc))
/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard. {
case BISHOP: return attacks_bb<BISHOP>(s, occ);
case ROOK : return attacks_bb<ROOK>(s, occ);
case QUEEN : return attacks_bb<BISHOP>(s, occ) | attacks_bb<ROOK>(s, occ);
default : return StepAttacksBB[pc][s];
}
}
/// lsb()/msb() finds the least/most significant bit in a non-zero bitboard.
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard.
#ifdef USE_BSFQ #ifdef USE_BSFQ
# if defined(_MSC_VER) && !defined(__INTEL_COMPILER) # if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
FORCE_INLINE Square lsb(Bitboard b) { FORCE_INLINE Square lsb(Bitboard b) {
unsigned long index; unsigned long idx;
_BitScanForward64(&index, b); _BitScanForward64(&idx, b);
return (Square) index; return (Square) idx;
} }
FORCE_INLINE Square msb(Bitboard b) { FORCE_INLINE Square msb(Bitboard b) {
unsigned long index; unsigned long idx;
_BitScanReverse64(&index, b); _BitScanReverse64(&idx, b);
return (Square) index; return (Square) idx;
} }
# elif defined(__arm__) # elif defined(__arm__)
@@ -292,15 +305,15 @@ FORCE_INLINE Square lsb(Bitboard b) {
# else # else
FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen
Bitboard index; Bitboard idx;
__asm__("bsfq %1, %0": "=r"(index): "rm"(b) ); __asm__("bsfq %1, %0": "=r"(idx): "rm"(b) );
return (Square) index; return (Square) idx;
} }
FORCE_INLINE Square msb(Bitboard b) { FORCE_INLINE Square msb(Bitboard b) {
Bitboard index; Bitboard idx;
__asm__("bsrq %1, %0": "=r"(index): "rm"(b) ); __asm__("bsrq %1, %0": "=r"(idx): "rm"(b) );
return (Square) index; return (Square) idx;
} }
# endif # endif

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -32,14 +32,14 @@ enum BitCountType {
CNT_HW_POPCNT CNT_HW_POPCNT
}; };
/// Determine at compile time the best popcount<> specialization according if /// Determine at compile time the best popcount<> specialization according to
/// platform is 32 or 64 bits, to the maximum number of nonzero bits to count /// whether the platform is 32 or 64 bit, the maximum number of non-zero
/// and if hardware popcnt instruction is available. /// bits to count and if the hardware popcnt instruction is available.
const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32; const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32;
const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15; const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15;
/// popcount() counts the number of nonzero bits in a bitboard /// popcount() counts the number of non-zero bits in a bitboard
template<BitCountType> inline int popcount(Bitboard); template<BitCountType> inline int popcount(Bitboard);
template<> template<>

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -25,7 +25,6 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <iostream>
#include "book.h" #include "book.h"
#include "misc.h" #include "misc.h"
@@ -36,8 +35,8 @@ using namespace std;
namespace { namespace {
// A Polyglot book is a series of "entries" of 16 bytes. All integers are // A Polyglot book is a series of "entries" of 16 bytes. All integers are
// stored in big-endian format, with highest byte first (regardless of size). // stored in big-endian format, with the highest byte first (regardless of
// The entries are ordered according to the key in ascending order. // size). The entries are ordered according to the key in ascending order.
struct Entry { struct Entry {
uint64_t key; uint64_t key;
uint16_t move; uint16_t move;
@@ -50,7 +49,7 @@ namespace {
Key PolyGlotRandoms[781]; Key PolyGlotRandoms[781];
struct { struct {
Key psq[12][64]; // [piece][square] Key psq[12][64]; // [piece][square]
Key castle[4]; // [castle right] Key castling[4]; // [castling flag]
Key enpassant[8]; // [file] Key enpassant[8]; // [file]
Key turn; Key turn;
} Zobrist; } Zobrist;
@@ -327,16 +326,16 @@ namespace {
while (b) while (b)
{ {
Square s = pop_lsb(&b); Square s = pop_lsb(&b);
Piece p = pos.piece_on(s); Piece pc = pos.piece_on(s);
// PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11 // PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11
key ^= PG.Zobrist.psq[2 * (type_of(p) - 1) + (color_of(p) == WHITE)][s]; key ^= PG.Zobrist.psq[2 * (type_of(pc) - 1) + (color_of(pc) == WHITE)][s];
} }
b = pos.can_castle(ALL_CASTLES); b = pos.can_castle(ANY_CASTLING);
while (b) while (b)
key ^= PG.Zobrist.castle[pop_lsb(&b)]; key ^= PG.Zobrist.castling[pop_lsb(&b)];
if (pos.ep_square() != SQ_NONE) if (pos.ep_square() != SQ_NONE)
key ^= PG.Zobrist.enpassant[file_of(pos.ep_square())]; key ^= PG.Zobrist.enpassant[file_of(pos.ep_square())];
@@ -355,7 +354,7 @@ PolyglotBook::~PolyglotBook() { if (is_open()) close(); }
/// operator>>() reads sizeof(T) chars from the file's binary byte stream and /// operator>>() reads sizeof(T) chars from the file's binary byte stream and
/// converts them in a number of type T. A Polyglot book stores numbers in /// converts them into a number of type T. A Polyglot book stores numbers in
/// big-endian format. /// big-endian format.
template<typename T> PolyglotBook& PolyglotBook::operator>>(T& n) { template<typename T> PolyglotBook& PolyglotBook::operator>>(T& n) {
@@ -373,7 +372,7 @@ template<> PolyglotBook& PolyglotBook::operator>>(Entry& e) {
/// open() tries to open a book file with the given name after closing any /// open() tries to open a book file with the given name after closing any
/// exsisting one. /// existing one.
bool PolyglotBook::open(const char* fName) { bool PolyglotBook::open(const char* fName) {
@@ -383,14 +382,15 @@ bool PolyglotBook::open(const char* fName) {
ifstream::open(fName, ifstream::in | ifstream::binary); ifstream::open(fName, ifstream::in | ifstream::binary);
fileName = is_open() ? fName : ""; fileName = is_open() ? fName : "";
ifstream::clear(); // Reset any error flag to allow retry ifstream::open() ifstream::clear(); // Reset any error flag to allow a retry ifstream::open()
return !fileName.empty(); return !fileName.empty();
} }
/// probe() tries to find a book move for the given position. If no move is /// probe() tries to find a book move for the given position. If no move is
/// found returns MOVE_NONE. If pickBest is true returns always the highest /// found, it returns MOVE_NONE. If pickBest is true, then it always returns
/// rated move, otherwise randomly chooses one, based on the move score. /// the highest-rated move, otherwise it randomly chooses one based on the
/// move score.
Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest) { Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest) {
@@ -410,9 +410,9 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
best = max(best, e.count); best = max(best, e.count);
sum += e.count; sum += e.count;
// Choose book move according to its score. If a move has a very // Choose book move according to its score. If a move has a very high
// high score it has higher probability to be choosen than a move // score it has a higher probability of being choosen than a move with
// with lower score. Note that first entry is always chosen. // a lower score. Note that first entry is always chosen.
if ( (!pickBest && sum && rkiss.rand<unsigned>() % sum < e.count) if ( (!pickBest && sum && rkiss.rand<unsigned>() % sum < e.count)
|| (pickBest && e.count == best)) || (pickBest && e.count == best))
move = Move(e.move); move = Move(e.move);
@@ -427,10 +427,10 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
// bit 6-11: origin square (from 0 to 63) // bit 6-11: origin square (from 0 to 63)
// bit 12-14: promotion piece (from KNIGHT == 1 to QUEEN == 4) // bit 12-14: promotion piece (from KNIGHT == 1 to QUEEN == 4)
// //
// Castling moves follow "king captures rook" representation. So in case book // Castling moves follow the "king captures rook" representation. If a book
// move is a promotion we have to convert to our representation, in all the // move is a promotion, we have to convert it to our representation and in
// other cases we can directly compare with a Move after having masked out // all other cases, we can directly compare with a Move after having masked
// the special Move's flags (bit 14-15) that are not supported by PolyGlot. // out the special Move flags (bit 14-15) that are not supported by PolyGlot.
int pt = (move >> 12) & 7; int pt = (move >> 12) & 7;
if (pt) if (pt)
move = make<PROMOTION>(from_sq(move), to_sq(move), PieceType(pt + 1)); move = make<PROMOTION>(from_sq(move), to_sq(move), PieceType(pt + 1));

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -80,10 +80,9 @@ namespace {
return sq; return sq;
} }
// Get the material key of a Position out of the given endgame key code // Get the material key of Position out of the given endgame key code
// like "KBPKN". The trick here is to first forge an ad-hoc fen string // like "KBPKN". The trick here is to first forge an ad-hoc FEN string
// and then let a Position object to do the work for us. Note that the // and then let a Position object do the work for us.
// fen string could correspond to an illegal position.
Key key(const string& code, Color c) { Key key(const string& code, Color c) {
assert(code.length() > 0 && code.length() < 8); assert(code.length() > 0 && code.length() < 8);
@@ -94,8 +93,8 @@ namespace {
std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower); std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower);
string fen = sides[0] + char('0' + int(8 - code.length())) string fen = sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/8/8/"
+ sides[1] + "/8/8/8/8/8/8/8 w - - 0 10"; + sides[1] + char(8 - sides[1].length() + '0') + " w - - 0 10";
return Position(fen, false, NULL).material_key(); return Position(fen, false, NULL).material_key();
} }
@@ -118,7 +117,6 @@ Endgames::Endgames() {
add<KRKN>("KRKN"); add<KRKN>("KRKN");
add<KQKP>("KQKP"); add<KQKP>("KQKP");
add<KQKR>("KQKR"); add<KQKR>("KQKR");
add<KBBKN>("KBBKN");
add<KNPK>("KNPK"); add<KNPK>("KNPK");
add<KNPKB>("KNPKB"); add<KNPKB>("KNPKB");
@@ -145,7 +143,7 @@ void Endgames::add(const string& code) {
/// Mate with KX vs K. This function is used to evaluate positions with /// Mate with KX vs K. This function is used to evaluate positions with
/// King and plenty of material vs a lone king. It simply gives the /// king and plenty of material vs a lone king. It simply gives the
/// attacking side a bonus for driving the defending king towards the edge /// attacking side a bonus for driving the defending king towards the edge
/// of the board, and for keeping the distance between the two kings small. /// of the board, and for keeping the distance between the two kings small.
template<> template<>
@@ -168,6 +166,7 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
if ( pos.count<QUEEN>(strongSide) if ( pos.count<QUEEN>(strongSide)
|| pos.count<ROOK>(strongSide) || pos.count<ROOK>(strongSide)
||(pos.count<BISHOP>(strongSide) && pos.count<KNIGHT>(strongSide))
|| pos.bishop_pair(strongSide)) || pos.bishop_pair(strongSide))
result += VALUE_KNOWN_WIN; result += VALUE_KNOWN_WIN;
@@ -187,9 +186,9 @@ Value Endgame<KBNK>::operator()(const Position& pos) const {
Square loserKSq = pos.king_square(weakSide); Square loserKSq = pos.king_square(weakSide);
Square bishopSq = pos.list<BISHOP>(strongSide)[0]; Square bishopSq = pos.list<BISHOP>(strongSide)[0];
// kbnk_mate_table() tries to drive toward corners A1 or H8, // kbnk_mate_table() tries to drive toward corners A1 or H8. If we have a
// if we have a bishop that cannot reach the above squares we // bishop that cannot reach the above squares, we flip the kings in order
// flip the kings so to drive enemy toward corners A8 or H1. // to drive the enemy toward corners A8 or H1.
if (opposite_colors(bishopSq, SQ_A1)) if (opposite_colors(bishopSq, SQ_A1))
{ {
winnerKSq = ~winnerKSq; winnerKSq = ~winnerKSq;
@@ -242,18 +241,18 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
Square rsq = relative_square(strongSide, pos.list<ROOK>(strongSide)[0]); Square rsq = relative_square(strongSide, pos.list<ROOK>(strongSide)[0]);
Square psq = relative_square(strongSide, pos.list<PAWN>(weakSide)[0]); Square psq = relative_square(strongSide, pos.list<PAWN>(weakSide)[0]);
Square queeningSq = file_of(psq) | RANK_1; Square queeningSq = make_square(file_of(psq), RANK_1);
Value result; Value result;
// If the stronger side's king is in front of the pawn, it's a win // If the stronger side's king is in front of the pawn, it's a win
if (wksq < psq && file_of(wksq) == file_of(psq)) if (wksq < psq && file_of(wksq) == file_of(psq))
result = RookValueEg - Value(square_distance(wksq, psq)); result = RookValueEg - square_distance(wksq, psq);
// If the weaker side's king is too far from the pawn and the rook, // If the weaker side's king is too far from the pawn and the rook,
// it's a win. // it's a win.
else if ( square_distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide) else if ( square_distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide)
&& square_distance(bksq, rsq) >= 3) && square_distance(bksq, rsq) >= 3)
result = RookValueEg - Value(square_distance(wksq, psq)); result = RookValueEg - square_distance(wksq, psq);
// If the pawn is far advanced and supported by the defending king, // If the pawn is far advanced and supported by the defending king,
// the position is drawish // the position is drawish
@@ -261,13 +260,12 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
&& square_distance(bksq, psq) == 1 && square_distance(bksq, psq) == 1
&& rank_of(wksq) >= RANK_4 && rank_of(wksq) >= RANK_4
&& square_distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide)) && square_distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide))
result = Value(80 - square_distance(wksq, psq) * 8); result = Value(80) - 8 * square_distance(wksq, psq);
else else
result = Value(200) result = Value(200) - 8 * ( square_distance(wksq, psq + DELTA_S)
- Value(square_distance(wksq, psq + DELTA_S) * 8) - square_distance(bksq, psq + DELTA_S)
+ Value(square_distance(bksq, psq + DELTA_S) * 8) - square_distance(psq, queeningSq));
+ Value(square_distance(psq, queeningSq) * 8);
return strongSide == pos.side_to_move() ? result : -result; return strongSide == pos.side_to_move() ? result : -result;
} }
@@ -301,9 +299,10 @@ Value Endgame<KRKN>::operator()(const Position& pos) const {
} }
/// KQ vs KP. In general, a win for the stronger side, however, there are a few /// KQ vs KP. In general, this is a win for the stronger side, but there are a
/// important exceptions. Pawn on 7th rank, A,C,F or H file, with king next can /// few important exceptions. A pawn on 7th rank and on the A,C,F or H files
/// be a draw, so we scale down to distance between kings only. /// with a king positioned next to it can be a draw, so in that case, we only
/// use the distance between the kings.
template<> template<>
Value Endgame<KQKP>::operator()(const Position& pos) const { Value Endgame<KQKP>::operator()(const Position& pos) const {
@@ -327,9 +326,8 @@ Value Endgame<KQKP>::operator()(const Position& pos) const {
/// KQ vs KR. This is almost identical to KX vs K: We give the attacking /// KQ vs KR. This is almost identical to KX vs K: We give the attacking
/// king a bonus for having the kings close together, and for forcing the /// king a bonus for having the kings close together, and for forcing the
/// defending king towards the edge. If we also take care to avoid null move /// defending king towards the edge. If we also take care to avoid null move for
/// for the defending side in the search, this is usually sufficient to be /// the defending side in the search, this is usually sufficient to win KQ vs KR.
/// able to win KQ vs KR.
template<> template<>
Value Endgame<KQKR>::operator()(const Position& pos) const { Value Endgame<KQKR>::operator()(const Position& pos) const {
@@ -348,35 +346,11 @@ Value Endgame<KQKR>::operator()(const Position& pos) const {
} }
/// KBB vs KN. This is almost always a win. We try to push enemy king to a corner
/// and away from his knight. For a reference of this difficult endgame see:
/// en.wikipedia.org/wiki/Chess_endgame#Effect_of_tablebases_on_endgame_theory
template<>
Value Endgame<KBBKN>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, 2 * BishopValueMg, 0));
assert(verify_material(pos, weakSide, KnightValueMg, 0));
Square winnerKSq = pos.king_square(strongSide);
Square loserKSq = pos.king_square(weakSide);
Square knightSq = pos.list<KNIGHT>(weakSide)[0];
Value result = VALUE_KNOWN_WIN
+ PushToCorners[loserKSq]
+ PushClose[square_distance(winnerKSq, loserKSq)]
+ PushAway[square_distance(loserKSq, knightSq)];
return strongSide == pos.side_to_move() ? result : -result;
}
/// Some cases of trivial draws /// Some cases of trivial draws
template<> Value Endgame<KNNK>::operator()(const Position&) const { return VALUE_DRAW; } template<> Value Endgame<KNNK>::operator()(const Position&) const { return VALUE_DRAW; }
template<> Value Endgame<KmmKm>::operator()(const Position&) const { return VALUE_DRAW; }
/// K, bishop and one or more pawns vs K. It checks for draws with rook pawns and /// KB and one or more pawns vs K. It checks for draws with rook pawns and
/// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW /// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW
/// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling /// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
/// will be used. /// will be used.
@@ -397,7 +371,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
&& !(pawns & ~file_bb(pawnFile))) && !(pawns & ~file_bb(pawnFile)))
{ {
Square bishopSq = pos.list<BISHOP>(strongSide)[0]; Square bishopSq = pos.list<BISHOP>(strongSide)[0];
Square queeningSq = relative_square(strongSide, pawnFile | RANK_8); Square queeningSq = relative_square(strongSide, make_square(pawnFile, RANK_8));
Square kingSq = pos.king_square(weakSide); Square kingSq = pos.king_square(weakSide);
if ( opposite_colors(queeningSq, bishopSq) if ( opposite_colors(queeningSq, bishopSq)
@@ -405,20 +379,20 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
} }
// All pawns on same B or G file? Then potential draw // If all the pawns are on the same B or G file, then it's potentially a draw
if ( (pawnFile == FILE_B || pawnFile == FILE_G) if ( (pawnFile == FILE_B || pawnFile == FILE_G)
&& !(pos.pieces(PAWN) & ~file_bb(pawnFile)) && !(pos.pieces(PAWN) & ~file_bb(pawnFile))
&& pos.non_pawn_material(weakSide) == 0 && pos.non_pawn_material(weakSide) == 0
&& pos.count<PAWN>(weakSide) >= 1) && pos.count<PAWN>(weakSide) >= 1)
{ {
// Get weakSide pawn that is closest to home rank // Get weakSide pawn that is closest to the home rank
Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN)); Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN));
Square strongKingSq = pos.king_square(strongSide); Square strongKingSq = pos.king_square(strongSide);
Square weakKingSq = pos.king_square(weakSide); Square weakKingSq = pos.king_square(weakSide);
Square bishopSq = pos.list<BISHOP>(strongSide)[0]; Square bishopSq = pos.list<BISHOP>(strongSide)[0];
// Potential for a draw if our pawn is blocked on the 7th rank // There's potential for a draw if our pawn is blocked on the 7th rank,
// the bishop cannot attack it or they only have one pawn left // the bishop cannot attack it or they only have one pawn left
if ( relative_rank(strongSide, weakPawnSq) == RANK_7 if ( relative_rank(strongSide, weakPawnSq) == RANK_7
&& (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide))) && (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide)))
@@ -427,7 +401,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
int strongKingDist = square_distance(weakPawnSq, strongKingSq); int strongKingDist = square_distance(weakPawnSq, strongKingSq);
int weakKingDist = square_distance(weakPawnSq, weakKingSq); int weakKingDist = square_distance(weakPawnSq, weakKingSq);
// Draw if the weak king is on it's back two ranks, within 2 // It's a draw if the weak king is on its back two ranks, within 2
// squares of the blocking pawn and the strong king is not // squares of the blocking pawn and the strong king is not
// closer. (I think this rule only fails in practically // closer. (I think this rule only fails in practically
// unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w
@@ -444,8 +418,8 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
} }
/// K and queen vs K, rook and one or more pawns. It tests for fortress draws with /// KQ vs KR and one or more pawns. It tests for fortress draws with a rook on
/// a rook on the third rank defended by a pawn. /// the third rank defended by a pawn.
template<> template<>
ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const { ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
@@ -468,12 +442,12 @@ ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
} }
/// K, rook and one pawn vs K and a rook. This function knows a handful of the /// KRP vs KR. This function knows a handful of the most important classes of
/// most important classes of drawn positions, but is far from perfect. It would /// drawn positions, but is far from perfect. It would probably be a good idea
/// probably be a good idea to add more knowledge in the future. /// to add more knowledge in the future.
/// ///
/// It would also be nice to rewrite the actual code for this function, /// It would also be nice to rewrite the actual code for this function,
/// which is mostly copied from Glaurung 1.x, and not very pretty. /// which is mostly copied from Glaurung 1.x, and isn't very pretty.
template<> template<>
ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const { ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
@@ -489,7 +463,7 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
File f = file_of(wpsq); File f = file_of(wpsq);
Rank r = rank_of(wpsq); Rank r = rank_of(wpsq);
Square queeningSq = f | RANK_8; Square queeningSq = make_square(f, RANK_8);
int tempo = (pos.side_to_move() == strongSide); int tempo = (pos.side_to_move() == strongSide);
// If the pawn is not too far advanced and the defending king defends the // If the pawn is not too far advanced and the defending king defends the
@@ -555,7 +529,7 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
- 8 * square_distance(wpsq, queeningSq) - 8 * square_distance(wpsq, queeningSq)
- 2 * square_distance(wksq, queeningSq)); - 2 * square_distance(wksq, queeningSq));
// If the pawn is not far advanced, and the defending king is somewhere in // If the pawn is not far advanced and the defending king is somewhere in
// the pawn's path, it's probably a draw. // the pawn's path, it's probably a draw.
if (r <= RANK_4 && bksq > wpsq) if (r <= RANK_4 && bksq > wpsq)
{ {
@@ -612,9 +586,8 @@ ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
/// K, rook and two pawns vs K, rook and one pawn. There is only a single /// KRPP vs KRP. There is just a single rule: if the stronger side has no passed
/// pattern: If the stronger side has no passed pawns and the defending king /// pawns and the defending king is actively placed, the position is drawish.
/// is actively placed, the position is drawish.
template<> template<>
ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const { ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
@@ -661,8 +634,8 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
Bitboard pawns = pos.pieces(strongSide, PAWN); Bitboard pawns = pos.pieces(strongSide, PAWN);
Square psq = pos.list<PAWN>(strongSide)[0]; Square psq = pos.list<PAWN>(strongSide)[0];
// If all pawns are ahead of the king, all pawns are on a single // If all pawns are ahead of the king, on a single rook file and
// rook file and the king is within one file of the pawns then draw. // the king is within one file of the pawns, it's a draw.
if ( !(pawns & ~in_front_bb(weakSide, rank_of(ksq))) if ( !(pawns & ~in_front_bb(weakSide, rank_of(ksq)))
&& !((pawns & ~FileABB) && (pawns & ~FileHBB)) && !((pawns & ~FileABB) && (pawns & ~FileHBB))
&& file_distance(ksq, psq) <= 1) && file_distance(ksq, psq) <= 1)
@@ -672,10 +645,10 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
} }
/// K, bishop and a pawn vs K and a bishop. There are two rules: If the defending /// KBP vs KB. There are two rules: if the defending king is somewhere along the
/// king is somewhere along the path of the pawn, and the square of the king is /// path of the pawn, and the square of the king is not of the same color as the
/// not of the same color as the stronger side's bishop, it's a draw. If the two /// stronger side's bishop, it's a draw. If the two bishops have opposite color,
/// bishops have opposite color, it's almost always a draw. /// it's almost always a draw.
template<> template<>
ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const { ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
@@ -725,8 +698,7 @@ ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
} }
/// K, bishop and two pawns vs K and bishop. It detects a few basic draws with /// KBPP vs KB. It detects a few basic draws with opposite-colored bishops
/// opposite-colored bishops.
template<> template<>
ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const { ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
@@ -749,19 +721,19 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2)) if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2))
{ {
blockSq1 = psq1 + pawn_push(strongSide); blockSq1 = psq1 + pawn_push(strongSide);
blockSq2 = file_of(psq2) | rank_of(psq1); blockSq2 = make_square(file_of(psq2), rank_of(psq1));
} }
else else
{ {
blockSq1 = psq2 + pawn_push(strongSide); blockSq1 = psq2 + pawn_push(strongSide);
blockSq2 = file_of(psq1) | rank_of(psq2); blockSq2 = make_square(file_of(psq1), rank_of(psq2));
} }
switch (file_distance(psq1, psq2)) switch (file_distance(psq1, psq2))
{ {
case 0: case 0:
// Both pawns are on the same file. Easy draw if defender firmly controls // Both pawns are on the same file. It's an easy draw if the defender firmly
// some square in the frontmost pawn's path. // controls some square in the frontmost pawn's path.
if ( file_of(ksq) == file_of(blockSq1) if ( file_of(ksq) == file_of(blockSq1)
&& relative_rank(strongSide, ksq) >= relative_rank(strongSide, blockSq1) && relative_rank(strongSide, ksq) >= relative_rank(strongSide, blockSq1)
&& opposite_colors(ksq, wbsq)) && opposite_colors(ksq, wbsq))
@@ -770,9 +742,9 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
case 1: case 1:
// Pawns on adjacent files. Draw if defender firmly controls the square // Pawns on adjacent files. It's a draw if the defender firmly controls the
// in front of the frontmost pawn's path, and the square diagonally behind // square in front of the frontmost pawn's path, and the square diagonally
// this square on the file of the other pawn. // behind this square on the file of the other pawn.
if ( ksq == blockSq1 if ( ksq == blockSq1
&& opposite_colors(ksq, wbsq) && opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq2 && ( bbsq == blockSq2
@@ -795,9 +767,9 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
} }
/// K, bisop and a pawn vs K and knight. There is a single rule: If the defending /// KBP vs KN. There is a single rule: If the defending king is somewhere along
/// king is somewhere along the path of the pawn, and the square of the king is /// the path of the pawn, and the square of the king is not of the same color as
/// not of the same color as the stronger side's bishop, it's a draw. /// the stronger side's bishop, it's a draw.
template<> template<>
ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const { ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
@@ -818,9 +790,8 @@ ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
} }
/// K, knight and a pawn vs K. There is a single rule: If the pawn is a rook pawn /// KNP vs K. There is a single rule: if the pawn is a rook pawn on the 7th rank
/// on the 7th rank and the defending king prevents the pawn from advancing, the /// and the defending king prevents the pawn from advancing, the position is drawn.
/// position is drawn.
template<> template<>
ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const { ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
@@ -838,8 +809,8 @@ ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
} }
/// K, knight and a pawn vs K and bishop. If knight can block bishop from taking /// KNP vs KB. If knight can block bishop from taking pawn, it's a win.
/// pawn, it's a win. Otherwise, drawn. /// Otherwise the position is drawn.
template<> template<>
ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const { ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
@@ -856,11 +827,11 @@ ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
} }
/// K and a pawn vs K and a pawn. This is done by removing the weakest side's /// KP vs KP. This is done by removing the weakest side's pawn and probing the
/// pawn and probing the KP vs K bitbase: If the weakest side has a draw without /// KP vs K bitbase: If the weakest side has a draw without the pawn, it probably
/// the pawn, she probably has at least a draw with the pawn as well. The exception /// has at least a draw with the pawn as well. The exception is when the stronger
/// is when the stronger side's pawn is far advanced and not on a rook file; in /// side's pawn is far advanced and not on a rook file; in this case it is often
/// this case it is often possible to win (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1). /// possible to win (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1).
template<> template<>
ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const { ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -42,19 +42,17 @@ enum EndgameType {
KRKN, // KR vs KN KRKN, // KR vs KN
KQKP, // KQ vs KP KQKP, // KQ vs KP
KQKR, // KQ vs KR KQKR, // KQ vs KR
KBBKN, // KBB vs KN
KmmKm, // K and two minors vs K and one or two minors
// Scaling functions // Scaling functions
SCALE_FUNS, SCALE_FUNS,
KBPsK, // KB+pawns vs K KBPsK, // KB and pawns vs K
KQKRPs, // KQ vs KR+pawns KQKRPs, // KQ vs KR and pawns
KRPKR, // KRP vs KR KRPKR, // KRP vs KR
KRPKB, // KRP vs KB KRPKB, // KRP vs KB
KRPPKRP, // KRPP vs KRP KRPPKRP, // KRPP vs KRP
KPsK, // King and pawns vs king KPsK, // K and pawns vs K
KBPKB, // KBP vs KB KBPKB, // KBP vs KB
KBPPKB, // KBPP vs KB KBPPKB, // KBPP vs KB
KBPKN, // KBP vs KN KBPKN, // KBP vs KN
@@ -64,9 +62,9 @@ enum EndgameType {
}; };
/// Endgame functions can be of two types according if return a Value or a /// Endgame functions can be of two types depending on whether they return a
/// ScaleFactor. Type eg_fun<int>::type equals to either ScaleFactor or Value /// Value or a ScaleFactor. Type eg_fun<int>::type returns either ScaleFactor
/// depending if the template parameter is 0 or 1. /// or Value depending on whether the template parameter is 0 or 1.
template<int> struct eg_fun { typedef Value type; }; template<int> struct eg_fun { typedef Value type; };
template<> struct eg_fun<1> { typedef ScaleFactor type; }; template<> struct eg_fun<1> { typedef ScaleFactor type; };
@@ -95,9 +93,9 @@ private:
}; };
/// Endgames class stores in two std::map the pointers to endgame evaluation /// The Endgames class stores the pointers to endgame evaluation and scaling
/// and scaling base objects. Then we use polymorphism to invoke the actual /// base objects in two std::map typedefs. We then use polymorphism to invoke
/// endgame function calling its operator() that is virtual. /// the actual endgame function by calling its virtual operator().
class Endgames { class Endgames {

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
*/ */
#include <iostream> #include <iostream>
#include <string>
#include "bitboard.h" #include "bitboard.h"
#include "evaluate.h" #include "evaluate.h"
@@ -40,14 +39,9 @@ int main(int argc, char* argv[]) {
Pawns::init(); Pawns::init();
Eval::init(); Eval::init();
Threads.init(); Threads.init();
TT.set_size(Options["Hash"]); TT.resize(Options["Hash"]);
std::string args; UCI::loop(argc, argv);
for (int i = 1; i < argc; ++i)
args += std::string(argv[i]) + " ";
UCI::loop(args);
Threads.exit(); Threads.exit();
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -27,29 +27,23 @@ using namespace std;
namespace { namespace {
// Values modified by Joona Kiiski
const Value MidgameLimit = Value(15581);
const Value EndgameLimit = Value(3998);
// Scale factors used when one side has no more pawns
const int NoPawnsSF[4] = { 6, 12, 32 };
// Polynomial material balance parameters // Polynomial material balance parameters
// pair pawn knight bishop rook queen // pair pawn knight bishop rook queen
const int LinearCoefficients[6] = { 1852, -162, -1122, -183, 249, -52 }; const int LinearCoefficients[6] = { 1852, -162, -1122, -183, 249, -154 };
const int QuadraticCoefficientsSameColor[][PIECE_TYPE_NB] = { const int QuadraticCoefficientsSameSide[][PIECE_TYPE_NB] = {
// OUR PIECES
// pair pawn knight bishop rook queen // pair pawn knight bishop rook queen
{ 0 }, // Bishop pair { 0 }, // Bishop pair
{ 39, 2 }, // Pawn { 39, 2 }, // Pawn
{ 35, 271, -4 }, // Knight { 35, 271, -4 }, // knight OUR PIECES
{ 0, 105, 4, 0 }, // Bishop { 0, 105, 4, 0 }, // Bishop
{ -27, -2, 46, 100, -141 }, // Rook { -27, -2, 46, 100, -141 }, // Rook
{ 58, 29, 83, 148, -163, 0 } // Queen {-177, 25, 129, 142, -137, 0 } // Queen
}; };
const int QuadraticCoefficientsOppositeColor[][PIECE_TYPE_NB] = { const int QuadraticCoefficientsOppositeSide[][PIECE_TYPE_NB] = {
// THEIR PIECES // THEIR PIECES
// pair pawn knight bishop rook queen // pair pawn knight bishop rook queen
{ 0 }, // Bishop pair { 0 }, // Bishop pair
@@ -57,12 +51,11 @@ namespace {
{ 10, 62, 0 }, // Knight OUR PIECES { 10, 62, 0 }, // Knight OUR PIECES
{ 57, 64, 39, 0 }, // Bishop { 57, 64, 39, 0 }, // Bishop
{ 50, 40, 23, -22, 0 }, // Rook { 50, 40, 23, -22, 0 }, // Rook
{ 106, 101, 3, 151, 171, 0 } // Queen { 98, 105, -39, 141, 274, 0 } // Queen
}; };
// Endgame evaluation and scaling functions accessed direcly and not through // Endgame evaluation and scaling functions are accessed directly and not through
// the function maps because correspond to more then one material hash key. // the function maps because they correspond to more than one material hash key.
Endgame<KmmKm> EvaluateKmmKm[] = { Endgame<KmmKm>(WHITE), Endgame<KmmKm>(BLACK) };
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) }; Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
Endgame<KBPsK> ScaleKBPsK[] = { Endgame<KBPsK>(WHITE), Endgame<KBPsK>(BLACK) }; Endgame<KBPsK> ScaleKBPsK[] = { Endgame<KBPsK>(WHITE), Endgame<KBPsK>(BLACK) };
@@ -93,7 +86,7 @@ namespace {
&& pos.count<PAWN>(Them) >= 1; && pos.count<PAWN>(Them) >= 1;
} }
/// imbalance() calculates imbalance comparing piece count of each /// imbalance() calculates the imbalance by comparing the piece count of each
/// piece type for both colors. /// piece type for both colors.
template<Color Us> template<Color Us>
@@ -114,11 +107,12 @@ namespace {
v = LinearCoefficients[pt1]; v = LinearCoefficients[pt1];
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2) for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2)
v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2] v += QuadraticCoefficientsSameSide[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2]; + QuadraticCoefficientsOppositeSide[pt1][pt2] * pieceCount[Them][pt2];
value += pc * v; value += pc * v;
} }
return value; return value;
} }
@@ -147,9 +141,9 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL; e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
e->gamePhase = game_phase(pos); e->gamePhase = game_phase(pos);
// Let's look if we have a specialized evaluation function for this // Let's look if we have a specialized evaluation function for this particular
// particular material configuration. First we look for a fixed // material configuration. Firstly we look for a fixed configuration one, then
// configuration one, then a generic one if previous search failed. // for a generic one if the previous search failed.
if (endgames.probe(key, e->evaluationFunction)) if (endgames.probe(key, e->evaluationFunction))
return e; return e;
@@ -165,21 +159,6 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
return e; return e;
} }
if (!pos.pieces(PAWN) && !pos.pieces(ROOK) && !pos.pieces(QUEEN))
{
// Minor piece endgame with at least one minor piece per side and
// no pawns. Note that the case KmmK is already handled by KXK.
assert((pos.pieces(WHITE, KNIGHT) | pos.pieces(WHITE, BISHOP)));
assert((pos.pieces(BLACK, KNIGHT) | pos.pieces(BLACK, BISHOP)));
if ( pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE) <= 2
&& pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK) <= 2)
{
e->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()];
return e;
}
}
// OK, we didn't find any special evaluation function for the current // OK, we didn't find any special evaluation function for the current
// material configuration. Is there a suitable scaling function? // material configuration. Is there a suitable scaling function?
// //
@@ -193,8 +172,8 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
return e; return e;
} }
// Generic scaling functions that refer to more then one material // Generic scaling functions that refer to more than one material
// distribution. Should be probed after the specialized ones. // distribution. They should be probed after the specialized ones.
// Note that these ones don't return after setting the function. // Note that these ones don't return after setting the function.
if (is_KBPsKs<WHITE>(pos)) if (is_KBPsKs<WHITE>(pos))
e->scalingFunction[WHITE] = &ScaleKBPsK[WHITE]; e->scalingFunction[WHITE] = &ScaleKBPsK[WHITE];
@@ -211,7 +190,7 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
Value npm_w = pos.non_pawn_material(WHITE); Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK); Value npm_b = pos.non_pawn_material(BLACK);
if (npm_w + npm_b == VALUE_ZERO) if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN))
{ {
if (!pos.count<PAWN>(BLACK)) if (!pos.count<PAWN>(BLACK))
{ {
@@ -233,18 +212,19 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
} }
// No pawns makes it difficult to win, even with a material advantage. This // No pawns makes it difficult to win, even with a material advantage. This
// catches some trivial draws like KK, KBK and KNK // catches some trivial draws like KK, KBK and KNK and gives a very drawish
// scale factor for cases such as KRKBP and KmmKm (except for KBBKN).
if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg) if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg)
{ e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW : npm_b <= BishopValueMg ? 4 : 12);
e->factor[WHITE] = (uint8_t)
(npm_w == npm_b || npm_w < RookValueMg ? 0 : NoPawnsSF[std::min(pos.count<BISHOP>(WHITE), 2)]);
}
if (!pos.count<PAWN>(BLACK) && npm_b - npm_w <= BishopValueMg) if (!pos.count<PAWN>(BLACK) && npm_b - npm_w <= BishopValueMg)
{ e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW : npm_w <= BishopValueMg ? 4 : 12);
e->factor[BLACK] = (uint8_t)
(npm_w == npm_b || npm_b < RookValueMg ? 0 : NoPawnsSF[std::min(pos.count<BISHOP>(BLACK), 2)]); if (pos.count<PAWN>(WHITE) == 1 && npm_w - npm_b <= BishopValueMg)
} e->factor[WHITE] = (uint8_t) SCALE_FACTOR_ONEPAWN;
if (pos.count<PAWN>(BLACK) == 1 && npm_b - npm_w <= BishopValueMg)
e->factor[BLACK] = (uint8_t) SCALE_FACTOR_ONEPAWN;
// Compute the space weight // Compute the space weight
if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg) if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg)
@@ -256,7 +236,7 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
} }
// Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
// for the bishop pair "extended piece", this allow us to be more flexible // for the bishop pair "extended piece", which allows us to be more flexible
// in defining bishop pair bonuses. // in defining bishop pair bonuses.
const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = { const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = {
{ pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE), { pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE),

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -42,8 +42,19 @@ struct Entry {
Score space_weight() const { return spaceWeight; } Score space_weight() const { return spaceWeight; }
Phase game_phase() const { return gamePhase; } Phase game_phase() const { return gamePhase; }
bool specialized_eval_exists() const { return evaluationFunction != NULL; } bool specialized_eval_exists() const { return evaluationFunction != NULL; }
Value evaluate(const Position& p) const { return (*evaluationFunction)(p); } Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); }
ScaleFactor scale_factor(const Position& pos, Color c) const;
// scale_factor takes a position and a color as input, and returns a scale factor
// for the given color. We have to provide the position in addition to the color,
// because the scale factor need not be a constant: It can also be a function
// which should be applied to the position. For instance, in KBP vs K endgames,
// a scaling function for draws with rook pawns and wrong-colored bishops.
ScaleFactor scale_factor(const Position& pos, Color c) const {
return !scalingFunction[c] || (*scalingFunction[c])(pos) == SCALE_FACTOR_NONE
? ScaleFactor(factor[c]) : (*scalingFunction[c])(pos);
}
Key key; Key key;
int16_t value; int16_t value;
@@ -59,19 +70,6 @@ typedef HashTable<Entry, 8192> Table;
Entry* probe(const Position& pos, Table& entries, Endgames& endgames); Entry* probe(const Position& pos, Table& entries, Endgames& endgames);
Phase game_phase(const Position& pos); Phase game_phase(const Position& pos);
/// Material::scale_factor takes a position and a color as input, and } // namespace Material
/// returns a scale factor for the given color. We have to provide the
/// position in addition to the color, because the scale factor need not
/// to be a constant: It can also be a function which should be applied to
/// the position. For instance, in KBP vs K endgames, a scaling function
/// which checks for draws with rook pawns and wrong-colored bishops.
inline ScaleFactor Entry::scale_factor(const Position& pos, Color c) const {
return !scalingFunction[c] || (*scalingFunction[c])(pos) == SCALE_FACTOR_NONE
? ScaleFactor(factor[c]) : (*scalingFunction[c])(pos);
}
}
#endif // #ifndef MATERIAL_H_INCLUDED #endif // #ifndef MATERIAL_H_INCLUDED

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -26,9 +26,9 @@
using namespace std; using namespace std;
/// Version number. If Version is left empty, then compile date, in the /// Version number. If Version is left empty, then compile date in the format
/// format DD-MM-YY, is shown in engine_info. /// DD-MM-YY and show in engine_info.
static const string Version = "DD"; static const string Version = "5";
/// engine_info() returns the full name of the current Stockfish version. This /// engine_info() returns the full name of the current Stockfish version. This
@@ -40,28 +40,28 @@ const string engine_info(bool to_uci) {
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
string month, day, year; string month, day, year;
stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008" stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008"
s << "Stockfish " << Version << setfill('0'); ss << "Stockfish " << Version << setfill('0');
if (Version.empty()) if (Version.empty())
{ {
date >> month >> day >> year; date >> month >> day >> year;
s << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2); ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
} }
s << (Is64Bit ? " 64" : "") ss << (Is64Bit ? " 64" : "")
<< (HasPopCnt ? " SSE4.2" : "") << (HasPext ? " BMI2" : (HasPopCnt ? " SSE4.2" : ""))
<< (to_uci ? "\nid author ": " by ") << (to_uci ? "\nid author ": " by ")
<< "Tord Romstad, Marco Costalba and Joona Kiiski"; << "Tord Romstad, Marco Costalba and Joona Kiiski";
return s.str(); return ss.str();
} }
/// Debug functions used mainly to collect run-time statistics /// Debug functions used mainly to collect run-time statistics
static uint64_t hits[2], means[2]; static int64_t hits[2], means[2];
void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; } void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; }
void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); } void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); }
@@ -81,8 +81,8 @@ void dbg_print() {
/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
/// can toggle the logging of std::cout and std:cin at runtime while preserving /// can toggle the logging of std::cout and std:cin at runtime whilst preserving
/// usual i/o functionality and without changing a single line of code! /// usual i/o functionality, all without changing a single line of code!
/// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81 /// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
struct Tie: public streambuf { // MSVC requires splitted streambuf for cin and cout struct Tie: public streambuf { // MSVC requires splitted streambuf for cin and cout
@@ -137,17 +137,17 @@ public:
}; };
/// Used to serialize access to std::cout to avoid multiple threads to write at /// Used to serialize access to std::cout to avoid multiple threads writing at
/// the same time. /// the same time.
std::ostream& operator<<(std::ostream& os, SyncCout sc) { std::ostream& operator<<(std::ostream& os, SyncCout sc) {
static Mutex m; static Mutex m;
if (sc == io_lock) if (sc == IO_LOCK)
m.lock(); m.lock();
if (sc == io_unlock) if (sc == IO_UNLOCK)
m.unlock(); m.unlock();
return os; return os;
@@ -158,8 +158,8 @@ std::ostream& operator<<(std::ostream& os, SyncCout sc) {
void start_logger(bool b) { Logger::start(b); } void start_logger(bool b) { Logger::start(b); }
/// timed_wait() waits for msec milliseconds. It is mainly an helper to wrap /// timed_wait() waits for msec milliseconds. It is mainly a helper to wrap
/// conversion from milliseconds to struct timespec, as used by pthreads. /// the conversion from milliseconds to struct timespec, as used by pthreads.
void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) { void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) {
@@ -177,9 +177,9 @@ void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) {
} }
/// prefetch() preloads the given address in L1/L2 cache. This is a non /// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking
/// blocking function and do not stalls the CPU waiting for data to be /// function that doesn't stall the CPU waiting for data to be loaded from memory,
/// loaded from memory, that can be quite slow. /// which can be quite slow.
#ifdef NO_PREFETCH #ifdef NO_PREFETCH
void prefetch(char*) {} void prefetch(char*) {}
@@ -189,8 +189,8 @@ void prefetch(char*) {}
void prefetch(char* addr) { void prefetch(char* addr) {
# if defined(__INTEL_COMPILER) # if defined(__INTEL_COMPILER)
// This hack prevents prefetches to be optimized away by // This hack prevents prefetches from being optimized away by
// Intel compiler. Both MSVC and gcc seems not affected. // Intel compiler. Both MSVC and gcc seem not be affected by this.
__asm__ (""); __asm__ ("");
# endif # endif

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -51,18 +51,18 @@ namespace Time {
template<class Entry, int Size> template<class Entry, int Size>
struct HashTable { struct HashTable {
HashTable() : e(Size, Entry()) {} HashTable() : table(Size, Entry()) {}
Entry* operator[](Key k) { return &e[(uint32_t)k & (Size - 1)]; } Entry* operator[](Key k) { return &table[(uint32_t)k & (Size - 1)]; }
private: private:
std::vector<Entry> e; std::vector<Entry> table;
}; };
enum SyncCout { io_lock, io_unlock }; enum SyncCout { IO_LOCK, IO_UNLOCK };
std::ostream& operator<<(std::ostream&, SyncCout); std::ostream& operator<<(std::ostream&, SyncCout);
#define sync_cout std::cout << io_lock #define sync_cout std::cout << IO_LOCK
#define sync_endl std::endl << io_unlock #define sync_endl std::endl << IO_UNLOCK
#endif // #ifndef MISC_H_INCLUDED #endif // #ifndef MISC_H_INCLUDED

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -22,34 +22,29 @@
#include "movegen.h" #include "movegen.h"
#include "position.h" #include "position.h"
/// Simple macro to wrap a very common while loop, no facny, no flexibility,
/// hardcoded names 'mlist' and 'from'.
#define SERIALIZE(b) while (b) (mlist++)->move = make_move(from, pop_lsb(&b))
/// Version used for pawns, where the 'from' square is given as a delta from the 'to' square
#define SERIALIZE_PAWNS(b, d) while (b) { Square to = pop_lsb(&b); \
(mlist++)->move = make_move(to - (d), to); }
namespace { namespace {
template<CastlingSide Side, bool Checks, bool Chess960> template<CastlingRight Cr, bool Checks, bool Chess960>
ExtMove* generate_castle(const Position& pos, ExtMove* mlist, Color us) { ExtMove* generate_castling(const Position& pos, ExtMove* mlist, Color us, const CheckInfo* ci) {
if (pos.castle_impeded(us, Side) || !pos.can_castle(make_castle_right(us, Side))) static const bool KingSide = (Cr == WHITE_OO || Cr == BLACK_OO);
if (pos.castling_impeded(Cr) || !pos.can_castle(Cr))
return mlist; return mlist;
// After castling, the rook and king final positions are the same in Chess960 // After castling, the rook and king final positions are the same in Chess960
// as they would be in standard chess. // as they would be in standard chess.
Square kfrom = pos.king_square(us); Square kfrom = pos.king_square(us);
Square rfrom = pos.castle_rook_square(us, Side); Square rfrom = pos.castling_rook_square(Cr);
Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1); Square kto = relative_square(us, KingSide ? SQ_G1 : SQ_C1);
Bitboard enemies = pos.pieces(~us); Bitboard enemies = pos.pieces(~us);
assert(!pos.checkers()); assert(!pos.checkers());
const int K = Chess960 ? kto > kfrom ? -1 : 1 const Square K = Chess960 ? kto > kfrom ? DELTA_W : DELTA_E
: Side == KING_SIDE ? -1 : 1; : KingSide ? DELTA_W : DELTA_E;
for (Square s = kto; s != kfrom; s += (Square)K) for (Square s = kto; s != kfrom; s += K)
if (pos.attackers_to(s) & enemies) if (pos.attackers_to(s) & enemies)
return mlist; return mlist;
@@ -59,10 +54,12 @@ namespace {
if (Chess960 && (attacks_bb<ROOK>(kto, pos.pieces() ^ rfrom) & pos.pieces(~us, ROOK, QUEEN))) if (Chess960 && (attacks_bb<ROOK>(kto, pos.pieces() ^ rfrom) & pos.pieces(~us, ROOK, QUEEN)))
return mlist; return mlist;
(mlist++)->move = make<CASTLE>(kfrom, rfrom); Move m = make<CASTLING>(kfrom, rfrom);
if (Checks && !pos.gives_check((mlist - 1)->move, CheckInfo(pos))) if (Checks && !pos.gives_check(m, *ci))
--mlist; return mlist;
(mlist++)->move = m;
return mlist; return mlist;
} }
@@ -88,8 +85,8 @@ namespace {
(mlist++)->move = make<PROMOTION>(to - Delta, to, KNIGHT); (mlist++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
} }
// Knight-promotion is the only one that can give a direct check not // Knight promotion is the only promotion that can give a direct check
// already included in the queen-promotion. // that's not already included in the queen promotion.
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq)) if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq))
(mlist++)->move = make<PROMOTION>(to - Delta, to, KNIGHT); (mlist++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
else else
@@ -144,7 +141,7 @@ namespace {
// Add pawn pushes which give discovered check. This is possible only // Add pawn pushes which give discovered check. This is possible only
// if the pawn is not on the same file as the enemy king, because we // if the pawn is not on the same file as the enemy king, because we
// don't generate captures. Note that a possible discovery check // don't generate captures. Note that a possible discovery check
// promotion has been already generated among captures. // promotion has been already generated amongst the captures.
if (pawnsNotOn7 & ci->dcCandidates) if (pawnsNotOn7 & ci->dcCandidates)
{ {
dc1 = shift_bb<Up>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq); dc1 = shift_bb<Up>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq);
@@ -155,8 +152,17 @@ namespace {
} }
} }
SERIALIZE_PAWNS(b1, Up); while (b1)
SERIALIZE_PAWNS(b2, Up + Up); {
Square to = pop_lsb(&b1);
(mlist++)->move = make_move(to - Up, to);
}
while (b2)
{
Square to = pop_lsb(&b2);
(mlist++)->move = make_move(to - Up - Up, to);
}
} }
// Promotions and underpromotions // Promotions and underpromotions
@@ -179,8 +185,17 @@ namespace {
b1 = shift_bb<Right>(pawnsNotOn7) & enemies; b1 = shift_bb<Right>(pawnsNotOn7) & enemies;
b2 = shift_bb<Left >(pawnsNotOn7) & enemies; b2 = shift_bb<Left >(pawnsNotOn7) & enemies;
SERIALIZE_PAWNS(b1, Right); while (b1)
SERIALIZE_PAWNS(b2, Left); {
Square to = pop_lsb(&b1);
(mlist++)->move = make_move(to - Right, to);
}
while (b2)
{
Square to = pop_lsb(&b2);
(mlist++)->move = make_move(to - Left, to);
}
if (pos.ep_square() != SQ_NONE) if (pos.ep_square() != SQ_NONE)
{ {
@@ -230,7 +245,8 @@ namespace {
if (Checks) if (Checks)
b &= ci->checkSq[Pt]; b &= ci->checkSq[Pt];
SERIALIZE(b); while (b)
(mlist++)->move = make_move(from, pop_lsb(&b));
} }
return mlist; return mlist;
@@ -251,22 +267,23 @@ namespace {
if (Type != QUIET_CHECKS && Type != EVASIONS) if (Type != QUIET_CHECKS && Type != EVASIONS)
{ {
Square from = pos.king_square(Us); Square ksq = pos.king_square(Us);
Bitboard b = pos.attacks_from<KING>(from) & target; Bitboard b = pos.attacks_from<KING>(ksq) & target;
SERIALIZE(b); while (b)
(mlist++)->move = make_move(ksq, pop_lsb(&b));
} }
if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us)) if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us))
{ {
if (pos.is_chess960()) if (pos.is_chess960())
{ {
mlist = generate_castle< KING_SIDE, Checks, true>(pos, mlist, Us); mlist = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, true>(pos, mlist, Us, ci);
mlist = generate_castle<QUEEN_SIDE, Checks, true>(pos, mlist, Us); mlist = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, true>(pos, mlist, Us, ci);
} }
else else
{ {
mlist = generate_castle< KING_SIDE, Checks, false>(pos, mlist, Us); mlist = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, false>(pos, mlist, Us, ci);
mlist = generate_castle<QUEEN_SIDE, Checks, false>(pos, mlist, Us); mlist = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, false>(pos, mlist, Us, ci);
} }
} }
@@ -325,14 +342,15 @@ ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* mlist) {
PieceType pt = type_of(pos.piece_on(from)); PieceType pt = type_of(pos.piece_on(from));
if (pt == PAWN) if (pt == PAWN)
continue; // Will be generated togheter with direct checks continue; // Will be generated together with direct checks
Bitboard b = pos.attacks_from(Piece(pt), from) & ~pos.pieces(); Bitboard b = pos.attacks_from(Piece(pt), from) & ~pos.pieces();
if (pt == KING) if (pt == KING)
b &= ~PseudoAttacks[QUEEN][ci.ksq]; b &= ~PseudoAttacks[QUEEN][ci.ksq];
SERIALIZE(b); while (b)
(mlist++)->move = make_move(from, pop_lsb(&b));
} }
return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, mlist, ~pos.pieces(), &ci) return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, mlist, ~pos.pieces(), &ci)
@@ -347,36 +365,30 @@ ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* mlist) {
assert(pos.checkers()); assert(pos.checkers());
int checkersCnt = 0;
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Square ksq = pos.king_square(us), from = ksq /* For SERIALIZE */, checksq; Square ksq = pos.king_square(us);
Bitboard sliderAttacks = 0; Bitboard sliderAttacks = 0;
Bitboard b = pos.checkers(); Bitboard sliders = pos.checkers() & ~pos.pieces(KNIGHT, PAWN);
assert(pos.checkers()); // Find all the squares attacked by slider checkers. We will remove them from
// the king evasions in order to skip known illegal moves, which avoids any
// Find squares attacked by slider checkers, we will remove them from the king // useless legality checks later on.
// evasions so to skip known illegal moves avoiding useless legality check later. while (sliders)
do
{ {
++checkersCnt; Square checksq = pop_lsb(&sliders);
checksq = pop_lsb(&b);
assert(color_of(pos.piece_on(checksq)) == ~us);
if (type_of(pos.piece_on(checksq)) > KNIGHT) // A slider
sliderAttacks |= LineBB[checksq][ksq] ^ checksq; sliderAttacks |= LineBB[checksq][ksq] ^ checksq;
}
} while (b);
// Generate evasions for king, capture and non capture moves // Generate evasions for king, capture and non capture moves
b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks; Bitboard b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
SERIALIZE(b); while (b)
(mlist++)->move = make_move(ksq, pop_lsb(&b));
if (checkersCnt > 1) if (more_than_one(pos.checkers()))
return mlist; // Double check, only a king move can save the day return mlist; // Double check, only a king move can save the day
// Generate blocking evasions or captures of the checking piece // Generate blocking evasions or captures of the checking piece
Square checksq = lsb(pos.checkers());
Bitboard target = between_bb(checksq, ksq) | checksq; Bitboard target = between_bb(checksq, ksq) | checksq;
return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, mlist, target) return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, mlist, target)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -36,8 +36,8 @@ class Position;
template<GenType> template<GenType>
ExtMove* generate(const Position& pos, ExtMove* mlist); ExtMove* generate(const Position& pos, ExtMove* mlist);
/// The MoveList struct is a simple wrapper around generate(), sometimes comes /// The MoveList struct is a simple wrapper around generate(). It sometimes comes
/// handy to use this class instead of the low level generate() function. /// in handy to use this class instead of the low level generate() function.
template<GenType T> template<GenType T>
struct MoveList { struct MoveList {

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -35,7 +35,7 @@ namespace {
STOP STOP
}; };
// Our insertion sort, guaranteed to be stable, as is needed // Our insertion sort, which is guaranteed (and also needed) to be stable
void insertion_sort(ExtMove* begin, ExtMove* end) void insertion_sort(ExtMove* begin, ExtMove* end)
{ {
ExtMove tmp, *p, *q; ExtMove tmp, *p, *q;
@@ -49,13 +49,13 @@ namespace {
} }
} }
// Unary predicate used by std::partition to split positive scores from remaining // Unary predicate used by std::partition to split positive values from remaining
// ones so to sort separately the two sets, and with the second sort delayed. // ones so as to sort the two sets separately, with the second sort delayed.
inline bool has_positive_score(const ExtMove& ms) { return ms.score > 0; } inline bool has_positive_value(const ExtMove& ms) { return ms.value > 0; }
// Picks and moves to the front the best move in the range [begin, end), // Picks the best move in the range (begin, end) and moves it to the front.
// it is faster than sorting all the moves in advance when moves are few, as // It's faster than sorting all the moves in advance when there are few
// normally are the possible captures. // moves e.g. possible captures.
inline ExtMove* pick_best(ExtMove* begin, ExtMove* end) inline ExtMove* pick_best(ExtMove* begin, ExtMove* end)
{ {
std::swap(*begin, *std::max_element(begin, end)); std::swap(*begin, *std::max_element(begin, end));
@@ -65,22 +65,23 @@ namespace {
/// Constructors of the MovePicker class. As arguments we pass information /// Constructors of the MovePicker class. As arguments we pass information
/// to help it to return the presumably good moves first, to decide which /// 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 /// moves to return (in the quiescence search, for instance, we only want to
/// search captures, promotions and some checks) and about how important good /// search captures, promotions and some checks) and how important good move
/// move ordering is at the current node. /// ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h, MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h,
Move* cm, Search::Stack* s) : pos(p), history(h), depth(d) { Move* cm, Move* fm, Search::Stack* s) : pos(p), history(h), depth(d) {
assert(d > DEPTH_ZERO); assert(d > DEPTH_ZERO);
cur = end = moves; cur = end = moves;
endBadCaptures = moves + MAX_MOVES - 1; endBadCaptures = moves + MAX_MOVES - 1;
countermoves = cm; countermoves = cm;
followupmoves = fm;
ss = s; ss = s;
if (p.checkers()) if (pos.checkers())
stage = EVASION; stage = EVASION;
else else
@@ -91,11 +92,11 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
} }
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h, MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h,
Square sq) : pos(p), history(h), cur(moves), end(moves) { Square s) : pos(p), history(h), cur(moves), end(moves) {
assert(d <= DEPTH_ZERO); assert(d <= DEPTH_ZERO);
if (p.checkers()) if (pos.checkers())
stage = EVASION; stage = EVASION;
else if (d > DEPTH_QS_NO_CHECKS) else if (d > DEPTH_QS_NO_CHECKS)
@@ -105,7 +106,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
{ {
stage = QSEARCH_1; stage = QSEARCH_1;
// Skip TT move if is not a capture or a promotion, this avoids qsearch // Skip TT move if is not a capture or a promotion. This avoids qsearch
// tree explosion due to a possible perpetual check or similar rare cases // tree explosion due to a possible perpetual check or similar rare cases
// when TT table is full. // when TT table is full.
if (ttm && !pos.capture_or_promotion(ttm)) if (ttm && !pos.capture_or_promotion(ttm))
@@ -114,7 +115,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
else else
{ {
stage = RECAPTURE; stage = RECAPTURE;
recaptureSquare = sq; recaptureSquare = s;
ttm = MOVE_NONE; ttm = MOVE_NONE;
} }
@@ -129,7 +130,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, Piece
stage = PROBCUT; stage = PROBCUT;
// In ProbCut we generate only captures better than parent's captured piece // In ProbCut we generate only captures that are better than the parent's
// captured piece.
captureThreshold = PieceValue[MG][pt]; captureThreshold = PieceValue[MG][pt];
ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE); ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE);
@@ -140,8 +142,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, Piece
} }
/// score() assign a numerical move ordering score to each move in a move list. /// score() assign a numerical value to each move in a move list. The moves with
/// The moves with highest scores will be picked first. /// highest values will be picked first.
template<> template<>
void MovePicker::score<CAPTURES>() { void MovePicker::score<CAPTURES>() {
// Winning and equal captures in the main search are ordered by MVV/LVA. // Winning and equal captures in the main search are ordered by MVV/LVA.
@@ -153,23 +155,23 @@ void MovePicker::score<CAPTURES>() {
// where it is possible to recapture with the hanging piece). Exchanging // where it is possible to recapture with the hanging piece). Exchanging
// big pieces before capturing a hanging piece probably helps to reduce // big pieces before capturing a hanging piece probably helps to reduce
// the subtree size. // the subtree size.
// In main search we want to push captures with negative SEE values to // In main search we want to push captures with negative SEE values to the
// badCaptures[] array, but instead of doing it now we delay till when // badCaptures[] array, but instead of doing it now we delay until the move
// the move has been picked up in pick_move_from_list(), this way we save // has been picked up in pick_move_from_list(). This way we save some SEE
// some SEE calls in case we get a cutoff (idea from Pablo Vazquez). // calls in case we get a cutoff.
Move m; Move m;
for (ExtMove* it = moves; it != end; ++it) for (ExtMove* it = moves; it != end; ++it)
{ {
m = it->move; m = it->move;
it->score = PieceValue[MG][pos.piece_on(to_sq(m))] it->value = PieceValue[MG][pos.piece_on(to_sq(m))]
- type_of(pos.moved_piece(m)); - Value(type_of(pos.moved_piece(m)));
if (type_of(m) == PROMOTION) if (type_of(m) == ENPASSANT)
it->score += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN]; it->value += PieceValue[MG][PAWN];
else if (type_of(m) == ENPASSANT) else if (type_of(m) == PROMOTION)
it->score += PieceValue[MG][PAWN]; it->value += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN];
} }
} }
@@ -181,7 +183,7 @@ void MovePicker::score<QUIETS>() {
for (ExtMove* it = moves; it != end; ++it) for (ExtMove* it = moves; it != end; ++it)
{ {
m = it->move; m = it->move;
it->score = history[pos.moved_piece(m)][to_sq(m)]; it->value = history[pos.moved_piece(m)][to_sq(m)];
} }
} }
@@ -189,29 +191,29 @@ template<>
void MovePicker::score<EVASIONS>() { void MovePicker::score<EVASIONS>() {
// Try good captures ordered by MVV/LVA, then non-captures if destination square // Try good captures ordered by MVV/LVA, then non-captures if destination square
// is not under attack, ordered by history value, then bad-captures and quiet // is not under attack, ordered by history value, then bad-captures and quiet
// moves with a negative SEE. This last group is ordered by the SEE score. // moves with a negative SEE. This last group is ordered by the SEE value.
Move m; Move m;
int seeScore; Value see;
for (ExtMove* it = moves; it != end; ++it) for (ExtMove* it = moves; it != end; ++it)
{ {
m = it->move; m = it->move;
if ((seeScore = pos.see_sign(m)) < 0) if ((see = pos.see_sign(m)) < VALUE_ZERO)
it->score = seeScore - HistoryStats::Max; // At the bottom it->value = see - HistoryStats::Max; // At the bottom
else if (pos.capture(m)) else if (pos.capture(m))
it->score = PieceValue[MG][pos.piece_on(to_sq(m))] it->value = PieceValue[MG][pos.piece_on(to_sq(m))]
- type_of(pos.moved_piece(m)) + HistoryStats::Max; - Value(type_of(pos.moved_piece(m))) + HistoryStats::Max;
else else
it->score = history[pos.moved_piece(m)][to_sq(m)]; it->value = history[pos.moved_piece(m)][to_sq(m)];
} }
} }
/// generate_next() generates, scores and sorts the next bunch of moves, when /// generate_next_stage() generates, scores and sorts the next bunch of moves,
/// there are no more moves to try for the current phase. /// when there are no more moves to try for the current stage.
void MovePicker::generate_next() { void MovePicker::generate_next_stage() {
cur = moves; cur = moves;
@@ -229,21 +231,30 @@ void MovePicker::generate_next() {
killers[0].move = ss->killers[0]; killers[0].move = ss->killers[0];
killers[1].move = ss->killers[1]; killers[1].move = ss->killers[1];
killers[2].move = killers[3].move = MOVE_NONE; killers[2].move = killers[3].move = MOVE_NONE;
killers[4].move = killers[5].move = MOVE_NONE;
// Please note that following code is racy and could yield to rare (less
// than 1 out of a million) duplicated entries in SMP case. This is harmless.
// Be sure countermoves are different from killers // Be sure countermoves are different from killers
for (int i = 0; i < 2; ++i) for (int i = 0; i < 2; ++i)
if (countermoves[i] != cur->move && countermoves[i] != (cur+1)->move) if ( countermoves[i] != (cur+0)->move
&& countermoves[i] != (cur+1)->move)
(end++)->move = countermoves[i]; (end++)->move = countermoves[i];
if (countermoves[1] && countermoves[1] == countermoves[0]) // Due to SMP races // Be sure followupmoves are different from killers and countermoves
killers[3].move = MOVE_NONE; for (int i = 0; i < 2; ++i)
if ( followupmoves[i] != (cur+0)->move
&& followupmoves[i] != (cur+1)->move
&& followupmoves[i] != (cur+2)->move
&& followupmoves[i] != (cur+3)->move)
(end++)->move = followupmoves[i];
return; return;
case QUIETS_1_S1: case QUIETS_1_S1:
endQuiets = end = generate<QUIETS>(pos, moves); endQuiets = end = generate<QUIETS>(pos, moves);
score<QUIETS>(); score<QUIETS>();
end = std::partition(cur, end, has_positive_score); end = std::partition(cur, end, has_positive_value);
insertion_sort(cur, end); insertion_sort(cur, end);
return; return;
@@ -272,6 +283,8 @@ void MovePicker::generate_next() {
case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE:
stage = STOP; stage = STOP;
/* Fall through */
case STOP: case STOP:
end = cur + 1; // Avoid another next_phase() call end = cur + 1; // Avoid another next_phase() call
return; return;
@@ -283,9 +296,9 @@ void MovePicker::generate_next() {
/// next_move() is the most important method of the MovePicker class. It returns /// next_move() is the most important method of the MovePicker class. It returns
/// a new pseudo legal move every time is called, until there are no more moves /// a new pseudo legal move every time it is called, until there are no more moves
/// left. It picks the move with the biggest score from a list of generated moves /// left. It picks the move with the biggest value from a list of generated moves
/// taking care not returning the ttMove if has already been searched previously. /// taking care not to return the ttMove if it has already been searched.
template<> template<>
Move MovePicker::next_move<false>() { Move MovePicker::next_move<false>() {
@@ -294,7 +307,7 @@ Move MovePicker::next_move<false>() {
while (true) while (true)
{ {
while (cur == end) while (cur == end)
generate_next(); generate_next_stage();
switch (stage) { switch (stage) {
@@ -306,7 +319,7 @@ Move MovePicker::next_move<false>() {
move = pick_best(cur++, end)->move; move = pick_best(cur++, end)->move;
if (move != ttMove) if (move != ttMove)
{ {
if (pos.see_sign(move) >= 0) if (pos.see_sign(move) >= VALUE_ZERO)
return move; return move;
// Losing capture, move it to the tail of the array // Losing capture, move it to the tail of the array
@@ -317,8 +330,8 @@ Move MovePicker::next_move<false>() {
case KILLERS_S1: case KILLERS_S1:
move = (cur++)->move; move = (cur++)->move;
if ( move != MOVE_NONE if ( move != MOVE_NONE
&& pos.pseudo_legal(move)
&& move != ttMove && move != ttMove
&& pos.pseudo_legal(move)
&& !pos.capture(move)) && !pos.capture(move))
return move; return move;
break; break;
@@ -329,7 +342,9 @@ Move MovePicker::next_move<false>() {
&& move != killers[0].move && move != killers[0].move
&& move != killers[1].move && move != killers[1].move
&& move != killers[2].move && move != killers[2].move
&& move != killers[3].move) && move != killers[3].move
&& move != killers[4].move
&& move != killers[5].move)
return move; return move;
break; break;

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -35,32 +35,32 @@
/// and is used for reduction and move ordering decisions. Gains records the move's /// and is used for reduction and move ordering decisions. Gains records the move's
/// best evaluation gain from one ply to the next and is used for pruning decisions. /// best evaluation gain from one ply to the next and is used for pruning decisions.
/// Countermoves store the move that refute a previous one. Entries are stored /// Countermoves store the move that refute a previous one. Entries are stored
/// according only to moving piece and destination square, hence two moves with /// using only the moving piece and destination square, hence two moves with
/// different origin but same destination and piece will be considered identical. /// different origin but same destination and piece will be considered identical.
template<bool Gain, typename T> template<bool Gain, typename T>
struct Stats { struct Stats {
static const Value Max = Value(2000); static const Value Max = Value(2000);
const T* operator[](Piece p) const { return table[p]; } const T* operator[](Piece pc) const { return table[pc]; }
void clear() { std::memset(table, 0, sizeof(table)); } void clear() { std::memset(table, 0, sizeof(table)); }
void update(Piece p, Square to, Move m) { void update(Piece pc, Square to, Move m) {
if (m == table[p][to].first) if (m == table[pc][to].first)
return; return;
table[p][to].second = table[p][to].first; table[pc][to].second = table[pc][to].first;
table[p][to].first = m; table[pc][to].first = m;
} }
void update(Piece p, Square to, Value v) { void update(Piece pc, Square to, Value v) {
if (Gain) if (Gain)
table[p][to] = std::max(v, table[p][to] - 1); table[pc][to] = std::max(v, table[pc][to] - 1);
else if (abs(table[p][to] + v) < Max) else if (abs(table[pc][to] + v) < Max)
table[p][to] += v; table[pc][to] += v;
} }
private: private:
@@ -69,7 +69,7 @@ private:
typedef Stats< true, Value> GainsStats; typedef Stats< true, Value> GainsStats;
typedef Stats<false, Value> HistoryStats; typedef Stats<false, Value> HistoryStats;
typedef Stats<false, std::pair<Move, Move> > CountermovesStats; typedef Stats<false, std::pair<Move, Move> > MovesStats;
/// MovePicker class is used to pick one pseudo legal move at a time from the /// MovePicker class is used to pick one pseudo legal move at a time from the
@@ -86,23 +86,25 @@ class MovePicker {
public: public:
MovePicker(const Position&, Move, Depth, const HistoryStats&, Square); MovePicker(const Position&, Move, Depth, const HistoryStats&, Square);
MovePicker(const Position&, Move, const HistoryStats&, PieceType); MovePicker(const Position&, Move, const HistoryStats&, PieceType);
MovePicker(const Position&, Move, Depth, const HistoryStats&, Move*, Search::Stack*); MovePicker(const Position&, Move, Depth, const HistoryStats&, Move*, Move*, Search::Stack*);
template<bool SpNode> Move next_move(); template<bool SpNode> Move next_move();
private: private:
template<GenType> void score(); template<GenType> void score();
void generate_next(); void generate_next_stage();
const Position& pos; const Position& pos;
const HistoryStats& history; const HistoryStats& history;
Search::Stack* ss; Search::Stack* ss;
Move* countermoves; Move* countermoves;
Move* followupmoves;
Depth depth; Depth depth;
Move ttMove; Move ttMove;
ExtMove killers[4]; ExtMove killers[6];
Square recaptureSquare; Square recaptureSquare;
int captureThreshold, stage; Value captureThreshold;
int stage;
ExtMove *cur, *end, *endQuiets, *endBadCaptures; ExtMove *cur, *end, *endQuiets, *endBadCaptures;
ExtMove moves[MAX_MOVES]; ExtMove moves[MAX_MOVES];
}; };

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -40,23 +40,23 @@ static const char* PieceToChar[COLOR_NB] = { " PNBRQK", " pnbrqk" };
string score_to_uci(Value v, Value alpha, Value beta) { string score_to_uci(Value v, Value alpha, Value beta) {
stringstream s; stringstream ss;
if (abs(v) < VALUE_MATE_IN_MAX_PLY) if (abs(v) < VALUE_MATE_IN_MAX_PLY)
s << "cp " << v * 100 / int(PawnValueMg); ss << "cp " << v * 100 / PawnValueEg;
else else
s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
s << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : "");
return s.str(); return ss.str();
} }
/// move_to_uci() converts a move to a string in coordinate notation /// move_to_uci() converts a move to a string in coordinate notation
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we print /// (g1f3, a7a8q, etc.). The only special case is castling moves, where we print
/// in the e1g1 notation in normal chess mode, and in e1h1 notation in chess960 /// in the e1g1 notation in normal chess mode, and in e1h1 notation in chess960
/// mode. Internally castle moves are always coded as "king captures rook". /// mode. Internally castling moves are always encoded as "king captures rook".
const string move_to_uci(Move m, bool chess960) { const string move_to_uci(Move m, bool chess960) {
@@ -69,10 +69,10 @@ const string move_to_uci(Move m, bool chess960) {
if (m == MOVE_NULL) if (m == MOVE_NULL)
return "0000"; return "0000";
if (type_of(m) == CASTLE && !chess960) if (type_of(m) == CASTLING && !chess960)
to = (to > from ? FILE_G : FILE_C) | rank_of(from); to = make_square(to > from ? FILE_G : FILE_C, rank_of(from));
string move = square_to_string(from) + square_to_string(to); string move = to_string(from) + to_string(to);
if (type_of(m) == PROMOTION) if (type_of(m) == PROMOTION)
move += PieceToChar[BLACK][promotion_type(m)]; // Lower case move += PieceToChar[BLACK][promotion_type(m)]; // Lower case
@@ -118,7 +118,7 @@ const string move_to_san(Position& pos, Move m) {
Piece pc = pos.piece_on(from); Piece pc = pos.piece_on(from);
PieceType pt = type_of(pc); PieceType pt = type_of(pc);
if (type_of(m) == CASTLE) if (type_of(m) == CASTLING)
san = to > from ? "O-O" : "O-O-O"; san = to > from ? "O-O" : "O-O-O";
else else
{ {
@@ -126,36 +126,36 @@ const string move_to_san(Position& pos, Move m) {
{ {
san = PieceToChar[WHITE][pt]; // Upper case san = PieceToChar[WHITE][pt]; // Upper case
// Disambiguation if we have more then one piece of type 'pt' that can // A disambiguation occurs if we have more then one piece of type 'pt'
// reach 'to' with a legal move. // that can reach 'to' with a legal move.
others = b = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from; others = b = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from;
while (b) while (b)
{ {
Move move = make_move(pop_lsb(&b), to); Square s = pop_lsb(&b);
if (!pos.legal(move, pos.pinned_pieces(pos.side_to_move()))) if (!pos.legal(make_move(s, to), pos.pinned_pieces(us)))
others ^= from_sq(move); others ^= s;
} }
if (others) if (!others)
{ { /* Disambiguation is not needed */ }
if (!(others & file_bb(from)))
san += file_to_char(file_of(from)); else if (!(others & file_bb(from)))
san += to_char(file_of(from));
else if (!(others & rank_bb(from))) else if (!(others & rank_bb(from)))
san += rank_to_char(rank_of(from)); san += to_char(rank_of(from));
else else
san += square_to_string(from); san += to_string(from);
}
} }
else if (pos.capture(m)) else if (pos.capture(m))
san = file_to_char(file_of(from)); san = to_char(file_of(from));
if (pos.capture(m)) if (pos.capture(m))
san += 'x'; san += 'x';
san += square_to_string(to); san += to_string(to);
if (type_of(m) == PROMOTION) if (type_of(m) == PROMOTION)
san += string("=") + PieceToChar[WHITE][promotion_type(m)]; san += string("=") + PieceToChar[WHITE][promotion_type(m)];
@@ -175,9 +175,9 @@ const string move_to_san(Position& pos, Move m) {
/// pretty_pv() formats human-readable search information, typically to be /// pretty_pv() formats human-readable search information, typically to be
/// appended to the search log file. It uses the two helpers below to pretty /// appended to the search log file. It uses the two helpers below to pretty
/// format time and score respectively. /// format the time and score respectively.
static string time_to_string(int64_t msecs) { static string format(int64_t msecs) {
const int MSecMinute = 1000 * 60; const int MSecMinute = 1000 * 60;
const int MSecHour = 1000 * 60 * 60; const int MSecHour = 1000 * 60 * 60;
@@ -186,71 +186,64 @@ static string time_to_string(int64_t msecs) {
int64_t minutes = (msecs % MSecHour) / MSecMinute; int64_t minutes = (msecs % MSecHour) / MSecMinute;
int64_t seconds = ((msecs % MSecHour) % MSecMinute) / 1000; int64_t seconds = ((msecs % MSecHour) % MSecMinute) / 1000;
stringstream s; stringstream ss;
if (hours) if (hours)
s << hours << ':'; ss << hours << ':';
s << setfill('0') << setw(2) << minutes << ':' << setw(2) << seconds; ss << setfill('0') << setw(2) << minutes << ':' << setw(2) << seconds;
return s.str(); return ss.str();
} }
static string score_to_string(Value v) { static string format(Value v) {
stringstream s; stringstream ss;
if (v >= VALUE_MATE_IN_MAX_PLY) if (v >= VALUE_MATE_IN_MAX_PLY)
s << "#" << (VALUE_MATE - v + 1) / 2; ss << "#" << (VALUE_MATE - v + 1) / 2;
else if (v <= VALUE_MATED_IN_MAX_PLY) else if (v <= VALUE_MATED_IN_MAX_PLY)
s << "-#" << (VALUE_MATE + v) / 2; ss << "-#" << (VALUE_MATE + v) / 2;
else else
s << setprecision(2) << fixed << showpos << double(v) / PawnValueMg; ss << setprecision(2) << fixed << showpos << double(v) / PawnValueEg;
return s.str(); return ss.str();
} }
string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]) { string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]) {
const int64_t K = 1000; const uint64_t K = 1000;
const int64_t M = 1000000; const uint64_t M = 1000000;
std::stack<StateInfo> st; std::stack<StateInfo> st;
Move* m = pv; Move* m = pv;
string san, padding; string san, str, padding;
size_t length; stringstream ss;
stringstream s;
s << setw(2) << depth ss << setw(2) << depth << setw(8) << format(value) << setw(8) << format(msecs);
<< setw(8) << score_to_string(value)
<< setw(8) << time_to_string(msecs);
if (pos.nodes_searched() < M) if (pos.nodes_searched() < M)
s << setw(8) << pos.nodes_searched() / 1 << " "; ss << setw(8) << pos.nodes_searched() / 1 << " ";
else if (pos.nodes_searched() < K * M) else if (pos.nodes_searched() < K * M)
s << setw(7) << pos.nodes_searched() / K << "K "; ss << setw(7) << pos.nodes_searched() / K << "K ";
else else
s << setw(7) << pos.nodes_searched() / M << "M "; ss << setw(7) << pos.nodes_searched() / M << "M ";
padding = string(s.str().length(), ' '); str = ss.str();
length = padding.length(); padding = string(str.length(), ' ');
while (*m != MOVE_NONE) while (*m != MOVE_NONE)
{ {
san = move_to_san(pos, *m); san = move_to_san(pos, *m) + ' ';
if (length + san.length() > 80) if ((str.length() + san.length()) % 80 <= san.length()) // Exceed 80 cols
{ str += "\n" + padding;
s << "\n" + padding;
length = padding.length();
}
s << san << ' '; str += san;
length += san.length() + 1;
st.push(StateInfo()); st.push(StateInfo());
pos.do_move(*m++, st.top()); pos.do_move(*m++, st.top());
@@ -259,5 +252,5 @@ string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]
while (m != pv) while (m != pv)
pos.undo_move(*--m); pos.undo_move(*--m);
return s.str(); return str;
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -49,14 +49,20 @@ namespace {
{ S(20, 28), S(29, 31), S(33, 31), S(33, 31), { S(20, 28), S(29, 31), S(33, 31), S(33, 31),
S(33, 31), S(33, 31), S(29, 31), S(20, 28) } }; S(33, 31), S(33, 31), S(29, 31), S(20, 28) } };
// Pawn chain membership bonus by file and rank (initialized by formula) // Connected pawn bonus by file and rank (initialized by formula)
Score ChainMember[FILE_NB][RANK_NB]; Score Connected[FILE_NB][RANK_NB];
// Candidate passed pawn bonus by rank // Candidate passed pawn bonus by rank
const Score CandidatePassed[RANK_NB] = { const Score CandidatePassed[RANK_NB] = {
S( 0, 0), S( 6, 13), S(6,13), S(14,29), S( 0, 0), S( 6, 13), S(6,13), S(14,29),
S(34,68), S(83,166), S(0, 0), S( 0, 0) }; S(34,68), S(83,166), S(0, 0), S( 0, 0) };
// Bonus for file distance of the two outermost pawns
const Score PawnsFileSpan = S(0, 15);
// Unsupported pawn penalty
const Score UnsupportedPawnPenalty = S(20, 10);
// Weakness of our pawn shelter in front of the king indexed by [rank] // Weakness of our pawn shelter in front of the king indexed by [rank]
const Value ShelterWeakness[RANK_NB] = const Value ShelterWeakness[RANK_NB] =
{ V(100), V(0), V(27), V(73), V(92), V(101), V(101) }; { V(100), V(0), V(27), V(73), V(92), V(101), V(101) };
@@ -66,7 +72,7 @@ namespace {
const Value StormDanger[3][RANK_NB] = { const Value StormDanger[3][RANK_NB] = {
{ V( 0), V(64), V(128), V(51), V(26) }, { V( 0), V(64), V(128), V(51), V(26) },
{ V(26), V(32), V( 96), V(38), V(20) }, { V(26), V(32), V( 96), V(38), V(20) },
{ V( 0), V( 0), V( 64), V(25), V(13) } }; { V( 0), V( 0), V(160), V(25), V(13) } };
// Max bonus for king safety. Corresponds to start position with all the pawns // Max bonus for king safety. Corresponds to start position with all the pawns
// in front of the king and no enemy pawn on the horizon. // in front of the king and no enemy pawn on the horizon.
@@ -83,10 +89,10 @@ namespace {
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW); const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE); const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
Bitboard b; Bitboard b, p, doubled;
Square s; Square s;
File f; File f;
bool passed, isolated, doubled, opposed, chain, backward, candidate; bool passed, isolated, opposed, connected, backward, candidate, unsupported;
Score value = SCORE_ZERO; Score value = SCORE_ZERO;
const Square* pl = pos.list<PAWN>(Us); const Square* pl = pos.list<PAWN>(Us);
@@ -110,22 +116,26 @@ namespace {
// This file cannot be semi-open // This file cannot be semi-open
e->semiopenFiles[Us] &= ~(1 << f); e->semiopenFiles[Us] &= ~(1 << f);
// Our rank plus previous one. Used for chain detection // Previous rank
b = rank_bb(s) | rank_bb(s - pawn_push(Us)); p = rank_bb(s - pawn_push(Us));
// Flag the pawn as passed, isolated, doubled or member of a pawn // Our rank plus previous one
// chain (but not the backward one). b = rank_bb(s) | p;
chain = ourPawns & adjacent_files_bb(f) & b;
// Flag the pawn as passed, isolated, doubled,
// unsupported or connected (but not the backward one).
connected = ourPawns & adjacent_files_bb(f) & b;
unsupported = !(ourPawns & adjacent_files_bb(f) & p);
isolated = !(ourPawns & adjacent_files_bb(f)); isolated = !(ourPawns & adjacent_files_bb(f));
doubled = ourPawns & forward_bb(Us, s); doubled = ourPawns & forward_bb(Us, s);
opposed = theirPawns & forward_bb(Us, s); opposed = theirPawns & forward_bb(Us, s);
passed = !(theirPawns & passed_pawn_mask(Us, s)); passed = !(theirPawns & passed_pawn_mask(Us, s));
// Test for backward pawn. // Test for backward pawn.
// If the pawn is passed, isolated, or member of a pawn chain it cannot // If the pawn is passed, isolated, or connected it cannot be
// be backward. If there are friendly pawns behind on adjacent files // backward. If there are friendly pawns behind on adjacent files
// or if can capture an enemy pawn it cannot be backward either. // or if it can capture an enemy pawn it cannot be backward either.
if ( (passed | isolated | chain) if ( (passed | isolated | connected)
|| (ourPawns & pawn_attack_span(Them, s)) || (ourPawns & pawn_attack_span(Them, s))
|| (pos.attacks_from<PAWN>(s, Us) & theirPawns)) || (pos.attacks_from<PAWN>(s, Us) & theirPawns))
backward = false; backward = false;
@@ -145,9 +155,9 @@ namespace {
assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns)); assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns));
// A not passed pawn is a candidate to become passed, if it is free to // A not-passed pawn is a candidate to become passed, if it is free to
// advance and if the number of friendly pawns beside or behind this // advance and if the number of friendly pawns beside or behind this
// pawn on adjacent files is higher or equal than the number of // pawn on adjacent files is higher than or equal to the number of
// enemy pawns in the forward direction on the adjacent files. // enemy pawns in the forward direction on the adjacent files.
candidate = !(opposed | passed | backward | isolated) candidate = !(opposed | passed | backward | isolated)
&& (b = pawn_attack_span(Them, s + pawn_push(Us)) & ourPawns) != 0 && (b = pawn_attack_span(Them, s + pawn_push(Us)) & ourPawns) != 0
@@ -163,14 +173,17 @@ namespace {
if (isolated) if (isolated)
value -= Isolated[opposed][f]; value -= Isolated[opposed][f];
if (unsupported && !isolated)
value -= UnsupportedPawnPenalty;
if (doubled) if (doubled)
value -= Doubled[f]; value -= Doubled[f] / rank_distance(s, lsb(doubled));
if (backward) if (backward)
value -= Backward[opposed][f]; value -= Backward[opposed][f];
if (chain) if (connected)
value += ChainMember[f][relative_rank(Us, s)]; value += Connected[f][relative_rank(Us, s)];
if (candidate) if (candidate)
{ {
@@ -181,6 +194,14 @@ namespace {
} }
} }
// In endgame it's better to have pawns on both wings. So give a bonus according
// to file distance between left and right outermost pawns.
if (pos.count<PAWN>(Us) > 1)
{
b = e->semiopenFiles[Us] ^ 0xFF;
value += PawnsFileSpan * int(msb(b) - lsb(b));
}
return value; return value;
} }
@@ -188,18 +209,18 @@ namespace {
namespace Pawns { namespace Pawns {
/// init() initializes some tables by formula instead of hard-code their values /// init() initializes some tables by formula instead of hard-coding their values
void init() { void init() {
const int chainByFile[8] = { 1, 3, 3, 4, 4, 3, 3, 1 }; const int bonusesByFile[8] = { 1, 3, 3, 4, 4, 3, 3, 1 };
int bonus; int bonus;
for (Rank r = RANK_1; r < RANK_8; ++r) for (Rank r = RANK_1; r < RANK_8; ++r)
for (File f = FILE_A; f <= FILE_H; ++f) for (File f = FILE_A; f <= FILE_H; ++f)
{ {
bonus = r * (r-1) * (r-2) + chainByFile[f] * (r/2 + 1); bonus = r * (r-1) * (r-2) + bonusesByFile[f] * (r/2 + 1);
ChainMember[f][r] = make_score(bonus, bonus); Connected[f][r] = make_score(bonus, bonus);
} }
} }
@@ -229,6 +250,7 @@ template<Color Us>
Value Entry::shelter_storm(const Position& pos, Square ksq) { Value Entry::shelter_storm(const Position& pos, Square ksq) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
static const Bitboard MiddleEdges = (FileABB | FileHBB) & (Rank2BB | Rank3BB);
Value safety = MaxSafetyBonus; Value safety = MaxSafetyBonus;
Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq)); Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq));
@@ -241,25 +263,31 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) {
{ {
b = ourPawns & file_bb(f); b = ourPawns & file_bb(f);
rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1; rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
safety -= ShelterWeakness[rkUs];
b = theirPawns & file_bb(f); b = theirPawns & file_bb(f);
rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
safety -= StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem];
if ( (MiddleEdges & make_square(f, rkThem))
&& file_of(ksq) == f
&& relative_rank(Us, ksq) == rkThem - 1)
safety += 200;
else
safety -= ShelterWeakness[rkUs]
+ StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem];
} }
return safety; return safety;
} }
/// Entry::update_safety() calculates and caches a bonus for king safety. It is /// Entry::do_king_safety() calculates a bonus for king safety. It is called only
/// called only when king square changes, about 20% of total king_safety() calls. /// when king square changes, which is about 20% of total king_safety() calls.
template<Color Us> template<Color Us>
Score Entry::update_safety(const Position& pos, Square ksq) { Score Entry::do_king_safety(const Position& pos, Square ksq) {
kingSquares[Us] = ksq; kingSquares[Us] = ksq;
castleRights[Us] = pos.can_castle(Us); castlingRights[Us] = pos.can_castle(Us);
minKPdistance[Us] = 0; minKPdistance[Us] = 0;
Bitboard pawns = pos.pieces(Us, PAWN); Bitboard pawns = pos.pieces(Us, PAWN);
@@ -267,22 +295,22 @@ Score Entry::update_safety(const Position& pos, Square ksq) {
while (!(DistanceRingsBB[ksq][minKPdistance[Us]++] & pawns)) {} while (!(DistanceRingsBB[ksq][minKPdistance[Us]++] & pawns)) {}
if (relative_rank(Us, ksq) > RANK_4) if (relative_rank(Us, ksq) > RANK_4)
return kingSafety[Us] = make_score(0, -16 * minKPdistance[Us]); return make_score(0, -16 * minKPdistance[Us]);
Value bonus = shelter_storm<Us>(pos, ksq); Value bonus = shelter_storm<Us>(pos, ksq);
// If we can castle use the bonus after the castle if it is bigger // If we can castle use the bonus after the castling if it is bigger
if (pos.can_castle(make_castle_right(Us, KING_SIDE))) if (pos.can_castle(MakeCastling<Us, KING_SIDE>::right))
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_G1))); bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_G1)));
if (pos.can_castle(make_castle_right(Us, QUEEN_SIDE))) if (pos.can_castle(MakeCastling<Us, QUEEN_SIDE>::right))
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1))); bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1)));
return kingSafety[Us] = make_score(bonus, -16 * minKPdistance[Us]); return make_score(bonus, -16 * minKPdistance[Us]);
} }
// Explicit template instantiation // Explicit template instantiation
template Score Entry::update_safety<WHITE>(const Position& pos, Square ksq); template Score Entry::do_king_safety<WHITE>(const Position& pos, Square ksq);
template Score Entry::update_safety<BLACK>(const Position& pos, Square ksq); template Score Entry::do_king_safety<BLACK>(const Position& pos, Square ksq);
} // namespace Pawns } // namespace Pawns

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -26,11 +26,9 @@
namespace Pawns { namespace Pawns {
/// Pawns::Entry contains various information about a pawn structure. Currently, /// Pawns::Entry contains various information about a pawn structure. A lookup
/// it only includes a middle game and end game pawn structure evaluation, and a /// to the pawn hash table (performed by calling the probe function) returns a
/// bitboard of passed pawns. We may want to add further information in the future. /// pointer to an Entry object.
/// A lookup to the pawn hash table (performed by calling the probe function)
/// returns a pointer to an Entry object.
struct Entry { struct Entry {
@@ -38,37 +36,42 @@ struct Entry {
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
Bitboard candidate_pawns(Color c) const { return candidatePawns[c]; } Bitboard candidate_pawns(Color c) const { return candidatePawns[c]; }
int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(DarkSquares & s)]; }
int semiopen(Color c, File f) const { return semiopenFiles[c] & (1 << int(f)); }
int semiopen_on_side(Color c, File f, bool left) const {
return semiopenFiles[c] & (left ? ((1 << int(f)) - 1) : ~((1 << int(f+1)) - 1)); int semiopen_file(Color c, File f) const {
return semiopenFiles[c] & (1 << f);
}
int semiopen_side(Color c, File f, bool leftSide) const {
return semiopenFiles[c] & (leftSide ? (1 << f) - 1 : ~((1 << (f + 1)) - 1));
}
int pawns_on_same_color_squares(Color c, Square s) const {
return pawnsOnSquares[c][!!(DarkSquares & s)];
} }
template<Color Us> 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)
return kingSquares[Us] == ksq && castleRights[Us] == pos.can_castle(Us) ? kingSafety[Us] : (kingSafety[Us] = do_king_safety<Us>(pos, ksq));
? kingSafety[Us] : update_safety<Us>(pos, ksq);
} }
template<Color Us> template<Color Us>
Score update_safety(const Position& pos, Square ksq); Score do_king_safety(const Position& pos, Square ksq);
template<Color Us> template<Color Us>
Value shelter_storm(const Position& pos, Square ksq); Value shelter_storm(const Position& pos, Square ksq);
Key key; Key key;
Score value;
Bitboard passedPawns[COLOR_NB]; Bitboard passedPawns[COLOR_NB];
Bitboard candidatePawns[COLOR_NB]; Bitboard candidatePawns[COLOR_NB];
Bitboard pawnAttacks[COLOR_NB]; Bitboard pawnAttacks[COLOR_NB];
Square kingSquares[COLOR_NB]; Square kingSquares[COLOR_NB];
int minKPdistance[COLOR_NB];
int castleRights[COLOR_NB];
Score value;
int semiopenFiles[COLOR_NB];
Score kingSafety[COLOR_NB]; Score kingSafety[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB]; int minKPdistance[COLOR_NB];
int castlingRights[COLOR_NB];
int semiopenFiles[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
}; };
typedef HashTable<Entry, 16384> Table; typedef HashTable<Entry, 16384> Table;
@@ -76,6 +79,6 @@ typedef HashTable<Entry, 16384> Table;
void init(); void init();
Entry* probe(const Position& pos, Table& entries); Entry* probe(const Position& pos, Table& entries);
} } // namespace Pawns
#endif // #ifndef PAWNS_H_INCLUDED #endif // #ifndef PAWNS_H_INCLUDED

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@
#ifdef _MSC_VER #ifdef _MSC_VER
// Disable some silly and noisy warning from MSVC compiler // Disable some silly and noisy warnings from MSVC compiler
#pragma warning(disable: 4127) // Conditional expression is constant #pragma warning(disable: 4127) // Conditional expression is constant
#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type #pragma warning(disable: 4146) // Unary minus operator applied to unsigned type
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false' #pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
@@ -89,14 +89,14 @@ inline int64_t system_time_to_msec() {
#undef WIN32_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX #undef NOMINMAX
// We use critical sections on Windows to support Windows XP and older versions, // We use critical sections on Windows to support Windows XP and older versions.
// unfortunatly cond_wait() is racy between lock_release() and WaitForSingleObject() // Unfortunately, cond_wait() is racy between lock_release() and WaitForSingleObject()
// but apart from this they have the same speed performance of SRW locks. // but apart from this they have the same speed performance of SRW locks.
typedef CRITICAL_SECTION Lock; typedef CRITICAL_SECTION Lock;
typedef HANDLE WaitCondition; typedef HANDLE WaitCondition;
typedef HANDLE NativeHandle; typedef HANDLE NativeHandle;
// On Windows 95 and 98 parameter lpThreadId my not be null // On Windows 95 and 98 parameter lpThreadId may not be null
inline DWORD* dwWin9xKludge() { static DWORD dw; return &dw; } inline DWORD* dwWin9xKludge() { static DWORD dw; return &dw; }
# define lock_init(x) InitializeCriticalSection(&(x)) # define lock_init(x) InitializeCriticalSection(&(x))

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -51,7 +51,7 @@ struct CheckInfo {
struct StateInfo { struct StateInfo {
Key pawnKey, materialKey; Key pawnKey, materialKey;
Value npMaterial[COLOR_NB]; Value npMaterial[COLOR_NB];
int castleRights, rule50, pliesFromNull; int castlingRights, rule50, pliesFromNull;
Score psq; Score psq;
Square epSquare; Square epSquare;
@@ -75,13 +75,13 @@ const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1;
class Position { class Position {
public: public:
Position() {} Position() {}
Position(const Position& p, Thread* t) { *this = p; thisThread = t; } Position(const Position& pos, Thread* t) { *this = pos; thisThread = t; }
Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); } Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); }
Position& operator=(const Position&); Position& operator=(const Position&);
static void init(); static void init();
// Text input/output // Text input/output
void set(const std::string& fen, bool isChess960, Thread* th); void set(const std::string& fenStr, bool isChess960, Thread* th);
const std::string fen() const; const std::string fen() const;
const std::string pretty(Move m = MOVE_NONE) const; const std::string pretty(Move m = MOVE_NONE) const;
@@ -100,21 +100,20 @@ public:
template<PieceType Pt> const Square* list(Color c) const; template<PieceType Pt> const Square* list(Color c) const;
// Castling // Castling
int can_castle(CastleRight f) const;
int can_castle(Color c) const; int can_castle(Color c) const;
bool castle_impeded(Color c, CastlingSide s) const; int can_castle(CastlingRight cr) const;
Square castle_rook_square(Color c, CastlingSide s) const; bool castling_impeded(CastlingRight cr) const;
Square castling_rook_square(CastlingRight cr) const;
// Checking // Checking
Bitboard checkers() const; Bitboard checkers() const;
Bitboard discovered_check_candidates() const; Bitboard discovered_check_candidates() const;
Bitboard pinned_pieces(Color toMove) const; Bitboard pinned_pieces(Color c) const;
// Attacks to/from a given square // Attacks to/from a given square
Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s) const;
Bitboard attackers_to(Square s, Bitboard occ) const; Bitboard attackers_to(Square s, Bitboard occ) const;
Bitboard attacks_from(Piece p, Square s) const; Bitboard attacks_from(Piece pc, Square s) const;
static Bitboard attacks_from(Piece p, Square s, Bitboard occ);
template<PieceType> Bitboard attacks_from(Square s) const; template<PieceType> Bitboard attacks_from(Square s) const;
template<PieceType> Bitboard attacks_from(Square s, Color c) const; template<PieceType> Bitboard attacks_from(Square s, Color c) const;
@@ -124,7 +123,7 @@ public:
bool capture(Move m) const; bool capture(Move m) const;
bool capture_or_promotion(Move m) const; bool capture_or_promotion(Move m) const;
bool gives_check(Move m, const CheckInfo& ci) const; bool gives_check(Move m, const CheckInfo& ci) const;
bool passed_pawn_push(Move m) const; bool advanced_pawn_push(Move m) const;
Piece moved_piece(Move m) const; Piece moved_piece(Move m) const;
PieceType captured_piece_type() const; PieceType captured_piece_type() const;
@@ -142,8 +141,8 @@ public:
void undo_null_move(); void undo_null_move();
// Static exchange evaluation // Static exchange evaluation
int see(Move m, int asymmThreshold = 0) const; Value see(Move m) const;
int see_sign(Move m) const; Value see_sign(Move m) const;
// Accessing hash keys // Accessing hash keys
Key key() const; Key key() const;
@@ -160,34 +159,27 @@ public:
int game_ply() const; int game_ply() const;
bool is_chess960() const; bool is_chess960() const;
Thread* this_thread() const; Thread* this_thread() const;
int64_t nodes_searched() const; uint64_t nodes_searched() const;
void set_nodes_searched(int64_t n); void set_nodes_searched(uint64_t n);
bool is_draw() const; bool is_draw() const;
// Position consistency check, for debugging // Position consistency check, for debugging
bool pos_is_ok(int* failedStep = NULL) const; bool pos_is_ok(int* step = NULL) const;
void flip(); void flip();
private: private:
// Initialization helpers (used while setting up a position) // Initialization helpers (used while setting up a position)
void clear(); void clear();
void set_castle_right(Color c, Square rfrom); void set_castling_right(Color c, Square rfrom);
void set_state(StateInfo* si) const;
// Helper functions // Helper functions
void do_castle(Square kfrom, Square kto, Square rfrom, Square rto); Bitboard check_blockers(Color c, Color kingColor) const;
Bitboard hidden_checkers(Square ksq, Color c, Color toMove) const;
void put_piece(Square s, Color c, PieceType pt); void put_piece(Square s, Color c, PieceType pt);
void remove_piece(Square s, Color c, PieceType pt); void remove_piece(Square s, Color c, PieceType pt);
void move_piece(Square from, Square to, Color c, PieceType pt); void move_piece(Square from, Square to, Color c, PieceType pt);
template<bool Do>
// Computing hash keys from scratch (for initialization and debugging) void do_castling(Square from, Square& to, Square& rfrom, Square& rto);
Key compute_key() const;
Key compute_pawn_key() const;
Key compute_material_key() const;
// Computing incremental evaluation scores and material counts
Score compute_psq_score() const;
Value compute_non_pawn_material(Color c) const;
// Board and pieces // Board and pieces
Piece board[SQUARE_NB]; Piece board[SQUARE_NB];
@@ -198,23 +190,23 @@ private:
int index[SQUARE_NB]; int index[SQUARE_NB];
// Other info // Other info
int castleRightsMask[SQUARE_NB]; int castlingRightsMask[SQUARE_NB];
Square castleRookSquare[COLOR_NB][CASTLING_SIDE_NB]; Square castlingRookSquare[CASTLING_RIGHT_NB];
Bitboard castlePath[COLOR_NB][CASTLING_SIDE_NB]; Bitboard castlingPath[CASTLING_RIGHT_NB];
StateInfo startState; StateInfo startState;
int64_t nodes; uint64_t nodes;
int gamePly; int gamePly;
Color sideToMove; Color sideToMove;
Thread* thisThread; Thread* thisThread;
StateInfo* st; StateInfo* st;
int chess960; bool chess960;
}; };
inline int64_t Position::nodes_searched() const { inline uint64_t Position::nodes_searched() const {
return nodes; return nodes;
} }
inline void Position::set_nodes_searched(int64_t n) { inline void Position::set_nodes_searched(uint64_t n) {
nodes = n; nodes = n;
} }
@@ -274,26 +266,26 @@ inline Square Position::king_square(Color c) const {
return pieceList[c][KING][0]; return pieceList[c][KING][0];
} }
inline int Position::can_castle(CastleRight f) const { inline int Position::can_castle(CastlingRight cr) const {
return st->castleRights & f; return st->castlingRights & cr;
} }
inline int Position::can_castle(Color c) const { inline int Position::can_castle(Color c) const {
return st->castleRights & ((WHITE_OO | WHITE_OOO) << (2 * c)); return st->castlingRights & ((WHITE_OO | WHITE_OOO) << (2 * c));
} }
inline bool Position::castle_impeded(Color c, CastlingSide s) const { inline bool Position::castling_impeded(CastlingRight cr) const {
return byTypeBB[ALL_PIECES] & castlePath[c][s]; return byTypeBB[ALL_PIECES] & castlingPath[cr];
} }
inline Square Position::castle_rook_square(Color c, CastlingSide s) const { inline Square Position::castling_rook_square(CastlingRight cr) const {
return castleRookSquare[c][s]; return castlingRookSquare[cr];
} }
template<PieceType Pt> template<PieceType Pt>
inline Bitboard Position::attacks_from(Square s) const { inline Bitboard Position::attacks_from(Square s) const {
return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, pieces()) return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, byTypeBB[ALL_PIECES])
: Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s) : Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s)
: StepAttacksBB[Pt][s]; : StepAttacksBB[Pt][s];
} }
@@ -303,8 +295,8 @@ inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const {
return StepAttacksBB[make_piece(c, PAWN)][s]; return StepAttacksBB[make_piece(c, PAWN)][s];
} }
inline Bitboard Position::attacks_from(Piece p, Square s) const { inline Bitboard Position::attacks_from(Piece pc, Square s) const {
return attacks_from(p, s, byTypeBB[ALL_PIECES]); return attacks_bb(pc, s, byTypeBB[ALL_PIECES]);
} }
inline Bitboard Position::attackers_to(Square s) const { inline Bitboard Position::attackers_to(Square s) const {
@@ -316,21 +308,20 @@ inline Bitboard Position::checkers() const {
} }
inline Bitboard Position::discovered_check_candidates() const { inline Bitboard Position::discovered_check_candidates() const {
return hidden_checkers(king_square(~sideToMove), sideToMove, sideToMove); return check_blockers(sideToMove, ~sideToMove);
} }
inline Bitboard Position::pinned_pieces(Color toMove) const { inline Bitboard Position::pinned_pieces(Color c) const {
return hidden_checkers(king_square(toMove), ~toMove, toMove); return check_blockers(c, c);
} }
inline bool Position::pawn_passed(Color c, Square s) const { inline bool Position::pawn_passed(Color c, Square s) const {
return !(pieces(~c, PAWN) & passed_pawn_mask(c, s)); return !(pieces(~c, PAWN) & passed_pawn_mask(c, s));
} }
inline bool Position::passed_pawn_push(Move m) const { inline bool Position::advanced_pawn_push(Move m) const {
return type_of(moved_piece(m)) == PAWN return type_of(moved_piece(m)) == PAWN
&& pawn_passed(sideToMove, to_sq(m)); && relative_rank(sideToMove, from_sq(m)) > RANK_4;
} }
inline Key Position::key() const { inline Key Position::key() const {
@@ -381,14 +372,14 @@ inline bool Position::is_chess960() const {
inline bool Position::capture_or_promotion(Move m) const { inline bool Position::capture_or_promotion(Move m) const {
assert(is_ok(m)); assert(is_ok(m));
return type_of(m) ? type_of(m) != CASTLE : !empty(to_sq(m)); return type_of(m) != NORMAL ? type_of(m) != CASTLING : !empty(to_sq(m));
} }
inline bool Position::capture(Move m) const { inline bool Position::capture(Move m) const {
// Note that castle is coded as "king captures the rook" // Note that castling is encoded as "king captures the rook"
assert(is_ok(m)); assert(is_ok(m));
return (!empty(to_sq(m)) && type_of(m) != CASTLE) || type_of(m) == ENPASSANT; return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT;
} }
inline PieceType Position::captured_piece_type() const { inline PieceType Position::captured_piece_type() const {
@@ -405,7 +396,6 @@ inline void Position::put_piece(Square s, Color c, PieceType pt) {
byTypeBB[ALL_PIECES] |= s; byTypeBB[ALL_PIECES] |= s;
byTypeBB[pt] |= s; byTypeBB[pt] |= s;
byColorBB[c] |= s; byColorBB[c] |= s;
pieceCount[c][ALL_PIECES]++;
index[s] = pieceCount[c][pt]++; index[s] = pieceCount[c][pt]++;
pieceList[c][pt][index[s]] = s; pieceList[c][pt][index[s]] = s;
} }
@@ -434,7 +424,6 @@ inline void Position::remove_piece(Square s, Color c, PieceType pt) {
byTypeBB[pt] ^= s; byTypeBB[pt] ^= s;
byColorBB[c] ^= s; byColorBB[c] ^= s;
/* board[s] = NO_PIECE; */ // Not needed, will be overwritten by capturing /* board[s] = NO_PIECE; */ // Not needed, will be overwritten by capturing
pieceCount[c][ALL_PIECES]--;
Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]]; Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]];
index[lastSquare] = index[s]; index[lastSquare] = index[s];
pieceList[c][pt][index[lastSquare]] = lastSquare; pieceList[c][pt][index[lastSquare]] = lastSquare;

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -26,70 +26,70 @@
/// PSQT[PieceType][Square] contains Piece-Square scores. For each piece type on /// PSQT[PieceType][Square] contains Piece-Square scores. For each piece type on
/// a given square a (midgame, endgame) score pair is assigned. PSQT is defined /// a given square a (middlegame, endgame) score pair is assigned. PSQT is defined
/// for white side, for black side the tables are symmetric. /// for the white side and the tables are symmetric for the black side.
static const Score PSQT[][SQUARE_NB] = { static const Score PSQT[][SQUARE_NB] = {
{ }, { },
{ // Pawn { // Pawn
S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0),
S(-20,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-20,-8), S(-20, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S(-20, 0),
S(-20,-8), S(-6,-8), S( 9,-8), S(34,-8), S(34,-8), S( 9,-8), S(-6,-8), S(-20,-8), S(-20, 0), S( 0, 0), S(10, 0), S(20, 0), S(20, 0), S(10, 0), S( 0, 0), S(-20, 0),
S(-20,-8), S(-6,-8), S(17,-8), S(54,-8), S(54,-8), S(17,-8), S(-6,-8), S(-20,-8), S(-20, 0), S( 0, 0), S(20, 0), S(40, 0), S(40, 0), S(20, 0), S( 0, 0), S(-20, 0),
S(-20,-8), S(-6,-8), S(17,-8), S(34,-8), S(34,-8), S(17,-8), S(-6,-8), S(-20,-8), S(-20, 0), S( 0, 0), S(10, 0), S(20, 0), S(20, 0), S(10, 0), S( 0, 0), S(-20, 0),
S(-20,-8), S(-6,-8), S( 9,-8), S(14,-8), S(14,-8), S( 9,-8), S(-6,-8), S(-20,-8), S(-20, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S(-20, 0),
S(-20,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-20,-8), S(-20, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S(-20, 0),
S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0) S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0)
}, },
{ // Knight { // Knight
S(-135,-104), S(-107,-79), S(-80,-55), S(-67,-42), S(-67,-42), S(-80,-55), S(-107,-79), S(-135,-104), S(-144,-98), S(-109,-83), S(-85,-51), S(-73,-16), S(-73,-16), S(-85,-51), S(-109,-83), S(-144,-98),
S( -93, -79), S( -67,-55), S(-39,-30), S(-25,-17), S(-25,-17), S(-39,-30), S( -67,-55), S( -93, -79), S( -88,-68), S( -43,-53), S(-19,-21), S( -7, 14), S( -7, 14), S(-19,-21), S( -43,-53), S( -88,-68),
S( -53, -55), S( -25,-30), S( 1, -6), S( 13, 5), S( 13, 5), S( 1, -6), S( -25,-30), S( -53, -55), S( -69,-53), S( -24,-38), S( 0, -6), S( 12, 29), S( 12, 29), S( 0, -6), S( -24,-38), S( -69,-53),
S( -25, -42), S( 1,-17), S( 27, 5), S( 41, 18), S( 41, 18), S( 27, 5), S( 1,-17), S( -25, -42), S( -28,-42), S( 17,-27), S( 41, 5), S( 53, 40), S( 53, 40), S( 41, 5), S( 17,-27), S( -28,-42),
S( -11, -42), S( 13,-17), S( 41, 5), S( 55, 18), S( 55, 18), S( 41, 5), S( 13,-17), S( -11, -42), S( -30,-42), S( 15,-27), S( 39, 5), S( 51, 40), S( 51, 40), S( 39, 5), S( 15,-27), S( -30,-42),
S( -11, -55), S( 13,-30), S( 41, -6), S( 55, 5), S( 55, 5), S( 41, -6), S( 13,-30), S( -11, -55), S( -10,-53), S( 35,-38), S( 59, -6), S( 71, 29), S( 71, 29), S( 59, -6), S( 35,-38), S( -10,-53),
S( -53, -79), S( -25,-55), S( 1,-30), S( 13,-17), S( 13,-17), S( 1,-30), S( -25,-55), S( -53, -79), S( -64,-68), S( -19,-53), S( 5,-21), S( 17, 14), S( 17, 14), S( 5,-21), S( -19,-53), S( -64,-68),
S(-193,-104), S( -67,-79), S(-39,-55), S(-25,-42), S(-25,-42), S(-39,-55), S( -67,-79), S(-193,-104) S(-200,-98), S( -65,-83), S(-41,-51), S(-29,-16), S(-29,-16), S(-41,-51), S( -65,-83), S(-200,-98)
}, },
{ // Bishop { // Bishop
S(-40,-59), S(-40,-42), S(-35,-35), S(-30,-26), S(-30,-26), S(-35,-35), S(-40,-42), S(-40,-59), S(-54,-65), S(-27,-42), S(-34,-44), S(-43,-26), S(-43,-26), S(-34,-44), S(-27,-42), S(-54,-65),
S(-17,-42), S( 0,-26), S( -4,-18), S( 0,-11), S( 0,-11), S( -4,-18), S( 0,-26), S(-17,-42), S(-29,-43), S( 8,-20), S( 1,-22), S( -8, -4), S( -8, -4), S( 1,-22), S( 8,-20), S(-29,-43),
S(-13,-35), S( -4,-18), S( 8,-11), S( 4, -4), S( 4, -4), S( 8,-11), S( -4,-18), S(-13,-35), S(-20,-33), S( 17,-10), S( 10,-12), S( 1, 6), S( 1, 6), S( 10,-12), S( 17,-10), S(-20,-33),
S( -8,-26), S( 0,-11), S( 4, -4), S( 17, 4), S( 17, 4), S( 4, -4), S( 0,-11), S( -8,-26), S(-19,-35), S( 18,-12), S( 11,-14), S( 2, 4), S( 2, 4), S( 11,-14), S( 18,-12), S(-19,-35),
S( -8,-26), S( 0,-11), S( 4, -4), S( 17, 4), S( 17, 4), S( 4, -4), S( 0,-11), S( -8,-26), S(-22,-35), S( 15,-12), S( 8,-14), S( -1, 4), S( -1, 4), S( 8,-14), S( 15,-12), S(-22,-35),
S(-13,-35), S( -4,-18), S( 8,-11), S( 4, -4), S( 4, -4), S( 8,-11), S( -4,-18), S(-13,-35), S(-28,-33), S( 9,-10), S( 2,-12), S( -7, 6), S( -7, 6), S( 2,-12), S( 9,-10), S(-28,-33),
S(-17,-42), S( 0,-26), S( -4,-18), S( 0,-11), S( 0,-11), S( -4,-18), S( 0,-26), S(-17,-42), S(-32,-43), S( 5,-20), S( -2,-22), S(-11, -4), S(-11, -4), S( -2,-22), S( 5,-20), S(-32,-43),
S(-17,-59), S(-17,-42), S(-13,-35), S( -8,-26), S( -8,-26), S(-13,-35), S(-17,-42), S(-17,-59) S(-49,-65), S(-22,-42), S(-29,-44), S(-38,-26), S(-38,-26), S(-29,-44), S(-22,-42), S(-49,-65)
}, },
{ // Rook { // Rook
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), S(-22, 3), S(-17, 3), S(-12, 3), S(-8, 3), S(-8, 3), S(-12, 3), S(-17, 3), S(-22, 3),
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3),
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3),
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3),
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3),
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3),
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), S(-11, 3), S( 4, 3), S( 9, 3), S(13, 3), S(13, 3), S( 9, 3), S( 4, 3), S(-11, 3),
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3) S(-22, 3), S(-17, 3), S(-12, 3), S(-8, 3), S(-8, 3), S(-12, 3), S(-17, 3), S(-22, 3)
}, },
{ // Queen { // Queen
S(8,-80), S(8,-54), S(8,-42), S(8,-30), S(8,-30), S(8,-42), S(8,-54), S(8,-80), S(-2,-80), S(-2,-54), S(-2,-42), S(-2,-30), S(-2,-30), S(-2,-42), S(-2,-54), S(-2,-80),
S(8,-54), S(8,-30), S(8,-18), S(8, -6), S(8, -6), S(8,-18), S(8,-30), S(8,-54), S(-2,-54), S( 8,-30), S( 8,-18), S( 8, -6), S( 8, -6), S( 8,-18), S( 8,-30), S(-2,-54),
S(8,-42), S(8,-18), S(8, -6), S(8, 6), S(8, 6), S(8, -6), S(8,-18), S(8,-42), S(-2,-42), S( 8,-18), S( 8, -6), S( 8, 6), S( 8, 6), S( 8, -6), S( 8,-18), S(-2,-42),
S(8,-30), S(8, -6), S(8, 6), S(8, 18), S(8, 18), S(8, 6), S(8, -6), S(8,-30), S(-2,-30), S( 8, -6), S( 8, 6), S( 8, 18), S( 8, 18), S( 8, 6), S( 8, -6), S(-2,-30),
S(8,-30), S(8, -6), S(8, 6), S(8, 18), S(8, 18), S(8, 6), S(8, -6), S(8,-30), S(-2,-30), S( 8, -6), S( 8, 6), S( 8, 18), S( 8, 18), S( 8, 6), S( 8, -6), S(-2,-30),
S(8,-42), S(8,-18), S(8, -6), S(8, 6), S(8, 6), S(8, -6), S(8,-18), S(8,-42), S(-2,-42), S( 8,-18), S( 8, -6), S( 8, 6), S( 8, 6), S( 8, -6), S( 8,-18), S(-2,-42),
S(8,-54), S(8,-30), S(8,-18), S(8, -6), S(8, -6), S(8,-18), S(8,-30), S(8,-54), S(-2,-54), S( 8,-30), S( 8,-18), S( 8, -6), S( 8, -6), S( 8,-18), S( 8,-30), S(-2,-54),
S(8,-80), S(8,-54), S(8,-42), S(8,-30), S(8,-30), S(8,-42), S(8,-54), S(8,-80) S(-2,-80), S(-2,-54), S(-2,-42), S(-2,-30), S(-2,-30), S(-2,-42), S(-2,-54), S(-2,-80)
}, },
{ // King { // King
S(287, 18), S(311, 77), S(262,105), S(214,135), S(214,135), S(262,105), S(311, 77), S(287, 18), S(298, 27), S(332, 81), S(273,108), S(225,116), S(225,116), S(273,108), S(332, 81), S(298, 27),
S(262, 77), S(287,135), S(238,165), S(190,193), S(190,193), S(238,165), S(287,135), S(262, 77), S(287, 74), S(321,128), S(262,155), S(214,163), S(214,163), S(262,155), S(321,128), S(287, 74),
S(214,105), S(238,165), S(190,193), S(142,222), S(142,222), S(190,193), S(238,165), S(214,105), S(224,111), S(258,165), S(199,192), S(151,200), S(151,200), S(199,192), S(258,165), S(224,111),
S(190,135), S(214,193), S(167,222), S(119,251), S(119,251), S(167,222), S(214,193), S(190,135), S(196,135), S(230,189), S(171,216), S(123,224), S(123,224), S(171,216), S(230,189), S(196,135),
S(167,135), S(190,193), S(142,222), S( 94,251), S( 94,251), S(142,222), S(190,193), S(167,135), S(173,135), S(207,189), S(148,216), S(100,224), S(100,224), S(148,216), S(207,189), S(173,135),
S(142,105), S(167,165), S(119,193), S( 69,222), S( 69,222), S(119,193), S(167,165), S(142,105), S(146,111), S(180,165), S(121,192), S( 73,200), S( 73,200), S(121,192), S(180,165), S(146,111),
S(119, 77), S(142,135), S( 94,165), S( 46,193), S( 46,193), S( 94,165), S(142,135), S(119, 77), S(119, 74), S(153,128), S( 94,155), S( 46,163), S( 46,163), S( 94,155), S(153,128), S(119, 74),
S(94, 18), S(119, 77), S( 69,105), S( 21,135), S( 21,135), S( 69,105), S(119, 77), S( 94, 18) S( 98, 27), S(132, 81), S( 73,108), S( 25,116), S( 25,116), S( 73,108), S(132, 81), S( 98, 27)
} }
}; };

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -45,15 +45,15 @@ class RKISS {
uint64_t a, b, c, d; uint64_t a, b, c, d;
uint64_t rotate(uint64_t x, uint64_t k) const { uint64_t rotate_L(uint64_t x, unsigned k) const {
return (x << k) | (x >> (64 - k)); return (x << k) | (x >> (64 - k));
} }
uint64_t rand64() { uint64_t rand64() {
const uint64_t e = a - rotate(b, 7); const uint64_t e = a - rotate_L(b, 7);
a = b ^ rotate(c, 13); a = b ^ rotate_L(c, 13);
b = c + rotate(d, 37); b = c + rotate_L(d, 37);
c = d + e; c = d + e;
return d = e + a; return d = e + a;
} }
@@ -68,6 +68,14 @@ public:
} }
template<typename T> T rand() { return T(rand64()); } template<typename T> T rand() { return T(rand64()); }
/// Special generator used to fast init magic numbers. Here the
/// trick is to rotate the randoms of a given quantity 's' known
/// to be optimal to quickly find a good magic candidate.
template<typename T> T magic_rand(int s) {
return rotate_L(rotate_L(rand<T>(), (s >> 0) & 0x3F) & rand<T>()
, (s >> 6) & 0x3F) & rand<T>();
}
}; };
#endif // #ifndef RKISS_H_INCLUDED #endif // #ifndef RKISS_H_INCLUDED

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,7 +20,6 @@
#ifndef SEARCH_H_INCLUDED #ifndef SEARCH_H_INCLUDED
#define SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED
#include <cstring>
#include <memory> #include <memory>
#include <stack> #include <stack>
#include <vector> #include <vector>
@@ -41,6 +40,7 @@ struct Stack {
SplitPoint* splitPoint; SplitPoint* splitPoint;
int ply; int ply;
Move currentMove; Move currentMove;
Move ttMove;
Move excludedMove; Move excludedMove;
Move killers[2]; Move killers[2];
Depth reduction; Depth reduction;
@@ -73,22 +73,26 @@ struct RootMove {
/// The LimitsType struct stores information sent by GUI about available time /// The LimitsType struct stores information sent by GUI about available time
/// to search the current move, maximum depth/time, if we are in analysis mode /// to search the current move, maximum depth/time, if we are in analysis mode
/// or if we have to ponder while is our opponent's side to move. /// or if we have to ponder while it's our opponent's turn to move.
struct LimitsType { struct LimitsType {
LimitsType() { std::memset(this, 0, sizeof(LimitsType)); } LimitsType() { // Using memset on a std::vector is undefined behavior
time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = movestogo =
depth = nodes = movetime = mate = infinite = ponder = 0;
}
bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); } bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); }
std::vector<Move> searchmoves;
int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder; int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder;
}; };
/// The SignalsType struct stores volatile flags updated during the search /// The SignalsType struct stores volatile flags updated during the search
/// typically in an async fashion, for instance to stop the search by the GUI. /// typically in an async fashion e.g. to stop the search by the GUI.
struct SignalsType { struct SignalsType {
bool stopOnPonderhit, firstRootMove, stop, failedLowAtRoot; bool stop, stopOnPonderhit, firstRootMove, failedLowAtRoot;
}; };
typedef std::auto_ptr<std::stack<StateInfo> > StateStackPtr; typedef std::auto_ptr<std::stack<StateInfo> > StateStackPtr;
@@ -102,7 +106,7 @@ extern Time::point SearchTime;
extern StateStackPtr SetupStates; extern StateStackPtr SetupStates;
extern void init(); extern void init();
extern size_t perft(Position& pos, Depth depth); extern uint64_t perft(Position& pos, Depth depth);
extern void think(); extern void think();
} // namespace Search } // namespace Search

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -29,6 +29,8 @@ using namespace Search;
ThreadPool Threads; // Global object ThreadPool Threads; // Global object
extern void check_time();
namespace { namespace {
// start_routine() is the C function which is called when a new thread // start_routine() is the C function which is called when a new thread
@@ -38,7 +40,7 @@ namespace {
// Helpers to launch a thread after creation and joining before delete. Must be // Helpers to launch a thread after creation and joining before delete. Must be
// outside Thread c'tor and d'tor because object shall be fully initialized // outside Thread c'tor and d'tor because the object will be fully initialized
// when start_routine (and hence virtual idle_loop) is called and when joining. // when start_routine (and hence virtual idle_loop) is called and when joining.
template<typename T> T* new_thread() { template<typename T> T* new_thread() {
@@ -57,7 +59,7 @@ namespace {
} }
// ThreadBase::notify_one() wakes up the thread when there is some work to do // notify_one() wakes up the thread when there is some work to do
void ThreadBase::notify_one() { void ThreadBase::notify_one() {
@@ -67,7 +69,7 @@ void ThreadBase::notify_one() {
} }
// ThreadBase::wait_for() set the thread to sleep until condition 'b' turns true // wait_for() set the thread to sleep until condition 'b' turns true
void ThreadBase::wait_for(volatile const bool& b) { void ThreadBase::wait_for(volatile const bool& b) {
@@ -77,8 +79,8 @@ void ThreadBase::wait_for(volatile const bool& b) {
} }
// Thread c'tor just inits data but does not launch any thread of execution that // Thread c'tor just inits data and does not launch any execution thread.
// instead will be started only upon c'tor returns. // Such a thread will only be started when c'tor returns.
Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC
@@ -86,13 +88,47 @@ Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC
maxPly = splitPointsSize = 0; maxPly = splitPointsSize = 0;
activeSplitPoint = NULL; activeSplitPoint = NULL;
activePosition = NULL; activePosition = NULL;
idx = Threads.size(); idx = Threads.size(); // Starts from 0
}
// cutoff_occurred() checks whether a beta cutoff has occurred in the
// current active split point, or in some ancestor of the split point.
bool Thread::cutoff_occurred() const {
for (SplitPoint* sp = activeSplitPoint; sp; sp = sp->parentSplitPoint)
if (sp->cutoff)
return true;
return false;
}
// Thread::available_to() checks whether the thread is available to help the
// thread 'master' at a split point. An obvious requirement is that thread must
// be idle. With more than two threads, this is not sufficient: If the thread is
// the master of some split point, it is only available as a slave to the slaves
// which are busy searching the split point at the top of slave's split point
// stack (the "helpful master concept" in YBWC terminology).
bool Thread::available_to(const Thread* master) const {
if (searching)
return false;
// Make a local copy to be sure it doesn't become zero under our feet while
// testing next condition and so leading to an out of bounds access.
int size = splitPointsSize;
// No split points means that the thread is available as a slave for any
// other thread otherwise apply the "helpful master" concept if possible.
return !size || splitPoints[size - 1].slavesMask.test(master->idx);
} }
// TimerThread::idle_loop() is where the timer thread waits msec milliseconds // TimerThread::idle_loop() is where the timer thread waits msec milliseconds
// and then calls check_time(). If msec is 0 thread sleeps until is woken up. // and then calls check_time(). If msec is 0 thread sleeps until it's woken up.
extern void check_time();
void TimerThread::idle_loop() { void TimerThread::idle_loop() {
@@ -112,7 +148,7 @@ void TimerThread::idle_loop() {
// MainThread::idle_loop() is where the main thread is parked waiting to be started // MainThread::idle_loop() is where the main thread is parked waiting to be started
// when there is a new search. Main thread will launch all the slave threads. // when there is a new search. The main thread will launch all the slave threads.
void MainThread::idle_loop() { void MainThread::idle_loop() {
@@ -124,7 +160,7 @@ void MainThread::idle_loop() {
while (!thinking && !exit) while (!thinking && !exit)
{ {
Threads.sleepCondition.notify_one(); // Wake up UI thread if needed Threads.sleepCondition.notify_one(); // Wake up the UI thread if needed
sleepCondition.wait(mutex); sleepCondition.wait(mutex);
} }
@@ -144,56 +180,21 @@ void MainThread::idle_loop() {
} }
// Thread::cutoff_occurred() checks whether a beta cutoff has occurred in the
// current active split point, or in some ancestor of the split point.
bool Thread::cutoff_occurred() const {
for (SplitPoint* sp = activeSplitPoint; sp; sp = sp->parentSplitPoint)
if (sp->cutoff)
return true;
return false;
}
// Thread::available_to() checks whether the thread is available to help the
// thread 'master' at a split point. An obvious requirement is that thread must
// be idle. With more than two threads, this is not sufficient: If the thread is
// the master of some split point, it is only available as a slave to the slaves
// which are busy searching the split point at the top of slaves split point
// stack (the "helpful master concept" in YBWC terminology).
bool Thread::available_to(const Thread* master) const {
if (searching)
return false;
// Make a local copy to be sure doesn't become zero under our feet while
// testing next condition and so leading to an out of bound access.
int size = splitPointsSize;
// No split points means that the thread is available as a slave for any
// other thread otherwise apply the "helpful master" concept if possible.
return !size || (splitPoints[size - 1].slavesMask & (1ULL << master->idx));
}
// init() is called at startup to create and launch requested threads, that will // init() is called at startup to create and launch requested threads, that will
// go immediately to sleep due to 'sleepWhileIdle' set to true. We cannot use // go immediately to sleep. We cannot use a c'tor because Threads is a static
// a c'tor becuase Threads is a static object and we need a fully initialized // object and we need a fully initialized engine at this point due to allocation
// engine at this point due to allocation of Endgames in Thread c'tor. // of Endgames in Thread c'tor.
void ThreadPool::init() { void ThreadPool::init() {
sleepWhileIdle = true;
timer = new_thread<TimerThread>(); timer = new_thread<TimerThread>();
push_back(new_thread<MainThread>()); push_back(new_thread<MainThread>());
read_uci_options(); read_uci_options();
} }
// exit() cleanly terminates the threads before the program exits // exit() cleanly terminates the threads before the program exits. Cannot be done in
// d'tor because we have to terminate the threads before to free ThreadPool object.
void ThreadPool::exit() { void ThreadPool::exit() {
@@ -206,23 +207,20 @@ void ThreadPool::exit() {
// read_uci_options() updates internal threads parameters from the corresponding // read_uci_options() updates internal threads parameters from the corresponding
// UCI options and creates/destroys threads to match the requested number. Thread // UCI options and creates/destroys threads to match the requested number. Thread
// objects are dynamically allocated to avoid creating in advance all possible // objects are dynamically allocated to avoid creating all possible threads
// threads, with included pawns and material tables, if only few are used. // in advance (which include pawns and material tables), even if only a few
// are to be used.
void ThreadPool::read_uci_options() { void ThreadPool::read_uci_options() {
maxThreadsPerSplitPoint = Options["Max Threads per Split Point"];
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY; minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
size_t requested = Options["Threads"]; size_t requested = Options["Threads"];
assert(requested > 0); assert(requested > 0);
// Value 0 has a special meaning: We determine the optimal minimum split depth // If zero (default) then set best minimum split depth automatically
// automatically. Anyhow the minimumSplitDepth should never be under 4 plies.
if (!minimumSplitDepth) if (!minimumSplitDepth)
minimumSplitDepth = (requested < 8 ? 4 : 7) * ONE_PLY; minimumSplitDepth = requested < 8 ? 4 * ONE_PLY : 7 * ONE_PLY;
else
minimumSplitDepth = std::max(4 * ONE_PLY, minimumSplitDepth);
while (size() < requested) while (size() < requested)
push_back(new_thread<Thread>()); push_back(new_thread<Thread>());
@@ -235,7 +233,7 @@ void ThreadPool::read_uci_options() {
} }
// slave_available() tries to find an idle thread which is available as a slave // available_slave() tries to find an idle thread which is available as a slave
// for the thread 'master'. // for the thread 'master'.
Thread* ThreadPool::available_slave(const Thread* master) const { Thread* ThreadPool::available_slave(const Thread* master) const {
@@ -259,12 +257,11 @@ Thread* ThreadPool::available_slave(const Thread* master) const {
template <bool Fake> template <bool Fake>
void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Value* bestValue, void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Value* bestValue,
Move* bestMove, Depth depth, Move threatMove, int moveCount, Move* bestMove, Depth depth, int moveCount,
MovePicker* movePicker, int nodeType, bool cutNode) { MovePicker* movePicker, int nodeType, bool cutNode) {
assert(pos.pos_is_ok()); assert(pos.pos_is_ok());
assert(*bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE); assert(-VALUE_INFINITE < *bestValue && *bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE);
assert(*bestValue > -VALUE_INFINITE);
assert(depth >= Threads.minimumSplitDepth); assert(depth >= Threads.minimumSplitDepth);
assert(searching); assert(searching);
assert(splitPointsSize < MAX_SPLITPOINTS_PER_THREAD); assert(splitPointsSize < MAX_SPLITPOINTS_PER_THREAD);
@@ -274,11 +271,10 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
sp.masterThread = this; sp.masterThread = this;
sp.parentSplitPoint = activeSplitPoint; sp.parentSplitPoint = activeSplitPoint;
sp.slavesMask = 1ULL << idx; sp.slavesMask = 0, sp.slavesMask.set(idx);
sp.depth = depth; sp.depth = depth;
sp.bestValue = *bestValue; sp.bestValue = *bestValue;
sp.bestMove = *bestMove; sp.bestMove = *bestMove;
sp.threatMove = threatMove;
sp.alpha = alpha; sp.alpha = alpha;
sp.beta = beta; sp.beta = beta;
sp.nodeType = nodeType; sp.nodeType = nodeType;
@@ -296,17 +292,15 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
Threads.mutex.lock(); Threads.mutex.lock();
sp.mutex.lock(); sp.mutex.lock();
sp.allSlavesSearching = true; // Must be set under lock protection
++splitPointsSize; ++splitPointsSize;
activeSplitPoint = &sp; activeSplitPoint = &sp;
activePosition = NULL; activePosition = NULL;
size_t slavesCnt = 1; // This thread is always included if (!Fake)
Thread* slave; for (Thread* slave; (slave = Threads.available_slave(this)) != NULL; )
while ( (slave = Threads.available_slave(this)) != NULL
&& ++slavesCnt <= Threads.maxThreadsPerSplitPoint && !Fake)
{ {
sp.slavesMask |= 1ULL << slave->idx; sp.slavesMask.set(slave->idx);
slave->activeSplitPoint = &sp; slave->activeSplitPoint = &sp;
slave->searching = true; // Slave leaves idle_loop() slave->searching = true; // Slave leaves idle_loop()
slave->notify_one(); // Could be sleeping slave->notify_one(); // Could be sleeping
@@ -316,15 +310,14 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
// it will instantly launch a search, because its 'searching' flag is set. // it will instantly launch a search, because its 'searching' flag is set.
// The thread will return from the idle loop when all slaves have finished // The thread will return from the idle loop when all slaves have finished
// their work at this split point. // their work at this split point.
if (slavesCnt > 1 || Fake)
{
sp.mutex.unlock(); sp.mutex.unlock();
Threads.mutex.unlock(); Threads.mutex.unlock();
Thread::idle_loop(); // Force a call to base class idle_loop() Thread::idle_loop(); // Force a call to base class idle_loop()
// In helpful master concept a master can help only a sub-tree of its split // In the helpful master concept, a master can help only a sub-tree of its
// point, and because here is all finished is not possible master is booked. // split point and because everything is finished here, it's not possible
// for the master to be booked.
assert(!searching); assert(!searching);
assert(!activePosition); assert(!activePosition);
@@ -333,7 +326,6 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
// done under lock protection to avoid a race with Thread::available_to(). // done under lock protection to avoid a race with Thread::available_to().
Threads.mutex.lock(); Threads.mutex.lock();
sp.mutex.lock(); sp.mutex.lock();
}
searching = true; searching = true;
--splitPointsSize; --splitPointsSize;
@@ -348,8 +340,8 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
} }
// Explicit template instantiations // Explicit template instantiations
template void Thread::split<false>(Position&, const Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int, bool); template void Thread::split<false>(Position&, const Stack*, Value, Value, Value*, Move*, Depth, int, MovePicker*, int, bool);
template void Thread::split< true>(Position&, const Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int, bool); template void Thread::split< true>(Position&, const Stack*, Value, Value, Value*, Move*, Depth, int, MovePicker*, int, bool);
// wait_for_think_finished() waits for main thread to go to sleep then returns // wait_for_think_finished() waits for main thread to go to sleep then returns
@@ -366,8 +358,8 @@ void ThreadPool::wait_for_think_finished() {
// start_thinking() wakes up the main thread sleeping in MainThread::idle_loop() // start_thinking() wakes up the main thread sleeping in MainThread::idle_loop()
// so to start a new search, then returns immediately. // so to start a new search, then returns immediately.
void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, StateStackPtr& states) {
const std::vector<Move>& searchMoves, StateStackPtr& states) {
wait_for_think_finished(); wait_for_think_finished();
SearchTime = Time::now(); // As early as possible SearchTime = Time::now(); // As early as possible
@@ -385,8 +377,8 @@ void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
} }
for (MoveList<LEGAL> it(pos); *it; ++it) for (MoveList<LEGAL> it(pos); *it; ++it)
if ( searchMoves.empty() if ( limits.searchmoves.empty()
|| std::count(searchMoves.begin(), searchMoves.end(), *it)) || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), *it))
RootMoves.push_back(RootMove(*it)); RootMoves.push_back(RootMove(*it));
main()->thinking = true; main()->thinking = true;

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
#ifndef THREAD_H_INCLUDED #ifndef THREAD_H_INCLUDED
#define THREAD_H_INCLUDED #define THREAD_H_INCLUDED
#include <bitset>
#include <vector> #include <vector>
#include "material.h" #include "material.h"
@@ -28,7 +29,7 @@
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
const int MAX_THREADS = 64; // Because SplitPoint::slavesMask is a uint64_t const int MAX_THREADS = 128;
const int MAX_SPLITPOINTS_PER_THREAD = 8; const int MAX_SPLITPOINTS_PER_THREAD = 8;
struct Mutex { struct Mutex {
@@ -67,7 +68,6 @@ struct SplitPoint {
Depth depth; Depth depth;
Value beta; Value beta;
int nodeType; int nodeType;
Move threatMove;
bool cutNode; bool cutNode;
// Const pointers to shared data // Const pointers to shared data
@@ -76,8 +76,9 @@ struct SplitPoint {
// Shared data // Shared data
Mutex mutex; Mutex mutex;
volatile uint64_t slavesMask; std::bitset<MAX_THREADS> slavesMask;
volatile int64_t nodes; volatile bool allSlavesSearching;
volatile uint64_t nodes;
volatile Value alpha; volatile Value alpha;
volatile Value bestValue; volatile Value bestValue;
volatile Move bestMove; volatile Move bestMove;
@@ -91,7 +92,7 @@ struct SplitPoint {
struct ThreadBase { struct ThreadBase {
ThreadBase() : exit(false) {} ThreadBase() : handle(NativeHandle()), exit(false) {}
virtual ~ThreadBase() {} virtual ~ThreadBase() {}
virtual void idle_loop() = 0; virtual void idle_loop() = 0;
void notify_one(); void notify_one();
@@ -118,7 +119,7 @@ struct Thread : public ThreadBase {
template <bool Fake> template <bool Fake>
void split(Position& pos, const Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove, void split(Position& pos, const Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove,
Depth depth, Move threatMove, int moveCount, MovePicker* movePicker, int nodeType, bool cutNode); Depth depth, int moveCount, MovePicker* movePicker, int nodeType, bool cutNode);
SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD]; SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD];
Material::Table materialTable; Material::Table materialTable;
@@ -151,24 +152,21 @@ struct TimerThread : public ThreadBase {
/// ThreadPool struct handles all the threads related stuff like init, starting, /// ThreadPool struct handles all the threads related stuff like init, starting,
/// parking and, the most important, launching a slave thread at a split point. /// parking and, most importantly, launching a slave thread at a split point.
/// All the access to shared thread data is done through this class. /// All the access to shared thread data is done through this class.
struct ThreadPool : public std::vector<Thread*> { struct ThreadPool : public std::vector<Thread*> {
void init(); // No c'tor and d'tor, threads rely on globals that should void init(); // No c'tor and d'tor, threads rely on globals that should
void exit(); // be initialized and valid during the whole thread lifetime. void exit(); // be initialized and are valid during the whole thread lifetime.
MainThread* main() { return static_cast<MainThread*>((*this)[0]); } MainThread* main() { return static_cast<MainThread*>((*this)[0]); }
void read_uci_options(); void read_uci_options();
Thread* available_slave(const Thread* master) const; Thread* available_slave(const Thread* master) const;
void wait_for_think_finished(); void wait_for_think_finished();
void start_thinking(const Position&, const Search::LimitsType&, void start_thinking(const Position&, const Search::LimitsType&, Search::StateStackPtr&);
const std::vector<Move>&, Search::StateStackPtr&);
bool sleepWhileIdle;
Depth minimumSplitDepth; Depth minimumSplitDepth;
size_t maxThreadsPerSplitPoint;
Mutex mutex; Mutex mutex;
ConditionVariable sleepCondition; ConditionVariable sleepCondition;
TimerThread* timer; TimerThread* timer;

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
*/ */
#include <algorithm> #include <algorithm>
#include <cfloat>
#include <cmath> #include <cmath>
#include "search.h" #include "search.h"
@@ -26,65 +27,51 @@
namespace { namespace {
/// Constants enum TimeType { OptimumTime, MaxTime };
const int MoveHorizon = 50; // Plan time management at most this many moves ahead const int MoveHorizon = 50; // Plan time management at most this many moves ahead
const double MaxRatio = 7.0; // When in trouble, we can step over reserved time with this ratio const double MaxRatio = 7.0; // When in trouble, we can step over reserved time with this ratio
const double StealRatio = 0.33; // However we must not steal time from remaining moves over this ratio const double StealRatio = 0.33; // However we must not steal time from remaining moves over this ratio
const double xscale = 9.3;
const double xshift = 59.8;
const double skewfactor = 0.172;
// MoveImportance[] is 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. // 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 CCRL game database with some simple filtering criteria.
const int MoveImportance[512] = {
7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780,
7780, 7780, 7780, 7780, 7778, 7778, 7776, 7776, 7776, 7773, 7770, 7768, 7766, 7763, 7757, 7751,
7743, 7735, 7724, 7713, 7696, 7689, 7670, 7656, 7627, 7605, 7571, 7549, 7522, 7493, 7462, 7425,
7385, 7350, 7308, 7272, 7230, 7180, 7139, 7094, 7055, 7010, 6959, 6902, 6841, 6778, 6705, 6651,
6569, 6508, 6435, 6378, 6323, 6253, 6152, 6085, 5995, 5931, 5859, 5794, 5717, 5646, 5544, 5462,
5364, 5282, 5172, 5078, 4988, 4901, 4831, 4764, 4688, 4609, 4536, 4443, 4365, 4293, 4225, 4155,
4085, 4005, 3927, 3844, 3765, 3693, 3634, 3560, 3479, 3404, 3331, 3268, 3207, 3146, 3077, 3011,
2947, 2894, 2828, 2776, 2727, 2676, 2626, 2589, 2538, 2490, 2442, 2394, 2345, 2302, 2243, 2192,
2156, 2115, 2078, 2043, 2004, 1967, 1922, 1893, 1845, 1809, 1772, 1736, 1702, 1674, 1640, 1605,
1566, 1536, 1509, 1479, 1452, 1423, 1388, 1362, 1332, 1304, 1289, 1266, 1250, 1228, 1206, 1180,
1160, 1134, 1118, 1100, 1080, 1068, 1051, 1034, 1012, 1001, 980, 960, 945, 934, 916, 900, 888,
878, 865, 852, 828, 807, 787, 770, 753, 744, 731, 722, 706, 700, 683, 676, 671, 664, 652, 641,
634, 627, 613, 604, 591, 582, 568, 560, 552, 540, 534, 529, 519, 509, 495, 484, 474, 467, 460,
450, 438, 427, 419, 410, 406, 399, 394, 387, 382, 377, 372, 366, 359, 353, 348, 343, 337, 333,
328, 321, 315, 309, 303, 298, 293, 287, 284, 281, 277, 273, 265, 261, 255, 251, 247, 241, 240,
235, 229, 218, 217, 213, 212, 208, 206, 197, 193, 191, 189, 185, 184, 180, 177, 172, 170, 170,
170, 166, 163, 159, 158, 156, 155, 151, 146, 141, 138, 136, 132, 130, 128, 125, 123, 122, 118,
118, 118, 117, 115, 114, 108, 107, 105, 105, 105, 102, 97, 97, 95, 94, 93, 91, 88, 86, 83, 80,
80, 79, 79, 79, 78, 76, 75, 72, 72, 71, 70, 68, 65, 63, 61, 61, 59, 59, 59, 58, 56, 55, 54, 54,
52, 49, 48, 48, 48, 48, 45, 45, 45, 44, 43, 41, 41, 41, 41, 40, 40, 38, 37, 36, 34, 34, 34, 33,
31, 29, 29, 29, 28, 28, 28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 24, 24, 23, 23, 22, 21, 20, 20,
19, 19, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, 17, 17, 16, 16, 15, 15, 14, 14, 14, 12, 12, 11,
9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 1 };
int move_importance(int ply) { return MoveImportance[std::min(ply, 511)]; } double move_importance(int ply) {
return pow((1 + exp((ply - xshift) / xscale)), -skewfactor) + DBL_MIN; // Ensure non-zero
}
/// Function Prototypes template<TimeType T>
int remaining(int myTime, int movesToGo, int currentPly, int slowMover)
{
const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
const double TStealRatio = (T == OptimumTime ? 0 : StealRatio);
enum TimeType { OptimumTime, MaxTime }; double thisMoveImportance = (move_importance(currentPly) * slowMover) / 100;
double otherMovesImportance = 0;
template<TimeType> for (int i = 1; i < movesToGo; ++i)
int remaining(int myTime, int movesToGo, int fullMoveNumber, int slowMover); otherMovesImportance += move_importance(currentPly + 2 * i);
}
double ratio1 = (TMaxRatio * thisMoveImportance) / (TMaxRatio * thisMoveImportance + otherMovesImportance);
double ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / (thisMoveImportance + otherMovesImportance);
void TimeManager::pv_instability(double bestMoveChanges) { return int(myTime * std::min(ratio1, ratio2));
}
unstablePVExtraTime = int(bestMoveChanges * optimumSearchTime / 1.4); } // namespace
}
void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color us) void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color us)
{ {
/* We support four different kind of time controls: /* We support four different kinds of time controls:
increment == 0 && movesToGo == 0 means: x basetime [sudden death!] increment == 0 && movesToGo == 0 means: x basetime [sudden death!]
increment == 0 && movesToGo != 0 means: x moves in y minutes increment == 0 && movesToGo != 0 means: x moves in y minutes
@@ -108,15 +95,15 @@ void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color u
int minThinkingTime = Options["Minimum Thinking Time"]; int minThinkingTime = Options["Minimum Thinking Time"];
int slowMover = Options["Slow Mover"]; int slowMover = Options["Slow Mover"];
// Initialize to maximum values but unstablePVExtraTime that is reset // Initialize unstablePvFactor to 1 and search times to maximum values
unstablePVExtraTime = 0; unstablePvFactor = 1;
optimumSearchTime = maximumSearchTime = limits.time[us]; optimumSearchTime = maximumSearchTime = std::max(limits.time[us], minThinkingTime);
// We calculate optimum time usage for different hypothetic "moves to go"-values and choose the // We calculate optimum time usage for different hypothetical "moves to go"-values and choose the
// minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values. // minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values.
for (hypMTG = 1; hypMTG <= (limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon); ++hypMTG) for (hypMTG = 1; hypMTG <= (limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon); ++hypMTG)
{ {
// Calculate thinking time for hypothetic "moves to go"-value // Calculate thinking time for hypothetical "moves to go"-value
hypMyTime = limits.time[us] hypMyTime = limits.time[us]
+ limits.inc[us] * (hypMTG - 1) + limits.inc[us] * (hypMTG - 1)
- emergencyBaseTime - emergencyBaseTime
@@ -137,25 +124,3 @@ void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color u
// Make sure that maxSearchTime is not over absoluteMaxSearchTime // Make sure that maxSearchTime is not over absoluteMaxSearchTime
optimumSearchTime = std::min(optimumSearchTime, maximumSearchTime); optimumSearchTime = std::min(optimumSearchTime, maximumSearchTime);
} }
namespace {
template<TimeType T>
int remaining(int myTime, int movesToGo, int currentPly, int slowMover)
{
const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
const double TStealRatio = (T == OptimumTime ? 0 : StealRatio);
double thisMoveImportance = double(move_importance(currentPly) * slowMover) / 100;
int otherMovesImportance = 0;
for (int i = 1; i < movesToGo; ++i)
otherMovesImportance += move_importance(currentPly + 2 * i);
double ratio1 = (TMaxRatio * thisMoveImportance) / (TMaxRatio * thisMoveImportance + otherMovesImportance);
double ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / (thisMoveImportance + otherMovesImportance);
return int(floor(myTime * std::min(ratio1, ratio2)));
}
}

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,19 +21,19 @@
#define TIMEMAN_H_INCLUDED #define TIMEMAN_H_INCLUDED
/// The TimeManager class computes the optimal time to think depending on the /// The TimeManager class computes the optimal time to think depending on the
/// maximum available time, the move game number and other parameters. /// maximum available time, the game move number and other parameters.
class TimeManager { class TimeManager {
public: public:
void init(const Search::LimitsType& limits, int currentPly, Color us); void init(const Search::LimitsType& limits, int currentPly, Color us);
void pv_instability(double bestMoveChanges); void pv_instability(double bestMoveChanges) { unstablePvFactor = 1 + bestMoveChanges; }
int available_time() const { return optimumSearchTime + unstablePVExtraTime; } int available_time() const { return int(optimumSearchTime * unstablePvFactor * 0.71); }
int maximum_time() const { return maximumSearchTime; } int maximum_time() const { return maximumSearchTime; }
private: private:
int optimumSearchTime; int optimumSearchTime;
int maximumSearchTime; int maximumSearchTime;
int unstablePVExtraTime; double unstablePvFactor;
}; };
#endif // #ifndef TIMEMAN_H_INCLUDED #endif // #ifndef TIMEMAN_H_INCLUDED

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -26,11 +26,11 @@
TranspositionTable TT; // Our global transposition table TranspositionTable TT; // Our global transposition table
/// TranspositionTable::set_size() sets the size of the transposition table, /// TranspositionTable::resize() sets the size of the transposition table,
/// measured in megabytes. Transposition table consists of a power of 2 number /// measured in megabytes. Transposition table consists of a power of 2 number
/// of clusters and each cluster consists of ClusterSize number of TTEntry. /// of clusters and each cluster consists of ClusterSize number of TTEntry.
void TranspositionTable::set_size(size_t mbSize) { void TranspositionTable::resize(uint64_t mbSize) {
assert(msb((mbSize << 20) / sizeof(TTEntry)) < 32); assert(msb((mbSize << 20) / sizeof(TTEntry)) < 32);
@@ -70,12 +70,15 @@ void TranspositionTable::clear() {
const TTEntry* TranspositionTable::probe(const Key key) const { const TTEntry* TranspositionTable::probe(const Key key) const {
const TTEntry* tte = first_entry(key); TTEntry* tte = first_entry(key);
uint32_t key32 = key >> 32; uint32_t key32 = key >> 32;
for (unsigned i = 0; i < ClusterSize; ++i, ++tte) for (unsigned i = 0; i < ClusterSize; ++i, ++tte)
if (tte->key() == key32) if (tte->key32 == key32)
{
tte->generation8 = generation; // Refresh
return tte; return tte;
}
return NULL; return NULL;
} }
@@ -83,15 +86,14 @@ const TTEntry* TranspositionTable::probe(const Key key) const {
/// TranspositionTable::store() writes a new entry containing position key and /// TranspositionTable::store() writes a new entry containing position key and
/// valuable information of current position. The lowest order bits of position /// valuable information of current position. The lowest order bits of position
/// key are used to decide on which cluster the position will be placed. /// key are used to decide in which cluster the position will be placed.
/// When a new entry is written and there are no empty entries available in cluster, /// When a new entry is written and there are no empty entries available in the
/// it replaces the least valuable of entries. A TTEntry t1 is considered to be /// cluster, it replaces the least valuable of the entries. A TTEntry t1 is considered
/// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from /// to be more valuable than a TTEntry t2 if t1 is from the current search and t2
/// a previous search, or if the depth of t1 is bigger than the depth of t2. /// is from a previous search, or if the depth of t1 is bigger than the depth of t2.
void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV) { void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV) {
int c1, c2, c3;
TTEntry *tte, *replace; TTEntry *tte, *replace;
uint32_t key32 = key >> 32; // Use the high 32 bits as key inside the cluster uint32_t key32 = key >> 32; // Use the high 32 bits as key inside the cluster
@@ -99,7 +101,7 @@ void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m,
for (unsigned i = 0; i < ClusterSize; ++i, ++tte) for (unsigned i = 0; i < ClusterSize; ++i, ++tte)
{ {
if (!tte->key() || tte->key() == key32) // Empty or overwrite old if (!tte->key32 || tte->key32 == key32) // Empty or overwrite old
{ {
if (!m) if (!m)
m = tte->move(); // Preserve any existing ttMove m = tte->move(); // Preserve any existing ttMove
@@ -109,11 +111,9 @@ void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m,
} }
// Implement replace strategy // Implement replace strategy
c1 = (replace->generation() == generation ? 2 : 0); if ( ( tte->generation8 == generation || tte->bound() == BOUND_EXACT)
c2 = (tte->generation() == generation || tte->bound() == BOUND_EXACT ? -2 : 0); - (replace->generation8 == generation)
c3 = (tte->depth() < replace->depth() ? 1 : 0); - (tte->depth16 < replace->depth16) < 0)
if (c1 + c2 + c3 > 0)
replace = tte; replace = tte;
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -23,20 +23,28 @@
#include "misc.h" #include "misc.h"
#include "types.h" #include "types.h"
/// The TTEntry is the 128 bit transposition table entry, defined as below: /// The TTEntry is the 14 bytes transposition table entry, defined as below:
/// ///
/// key: 32 bit /// key 32 bit
/// move: 16 bit /// move 16 bit
/// bound type: 8 bit /// bound type 8 bit
/// generation: 8 bit /// generation 8 bit
/// value: 16 bit /// value 16 bit
/// depth: 16 bit /// depth 16 bit
/// static value: 16 bit /// eval value 16 bit
/// static margin: 16 bit
struct TTEntry { struct TTEntry {
void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev) { Move move() const { return (Move )move16; }
Bound bound() const { return (Bound)bound8; }
Value value() const { return (Value)value16; }
Depth depth() const { return (Depth)depth16; }
Value eval_value() const { return (Value)evalValue; }
private:
friend class TranspositionTable;
void save(uint32_t k, Value v, Bound b, Depth d, Move m, uint8_t g, Value ev) {
key32 = (uint32_t)k; key32 = (uint32_t)k;
move16 = (uint16_t)m; move16 = (uint16_t)m;
@@ -46,17 +54,7 @@ struct TTEntry {
depth16 = (int16_t)d; depth16 = (int16_t)d;
evalValue = (int16_t)ev; evalValue = (int16_t)ev;
} }
void set_generation(uint8_t g) { generation8 = g; }
uint32_t key() const { return key32; }
Depth depth() const { return (Depth)depth16; }
Move move() const { return (Move)move16; }
Value value() const { return (Value)value16; }
Bound bound() const { return (Bound)bound8; }
int generation() const { return (int)generation8; }
Value eval_value() const { return (Value)evalValue; }
private:
uint32_t key32; uint32_t key32;
uint16_t move16; uint16_t move16;
uint8_t bound8, generation8; uint8_t bound8, generation8;
@@ -66,13 +64,13 @@ private:
/// A TranspositionTable consists of a power of 2 number of clusters and each /// A TranspositionTable consists of a power of 2 number of clusters and each
/// cluster consists of ClusterSize number of TTEntry. Each non-empty entry /// cluster consists of ClusterSize number of TTEntry. Each non-empty entry
/// contains information of exactly one position. Size of a cluster shall not be /// contains information of exactly one position. The size of a cluster should
/// bigger than a cache line size. In case it is less, it should be padded to /// not be bigger than a cache line size. In case it is less, it should be padded
/// guarantee always aligned accesses. /// to guarantee always aligned accesses.
class TranspositionTable { class TranspositionTable {
static const unsigned ClusterSize = 4; // A cluster is 64 Bytes static const unsigned ClusterSize = 4;
public: public:
~TranspositionTable() { free(mem); } ~TranspositionTable() { free(mem); }
@@ -80,8 +78,7 @@ public:
const TTEntry* probe(const Key key) const; const TTEntry* probe(const Key key) const;
TTEntry* first_entry(const Key key) const; TTEntry* first_entry(const Key key) const;
void refresh(const TTEntry* tte) const; void resize(uint64_t mbSize);
void set_size(size_t mbSize);
void clear(); void clear();
void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV); void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV);
@@ -104,13 +101,4 @@ inline TTEntry* TranspositionTable::first_entry(const Key key) const {
return table + ((uint32_t)key & hashMask); return table + ((uint32_t)key & hashMask);
} }
/// TranspositionTable::refresh() updates the 'generation' value of the TTEntry
/// to avoid aging. Normally called after a TT hit.
inline void TranspositionTable::refresh(const TTEntry* tte) const {
const_cast<TTEntry*>(tte)->set_generation(generation);
}
#endif // #ifndef TT_H_INCLUDED #endif // #ifndef TT_H_INCLUDED

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -26,7 +26,7 @@
/// For Windows, part of the configuration is detected automatically, but some /// For Windows, part of the configuration is detected automatically, but some
/// switches need to be set manually: /// switches need to be set manually:
/// ///
/// -DNDEBUG | Disable debugging mode. Use always. /// -DNDEBUG | Disable debugging mode. Always use this.
/// ///
/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want /// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want
/// | the executable to run on some very old machines. /// | the executable to run on some very old machines.
@@ -54,6 +54,12 @@
# include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic # include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic
#endif #endif
#if defined(USE_PEXT)
# include <immintrin.h> // Header for _pext_u64() intrinsic
#else
# define _pext_u64(b, m) (0)
#endif
# if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER)) # if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER))
# include <xmmintrin.h> // Intel and Microsoft header for _mm_prefetch() # include <xmmintrin.h> // Intel and Microsoft header for _mm_prefetch()
# endif # endif
@@ -79,6 +85,12 @@ const bool HasPopCnt = true;
const bool HasPopCnt = false; const bool HasPopCnt = false;
#endif #endif
#ifdef USE_PEXT
const bool HasPext = true;
#else
const bool HasPext = false;
#endif
#ifdef IS_64BIT #ifdef IS_64BIT
const bool Is64Bit = true; const bool Is64Bit = true;
#else #else
@@ -89,7 +101,7 @@ typedef uint64_t Key;
typedef uint64_t Bitboard; typedef uint64_t Bitboard;
const int MAX_MOVES = 256; const int MAX_MOVES = 256;
const int MAX_PLY = 100; const int MAX_PLY = 120;
const int MAX_PLY_PLUS_6 = MAX_PLY + 6; const int MAX_PLY_PLUS_6 = MAX_PLY + 6;
/// A move needs 16 bits to be stored /// A move needs 16 bits to be stored
@@ -97,7 +109,8 @@ const int MAX_PLY_PLUS_6 = MAX_PLY + 6;
/// bit 0- 5: destination square (from 0 to 63) /// bit 0- 5: destination square (from 0 to 63)
/// bit 6-11: origin square (from 0 to 63) /// bit 6-11: origin square (from 0 to 63)
/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2) /// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
/// bit 14-15: special move flag: promotion (1), en passant (2), castle (3) /// bit 14-15: special move flag: promotion (1), en passant (2), castling (3)
/// NOTE: EN-PASSANT bit is set only when a pawn can be captured
/// ///
/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in /// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in
/// any normal move destination square is always different from origin square /// any normal move destination square is always different from origin square
@@ -112,23 +125,31 @@ enum MoveType {
NORMAL, NORMAL,
PROMOTION = 1 << 14, PROMOTION = 1 << 14,
ENPASSANT = 2 << 14, ENPASSANT = 2 << 14,
CASTLE = 3 << 14 CASTLING = 3 << 14
}; };
enum CastleRight { // Defined as in PolyGlot book hash key enum Color {
CASTLES_NONE, WHITE, BLACK, NO_COLOR, COLOR_NB = 2
};
enum CastlingSide {
KING_SIDE, QUEEN_SIDE, CASTLING_SIDE_NB = 2
};
enum CastlingRight { // Defined as in PolyGlot book hash key
NO_CASTLING,
WHITE_OO, WHITE_OO,
WHITE_OOO = WHITE_OO << 1, WHITE_OOO = WHITE_OO << 1,
BLACK_OO = WHITE_OO << 2, BLACK_OO = WHITE_OO << 2,
BLACK_OOO = WHITE_OO << 3, BLACK_OOO = WHITE_OO << 3,
ALL_CASTLES = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO, ANY_CASTLING = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO,
CASTLE_RIGHT_NB = 16 CASTLING_RIGHT_NB = 16
}; };
enum CastlingSide { template<Color C, CastlingSide S> struct MakeCastling {
KING_SIDE, static const CastlingRight
QUEEN_SIDE, right = C == WHITE ? S == QUEEN_SIDE ? WHITE_OOO : WHITE_OO
CASTLING_SIDE_NB = 2 : S == QUEEN_SIDE ? BLACK_OOO : BLACK_OO;
}; };
enum Phase { enum Phase {
@@ -139,6 +160,7 @@ enum Phase {
enum ScaleFactor { enum ScaleFactor {
SCALE_FACTOR_DRAW = 0, SCALE_FACTOR_DRAW = 0,
SCALE_FACTOR_ONEPAWN = 48,
SCALE_FACTOR_NORMAL = 64, SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128, SCALE_FACTOR_MAX = 128,
SCALE_FACTOR_NONE = 255 SCALE_FACTOR_NONE = 255
@@ -154,10 +176,10 @@ enum Bound {
enum Value { enum Value {
VALUE_ZERO = 0, VALUE_ZERO = 0,
VALUE_DRAW = 0, VALUE_DRAW = 0,
VALUE_KNOWN_WIN = 15000, VALUE_KNOWN_WIN = 10000,
VALUE_MATE = 30000, VALUE_MATE = 32000,
VALUE_INFINITE = 30001, VALUE_INFINITE = 32001,
VALUE_NONE = 30002, VALUE_NONE = 32002,
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY,
@@ -169,7 +191,9 @@ enum Value {
KnightValueMg = 817, KnightValueEg = 846, KnightValueMg = 817, KnightValueEg = 846,
BishopValueMg = 836, BishopValueEg = 857, BishopValueMg = 836, BishopValueEg = 857,
RookValueMg = 1270, RookValueEg = 1278, RookValueMg = 1270, RookValueEg = 1278,
QueenValueMg = 2521, QueenValueEg = 2558 QueenValueMg = 2521, QueenValueEg = 2558,
MidgameLimit = 15581, EndgameLimit = 3998
}; };
enum PieceType { enum PieceType {
@@ -185,10 +209,6 @@ enum Piece {
PIECE_NB = 16 PIECE_NB = 16
}; };
enum Color {
WHITE, BLACK, NO_COLOR, COLOR_NB = 2
};
enum Depth { enum Depth {
ONE_PLY = 2, ONE_PLY = 2,
@@ -236,39 +256,42 @@ enum Rank {
}; };
/// Score enum keeps a midgame and an endgame value in a single integer (enum), /// The Score enum stores a middlegame and an endgame value in a single integer
/// first LSB 16 bits are used to store endgame value, while upper bits are used /// (enum). The least significant 16 bits are used to store the endgame value
/// for midgame value. Compiler is free to choose the enum type as long as can /// and the upper 16 bits are used to store the middlegame value. The compiler
/// keep its data, so ensure Score to be an integer type. /// is free to choose the enum type as long as it can store the data, so we
/// ensure that Score is an integer type by assigning some big int values.
enum Score { enum Score {
SCORE_ZERO, SCORE_ZERO,
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX, SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
}; };
inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); } typedef union {
uint32_t full;
struct { int16_t eg, mg; } half;
} ScoreView;
/// Extracting the signed lower and upper 16 bits it not so trivial because inline Score make_score(int mg, int eg) {
/// according to the standard a simple cast to short is implementation defined ScoreView v;
/// and so is a right shift of a signed integer. v.half.mg = (int16_t)(mg - (uint16_t(eg) >> 15));
inline Value mg_value(Score s) { return Value(((s + 0x8000) & ~0xffff) / 0x10000); } v.half.eg = (int16_t)eg;
return Score(v.full);
/// On Intel 64 bit we have a small speed regression with the standard conforming
/// version, so use a faster code in this case that, although not 100% standard
/// compliant it seems to work for Intel and MSVC.
#if defined(IS_64BIT) && (!defined(__GNUC__) || defined(__INTEL_COMPILER))
inline Value eg_value(Score s) { return Value(int16_t(s & 0xffff)); }
#else
inline Value eg_value(Score s) {
return Value((int)(unsigned(s) & 0x7fffu) - (int)(unsigned(s) & 0x8000u));
} }
#endif inline Value mg_value(Score s) {
ScoreView v;
v.full = s;
return Value(v.half.mg + (uint16_t(v.half.eg) >> 15));
}
#define ENABLE_SAFE_OPERATORS_ON(T) \ inline Value eg_value(Score s) {
ScoreView v;
v.full = s;
return Value(v.half.eg);
}
#define ENABLE_BASE_OPERATORS_ON(T) \
inline T operator+(const T d1, const T d2) { return T(int(d1) + int(d2)); } \ inline T operator+(const T d1, const T d2) { return T(int(d1) + int(d2)); } \
inline T operator-(const T d1, const T d2) { return T(int(d1) - int(d2)); } \ inline T operator-(const T d1, const T d2) { return T(int(d1) - int(d2)); } \
inline T operator*(int i, const T d) { return T(i * int(d)); } \ inline T operator*(int i, const T d) { return T(i * int(d)); } \
@@ -278,26 +301,32 @@ inline T& operator+=(T& d1, const T d2) { return d1 = d1 + d2; } \
inline T& operator-=(T& d1, const T d2) { return d1 = d1 - d2; } \ inline T& operator-=(T& d1, const T d2) { return d1 = d1 - d2; } \
inline T& operator*=(T& d, int i) { return d = T(int(d) * i); } inline T& operator*=(T& d, int i) { return d = T(int(d) * i); }
#define ENABLE_OPERATORS_ON(T) ENABLE_SAFE_OPERATORS_ON(T) \ ENABLE_BASE_OPERATORS_ON(Score)
#define ENABLE_FULL_OPERATORS_ON(T) \
ENABLE_BASE_OPERATORS_ON(T) \
inline T& operator++(T& d) { return d = T(int(d) + 1); } \ inline T& operator++(T& d) { return d = T(int(d) + 1); } \
inline T& operator--(T& d) { return d = T(int(d) - 1); } \ inline T& operator--(T& d) { return d = T(int(d) - 1); } \
inline T operator/(const T d, int i) { return T(int(d) / i); } \ inline T operator/(const T d, int i) { return T(int(d) / i); } \
inline T& operator/=(T& d, int i) { return d = T(int(d) / i); } inline T& operator/=(T& d, int i) { return d = T(int(d) / i); }
ENABLE_OPERATORS_ON(Value) ENABLE_FULL_OPERATORS_ON(Value)
ENABLE_OPERATORS_ON(PieceType) ENABLE_FULL_OPERATORS_ON(PieceType)
ENABLE_OPERATORS_ON(Piece) ENABLE_FULL_OPERATORS_ON(Piece)
ENABLE_OPERATORS_ON(Color) ENABLE_FULL_OPERATORS_ON(Color)
ENABLE_OPERATORS_ON(Depth) ENABLE_FULL_OPERATORS_ON(Depth)
ENABLE_OPERATORS_ON(Square) ENABLE_FULL_OPERATORS_ON(Square)
ENABLE_OPERATORS_ON(File) ENABLE_FULL_OPERATORS_ON(File)
ENABLE_OPERATORS_ON(Rank) ENABLE_FULL_OPERATORS_ON(Rank)
/// Added operators for adding integers to a Value #undef ENABLE_FULL_OPERATORS_ON
#undef ENABLE_BASE_OPERATORS_ON
/// Additional operators to add integers to a Value
inline Value operator+(Value v, int i) { return Value(int(v) + i); } inline Value operator+(Value v, int i) { return Value(int(v) + i); }
inline Value operator-(Value v, int i) { return Value(int(v) - i); } inline Value operator-(Value v, int i) { return Value(int(v) - i); }
inline Value& operator+=(Value& v, int i) { return v = v + i; }
ENABLE_SAFE_OPERATORS_ON(Score) inline Value& operator-=(Value& v, int i) { return v = v - i; }
/// Only declared but not defined. We don't want to multiply two scores due to /// Only declared but not defined. We don't want to multiply two scores due to
/// a very high risk of overflow. So user should explicitly convert to integer. /// a very high risk of overflow. So user should explicitly convert to integer.
@@ -308,18 +337,15 @@ inline Score operator/(Score s, int i) {
return make_score(mg_value(s) / i, eg_value(s) / i); return make_score(mg_value(s) / i, eg_value(s) / i);
} }
#undef ENABLE_OPERATORS_ON
#undef ENABLE_SAFE_OPERATORS_ON
extern Value PieceValue[PHASE_NB][PIECE_NB]; extern Value PieceValue[PHASE_NB][PIECE_NB];
struct ExtMove { struct ExtMove {
Move move; Move move;
int score; Value value;
}; };
inline bool operator<(const ExtMove& f, const ExtMove& s) { inline bool operator<(const ExtMove& f, const ExtMove& s) {
return f.score < s.score; return f.value < s.value;
} }
inline Color operator~(Color c) { inline Color operator~(Color c) {
@@ -330,8 +356,8 @@ inline Square operator~(Square s) {
return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8 return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8
} }
inline Square operator|(File f, Rank r) { inline CastlingRight operator|(Color c, CastlingSide s) {
return Square((r << 3) | f); return CastlingRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c));
} }
inline Value mate_in(int ply) { inline Value mate_in(int ply) {
@@ -342,21 +368,21 @@ inline Value mated_in(int ply) {
return -VALUE_MATE + ply; return -VALUE_MATE + ply;
} }
inline Square make_square(File f, Rank r) {
return Square((r << 3) | f);
}
inline Piece make_piece(Color c, PieceType pt) { inline Piece make_piece(Color c, PieceType pt) {
return Piece((c << 3) | pt); return Piece((c << 3) | pt);
} }
inline CastleRight make_castle_right(Color c, CastlingSide s) { inline PieceType type_of(Piece pc) {
return CastleRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c)); return PieceType(pc & 7);
} }
inline PieceType type_of(Piece p) { inline Color color_of(Piece pc) {
return PieceType(p & 7); assert(pc != NO_PIECE);
} return Color(pc >> 3);
inline Color color_of(Piece p) {
assert(p != NO_PIECE);
return Color(p >> 3);
} }
inline bool is_ok(Square s) { inline bool is_ok(Square s) {
@@ -388,11 +414,11 @@ inline bool opposite_colors(Square s1, Square s2) {
return ((s >> 3) ^ s) & 1; return ((s >> 3) ^ s) & 1;
} }
inline char file_to_char(File f, bool tolower = true) { inline char to_char(File f, bool tolower = true) {
return char(f - FILE_A + (tolower ? 'a' : 'A')); return char(f - FILE_A + (tolower ? 'a' : 'A'));
} }
inline char rank_to_char(Rank r) { inline char to_char(Rank r) {
return char(r - RANK_1 + '1'); return char(r - RANK_1 + '1');
} }
@@ -431,8 +457,8 @@ inline bool is_ok(Move m) {
#include <string> #include <string>
inline const std::string square_to_string(Square s) { inline const std::string to_string(Square s) {
char ch[] = { file_to_char(file_of(s)), rank_to_char(rank_of(s)), 0 }; char ch[] = { to_char(file_of(s)), to_char(rank_of(s)), 0 };
return ch; return ch;
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "thread.h" #include "thread.h"
#include "tt.h"
#include "ucioption.h" #include "ucioption.h"
using namespace std; using namespace std;
@@ -38,96 +39,14 @@ namespace {
// FEN string of the initial position, normal chess // FEN string of the initial position, normal chess
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
// Keep track of position keys along the setup moves (from start position to the // Keep a track of the position keys along the setup moves (from the start position
// position just before to start searching). Needed by repetition draw detection. // to the position just before the search starts). This is needed by the repetition
// draw detection code.
Search::StateStackPtr SetupStates; Search::StateStackPtr SetupStates;
void setoption(istringstream& up);
void position(Position& pos, istringstream& up);
void go(const Position& pos, istringstream& up);
}
/// Wait for a command from the user, parse this text string as an UCI command,
/// and call the appropriate functions. Also intercepts EOF from stdin to ensure
/// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI
/// commands, the function also supports a few debug commands.
void UCI::loop(const string& args) {
Position pos(StartFEN, false, Threads.main()); // The root position
string token, cmd = args;
do {
if (args.empty() && !getline(cin, cmd)) // Block here waiting for input
cmd = "quit";
istringstream is(cmd);
is >> skipws >> token;
if (token == "quit" || token == "stop" || token == "ponderhit")
{
// GUI sends 'ponderhit' to tell us to ponder on the same move the
// opponent has played. In case Signals.stopOnPonderhit is set we are
// waiting for 'ponderhit' to stop the search (for instance because we
// already ran out of time), otherwise we should continue searching but
// switching from pondering to normal search.
if (token != "ponderhit" || Search::Signals.stopOnPonderhit)
{
Search::Signals.stop = true;
Threads.main()->notify_one(); // Could be sleeping
}
else
Search::Limits.ponder = false;
}
else if (token == "perft" && (is >> token)) // Read perft depth
{
stringstream ss;
ss << Options["Hash"] << " "
<< Options["Threads"] << " " << token << " current perft";
benchmark(pos, ss);
}
else if (token == "key")
sync_cout << hex << uppercase << setfill('0')
<< "position key: " << setw(16) << pos.key()
<< "\nmaterial key: " << setw(16) << pos.material_key()
<< "\npawn key: " << setw(16) << pos.pawn_key()
<< dec << sync_endl;
else if (token == "uci")
sync_cout << "id name " << engine_info(true)
<< "\n" << Options
<< "\nuciok" << sync_endl;
else if (token == "eval")
{
Search::RootColor = pos.side_to_move(); // Ensure it is set
sync_cout << Eval::trace(pos) << sync_endl;
}
else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ }
else if (token == "go") go(pos, is);
else if (token == "position") position(pos, is);
else if (token == "setoption") setoption(is);
else if (token == "flip") pos.flip();
else if (token == "bench") benchmark(pos, is);
else if (token == "d") sync_cout << pos.pretty() << sync_endl;
else if (token == "isready") sync_cout << "readyok" << sync_endl;
else
sync_cout << "Unknown command: " << cmd << sync_endl;
} while (token != "quit" && args.empty()); // Args have one-shot behaviour
Threads.wait_for_think_finished(); // Cannot quit while search is running
}
namespace {
// position() is called when engine receives the "position" UCI command. // position() is called when engine receives the "position" UCI command.
// The function sets up the position described in the given fen string ("fen") // The function sets up the position described in the given FEN string ("fen")
// or the starting position ("startpos") and then makes the moves given in the // or the starting position ("startpos") and then makes the moves given in the
// following move list ("moves"). // following move list ("moves").
@@ -192,14 +111,13 @@ namespace {
void go(const Position& pos, istringstream& is) { void go(const Position& pos, istringstream& is) {
Search::LimitsType limits; Search::LimitsType limits;
vector<Move> searchMoves;
string token; string token;
while (is >> token) while (is >> token)
{ {
if (token == "searchmoves") if (token == "searchmoves")
while (is >> token) while (is >> token)
searchMoves.push_back(move_from_uci(pos, token)); limits.searchmoves.push_back(move_from_uci(pos, token));
else if (token == "wtime") is >> limits.time[WHITE]; else if (token == "wtime") is >> limits.time[WHITE];
else if (token == "btime") is >> limits.time[BLACK]; else if (token == "btime") is >> limits.time[BLACK];
@@ -214,6 +132,88 @@ namespace {
else if (token == "ponder") limits.ponder = true; else if (token == "ponder") limits.ponder = true;
} }
Threads.start_thinking(pos, limits, searchMoves, SetupStates); Threads.start_thinking(pos, limits, SetupStates);
} }
} // namespace
/// Wait for a command from the user, parse this text string as an UCI command,
/// and call the appropriate functions. Also intercepts EOF from stdin to ensure
/// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI
/// commands, the function also supports a few debug commands.
void UCI::loop(int argc, char* argv[]) {
Position pos(StartFEN, false, Threads.main()); // The root position
string token, cmd;
for (int i = 1; i < argc; ++i)
cmd += std::string(argv[i]) + " ";
do {
if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input
cmd = "quit";
istringstream is(cmd);
is >> skipws >> token;
if (token == "quit" || token == "stop" || token == "ponderhit")
{
// The GUI sends 'ponderhit' to tell us to ponder on the same move the
// opponent has played. In case Signals.stopOnPonderhit is set we are
// waiting for 'ponderhit' to stop the search (for instance because we
// already ran out of time), otherwise we should continue searching but
// switch from pondering to normal search.
if (token != "ponderhit" || Search::Signals.stopOnPonderhit)
{
Search::Signals.stop = true;
Threads.main()->notify_one(); // Could be sleeping
}
else
Search::Limits.ponder = false;
}
else if (token == "perft" || token == "divide")
{
int depth;
stringstream ss;
is >> depth;
ss << Options["Hash"] << " "
<< Options["Threads"] << " " << depth << " current " << token;
benchmark(pos, ss);
}
else if (token == "key")
sync_cout << hex << uppercase << setfill('0')
<< "position key: " << setw(16) << pos.key()
<< "\nmaterial key: " << setw(16) << pos.material_key()
<< "\npawn key: " << setw(16) << pos.pawn_key()
<< dec << nouppercase << setfill(' ') << sync_endl;
else if (token == "uci")
sync_cout << "id name " << engine_info(true)
<< "\n" << Options
<< "\nuciok" << sync_endl;
else if (token == "eval")
{
Search::RootColor = pos.side_to_move(); // Ensure it is set
sync_cout << Eval::trace(pos) << sync_endl;
}
else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ }
else if (token == "go") go(pos, is);
else if (token == "position") position(pos, is);
else if (token == "setoption") setoption(is);
else if (token == "flip") pos.flip();
else if (token == "bench") benchmark(pos, is);
else if (token == "d") sync_cout << pos.pretty() << sync_endl;
else if (token == "isready") sync_cout << "readyok" << sync_endl;
else
sync_cout << "Unknown command: " << cmd << sync_endl;
} while (token != "quit" && argc == 1); // Passed args have one-shot behaviour
Threads.wait_for_think_finished(); // Cannot quit whilst the search is running
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -38,7 +38,7 @@ namespace UCI {
void on_logger(const Option& o) { start_logger(o); } void on_logger(const Option& o) { start_logger(o); }
void on_eval(const Option&) { Eval::init(); } void on_eval(const Option&) { Eval::init(); }
void on_threads(const Option&) { Threads.read_uci_options(); } void on_threads(const Option&) { Threads.read_uci_options(); }
void on_hash_size(const Option& o) { TT.set_size(o); } void on_hash_size(const Option& o) { TT.resize(o); }
void on_clear_hash(const Option&) { TT.clear(); } void on_clear_hash(const Option&) { TT.clear(); }
@@ -50,42 +50,39 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const
} }
/// init() initializes the UCI options to their hard coded default values /// init() initializes the UCI options to their hard-coded default values
void init(OptionsMap& o) { void init(OptionsMap& o) {
o["Write Debug Log"] = Option(false, on_logger); o["Write Debug Log"] << Option(false, on_logger);
o["Write Search Log"] = Option(false); o["Write Search Log"] << Option(false);
o["Search Log Filename"] = Option("SearchLog.txt"); o["Search Log Filename"] << Option("SearchLog.txt");
o["Book File"] = Option("book.bin"); o["Book File"] << Option("book.bin");
o["Best Book Move"] = Option(false); o["Best Book Move"] << Option(false);
o["Contempt Factor"] = Option(0, -50, 50); o["Contempt Factor"] << Option(0, -50, 50);
o["Mobility (Midgame)"] = Option(100, 0, 200, on_eval); o["Mobility (Midgame)"] << Option(100, 0, 200, on_eval);
o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval); o["Mobility (Endgame)"] << Option(100, 0, 200, on_eval);
o["Pawn Structure (Midgame)"] = Option(100, 0, 200, on_eval); o["Pawn Structure (Midgame)"] << Option(100, 0, 200, on_eval);
o["Pawn Structure (Endgame)"] = Option(100, 0, 200, on_eval); o["Pawn Structure (Endgame)"] << Option(100, 0, 200, on_eval);
o["Passed Pawns (Midgame)"] = Option(100, 0, 200, on_eval); o["Passed Pawns (Midgame)"] << Option(100, 0, 200, on_eval);
o["Passed Pawns (Endgame)"] = Option(100, 0, 200, on_eval); o["Passed Pawns (Endgame)"] << Option(100, 0, 200, on_eval);
o["Space"] = Option(100, 0, 200, on_eval); o["Space"] << Option(100, 0, 200, on_eval);
o["Aggressiveness"] = Option(100, 0, 200, on_eval); o["Aggressiveness"] << Option(100, 0, 200, on_eval);
o["Cowardice"] = Option(100, 0, 200, on_eval); o["Cowardice"] << Option(100, 0, 200, on_eval);
o["Min Split Depth"] = Option(0, 0, 12, on_threads); o["Min Split Depth"] << Option(0, 0, 12, on_threads);
o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads); o["Threads"] << Option(1, 1, MAX_THREADS, on_threads);
o["Threads"] = Option(1, 1, MAX_THREADS, on_threads); o["Hash"] << Option(32, 1, 16384, on_hash_size);
o["Idle Threads Sleep"] = Option(true); o["Clear Hash"] << Option(on_clear_hash);
o["Hash"] = Option(32, 1, 8192, on_hash_size); o["Ponder"] << Option(true);
o["Clear Hash"] = Option(on_clear_hash); o["OwnBook"] << Option(false);
o["Ponder"] = Option(true); o["MultiPV"] << Option(1, 1, 500);
o["OwnBook"] = Option(false); o["Skill Level"] << Option(20, 0, 20);
o["MultiPV"] = Option(1, 1, 500); o["Emergency Move Horizon"] << Option(40, 0, 50);
o["Skill Level"] = Option(20, 0, 20); o["Emergency Base Time"] << Option(60, 0, 30000);
o["Emergency Move Horizon"] = Option(40, 0, 50); o["Emergency Move Time"] << Option(30, 0, 5000);
o["Emergency Base Time"] = Option(60, 0, 30000); o["Minimum Thinking Time"] << Option(20, 0, 5000);
o["Emergency Move Time"] = Option(30, 0, 5000); o["Slow Mover"] << Option(80, 10, 1000);
o["Minimum Thinking Time"] = Option(20, 0, 5000); o["UCI_Chess960"] << Option(false);
o["Slow Mover"] = Option(70, 10, 1000);
o["UCI_Chess960"] = Option(false);
o["UCI_AnalyseMode"] = Option(false, on_eval);
} }
@@ -113,18 +110,18 @@ std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
} }
/// Option c'tors and conversion operators /// Option class constructors and conversion operators
Option::Option(const char* v, Fn* f) : type("string"), min(0), max(0), idx(Options.size()), on_change(f) Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), on_change(f)
{ defaultValue = currentValue = v; } { defaultValue = currentValue = v; }
Option::Option(bool v, Fn* f) : type("check"), min(0), max(0), idx(Options.size()), on_change(f) Option::Option(bool v, OnChange f) : type("check"), min(0), max(0), on_change(f)
{ defaultValue = currentValue = (v ? "true" : "false"); } { defaultValue = currentValue = (v ? "true" : "false"); }
Option::Option(Fn* f) : type("button"), min(0), max(0), idx(Options.size()), on_change(f) Option::Option(OnChange f) : type("button"), min(0), max(0), on_change(f)
{} {}
Option::Option(int v, int minv, int maxv, Fn* f) : type("spin"), min(minv), max(maxv), idx(Options.size()), on_change(f) Option::Option(int v, int minv, int maxv, OnChange f) : type("spin"), min(minv), max(maxv), on_change(f)
{ std::ostringstream ss; ss << v; defaultValue = currentValue = ss.str(); } { std::ostringstream ss; ss << v; defaultValue = currentValue = ss.str(); }
@@ -139,6 +136,17 @@ Option::operator std::string() const {
} }
/// operator<<() inits options and assigns idx in the correct printing order
void Option::operator<<(const Option& o) {
static size_t insert_order = 0;
*this = o;
idx = insert_order++;
}
/// operator=() updates currentValue and triggers on_change() action. It's up to /// operator=() updates currentValue and triggers on_change() action. It's up to
/// the GUI to check for option's limits, but we could receive the new value from /// the GUI to check for option's limits, but we could receive the new value from
/// the user by console window, so let's check the bounds anyway. /// the user by console window, so let's check the bounds anyway.
@@ -156,7 +164,7 @@ Option& Option::operator=(const string& v) {
currentValue = v; currentValue = v;
if (on_change) if (on_change)
(*on_change)(*this); on_change(*this);
return *this; return *this;
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -38,15 +38,16 @@ typedef std::map<std::string, Option, CaseInsensitiveLess> OptionsMap;
/// Option class implements an option as defined by UCI protocol /// Option class implements an option as defined by UCI protocol
class Option { class Option {
typedef void (Fn)(const Option&); typedef void (*OnChange)(const Option&);
public: public:
Option(Fn* = NULL); Option(OnChange = NULL);
Option(bool v, Fn* = NULL); Option(bool v, OnChange = NULL);
Option(const char* v, Fn* = NULL); Option(const char* v, OnChange = NULL);
Option(int v, int min, int max, Fn* = NULL); Option(int v, int min, int max, OnChange = NULL);
Option& operator=(const std::string& v); Option& operator=(const std::string& v);
void operator<<(const Option& o);
operator int() const; operator int() const;
operator std::string() const; operator std::string() const;
@@ -56,11 +57,11 @@ private:
std::string defaultValue, currentValue, type; std::string defaultValue, currentValue, type;
int min, max; int min, max;
size_t idx; size_t idx;
Fn* on_change; OnChange on_change;
}; };
void init(OptionsMap&); void init(OptionsMap&);
void loop(const std::string&); void loop(int argc, char* argv[]);
} // namespace UCI } // namespace UCI