mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-12-18 03:32:18 +01:00
DroidFish: Updated Stockfish engine to version 5.
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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<>
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|
||||||
enum TimeType { OptimumTime, MaxTime };
|
|
||||||
|
|
||||||
template<TimeType>
|
|
||||||
int remaining(int myTime, int movesToGo, int fullMoveNumber, int slowMover);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
void TimeManager::pv_instability(double bestMoveChanges) {
|
double thisMoveImportance = (move_importance(currentPly) * slowMover) / 100;
|
||||||
|
double otherMovesImportance = 0;
|
||||||
|
|
||||||
unstablePVExtraTime = int(bestMoveChanges * optimumSearchTime / 1.4);
|
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(myTime * std::min(ratio1, ratio2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // 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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user