mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-12-10 08:02:40 +01:00
DroidFish: Updated stockfish engine to version 3.
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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -66,7 +66,7 @@ void benchmark(const Position& current, istream& is) {
|
|||||||
vector<string> fens;
|
vector<string> fens;
|
||||||
|
|
||||||
// Assign default values to missing arguments
|
// Assign default values to missing arguments
|
||||||
string ttSize = (is >> token) ? token : "128";
|
string ttSize = (is >> token) ? token : "32";
|
||||||
string threads = (is >> token) ? token : "1";
|
string threads = (is >> token) ? token : "1";
|
||||||
string limit = (is >> token) ? token : "12";
|
string limit = (is >> token) ? token : "12";
|
||||||
string fenFile = (is >> token) ? token : "default";
|
string fenFile = (is >> token) ? token : "default";
|
||||||
@@ -82,6 +82,9 @@ void benchmark(const Position& current, istream& is) {
|
|||||||
else if (limitType == "nodes")
|
else if (limitType == "nodes")
|
||||||
limits.nodes = atoi(limit.c_str());
|
limits.nodes = atoi(limit.c_str());
|
||||||
|
|
||||||
|
else if (limitType == "mate")
|
||||||
|
limits.mate = atoi(limit.c_str());
|
||||||
|
|
||||||
else
|
else
|
||||||
limits.depth = atoi(limit.c_str());
|
limits.depth = atoi(limit.c_str());
|
||||||
|
|
||||||
@@ -89,7 +92,7 @@ void benchmark(const Position& current, istream& is) {
|
|||||||
fens.assign(Defaults, Defaults + 16);
|
fens.assign(Defaults, Defaults + 16);
|
||||||
|
|
||||||
else if (fenFile == "current")
|
else if (fenFile == "current")
|
||||||
fens.push_back(current.to_fen());
|
fens.push_back(current.fen());
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -99,7 +102,7 @@ void benchmark(const Position& current, istream& is) {
|
|||||||
if (!file.is_open())
|
if (!file.is_open())
|
||||||
{
|
{
|
||||||
cerr << "Unable to open file " << fenFile << endl;
|
cerr << "Unable to open file " << fenFile << endl;
|
||||||
exit(EXIT_FAILURE);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (getline(file, fen))
|
while (getline(file, fen))
|
||||||
@@ -127,9 +130,9 @@ void benchmark(const Position& current, istream& is) {
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Threads.start_searching(pos, limits, vector<Move>(), st);
|
Threads.start_thinking(pos, limits, vector<Move>(), st);
|
||||||
Threads.wait_for_search_finished();
|
Threads.wait_for_think_finished();
|
||||||
nodes += Search::RootPosition.nodes_searched();
|
nodes += Search::RootPos.nodes_searched();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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,32 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "bitboard.h"
|
#include "bitboard.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// The possible pawns squares are 24, the first 4 files and ranks from 2 to 7
|
||||||
|
const unsigned IndexMax = 2*24*64*64; // stm * psq * wksq * bksq = 196608
|
||||||
|
|
||||||
|
// Each uint32_t stores results of 32 positions, one per bit
|
||||||
|
uint32_t KPKBitbase[IndexMax / 32];
|
||||||
|
|
||||||
|
// A KPK bitbase index is an integer in [0, IndexMax] range
|
||||||
|
//
|
||||||
|
// Information is mapped in a way that minimizes number of iterations:
|
||||||
|
//
|
||||||
|
// 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 12: side to move (WHITE or BLACK)
|
||||||
|
// bit 13-14: white pawn file (from FILE_A to FILE_D)
|
||||||
|
// bit 15-17: white pawn 6 - rank (from 6 - RANK_7 to 6 - RANK_2)
|
||||||
|
unsigned index(Color us, Square bksq, Square wksq, Square psq) {
|
||||||
|
return wksq + (bksq << 6) + (us << 12) + (file_of(psq) << 13) + ((6 - rank_of(psq)) << 15);
|
||||||
|
}
|
||||||
|
|
||||||
enum Result {
|
enum Result {
|
||||||
INVALID = 0,
|
INVALID = 0,
|
||||||
UNKNOWN = 1,
|
UNKNOWN = 1,
|
||||||
@@ -35,196 +55,119 @@ namespace {
|
|||||||
|
|
||||||
struct KPKPosition {
|
struct KPKPosition {
|
||||||
|
|
||||||
Result classify_leaf(int idx);
|
operator Result() const { return res; }
|
||||||
Result classify(int idx, Result db[]);
|
Result classify_leaf(unsigned idx);
|
||||||
|
Result classify(const std::vector<KPKPosition>& db)
|
||||||
|
{ return us == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<Color Us> Result classify(const Result db[]) const;
|
template<Color Us> Result classify(const std::vector<KPKPosition>& db);
|
||||||
|
|
||||||
template<Color Us> Bitboard k_attacks() const {
|
Color us;
|
||||||
return Us == WHITE ? StepAttacksBB[W_KING][wksq] : StepAttacksBB[B_KING][bksq];
|
Square bksq, wksq, psq;
|
||||||
}
|
Result res;
|
||||||
|
|
||||||
Bitboard p_attacks() const { return StepAttacksBB[W_PAWN][psq]; }
|
|
||||||
void decode_index(int idx);
|
|
||||||
|
|
||||||
Square wksq, bksq, psq;
|
|
||||||
Color stm;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// The possible pawns squares are 24, the first 4 files and ranks from 2 to 7
|
} // namespace
|
||||||
const int IndexMax = 2 * 24 * 64 * 64; // stm * wp_sq * wk_sq * bk_sq = 196608
|
|
||||||
|
|
||||||
// Each uint32_t stores results of 32 positions, one per bit
|
|
||||||
uint32_t KPKBitbase[IndexMax / 32];
|
|
||||||
|
|
||||||
int index(Square wksq, Square bksq, Square psq, Color stm);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm) {
|
bool Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color us) {
|
||||||
|
|
||||||
int idx = index(wksq, bksq, wpsq, stm);
|
assert(file_of(wpsq) <= FILE_D);
|
||||||
return KPKBitbase[idx / 32] & (1 << (idx & 31));
|
|
||||||
|
unsigned idx = index(us, bksq, wksq, wpsq);
|
||||||
|
return KPKBitbase[idx / 32] & (1 << (idx & 0x1F));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Bitbases::init_kpk() {
|
void Bitbases::init_kpk() {
|
||||||
|
|
||||||
Result db[IndexMax];
|
unsigned idx, repeat = 1;
|
||||||
KPKPosition pos;
|
std::vector<KPKPosition> db(IndexMax);
|
||||||
int idx, bit, repeat = 1;
|
|
||||||
|
|
||||||
// Initialize table with known win / draw positions
|
// Initialize db with known win / draw positions
|
||||||
for (idx = 0; idx < IndexMax; idx++)
|
for (idx = 0; idx < IndexMax; idx++)
|
||||||
db[idx] = pos.classify_leaf(idx);
|
db[idx].classify_leaf(idx);
|
||||||
|
|
||||||
// Iterate until all positions are classified (30 cycles needed)
|
// Iterate through the positions until no more of the unknown positions can be
|
||||||
|
// 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 < IndexMax; idx++)
|
||||||
if (db[idx] == UNKNOWN && (db[idx] = pos.classify(idx, db)) != UNKNOWN)
|
if (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN)
|
||||||
repeat = 1;
|
repeat = 1;
|
||||||
|
|
||||||
// Map 32 position results into one KPKBitbase[] entry
|
// Map 32 results into one KPKBitbase[] entry
|
||||||
for (idx = 0; idx < IndexMax / 32; idx++)
|
for (idx = 0; idx < IndexMax; idx++)
|
||||||
for (bit = 0; bit < 32; bit++)
|
if (db[idx] == WIN)
|
||||||
if (db[32 * idx + bit] == WIN)
|
KPKBitbase[idx / 32] |= 1 << (idx & 0x1F);
|
||||||
KPKBitbase[idx] |= 1 << bit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// A KPK bitbase index is an integer in [0, IndexMax] range
|
Result KPKPosition::classify_leaf(unsigned idx) {
|
||||||
//
|
|
||||||
// Information is mapped in this way
|
|
||||||
//
|
|
||||||
// bit 0: side to move (WHITE or BLACK)
|
|
||||||
// bit 1- 6: black king square (from SQ_A1 to SQ_H8)
|
|
||||||
// bit 7-12: white king square (from SQ_A1 to SQ_H8)
|
|
||||||
// bit 13-14: white pawn file (from FILE_A to FILE_D)
|
|
||||||
// bit 15-17: white pawn rank - 1 (from RANK_2 - 1 to RANK_7 - 1)
|
|
||||||
|
|
||||||
int index(Square w, Square b, Square p, Color c) {
|
wksq = Square((idx >> 0) & 0x3F);
|
||||||
|
bksq = Square((idx >> 6) & 0x3F);
|
||||||
assert(file_of(p) <= FILE_D);
|
us = Color((idx >> 12) & 0x01);
|
||||||
|
psq = File((idx >> 13) & 3) | Rank(6 - (idx >> 15));
|
||||||
return c + (b << 1) + (w << 7) + (file_of(p) << 13) + ((rank_of(p) - 1) << 15);
|
|
||||||
}
|
|
||||||
|
|
||||||
void KPKPosition::decode_index(int idx) {
|
|
||||||
|
|
||||||
stm = Color(idx & 1);
|
|
||||||
bksq = Square((idx >> 1) & 63);
|
|
||||||
wksq = Square((idx >> 7) & 63);
|
|
||||||
psq = File((idx >> 13) & 3) | Rank((idx >> 15) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result KPKPosition::classify_leaf(int idx) {
|
|
||||||
|
|
||||||
decode_index(idx);
|
|
||||||
|
|
||||||
// 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 ( wksq == psq || wksq == bksq || bksq == psq
|
if ( wksq == psq || wksq == bksq || bksq == psq
|
||||||
|| (k_attacks<WHITE>() & bksq)
|
|| (StepAttacksBB[KING][wksq] & bksq)
|
||||||
|| (stm == WHITE && (p_attacks() & bksq)))
|
|| (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq)))
|
||||||
return INVALID;
|
return res = INVALID;
|
||||||
|
|
||||||
// The position is an immediate win if it is white to move and the white
|
if (us == WHITE)
|
||||||
// pawn can be promoted without getting captured.
|
{
|
||||||
if ( rank_of(psq) == RANK_7
|
// Immediate win if pawn can be promoted without getting captured
|
||||||
&& stm == WHITE
|
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
|
||||||
||(k_attacks<WHITE>() & (psq + DELTA_N))))
|
||(StepAttacksBB[KING][wksq] & (psq + DELTA_N))))
|
||||||
return WIN;
|
return res = WIN;
|
||||||
|
}
|
||||||
|
// Immediate draw if is stalemate or king captures undefended pawn
|
||||||
|
else if ( !(StepAttacksBB[KING][bksq] & ~(StepAttacksBB[KING][wksq] | StepAttacksBB[PAWN][psq]))
|
||||||
|
|| (StepAttacksBB[KING][bksq] & psq & ~StepAttacksBB[KING][wksq]))
|
||||||
|
return res = DRAW;
|
||||||
|
|
||||||
// Check for known draw positions
|
return res = UNKNOWN;
|
||||||
//
|
|
||||||
// Case 1: Stalemate
|
|
||||||
if ( stm == BLACK
|
|
||||||
&& !(k_attacks<BLACK>() & ~(k_attacks<WHITE>() | p_attacks())))
|
|
||||||
return DRAW;
|
|
||||||
|
|
||||||
// Case 2: King can capture undefended pawn
|
|
||||||
if ( stm == BLACK
|
|
||||||
&& (k_attacks<BLACK>() & psq & ~k_attacks<WHITE>()))
|
|
||||||
return DRAW;
|
|
||||||
|
|
||||||
// Case 3: Black king in front of white pawn
|
|
||||||
if ( bksq == psq + DELTA_N
|
|
||||||
&& rank_of(psq) < RANK_7)
|
|
||||||
return DRAW;
|
|
||||||
|
|
||||||
// Case 4: White king in front of pawn and black has opposition
|
|
||||||
if ( stm == WHITE
|
|
||||||
&& wksq == psq + DELTA_N
|
|
||||||
&& bksq == wksq + DELTA_N + DELTA_N
|
|
||||||
&& rank_of(psq) < RANK_5)
|
|
||||||
return DRAW;
|
|
||||||
|
|
||||||
// Case 5: Stalemate with rook pawn
|
|
||||||
if ( bksq == SQ_A8
|
|
||||||
&& file_of(psq) == FILE_A)
|
|
||||||
return DRAW;
|
|
||||||
|
|
||||||
// Case 6: White king trapped on the rook file
|
|
||||||
if ( file_of(wksq) == FILE_A
|
|
||||||
&& file_of(psq) == FILE_A
|
|
||||||
&& rank_of(wksq) > rank_of(psq)
|
|
||||||
&& bksq == wksq + 2)
|
|
||||||
return DRAW;
|
|
||||||
|
|
||||||
return UNKNOWN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Color Us>
|
template<Color Us>
|
||||||
Result KPKPosition::classify(const Result db[]) const {
|
Result KPKPosition::classify(const std::vector<KPKPosition>& db) {
|
||||||
|
|
||||||
// White to Move: If one move leads to a position classified as RESULT_WIN,
|
// White to Move: If one move leads to a position classified as WIN, the result
|
||||||
// the result of the current position is RESULT_WIN. If all moves lead to
|
// of the current position is WIN. If all moves lead to positions classified
|
||||||
// positions classified as RESULT_DRAW, the current position is classified
|
// as DRAW, the current position is classified DRAW otherwise the current
|
||||||
// RESULT_DRAW otherwise the current position is classified as RESULT_UNKNOWN.
|
// position is classified as UNKNOWN.
|
||||||
//
|
//
|
||||||
// Black to Move: If one move leads to a position classified as RESULT_DRAW,
|
// Black to Move: If one move leads to a position classified as DRAW, the result
|
||||||
// the result of the current position is RESULT_DRAW. If all moves lead to
|
// of the current position is DRAW. If all moves lead to positions classified
|
||||||
// positions classified as RESULT_WIN, the position is classified RESULT_WIN.
|
// as WIN, the position is classified WIN otherwise the current position is
|
||||||
// Otherwise, the current position is classified as RESULT_UNKNOWN.
|
// classified UNKNOWN.
|
||||||
|
|
||||||
Result r = INVALID;
|
Result r = INVALID;
|
||||||
Bitboard b = k_attacks<Us>();
|
Bitboard b = StepAttacksBB[KING][Us == WHITE ? wksq : bksq];
|
||||||
|
|
||||||
while (b)
|
while (b)
|
||||||
{
|
r |= Us == WHITE ? db[index(~Us, bksq, pop_lsb(&b), psq)]
|
||||||
r |= Us == WHITE ? db[index(pop_lsb(&b), bksq, psq, BLACK)]
|
: db[index(~Us, pop_lsb(&b), wksq, psq)];
|
||||||
: db[index(wksq, pop_lsb(&b), psq, WHITE)];
|
|
||||||
|
|
||||||
if (Us == WHITE && (r & WIN))
|
|
||||||
return WIN;
|
|
||||||
|
|
||||||
if (Us == BLACK && (r & DRAW))
|
|
||||||
return DRAW;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Us == WHITE && rank_of(psq) < RANK_7)
|
if (Us == WHITE && rank_of(psq) < RANK_7)
|
||||||
{
|
{
|
||||||
Square s = psq + DELTA_N;
|
Square s = psq + DELTA_N;
|
||||||
r |= db[index(wksq, bksq, s, BLACK)]; // Single push
|
r |= db[index(BLACK, bksq, wksq, s)]; // Single push
|
||||||
|
|
||||||
if (rank_of(s) == RANK_3 && s != wksq && s != bksq)
|
if (rank_of(s) == RANK_3 && s != wksq && s != bksq)
|
||||||
r |= db[index(wksq, bksq, s + DELTA_N, BLACK)]; // Double push
|
r |= db[index(BLACK, bksq, wksq, s + DELTA_N)]; // Double push
|
||||||
|
|
||||||
if (r & WIN)
|
|
||||||
return WIN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return r & UNKNOWN ? UNKNOWN : Us == WHITE ? DRAW : WIN;
|
if (Us == WHITE)
|
||||||
|
return res = r & WIN ? WIN : r & UNKNOWN ? UNKNOWN : DRAW;
|
||||||
|
else
|
||||||
|
return res = r & DRAW ? DRAW : r & UNKNOWN ? UNKNOWN : WIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KPKPosition::classify(int idx, Result db[]) {
|
} // namespace
|
||||||
|
|
||||||
decode_index(idx);
|
|
||||||
return stm == WHITE ? classify<WHITE>(db) : classify<BLACK>(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -28,45 +28,44 @@
|
|||||||
|
|
||||||
CACHE_LINE_ALIGNMENT
|
CACHE_LINE_ALIGNMENT
|
||||||
|
|
||||||
Bitboard RMasks[64];
|
Bitboard RMasks[SQUARE_NB];
|
||||||
Bitboard RMagics[64];
|
Bitboard RMagics[SQUARE_NB];
|
||||||
Bitboard* RAttacks[64];
|
Bitboard* RAttacks[SQUARE_NB];
|
||||||
unsigned RShifts[64];
|
unsigned RShifts[SQUARE_NB];
|
||||||
|
|
||||||
Bitboard BMasks[64];
|
Bitboard BMasks[SQUARE_NB];
|
||||||
Bitboard BMagics[64];
|
Bitboard BMagics[SQUARE_NB];
|
||||||
Bitboard* BAttacks[64];
|
Bitboard* BAttacks[SQUARE_NB];
|
||||||
unsigned BShifts[64];
|
unsigned BShifts[SQUARE_NB];
|
||||||
|
|
||||||
Bitboard SquareBB[64];
|
Bitboard SquareBB[SQUARE_NB];
|
||||||
Bitboard FileBB[8];
|
Bitboard FileBB[FILE_NB];
|
||||||
Bitboard RankBB[8];
|
Bitboard RankBB[RANK_NB];
|
||||||
Bitboard AdjacentFilesBB[8];
|
Bitboard AdjacentFilesBB[FILE_NB];
|
||||||
Bitboard ThisAndAdjacentFilesBB[8];
|
Bitboard ThisAndAdjacentFilesBB[FILE_NB];
|
||||||
Bitboard InFrontBB[2][8];
|
Bitboard InFrontBB[COLOR_NB][RANK_NB];
|
||||||
Bitboard StepAttacksBB[16][64];
|
Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
|
||||||
Bitboard BetweenBB[64][64];
|
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
||||||
Bitboard DistanceRingsBB[64][8];
|
Bitboard DistanceRingsBB[SQUARE_NB][8];
|
||||||
Bitboard ForwardBB[2][64];
|
Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
|
||||||
Bitboard PassedPawnMask[2][64];
|
Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
|
||||||
Bitboard AttackSpanMask[2][64];
|
Bitboard AttackSpanMask[COLOR_NB][SQUARE_NB];
|
||||||
Bitboard PseudoAttacks[6][64];
|
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||||
|
|
||||||
int SquareDistance[64][64];
|
int SquareDistance[SQUARE_NB][SQUARE_NB];
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan
|
// De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan
|
||||||
const uint64_t DeBruijn_64 = 0x218A392CD3D5DBFULL;
|
const uint64_t DeBruijn_64 = 0x3F79D71B4CB0A89ULL;
|
||||||
const uint32_t DeBruijn_32 = 0x783A9B23;
|
const uint32_t DeBruijn_32 = 0x783A9B23;
|
||||||
|
|
||||||
CACHE_LINE_ALIGNMENT
|
CACHE_LINE_ALIGNMENT
|
||||||
|
|
||||||
int MS1BTable[256];
|
int MS1BTable[256];
|
||||||
Square BSFTable[64];
|
Square BSFTable[SQUARE_NB];
|
||||||
Bitboard RTable[0x19000]; // Storage space for rook attacks
|
Bitboard RTable[0x19000]; // Storage space for rook attacks
|
||||||
Bitboard BTable[0x1480]; // Storage space for bishop attacks
|
Bitboard BTable[0x1480]; // Storage space for bishop attacks
|
||||||
uint8_t BitCount8Bit[256];
|
|
||||||
|
|
||||||
typedef unsigned (Fn)(Square, Bitboard);
|
typedef unsigned (Fn)(Square, Bitboard);
|
||||||
|
|
||||||
@@ -75,12 +74,10 @@ namespace {
|
|||||||
|
|
||||||
FORCE_INLINE unsigned bsf_index(Bitboard b) {
|
FORCE_INLINE unsigned bsf_index(Bitboard b) {
|
||||||
|
|
||||||
if (Is64Bit)
|
// Matt Taylor's folding for 32 bit systems, extended to 64 bits by Kim Walisch
|
||||||
return ((b & -b) * DeBruijn_64) >> 58;
|
|
||||||
|
|
||||||
// Use Matt Taylor's folding trick for 32 bit systems
|
|
||||||
b ^= (b - 1);
|
b ^= (b - 1);
|
||||||
return ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26;
|
return Is64Bit ? (b * DeBruijn_64) >> 58
|
||||||
|
: ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,9 +158,6 @@ void Bitboards::init() {
|
|||||||
for (int i = 0; i < 64; i++)
|
for (int i = 0; i < 64; i++)
|
||||||
BSFTable[bsf_index(1ULL << i)] = Square(i);
|
BSFTable[bsf_index(1ULL << i)] = Square(i);
|
||||||
|
|
||||||
for (Bitboard b = 0; b < 256; b++)
|
|
||||||
BitCount8Bit[b] = (uint8_t)popcount<Max15>(b);
|
|
||||||
|
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
||||||
SquareBB[s] = 1ULL << s;
|
SquareBB[s] = 1ULL << s;
|
||||||
|
|
||||||
@@ -326,7 +320,7 @@ namespace {
|
|||||||
// 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] = pick_random(rk, booster);
|
||||||
while (BitCount8Bit[(magics[s] * masks[s]) >> 56] < 6);
|
while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6);
|
||||||
|
|
||||||
memset(attacks[s], 0, size * sizeof(Bitboard));
|
memset(attacks[s], 0, size * sizeof(Bitboard));
|
||||||
|
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -33,36 +33,37 @@ void print(Bitboard b);
|
|||||||
namespace Bitbases {
|
namespace Bitbases {
|
||||||
|
|
||||||
void init_kpk();
|
void init_kpk();
|
||||||
uint32_t probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm);
|
bool probe_kpk(Square wksq, Square wpsq, Square bksq, Color us);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CACHE_LINE_ALIGNMENT
|
CACHE_LINE_ALIGNMENT
|
||||||
|
|
||||||
extern Bitboard RMasks[64];
|
extern Bitboard RMasks[SQUARE_NB];
|
||||||
extern Bitboard RMagics[64];
|
extern Bitboard RMagics[SQUARE_NB];
|
||||||
extern Bitboard* RAttacks[64];
|
extern Bitboard* RAttacks[SQUARE_NB];
|
||||||
extern unsigned RShifts[64];
|
extern unsigned RShifts[SQUARE_NB];
|
||||||
|
|
||||||
extern Bitboard BMasks[64];
|
extern Bitboard BMasks[SQUARE_NB];
|
||||||
extern Bitboard BMagics[64];
|
extern Bitboard BMagics[SQUARE_NB];
|
||||||
extern Bitboard* BAttacks[64];
|
extern Bitboard* BAttacks[SQUARE_NB];
|
||||||
extern unsigned BShifts[64];
|
extern unsigned BShifts[SQUARE_NB];
|
||||||
|
|
||||||
extern Bitboard SquareBB[64];
|
extern Bitboard SquareBB[SQUARE_NB];
|
||||||
extern Bitboard FileBB[8];
|
extern Bitboard FileBB[FILE_NB];
|
||||||
extern Bitboard RankBB[8];
|
extern Bitboard RankBB[RANK_NB];
|
||||||
extern Bitboard AdjacentFilesBB[8];
|
extern Bitboard AdjacentFilesBB[FILE_NB];
|
||||||
extern Bitboard ThisAndAdjacentFilesBB[8];
|
extern Bitboard ThisAndAdjacentFilesBB[FILE_NB];
|
||||||
extern Bitboard InFrontBB[2][8];
|
extern Bitboard InFrontBB[COLOR_NB][RANK_NB];
|
||||||
extern Bitboard StepAttacksBB[16][64];
|
extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
|
||||||
extern Bitboard BetweenBB[64][64];
|
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
||||||
extern Bitboard DistanceRingsBB[64][8];
|
extern Bitboard DistanceRingsBB[SQUARE_NB][8];
|
||||||
extern Bitboard ForwardBB[2][64];
|
extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
|
||||||
extern Bitboard PassedPawnMask[2][64];
|
extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
|
||||||
extern Bitboard AttackSpanMask[2][64];
|
extern Bitboard AttackSpanMask[COLOR_NB][SQUARE_NB];
|
||||||
extern Bitboard PseudoAttacks[6][64];
|
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||||
|
|
||||||
|
const Bitboard BlackSquares = 0xAA55AA55AA55AA55ULL;
|
||||||
|
|
||||||
/// Overloads of bitwise operators between a Bitboard and a Square for testing
|
/// Overloads of bitwise operators between a Bitboard and a Square for testing
|
||||||
/// whether a given bit is set in a bitboard, and for setting and clearing bits.
|
/// whether a given bit is set in a bitboard, and for setting and clearing bits.
|
||||||
@@ -199,8 +200,7 @@ inline bool squares_aligned(Square s1, Square s2, Square s3) {
|
|||||||
/// the same color of the given square.
|
/// the same color of the given square.
|
||||||
|
|
||||||
inline Bitboard same_color_squares(Square s) {
|
inline Bitboard same_color_squares(Square s) {
|
||||||
return Bitboard(0xAA55AA55AA55AA55ULL) & s ? 0xAA55AA55AA55AA55ULL
|
return BlackSquares & s ? BlackSquares : ~BlackSquares;
|
||||||
: ~0xAA55AA55AA55AA55ULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -247,6 +247,21 @@ FORCE_INLINE Square msb(Bitboard b) {
|
|||||||
return (Square) index;
|
return (Square) index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# elif defined(__arm__)
|
||||||
|
|
||||||
|
FORCE_INLINE int lsb32(uint32_t v) {
|
||||||
|
__asm__("rbit %0, %1" : "=r"(v) : "r"(v));
|
||||||
|
return __builtin_clz(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCE_INLINE Square msb(Bitboard b) {
|
||||||
|
return (Square) (63 - __builtin_clzll(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCE_INLINE Square lsb(Bitboard b) {
|
||||||
|
return (Square) (uint32_t(b) ? lsb32(uint32_t(b)) : 32 + lsb32(uint32_t(b >> 32)));
|
||||||
|
}
|
||||||
|
|
||||||
# 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
|
||||||
@@ -265,7 +280,7 @@ FORCE_INLINE Square msb(Bitboard b) {
|
|||||||
|
|
||||||
FORCE_INLINE Square pop_lsb(Bitboard* b) {
|
FORCE_INLINE Square pop_lsb(Bitboard* b) {
|
||||||
const Square s = lsb(*b);
|
const Square s = lsb(*b);
|
||||||
*b &= ~(1ULL << s);
|
*b &= *b - 1;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -33,8 +33,8 @@ enum BitCountType {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Determine at compile time the best popcount<> specialization according if
|
/// Determine at compile time the best popcount<> specialization according if
|
||||||
/// platform is 32 or 64 bits, to the maximum number of nonzero bits to count or
|
/// platform is 32 or 64 bits, to the maximum number of nonzero bits to count
|
||||||
/// use hardware popcnt instruction when available.
|
/// and if 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;
|
||||||
|
|
||||||
@@ -44,44 +44,38 @@ template<BitCountType> inline int popcount(Bitboard);
|
|||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline int popcount<CNT_64>(Bitboard b) {
|
inline int popcount<CNT_64>(Bitboard b) {
|
||||||
b -= ((b>>1) & 0x5555555555555555ULL);
|
b -= (b >> 1) & 0x5555555555555555ULL;
|
||||||
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
|
b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
|
||||||
b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL;
|
b = ((b >> 4) + b) & 0x0F0F0F0F0F0F0F0FULL;
|
||||||
b *= 0x0101010101010101ULL;
|
return (b * 0x0101010101010101ULL) >> 56;
|
||||||
return int(b >> 56);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline int popcount<CNT_64_MAX15>(Bitboard b) {
|
inline int popcount<CNT_64_MAX15>(Bitboard b) {
|
||||||
b -= (b>>1) & 0x5555555555555555ULL;
|
b -= (b >> 1) & 0x5555555555555555ULL;
|
||||||
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
|
b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
|
||||||
b *= 0x1111111111111111ULL;
|
return (b * 0x1111111111111111ULL) >> 60;
|
||||||
return int(b >> 60);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline int popcount<CNT_32>(Bitboard b) {
|
inline int popcount<CNT_32>(Bitboard b) {
|
||||||
unsigned w = unsigned(b >> 32), v = unsigned(b);
|
unsigned w = unsigned(b >> 32), v = unsigned(b);
|
||||||
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
|
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
|
||||||
w -= (w >> 1) & 0x55555555;
|
w -= (w >> 1) & 0x55555555;
|
||||||
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
|
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
|
||||||
w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
|
w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
|
||||||
v = ((v >> 4) + v) & 0x0F0F0F0F; // 0-8 in 8 bits
|
v = ((v >> 4) + v + (w >> 4) + w) & 0x0F0F0F0F;
|
||||||
v += (((w >> 4) + w) & 0x0F0F0F0F); // 0-16 in 8 bits
|
return (v * 0x01010101) >> 24;
|
||||||
v *= 0x01010101; // mul is fast on amd procs
|
|
||||||
return int(v >> 24);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline int popcount<CNT_32_MAX15>(Bitboard b) {
|
inline int popcount<CNT_32_MAX15>(Bitboard b) {
|
||||||
unsigned w = unsigned(b >> 32), v = unsigned(b);
|
unsigned w = unsigned(b >> 32), v = unsigned(b);
|
||||||
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
|
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
|
||||||
w -= (w >> 1) & 0x55555555;
|
w -= (w >> 1) & 0x55555555;
|
||||||
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
|
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
|
||||||
w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
|
w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
|
||||||
v += w; // 0-8 in 4 bits
|
return ((v + w) * 0x11111111) >> 28;
|
||||||
v *= 0x11111111;
|
|
||||||
return int(v >> 28);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
@@ -102,9 +96,8 @@ inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
unsigned long ret;
|
__asm__("popcnt %1, %0" : "=r" (b) : "r" (b));
|
||||||
__asm__("popcnt %1, %0" : "=r" (ret) : "r" (b));
|
return b;
|
||||||
return ret;
|
|
||||||
|
|
||||||
#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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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 {
|
|||||||
// 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 highest byte first (regardless of size).
|
||||||
// The entries are ordered according to the key in ascending order.
|
// The entries are ordered according to the key in ascending order.
|
||||||
struct BookEntry {
|
struct Entry {
|
||||||
uint64_t key;
|
uint64_t key;
|
||||||
uint16_t move;
|
uint16_t move;
|
||||||
uint16_t count;
|
uint16_t count;
|
||||||
@@ -46,7 +46,15 @@ namespace {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Random numbers from PolyGlot, used to compute book hash keys
|
// Random numbers from PolyGlot, used to compute book hash keys
|
||||||
const Key PolyGlotRandoms[781] = {
|
const union {
|
||||||
|
Key PolyGlotRandoms[781];
|
||||||
|
struct {
|
||||||
|
Key psq[12][64]; // [piece][square]
|
||||||
|
Key castle[4]; // [castle right]
|
||||||
|
Key enpassant[8]; // [file]
|
||||||
|
Key turn;
|
||||||
|
} Zobrist;
|
||||||
|
} PG = {{
|
||||||
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
|
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
|
||||||
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
|
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
|
||||||
0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL,
|
0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL,
|
||||||
@@ -308,51 +316,40 @@ namespace {
|
|||||||
0x003A93D8B2806962ULL, 0x1C99DED33CB890A1ULL, 0xCF3145DE0ADD4289ULL,
|
0x003A93D8B2806962ULL, 0x1C99DED33CB890A1ULL, 0xCF3145DE0ADD4289ULL,
|
||||||
0xD0E4427A5514FB72ULL, 0x77C621CC9FB3A483ULL, 0x67A34DAC4356550BULL,
|
0xD0E4427A5514FB72ULL, 0x77C621CC9FB3A483ULL, 0x67A34DAC4356550BULL,
|
||||||
0xF8D626AAAF278509ULL
|
0xF8D626AAAF278509ULL
|
||||||
};
|
}};
|
||||||
|
|
||||||
// Offsets to the PolyGlotRandoms[] array of zobrist keys
|
// polyglot_key() returns the PolyGlot hash key of the given position
|
||||||
const Key* ZobPiece = PolyGlotRandoms;
|
Key polyglot_key(const Position& pos) {
|
||||||
const Key* ZobCastle = ZobPiece + 12 * 64; // Pieces * squares
|
|
||||||
const Key* ZobEnPassant = ZobCastle + 4; // Castle flags
|
|
||||||
const Key* ZobTurn = ZobEnPassant + 8; // Number of files
|
|
||||||
|
|
||||||
// book_key() returns the PolyGlot hash key of the given position
|
Key key = 0;
|
||||||
uint64_t book_key(const Position& pos) {
|
|
||||||
|
|
||||||
uint64_t key = 0;
|
|
||||||
Bitboard b = pos.pieces();
|
Bitboard b = pos.pieces();
|
||||||
|
|
||||||
while (b)
|
while (b)
|
||||||
{
|
{
|
||||||
// In PolyGlotRandoms[] pieces are stored in the following sequence:
|
|
||||||
// BP = 0, WP = 1, BN = 2, WN = 3, ... BK = 10, WK = 11
|
|
||||||
Square s = pop_lsb(&b);
|
Square s = pop_lsb(&b);
|
||||||
Piece p = pos.piece_on(s);
|
Piece p = pos.piece_on(s);
|
||||||
int pieceOfs = 2 * (type_of(p) - 1) + (color_of(p) == WHITE);
|
|
||||||
key ^= ZobPiece[64 * pieceOfs + s];
|
// 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];
|
||||||
}
|
}
|
||||||
|
|
||||||
b = pos.can_castle(ALL_CASTLES);
|
b = pos.can_castle(ALL_CASTLES);
|
||||||
|
|
||||||
while (b)
|
while (b)
|
||||||
key ^= ZobCastle[pop_lsb(&b)];
|
key ^= PG.Zobrist.castle[pop_lsb(&b)];
|
||||||
|
|
||||||
if (pos.ep_square() != SQ_NONE)
|
if (pos.ep_square() != SQ_NONE)
|
||||||
key ^= ZobEnPassant[file_of(pos.ep_square())];
|
key ^= PG.Zobrist.enpassant[file_of(pos.ep_square())];
|
||||||
|
|
||||||
if (pos.side_to_move() == WHITE)
|
if (pos.side_to_move() == WHITE)
|
||||||
key ^= ZobTurn[0];
|
key ^= PG.Zobrist.turn;
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
PolyglotBook::PolyglotBook() {
|
PolyglotBook::PolyglotBook() : rkiss(Time::now() % 10000) {}
|
||||||
|
|
||||||
for (int i = Time::now() % 10000; i > 0; i--)
|
|
||||||
RKiss.rand<unsigned>(); // Make random number generation less deterministic
|
|
||||||
}
|
|
||||||
|
|
||||||
PolyglotBook::~PolyglotBook() { if (is_open()) close(); }
|
PolyglotBook::~PolyglotBook() { if (is_open()) close(); }
|
||||||
|
|
||||||
@@ -370,7 +367,7 @@ template<typename T> PolyglotBook& PolyglotBook::operator>>(T& n) {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> PolyglotBook& PolyglotBook::operator>>(BookEntry& e) {
|
template<> PolyglotBook& PolyglotBook::operator>>(Entry& e) {
|
||||||
return *this >> e.key >> e.move >> e.count >> e.learn;
|
return *this >> e.key >> e.move >> e.count >> e.learn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,13 +397,13 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
|
|||||||
if (fileName != fName && !open(fName.c_str()))
|
if (fileName != fName && !open(fName.c_str()))
|
||||||
return MOVE_NONE;
|
return MOVE_NONE;
|
||||||
|
|
||||||
BookEntry e;
|
Entry e;
|
||||||
uint16_t best = 0;
|
uint16_t best = 0;
|
||||||
unsigned sum = 0;
|
unsigned sum = 0;
|
||||||
Move move = MOVE_NONE;
|
Move move = MOVE_NONE;
|
||||||
uint64_t key = book_key(pos);
|
Key key = polyglot_key(pos);
|
||||||
|
|
||||||
seekg(find_first(key) * sizeof(BookEntry), ios_base::beg);
|
seekg(find_first(key) * sizeof(Entry), ios_base::beg);
|
||||||
|
|
||||||
while (*this >> e, e.key == key && good())
|
while (*this >> e, e.key == key && good())
|
||||||
{
|
{
|
||||||
@@ -416,7 +413,7 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
|
|||||||
// 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 score it has higher probability to be choosen than a move
|
// high score it has higher probability to be choosen than a move
|
||||||
// with lower score. Note that first entry is always chosen.
|
// with lower score. Note that first entry is always chosen.
|
||||||
if ( (sum && RKiss.rand<unsigned>() % sum < e.count)
|
if ( (sum && rkiss.rand<unsigned>() % sum < e.count)
|
||||||
|| (pickBest && e.count == best))
|
|| (pickBest && e.count == best))
|
||||||
move = Move(e.move);
|
move = Move(e.move);
|
||||||
}
|
}
|
||||||
@@ -440,7 +437,7 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
|
|||||||
|
|
||||||
// Add 'special move' flags and verify it is legal
|
// Add 'special move' flags and verify it is legal
|
||||||
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
|
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
|
||||||
if (move == (ml.move() & 0x3FFF))
|
if (move == (ml.move() ^ type_of(ml.move())))
|
||||||
return ml.move();
|
return ml.move();
|
||||||
|
|
||||||
return MOVE_NONE;
|
return MOVE_NONE;
|
||||||
@@ -451,12 +448,12 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
|
|||||||
/// the book file for the given key. Returns the index of the leftmost book
|
/// the book file for the given key. Returns the index of the leftmost book
|
||||||
/// entry with the same key as the input.
|
/// entry with the same key as the input.
|
||||||
|
|
||||||
size_t PolyglotBook::find_first(uint64_t key) {
|
size_t PolyglotBook::find_first(Key key) {
|
||||||
|
|
||||||
seekg(0, ios::end); // Move pointer to end, so tellg() gets file's size
|
seekg(0, ios::end); // Move pointer to end, so tellg() gets file's size
|
||||||
|
|
||||||
size_t low = 0, mid, high = (size_t)tellg() / sizeof(BookEntry) - 1;
|
size_t low = 0, mid, high = (size_t)tellg() / sizeof(Entry) - 1;
|
||||||
BookEntry e;
|
Entry e;
|
||||||
|
|
||||||
assert(low <= high);
|
assert(low <= high);
|
||||||
|
|
||||||
@@ -466,7 +463,7 @@ size_t PolyglotBook::find_first(uint64_t key) {
|
|||||||
|
|
||||||
assert(mid >= low && mid < high);
|
assert(mid >= low && mid < high);
|
||||||
|
|
||||||
seekg(mid * sizeof(BookEntry), ios_base::beg);
|
seekg(mid * sizeof(Entry), ios_base::beg);
|
||||||
*this >> e;
|
*this >> e;
|
||||||
|
|
||||||
if (key <= e.key)
|
if (key <= e.key)
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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,9 +36,9 @@ private:
|
|||||||
template<typename T> PolyglotBook& operator>>(T& n);
|
template<typename T> PolyglotBook& operator>>(T& n);
|
||||||
|
|
||||||
bool open(const char* fName);
|
bool open(const char* fName);
|
||||||
size_t find_first(uint64_t key);
|
size_t find_first(Key key);
|
||||||
|
|
||||||
RKISS RKiss;
|
RKISS rkiss;
|
||||||
std::string fileName;
|
std::string fileName;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -31,7 +31,7 @@ namespace {
|
|||||||
|
|
||||||
// Table used to drive the defending king towards the edge of the board
|
// Table used to drive the defending king towards the edge of the board
|
||||||
// in KX vs K and KQ vs KR endgames.
|
// in KX vs K and KQ vs KR endgames.
|
||||||
const int MateTable[64] = {
|
const int MateTable[SQUARE_NB] = {
|
||||||
100, 90, 80, 70, 70, 80, 90, 100,
|
100, 90, 80, 70, 70, 80, 90, 100,
|
||||||
90, 70, 60, 50, 50, 60, 70, 90,
|
90, 70, 60, 50, 50, 60, 70, 90,
|
||||||
80, 60, 40, 30, 30, 40, 60, 80,
|
80, 60, 40, 30, 30, 40, 60, 80,
|
||||||
@@ -44,7 +44,7 @@ namespace {
|
|||||||
|
|
||||||
// Table used to drive the defending king towards a corner square of the
|
// Table used to drive the defending king towards a corner square of the
|
||||||
// right color in KBN vs K endgames.
|
// right color in KBN vs K endgames.
|
||||||
const int KBNKMateTable[64] = {
|
const int KBNKMateTable[SQUARE_NB] = {
|
||||||
200, 190, 180, 170, 160, 150, 140, 130,
|
200, 190, 180, 170, 160, 150, 140, 130,
|
||||||
190, 180, 170, 160, 150, 140, 130, 140,
|
190, 180, 170, 160, 150, 140, 130, 140,
|
||||||
180, 170, 155, 140, 140, 125, 140, 150,
|
180, 170, 155, 140, 140, 125, 140, 150,
|
||||||
@@ -95,10 +95,12 @@ Endgames::Endgames() {
|
|||||||
add<KRKP>("KRKP");
|
add<KRKP>("KRKP");
|
||||||
add<KRKB>("KRKB");
|
add<KRKB>("KRKB");
|
||||||
add<KRKN>("KRKN");
|
add<KRKN>("KRKN");
|
||||||
|
add<KQKP>("KQKP");
|
||||||
add<KQKR>("KQKR");
|
add<KQKR>("KQKR");
|
||||||
add<KBBKN>("KBBKN");
|
add<KBBKN>("KBBKN");
|
||||||
|
|
||||||
add<KNPK>("KNPK");
|
add<KNPK>("KNPK");
|
||||||
|
add<KNPKB>("KNPKB");
|
||||||
add<KRPKR>("KRPKR");
|
add<KRPKR>("KRPKR");
|
||||||
add<KBPKB>("KBPKB");
|
add<KBPKB>("KBPKB");
|
||||||
add<KBPKN>("KBPKN");
|
add<KBPKN>("KBPKN");
|
||||||
@@ -132,7 +134,7 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
|
|||||||
|
|
||||||
// Stalemate detection with lone king
|
// Stalemate detection with lone king
|
||||||
if ( pos.side_to_move() == weakerSide
|
if ( pos.side_to_move() == weakerSide
|
||||||
&& !pos.in_check()
|
&& !pos.checkers()
|
||||||
&& !MoveList<LEGAL>(pos).size()) {
|
&& !MoveList<LEGAL>(pos).size()) {
|
||||||
return VALUE_DRAW;
|
return VALUE_DRAW;
|
||||||
}
|
}
|
||||||
@@ -169,12 +171,12 @@ Value Endgame<KBNK>::operator()(const Position& pos) const {
|
|||||||
|
|
||||||
Square winnerKSq = pos.king_square(strongerSide);
|
Square winnerKSq = pos.king_square(strongerSide);
|
||||||
Square loserKSq = pos.king_square(weakerSide);
|
Square loserKSq = pos.king_square(weakerSide);
|
||||||
Square bishopSquare = pos.piece_list(strongerSide, BISHOP)[0];
|
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[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 bishop that cannot reach the above squares we
|
// if we have a bishop that cannot reach the above squares we
|
||||||
// mirror the kings so to drive enemy toward corners A8 or H1.
|
// mirror the kings so to drive enemy toward corners A8 or H1.
|
||||||
if (opposite_colors(bishopSquare, SQ_A1))
|
if (opposite_colors(bishopSq, SQ_A1))
|
||||||
{
|
{
|
||||||
winnerKSq = mirror(winnerKSq);
|
winnerKSq = mirror(winnerKSq);
|
||||||
loserKSq = mirror(loserKSq);
|
loserKSq = mirror(loserKSq);
|
||||||
@@ -198,21 +200,21 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
|
|||||||
assert(pos.piece_count(weakerSide, PAWN) == 0);
|
assert(pos.piece_count(weakerSide, PAWN) == 0);
|
||||||
|
|
||||||
Square wksq, bksq, wpsq;
|
Square wksq, bksq, wpsq;
|
||||||
Color stm;
|
Color us;
|
||||||
|
|
||||||
if (strongerSide == WHITE)
|
if (strongerSide == WHITE)
|
||||||
{
|
{
|
||||||
wksq = pos.king_square(WHITE);
|
wksq = pos.king_square(WHITE);
|
||||||
bksq = pos.king_square(BLACK);
|
bksq = pos.king_square(BLACK);
|
||||||
wpsq = pos.piece_list(WHITE, PAWN)[0];
|
wpsq = pos.piece_list(WHITE, PAWN)[0];
|
||||||
stm = pos.side_to_move();
|
us = pos.side_to_move();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
wksq = ~pos.king_square(BLACK);
|
wksq = ~pos.king_square(BLACK);
|
||||||
bksq = ~pos.king_square(WHITE);
|
bksq = ~pos.king_square(WHITE);
|
||||||
wpsq = ~pos.piece_list(BLACK, PAWN)[0];
|
wpsq = ~pos.piece_list(BLACK, PAWN)[0];
|
||||||
stm = ~pos.side_to_move();
|
us = ~pos.side_to_move();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_of(wpsq) >= FILE_E)
|
if (file_of(wpsq) >= FILE_E)
|
||||||
@@ -222,7 +224,7 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
|
|||||||
wpsq = mirror(wpsq);
|
wpsq = mirror(wpsq);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Bitbases::probe_kpk(wksq, wpsq, bksq, stm))
|
if (!Bitbases::probe_kpk(wksq, wpsq, bksq, us))
|
||||||
return VALUE_DRAW;
|
return VALUE_DRAW;
|
||||||
|
|
||||||
Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(wpsq));
|
Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(wpsq));
|
||||||
@@ -326,6 +328,37 @@ Value Endgame<KRKN>::operator()(const Position& pos) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// KQ vs KP. In general, a win for the stronger side, however, there are a few
|
||||||
|
/// important exceptions. Pawn on 7th rank, A,C,F or H file, with king next can
|
||||||
|
/// be a draw, so we scale down to distance between kings only.
|
||||||
|
template<>
|
||||||
|
Value Endgame<KQKP>::operator()(const Position& pos) const {
|
||||||
|
|
||||||
|
assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
|
||||||
|
assert(pos.piece_count(strongerSide, PAWN) == 0);
|
||||||
|
assert(pos.non_pawn_material(weakerSide) == 0);
|
||||||
|
assert(pos.piece_count(weakerSide, PAWN) == 1);
|
||||||
|
|
||||||
|
Square winnerKSq = pos.king_square(strongerSide);
|
||||||
|
Square loserKSq = pos.king_square(weakerSide);
|
||||||
|
Square pawnSq = pos.piece_list(weakerSide, PAWN)[0];
|
||||||
|
|
||||||
|
Value result = QueenValueEg
|
||||||
|
- PawnValueEg
|
||||||
|
+ DistanceBonus[square_distance(winnerKSq, loserKSq)];
|
||||||
|
|
||||||
|
if ( square_distance(loserKSq, pawnSq) == 1
|
||||||
|
&& relative_rank(weakerSide, pawnSq) == RANK_7)
|
||||||
|
{
|
||||||
|
File f = file_of(pawnSq);
|
||||||
|
|
||||||
|
if (f == FILE_A || f == FILE_C || f == FILE_F || f == FILE_H)
|
||||||
|
result = Value(DistanceBonus[square_distance(winnerKSq, loserKSq)]);
|
||||||
|
}
|
||||||
|
return strongerSide == pos.side_to_move() ? result : -result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// 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
|
||||||
@@ -439,6 +472,29 @@ 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 ( (pawnFile == FILE_B || pawnFile == FILE_G)
|
||||||
|
&& !(pos.pieces(PAWN) & ~file_bb(pawnFile))
|
||||||
|
&& pos.non_pawn_material(weakerSide) == 0
|
||||||
|
&& pos.piece_count(weakerSide, PAWN) >= 1)
|
||||||
|
{
|
||||||
|
// Get weaker pawn closest to opponent's queening square
|
||||||
|
Bitboard wkPawns = pos.pieces(weakerSide, PAWN);
|
||||||
|
Square weakerPawnSq = strongerSide == WHITE ? msb(wkPawns) : lsb(wkPawns);
|
||||||
|
|
||||||
|
Square strongerKingSq = pos.king_square(strongerSide);
|
||||||
|
Square weakerKingSq = pos.king_square(weakerSide);
|
||||||
|
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0];
|
||||||
|
|
||||||
|
// Draw if weaker pawn is on rank 7, bishop can't attack the pawn, and
|
||||||
|
// weaker king can stop opposing opponent's king from penetrating.
|
||||||
|
if ( relative_rank(strongerSide, weakerPawnSq) == RANK_7
|
||||||
|
&& opposite_colors(bishopSq, weakerPawnSq)
|
||||||
|
&& square_distance(weakerPawnSq, weakerKingSq) <= square_distance(weakerPawnSq, strongerKingSq))
|
||||||
|
return SCALE_FACTOR_DRAW;
|
||||||
|
}
|
||||||
|
|
||||||
return SCALE_FACTOR_NONE;
|
return SCALE_FACTOR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -849,6 +905,24 @@ ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// K, knight and a pawn vs K and bishop. If knight can block bishop from taking
|
||||||
|
/// pawn, it's a win. Otherwise, drawn.
|
||||||
|
template<>
|
||||||
|
ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
|
||||||
|
|
||||||
|
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0];
|
||||||
|
Square bishopSq = pos.piece_list(weakerSide, BISHOP)[0];
|
||||||
|
Square weakerKingSq = pos.king_square(weakerSide);
|
||||||
|
|
||||||
|
// King needs to get close to promoting pawn to prevent knight from blocking.
|
||||||
|
// Rules for this are very tricky, so just approximate.
|
||||||
|
if (forward_bb(strongerSide, pawnSq) & pos.attacks_from<BISHOP>(bishopSq))
|
||||||
|
return ScaleFactor(square_distance(weakerKingSq, pawnSq));
|
||||||
|
|
||||||
|
return SCALE_FACTOR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// K and a pawn vs K and a pawn. This is done by removing the weakest side's
|
/// K and a pawn vs K and a pawn. This is done by removing the weakest side's
|
||||||
/// pawn and probing the KP vs K bitbase: If the weakest side has a draw without
|
/// pawn and probing the KP vs K bitbase: If the weakest side has a draw without
|
||||||
/// the pawn, she probably has at least a draw with the pawn as well. The exception
|
/// the pawn, she probably has at least a draw with the pawn as well. The exception
|
||||||
@@ -865,14 +939,14 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
|
|||||||
Square wksq = pos.king_square(strongerSide);
|
Square wksq = pos.king_square(strongerSide);
|
||||||
Square bksq = pos.king_square(weakerSide);
|
Square bksq = pos.king_square(weakerSide);
|
||||||
Square wpsq = pos.piece_list(strongerSide, PAWN)[0];
|
Square wpsq = pos.piece_list(strongerSide, PAWN)[0];
|
||||||
Color stm = pos.side_to_move();
|
Color us = pos.side_to_move();
|
||||||
|
|
||||||
if (strongerSide == BLACK)
|
if (strongerSide == BLACK)
|
||||||
{
|
{
|
||||||
wksq = ~wksq;
|
wksq = ~wksq;
|
||||||
bksq = ~bksq;
|
bksq = ~bksq;
|
||||||
wpsq = ~wpsq;
|
wpsq = ~wpsq;
|
||||||
stm = ~stm;
|
us = ~us;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_of(wpsq) >= FILE_E)
|
if (file_of(wpsq) >= FILE_E)
|
||||||
@@ -890,5 +964,5 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
|
|||||||
|
|
||||||
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
|
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
|
||||||
// it's probably at least a draw even with the pawn.
|
// it's probably at least a draw even with the pawn.
|
||||||
return Bitbases::probe_kpk(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
|
return Bitbases::probe_kpk(wksq, wpsq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -39,6 +39,7 @@ enum EndgameType {
|
|||||||
KRKP, // KR vs KP
|
KRKP, // KR vs KP
|
||||||
KRKB, // KR vs KB
|
KRKB, // KR vs KB
|
||||||
KRKN, // KR vs KN
|
KRKN, // KR vs KN
|
||||||
|
KQKP, // KQ vs KP
|
||||||
KQKR, // KQ vs KR
|
KQKR, // KQ vs KR
|
||||||
KBBKN, // KBB vs KN
|
KBBKN, // KBB vs KN
|
||||||
KNNK, // KNN vs K
|
KNNK, // KNN vs K
|
||||||
@@ -57,6 +58,7 @@ enum EndgameType {
|
|||||||
KBPPKB, // KBPP vs KB
|
KBPPKB, // KBPP vs KB
|
||||||
KBPKN, // KBP vs KN
|
KBPKN, // KBP vs KN
|
||||||
KNPK, // KNP vs K
|
KNPK, // KNP vs K
|
||||||
|
KNPKB, // KNP vs KB
|
||||||
KPKP // KP vs KP
|
KPKP // KP vs KP
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -111,7 +113,7 @@ class Endgames {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
Endgames();
|
Endgames();
|
||||||
~Endgames();
|
~Endgames();
|
||||||
|
|
||||||
template<typename T> T probe(Key key, T& eg)
|
template<typename T> T probe(Key key, T& eg)
|
||||||
{ return eg = map(eg).count(key) ? map(eg)[key] : NULL; }
|
{ return eg = map(eg).count(key) ? map(eg)[key] : NULL; }
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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,13 +36,13 @@ namespace {
|
|||||||
struct EvalInfo {
|
struct EvalInfo {
|
||||||
|
|
||||||
// Pointers to material and pawn hash table entries
|
// Pointers to material and pawn hash table entries
|
||||||
MaterialEntry* mi;
|
Material::Entry* mi;
|
||||||
PawnEntry* pi;
|
Pawns::Entry* pi;
|
||||||
|
|
||||||
// attackedBy[color][piece type] is a bitboard representing all squares
|
// attackedBy[color][piece type] is a bitboard representing all squares
|
||||||
// attacked by a given color and piece type, attackedBy[color][0] contains
|
// attacked by a given color and piece type, attackedBy[color][ALL_PIECES]
|
||||||
// all squares attacked by the given color.
|
// contains all squares attacked by the given color.
|
||||||
Bitboard attackedBy[2][8];
|
Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB];
|
||||||
|
|
||||||
// kingRing[color] is the zone around the king which is considered
|
// kingRing[color] is the zone around the king which is considered
|
||||||
// by the king safety evaluation. This consists of the squares directly
|
// by the king safety evaluation. This consists of the squares directly
|
||||||
@@ -50,25 +50,25 @@ namespace {
|
|||||||
// squares two ranks in front of the king. For instance, if black's king
|
// squares two ranks in front of the king. For instance, if black's king
|
||||||
// is on g8, kingRing[BLACK] is a bitboard containing the squares f8, h8,
|
// is on g8, kingRing[BLACK] is a bitboard containing the squares f8, h8,
|
||||||
// f7, g7, h7, f6, g6 and h6.
|
// f7, g7, h7, f6, g6 and h6.
|
||||||
Bitboard kingRing[2];
|
Bitboard kingRing[COLOR_NB];
|
||||||
|
|
||||||
// kingAttackersCount[color] is the number of pieces of the given color
|
// kingAttackersCount[color] is the number of pieces of the given color
|
||||||
// which attack a square in the kingRing of the enemy king.
|
// which attack a square in the kingRing of the enemy king.
|
||||||
int kingAttackersCount[2];
|
int kingAttackersCount[COLOR_NB];
|
||||||
|
|
||||||
// kingAttackersWeight[color] is the sum of the "weight" of the pieces of the
|
// kingAttackersWeight[color] is the sum of the "weight" of the pieces of the
|
||||||
// given color which attack a square in the kingRing of the enemy king. The
|
// given color which attack a square in the kingRing of the enemy king. The
|
||||||
// weights of the individual piece types are given by the variables
|
// weights of the individual piece types are given by the variables
|
||||||
// QueenAttackWeight, RookAttackWeight, BishopAttackWeight and
|
// QueenAttackWeight, RookAttackWeight, BishopAttackWeight and
|
||||||
// KnightAttackWeight in evaluate.cpp
|
// KnightAttackWeight in evaluate.cpp
|
||||||
int kingAttackersWeight[2];
|
int kingAttackersWeight[COLOR_NB];
|
||||||
|
|
||||||
// kingAdjacentZoneAttacksCount[color] is the number of attacks to squares
|
// kingAdjacentZoneAttacksCount[color] is the number of attacks to squares
|
||||||
// directly adjacent to the king of the given color. Pieces which attack
|
// directly adjacent to the king of the given color. Pieces which attack
|
||||||
// more than one square are counted multiple times. For instance, if black's
|
// more than one square are counted multiple times. For instance, if black's
|
||||||
// king is on g8 and there's a white knight on g5, this knight adds
|
// king is on g8 and there's a white knight on g5, this knight adds
|
||||||
// 2 to kingAdjacentZoneAttacksCount[BLACK].
|
// 2 to kingAdjacentZoneAttacksCount[BLACK].
|
||||||
int kingAdjacentZoneAttacksCount[2];
|
int kingAdjacentZoneAttacksCount[COLOR_NB];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Evaluation grain size, must be a power of 2
|
// Evaluation grain size, must be a power of 2
|
||||||
@@ -88,7 +88,7 @@ namespace {
|
|||||||
//
|
//
|
||||||
// Values modified by Joona Kiiski
|
// Values modified by Joona Kiiski
|
||||||
const Score WeightsInternal[] = {
|
const Score WeightsInternal[] = {
|
||||||
S(252, 344), S(216, 266), S(46, 0), S(247, 0), S(259, 0)
|
S(289, 344), S(221, 273), S(46, 0), S(271, 0), S(307, 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
// MobilityBonus[PieceType][attacked] contains mobility bonuses for middle and
|
// MobilityBonus[PieceType][attacked] contains mobility bonuses for middle and
|
||||||
@@ -114,7 +114,7 @@ namespace {
|
|||||||
|
|
||||||
// OutpostBonus[PieceType][Square] contains outpost bonuses of knights and
|
// OutpostBonus[PieceType][Square] contains outpost bonuses of knights and
|
||||||
// bishops, indexed by piece type and square (from white's point of view).
|
// bishops, indexed by piece type and square (from white's point of view).
|
||||||
const Value OutpostBonus[][64] = {
|
const Value OutpostBonus[][SQUARE_NB] = {
|
||||||
{
|
{
|
||||||
// A B C D E F G H
|
// A B C D E F G H
|
||||||
V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), // Knights
|
V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), // Knights
|
||||||
@@ -134,7 +134,7 @@ namespace {
|
|||||||
|
|
||||||
// ThreatBonus[attacking][attacked] contains threat bonuses according to
|
// ThreatBonus[attacking][attacked] contains threat bonuses according to
|
||||||
// which piece type attacks which one.
|
// which piece type attacks which one.
|
||||||
const Score ThreatBonus[][8] = {
|
const Score ThreatBonus[][PIECE_TYPE_NB] = {
|
||||||
{}, {},
|
{}, {},
|
||||||
{ S(0, 0), S( 7, 39), S( 0, 0), S(24, 49), S(41,100), S(41,100) }, // KNIGHT
|
{ S(0, 0), S( 7, 39), S( 0, 0), S(24, 49), S(41,100), S(41,100) }, // KNIGHT
|
||||||
{ S(0, 0), S( 7, 39), S(24, 49), S( 0, 0), S(41,100), S(41,100) }, // BISHOP
|
{ S(0, 0), S( 7, 39), S(24, 49), S( 0, 0), S(41,100), S(41,100) }, // BISHOP
|
||||||
@@ -150,16 +150,18 @@ namespace {
|
|||||||
|
|
||||||
#undef S
|
#undef S
|
||||||
|
|
||||||
|
const Score BishopPinBonus = make_score(66, 11);
|
||||||
|
|
||||||
// Bonus for having the side to move (modified by Joona Kiiski)
|
// Bonus for having the side to move (modified by Joona Kiiski)
|
||||||
const Score Tempo = make_score(24, 11);
|
const Score Tempo = make_score(24, 11);
|
||||||
|
|
||||||
// Rooks and queens on the 7th rank
|
// Rooks and queens on the 7th rank
|
||||||
const Score RookOn7thBonus = make_score(3, 20);
|
const Score RookOn7thBonus = make_score(11, 20);
|
||||||
const Score QueenOn7thBonus = make_score(1, 8);
|
const Score QueenOn7thBonus = make_score( 3, 8);
|
||||||
|
|
||||||
// Rooks and queens attacking pawns on the same rank
|
// Rooks and queens attacking pawns on the same rank
|
||||||
const Score RookOnPawnBonus = make_score(3, 48);
|
const Score RookOnPawnBonus = make_score(10, 28);
|
||||||
const Score QueenOnPawnBonus = make_score(1, 40);
|
const Score QueenOnPawnBonus = make_score( 4, 20);
|
||||||
|
|
||||||
// Rooks on open files (modified by Joona Kiiski)
|
// Rooks on open files (modified by Joona Kiiski)
|
||||||
const Score RookOpenFileBonus = make_score(43, 21);
|
const Score RookOpenFileBonus = make_score(43, 21);
|
||||||
@@ -169,6 +171,9 @@ namespace {
|
|||||||
// right to castle.
|
// right to castle.
|
||||||
const Value TrappedRookPenalty = Value(180);
|
const Value TrappedRookPenalty = Value(180);
|
||||||
|
|
||||||
|
// Penalty for bishop with pawns on the same coloured squares
|
||||||
|
const Score BishopPawnsPenalty = make_score(8, 12);
|
||||||
|
|
||||||
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
|
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
|
||||||
// a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only
|
// a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only
|
||||||
// happen in Chess960 games.
|
// happen in Chess960 games.
|
||||||
@@ -221,11 +226,11 @@ namespace {
|
|||||||
|
|
||||||
// KingDangerTable[Color][attackUnits] contains the actual king danger
|
// KingDangerTable[Color][attackUnits] contains the actual king danger
|
||||||
// weighted scores, indexed by color and by a calculated integer number.
|
// weighted scores, indexed by color and by a calculated integer number.
|
||||||
Score KingDangerTable[2][128];
|
Score KingDangerTable[COLOR_NB][128];
|
||||||
|
|
||||||
// TracedTerms[Color][PieceType || TracedType] contains a breakdown of the
|
// TracedTerms[Color][PieceType || TracedType] contains a breakdown of the
|
||||||
// evaluation terms, used when tracing.
|
// evaluation terms, used when tracing.
|
||||||
Score TracedScores[2][16];
|
Score TracedScores[COLOR_NB][16];
|
||||||
std::stringstream TraceStream;
|
std::stringstream TraceStream;
|
||||||
|
|
||||||
enum TracedType {
|
enum TracedType {
|
||||||
@@ -267,8 +272,6 @@ namespace {
|
|||||||
|
|
||||||
namespace Eval {
|
namespace Eval {
|
||||||
|
|
||||||
Color RootColor;
|
|
||||||
|
|
||||||
/// evaluate() is the main evaluation function. It always computes two
|
/// evaluate() is the main evaluation function. It always computes two
|
||||||
/// values, an endgame score and a middle game score, and interpolates
|
/// values, an endgame score and a middle game score, and interpolates
|
||||||
/// between them based on the remaining material.
|
/// between them based on the remaining material.
|
||||||
@@ -289,14 +292,6 @@ namespace Eval {
|
|||||||
Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]);
|
Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]);
|
||||||
Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]);
|
Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]);
|
||||||
|
|
||||||
// King safety is asymmetrical. Our king danger level is weighted by
|
|
||||||
// "Cowardice" UCI parameter, instead the opponent one by "Aggressiveness".
|
|
||||||
// If running in analysis mode, make sure we use symmetrical king safety. We
|
|
||||||
// do this by replacing both Weights[kingDangerUs] and Weights[kingDangerThem]
|
|
||||||
// by their average.
|
|
||||||
if (Options["UCI_AnalyseMode"])
|
|
||||||
Weights[KingDangerUs] = Weights[KingDangerThem] = (Weights[KingDangerUs] + Weights[KingDangerThem]) / 2;
|
|
||||||
|
|
||||||
const int MaxSlope = 30;
|
const int MaxSlope = 30;
|
||||||
const int Peak = 1280;
|
const int Peak = 1280;
|
||||||
|
|
||||||
@@ -319,7 +314,7 @@ namespace Eval {
|
|||||||
Value margin;
|
Value margin;
|
||||||
std::string totals;
|
std::string totals;
|
||||||
|
|
||||||
RootColor = pos.side_to_move();
|
Search::RootColor = pos.side_to_move();
|
||||||
|
|
||||||
TraceStream.str("");
|
TraceStream.str("");
|
||||||
TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
|
TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
|
||||||
@@ -363,11 +358,12 @@ namespace {
|
|||||||
template<bool Trace>
|
template<bool Trace>
|
||||||
Value do_evaluate(const Position& pos, Value& margin) {
|
Value do_evaluate(const Position& pos, Value& margin) {
|
||||||
|
|
||||||
assert(!pos.in_check());
|
assert(!pos.checkers());
|
||||||
|
|
||||||
EvalInfo ei;
|
EvalInfo ei;
|
||||||
Value margins[2];
|
Value margins[COLOR_NB];
|
||||||
Score score, mobilityWhite, mobilityBlack;
|
Score score, mobilityWhite, mobilityBlack;
|
||||||
|
Thread* th = pos.this_thread();
|
||||||
|
|
||||||
// margins[] store the uncertainty estimation of position's evaluation
|
// margins[] store the uncertainty estimation of position's evaluation
|
||||||
// that typically is used by the search for pruning decisions.
|
// that typically is used by the search for pruning decisions.
|
||||||
@@ -379,7 +375,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo);
|
score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo);
|
||||||
|
|
||||||
// Probe the material hash table
|
// Probe the material hash table
|
||||||
ei.mi = pos.this_thread()->materialTable.probe(pos);
|
ei.mi = Material::probe(pos, th->materialTable, th->endgames);
|
||||||
score += ei.mi->material_value();
|
score += ei.mi->material_value();
|
||||||
|
|
||||||
// If we have a specialized evaluation function for the current material
|
// If we have a specialized evaluation function for the current material
|
||||||
@@ -391,7 +387,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Probe the pawn hash table
|
// Probe the pawn hash table
|
||||||
ei.pi = pos.this_thread()->pawnTable.probe(pos);
|
ei.pi = Pawns::probe(pos, th->pawnsTable);
|
||||||
score += ei.pi->pawns_value();
|
score += ei.pi->pawns_value();
|
||||||
|
|
||||||
// Initialize attack and king safety bitboards
|
// Initialize attack and king safety bitboards
|
||||||
@@ -496,7 +492,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
|
|
||||||
// Init king safety tables only if we are going to use them
|
// Init king safety tables only if we are going to use them
|
||||||
if ( pos.piece_count(Us, QUEEN)
|
if ( pos.piece_count(Us, QUEEN)
|
||||||
&& pos.non_pawn_material(Us) >= QueenValueMg + RookValueMg)
|
&& pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg)
|
||||||
{
|
{
|
||||||
ei.kingRing[Them] = (b | (Us == WHITE ? b >> 8 : b << 8));
|
ei.kingRing[Them] = (b | (Us == WHITE ? b >> 8 : b << 8));
|
||||||
b &= ei.attackedBy[Us][PAWN];
|
b &= ei.attackedBy[Us][PAWN];
|
||||||
@@ -577,23 +573,22 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
|
|
||||||
mobility += MobilityBonus[Piece][mob];
|
mobility += MobilityBonus[Piece][mob];
|
||||||
|
|
||||||
// Add a bonus if a slider is pinning an enemy piece
|
|
||||||
if ( (Piece == BISHOP || Piece == ROOK || Piece == QUEEN)
|
|
||||||
&& (PseudoAttacks[Piece][pos.king_square(Them)] & s))
|
|
||||||
{
|
|
||||||
b = BetweenBB[s][pos.king_square(Them)] & pos.pieces();
|
|
||||||
|
|
||||||
assert(b);
|
|
||||||
|
|
||||||
if (!more_than_one(b) && (b & pos.pieces(Them)))
|
|
||||||
score += ThreatBonus[Piece][type_of(pos.piece_on(lsb(b)))];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrease score if we are attacked by an enemy pawn. Remaining part
|
// Decrease score if we are attacked by an enemy pawn. Remaining part
|
||||||
// of threat evaluation must be done later when we have full attack info.
|
// of threat evaluation must be done later when we have full attack info.
|
||||||
if (ei.attackedBy[Them][PAWN] & s)
|
if (ei.attackedBy[Them][PAWN] & s)
|
||||||
score -= ThreatenedByPawnPenalty[Piece];
|
score -= ThreatenedByPawnPenalty[Piece];
|
||||||
|
|
||||||
|
// Otherwise give a bonus if we are a bishop and can pin a piece or
|
||||||
|
// can give a discovered check through an x-ray attack.
|
||||||
|
else if ( Piece == BISHOP
|
||||||
|
&& (PseudoAttacks[Piece][pos.king_square(Them)] & s)
|
||||||
|
&& !more_than_one(BetweenBB[s][pos.king_square(Them)] & pos.pieces()))
|
||||||
|
score += BishopPinBonus;
|
||||||
|
|
||||||
|
// Penalty for bishop with same coloured pawns
|
||||||
|
if (Piece == BISHOP)
|
||||||
|
score -= BishopPawnsPenalty * ei.pi->pawns_on_same_color_squares(Us, s);
|
||||||
|
|
||||||
// Bishop and knight outposts squares
|
// Bishop and knight outposts squares
|
||||||
if ( (Piece == BISHOP || Piece == KNIGHT)
|
if ( (Piece == BISHOP || Piece == KNIGHT)
|
||||||
&& !(pos.pieces(Them, PAWN) & attack_span_mask(Us, s)))
|
&& !(pos.pieces(Them, PAWN) & attack_span_mask(Us, s)))
|
||||||
@@ -696,16 +691,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
// Undefended minors get penalized even if not under attack
|
// Undefended minors get penalized even if not under attack
|
||||||
undefendedMinors = pos.pieces(Them)
|
undefendedMinors = pos.pieces(Them)
|
||||||
& (pos.pieces(BISHOP) | pos.pieces(KNIGHT))
|
& (pos.pieces(BISHOP) | pos.pieces(KNIGHT))
|
||||||
& ~ei.attackedBy[Them][0];
|
& ~ei.attackedBy[Them][ALL_PIECES];
|
||||||
|
|
||||||
if (undefendedMinors)
|
if (undefendedMinors)
|
||||||
score += more_than_one(undefendedMinors) ? UndefendedMinorPenalty * 2
|
score += UndefendedMinorPenalty;
|
||||||
: UndefendedMinorPenalty;
|
|
||||||
|
|
||||||
// Enemy pieces not defended by a pawn and under our attack
|
// Enemy pieces not defended by a pawn and under our attack
|
||||||
weakEnemies = pos.pieces(Them)
|
weakEnemies = pos.pieces(Them)
|
||||||
& ~ei.attackedBy[Them][PAWN]
|
& ~ei.attackedBy[Them][PAWN]
|
||||||
& ei.attackedBy[Us][0];
|
& ei.attackedBy[Us][ALL_PIECES];
|
||||||
|
|
||||||
if (!weakEnemies)
|
if (!weakEnemies)
|
||||||
return score;
|
return score;
|
||||||
@@ -744,9 +738,9 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
score += evaluate_pieces<QUEEN, Us, Trace>(pos, ei, mobility, mobilityArea);
|
score += evaluate_pieces<QUEEN, Us, Trace>(pos, ei, mobility, mobilityArea);
|
||||||
|
|
||||||
// Sum up all attacked squares
|
// Sum up all attacked squares
|
||||||
ei.attackedBy[Us][0] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
|
ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
|
||||||
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
|
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
|
||||||
| ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING];
|
| ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING];
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -772,7 +766,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
{
|
{
|
||||||
// Find the attacked squares around the king which has no defenders
|
// Find the attacked squares around the king which has no defenders
|
||||||
// apart from the king itself
|
// apart from the king itself
|
||||||
undefended = ei.attackedBy[Them][0] & ei.attackedBy[Us][KING];
|
undefended = ei.attackedBy[Them][ALL_PIECES] & ei.attackedBy[Us][KING];
|
||||||
undefended &= ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
|
undefended &= ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
|
||||||
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
|
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
|
||||||
| ei.attackedBy[Us][QUEEN]);
|
| ei.attackedBy[Us][QUEEN]);
|
||||||
@@ -820,7 +814,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Analyse enemy's safe distance checks for sliders and knights
|
// Analyse enemy's safe distance checks for sliders and knights
|
||||||
safe = ~(pos.pieces(Them) | ei.attackedBy[Us][0]);
|
safe = ~(pos.pieces(Them) | ei.attackedBy[Us][ALL_PIECES]);
|
||||||
|
|
||||||
b1 = pos.attacks_from<ROOK>(ksq) & safe;
|
b1 = pos.attacks_from<ROOK>(ksq) & safe;
|
||||||
b2 = pos.attacks_from<BISHOP>(ksq) & safe;
|
b2 = pos.attacks_from<BISHOP>(ksq) & safe;
|
||||||
@@ -853,8 +847,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
// value that will be used for pruning because this value can sometimes
|
// value that will be used for pruning because this value can sometimes
|
||||||
// be very big, and so capturing a single attacking piece can therefore
|
// be very big, and so capturing a single attacking piece can therefore
|
||||||
// result in a score change far bigger than the value of the captured piece.
|
// result in a score change far bigger than the value of the captured piece.
|
||||||
score -= KingDangerTable[Us == Eval::RootColor][attackUnits];
|
score -= KingDangerTable[Us == Search::RootColor][attackUnits];
|
||||||
margins[Us] += mg_value(KingDangerTable[Us == Eval::RootColor][attackUnits]);
|
margins[Us] += mg_value(KingDangerTable[Us == Search::RootColor][attackUnits]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Trace)
|
if (Trace)
|
||||||
@@ -907,7 +901,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
if (pos.is_empty(blockSq))
|
if (pos.is_empty(blockSq))
|
||||||
{
|
{
|
||||||
squaresToQueen = forward_bb(Us, s);
|
squaresToQueen = forward_bb(Us, s);
|
||||||
defendedSquares = squaresToQueen & ei.attackedBy[Us][0];
|
defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES];
|
||||||
|
|
||||||
// If there is an enemy rook or queen attacking the pawn from behind,
|
// If there is an enemy rook or queen attacking the pawn from behind,
|
||||||
// add all X-ray attacks by the rook or queen. Otherwise consider only
|
// add all X-ray attacks by the rook or queen. Otherwise consider only
|
||||||
@@ -916,7 +910,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
&& (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN) & pos.attacks_from<ROOK>(s)))
|
&& (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN) & pos.attacks_from<ROOK>(s)))
|
||||||
unsafeSquares = squaresToQueen;
|
unsafeSquares = squaresToQueen;
|
||||||
else
|
else
|
||||||
unsafeSquares = squaresToQueen & (ei.attackedBy[Them][0] | pos.pieces(Them));
|
unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
|
||||||
|
|
||||||
// If there aren't enemy attacks or pieces along the path to queen give
|
// If there aren't enemy attacks or pieces along the path to queen give
|
||||||
// huge bonus. Even bigger if we protect the pawn's path.
|
// huge bonus. Even bigger if we protect the pawn's path.
|
||||||
@@ -993,14 +987,14 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
// Compute plies to queening and check direct advancement
|
// Compute plies to queening and check direct advancement
|
||||||
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2);
|
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2);
|
||||||
oppMovesToGo = square_distance(pos.king_square(~c), queeningSquare) - int(c != pos.side_to_move());
|
oppMovesToGo = square_distance(pos.king_square(~c), queeningSquare) - int(c != pos.side_to_move());
|
||||||
pathDefended = ((ei.attackedBy[c][0] & queeningPath) == queeningPath);
|
pathDefended = ((ei.attackedBy[c][ALL_PIECES] & queeningPath) == queeningPath);
|
||||||
|
|
||||||
if (movesToGo >= oppMovesToGo && !pathDefended)
|
if (movesToGo >= oppMovesToGo && !pathDefended)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Opponent king cannot block because path is defended and position
|
// Opponent king cannot block because path is defended and position
|
||||||
// is not in check. So only friendly pieces can be blockers.
|
// is not in check. So only friendly pieces can be blockers.
|
||||||
assert(!pos.in_check());
|
assert(!pos.checkers());
|
||||||
assert((queeningPath & pos.pieces()) == (queeningPath & pos.pieces(c)));
|
assert((queeningPath & pos.pieces()) == (queeningPath & pos.pieces(c)));
|
||||||
|
|
||||||
// Add moves needed to free the path from friendly pieces and retest condition
|
// Add moves needed to free the path from friendly pieces and retest condition
|
||||||
@@ -1140,14 +1134,18 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
Bitboard safe = SpaceMask[Us]
|
Bitboard safe = SpaceMask[Us]
|
||||||
& ~pos.pieces(Us, PAWN)
|
& ~pos.pieces(Us, PAWN)
|
||||||
& ~ei.attackedBy[Them][PAWN]
|
& ~ei.attackedBy[Them][PAWN]
|
||||||
& (ei.attackedBy[Us][0] | ~ei.attackedBy[Them][0]);
|
& (ei.attackedBy[Us][ALL_PIECES] | ~ei.attackedBy[Them][ALL_PIECES]);
|
||||||
|
|
||||||
// Find all squares which are at most three squares behind some friendly pawn
|
// Find all squares which are at most three squares behind some friendly pawn
|
||||||
Bitboard behind = pos.pieces(Us, PAWN);
|
Bitboard behind = pos.pieces(Us, PAWN);
|
||||||
behind |= (Us == WHITE ? behind >> 8 : behind << 8);
|
behind |= (Us == WHITE ? behind >> 8 : behind << 8);
|
||||||
behind |= (Us == WHITE ? behind >> 16 : behind << 16);
|
behind |= (Us == WHITE ? behind >> 16 : behind << 16);
|
||||||
|
|
||||||
return popcount<Max15>(safe) + popcount<Max15>(behind & safe);
|
// Since SpaceMask[Us] is fully on our half of the board
|
||||||
|
assert(unsigned(safe >> (Us == WHITE ? 32 : 0)) == 0);
|
||||||
|
|
||||||
|
// Count safe + (behind & safe) with a single popcount
|
||||||
|
return popcount<Full>((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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,8 +26,6 @@ class Position;
|
|||||||
|
|
||||||
namespace Eval {
|
namespace Eval {
|
||||||
|
|
||||||
extern Color RootColor;
|
|
||||||
|
|
||||||
extern void init();
|
extern void init();
|
||||||
extern Value evaluate(const Position& pos, Value& margin);
|
extern Value evaluate(const Position& pos, Value& margin);
|
||||||
extern std::string trace(const Position& pos);
|
extern std::string trace(const Position& pos);
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(HISTORY_H_INCLUDED)
|
|
||||||
#define HISTORY_H_INCLUDED
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
/// The History class stores statistics about how often different moves
|
|
||||||
/// have been successful or unsuccessful during the current search. These
|
|
||||||
/// statistics are used for reduction and move ordering decisions. History
|
|
||||||
/// entries are stored according only to moving piece and destination square,
|
|
||||||
/// in particular two moves with different origin but same destination and
|
|
||||||
/// same piece will be considered identical.
|
|
||||||
|
|
||||||
class History {
|
|
||||||
|
|
||||||
public:
|
|
||||||
void clear();
|
|
||||||
Value value(Piece p, Square to) const;
|
|
||||||
void add(Piece p, Square to, Value bonus);
|
|
||||||
Value gain(Piece p, Square to) const;
|
|
||||||
void update_gain(Piece p, Square to, Value g);
|
|
||||||
|
|
||||||
static const Value MaxValue = Value(2000);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Value history[16][64]; // [piece][to_square]
|
|
||||||
Value maxGains[16][64]; // [piece][to_square]
|
|
||||||
};
|
|
||||||
|
|
||||||
inline void History::clear() {
|
|
||||||
memset(history, 0, 16 * 64 * sizeof(Value));
|
|
||||||
memset(maxGains, 0, 16 * 64 * sizeof(Value));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Value History::value(Piece p, Square to) const {
|
|
||||||
return history[p][to];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void History::add(Piece p, Square to, Value bonus) {
|
|
||||||
if (abs(history[p][to] + bonus) < MaxValue) history[p][to] += bonus;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Value History::gain(Piece p, Square to) const {
|
|
||||||
return maxGains[p][to];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void History::update_gain(Piece p, Square to, Value g) {
|
|
||||||
maxGains[p][to] = std::max(g, maxGains[p][to] - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // !defined(HISTORY_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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm> // For std::min
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
@@ -38,15 +38,29 @@ namespace {
|
|||||||
const Value RedundantQueenPenalty = Value(320);
|
const Value RedundantQueenPenalty = Value(320);
|
||||||
const Value RedundantRookPenalty = Value(554);
|
const Value RedundantRookPenalty = Value(554);
|
||||||
|
|
||||||
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
|
// pair pawn knight bishop rook queen
|
||||||
|
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
|
||||||
|
|
||||||
const int QuadraticCoefficientsSameColor[][8] = {
|
const int QuadraticCoefficientsSameColor[][PIECE_TYPE_NB] = {
|
||||||
{ 7, 7, 7, 7, 7, 7 }, { 39, 2, 7, 7, 7, 7 }, { 35, 271, -4, 7, 7, 7 },
|
// pair pawn knight bishop rook queen
|
||||||
{ 7, 25, 4, 7, 7, 7 }, { -27, -2, 46, 100, 56, 7 }, { 58, 29, 83, 148, -3, -25 } };
|
{ 7 }, // Bishop pair
|
||||||
|
{ 39, 2 }, // Pawn
|
||||||
|
{ 35, 271, -4 }, // Knight
|
||||||
|
{ 7, 105, 4, 7 }, // Bishop
|
||||||
|
{ -27, -2, 46, 100, 56 }, // Rook
|
||||||
|
{ 58, 29, 83, 148, -3, -25 } // Queen
|
||||||
|
};
|
||||||
|
|
||||||
const int QuadraticCoefficientsOppositeColor[][8] = {
|
const int QuadraticCoefficientsOppositeColor[][PIECE_TYPE_NB] = {
|
||||||
{ 41, 41, 41, 41, 41, 41 }, { 37, 41, 41, 41, 41, 41 }, { 10, 62, 41, 41, 41, 41 },
|
// THEIR PIECES
|
||||||
{ 57, 64, 39, 41, 41, 41 }, { 50, 40, 23, -22, 41, 41 }, { 106, 101, 3, 151, 171, 41 } };
|
// pair pawn knight bishop rook queen
|
||||||
|
{ 41 }, // Bishop pair
|
||||||
|
{ 37, 41 }, // Pawn
|
||||||
|
{ 10, 62, 41 }, // Knight OUR PIECES
|
||||||
|
{ 57, 64, 39, 41 }, // Bishop
|
||||||
|
{ 50, 40, 23, -22, 41 }, // Rook
|
||||||
|
{ 106, 101, 3, 151, 171, 41 } // Queen
|
||||||
|
};
|
||||||
|
|
||||||
// Endgame evaluation and scaling functions accessed direcly and not through
|
// Endgame evaluation and scaling functions accessed direcly and not through
|
||||||
// the function maps because correspond to more then one material hash key.
|
// the function maps because correspond to more then one material hash key.
|
||||||
@@ -81,18 +95,54 @@ namespace {
|
|||||||
&& pos.piece_count(Them, PAWN) >= 1;
|
&& pos.piece_count(Them, PAWN) >= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// imbalance() calculates imbalance comparing piece count of each
|
||||||
|
/// piece type for both colors.
|
||||||
|
|
||||||
|
template<Color Us>
|
||||||
|
int imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
|
||||||
|
|
||||||
|
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||||
|
|
||||||
|
int pt1, pt2, pc, v;
|
||||||
|
int value = 0;
|
||||||
|
|
||||||
|
// Redundancy of major pieces, formula based on Kaufman's paper
|
||||||
|
// "The Evaluation of Material Imbalances in Chess"
|
||||||
|
if (pieceCount[Us][ROOK] > 0)
|
||||||
|
value -= RedundantRookPenalty * (pieceCount[Us][ROOK] - 1)
|
||||||
|
+ RedundantQueenPenalty * pieceCount[Us][QUEEN];
|
||||||
|
|
||||||
|
// Second-degree polynomial material imbalance by Tord Romstad
|
||||||
|
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
|
||||||
|
{
|
||||||
|
pc = pieceCount[Us][pt1];
|
||||||
|
if (!pc)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
v = LinearCoefficients[pt1];
|
||||||
|
|
||||||
|
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
|
||||||
|
v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2]
|
||||||
|
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];
|
||||||
|
|
||||||
|
value += pc * v;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace Material {
|
||||||
|
|
||||||
/// MaterialTable::probe() takes a position object as input, looks up a MaterialEntry
|
/// Material::probe() takes a position object as input, looks up a MaterialEntry
|
||||||
/// object, and returns a pointer to it. If the material configuration is not
|
/// object, and returns a pointer to it. If the material configuration is not
|
||||||
/// already present in the table, it is computed and stored there, so we don't
|
/// already present in the table, it is computed and stored there, so we don't
|
||||||
/// have to recompute everything when the same material configuration occurs again.
|
/// have to recompute everything when the same material configuration occurs again.
|
||||||
|
|
||||||
MaterialEntry* MaterialTable::probe(const Position& pos) {
|
Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
|
||||||
|
|
||||||
Key key = pos.material_key();
|
Key key = pos.material_key();
|
||||||
MaterialEntry* e = entries[key];
|
Entry* e = entries[key];
|
||||||
|
|
||||||
// If e->key matches the position's material hash key, it means that we
|
// If e->key matches the position's material hash key, it means that we
|
||||||
// have analysed this material configuration before, and we can simply
|
// have analysed this material configuration before, and we can simply
|
||||||
@@ -100,10 +150,10 @@ MaterialEntry* MaterialTable::probe(const Position& pos) {
|
|||||||
if (e->key == key)
|
if (e->key == key)
|
||||||
return e;
|
return e;
|
||||||
|
|
||||||
memset(e, 0, sizeof(MaterialEntry));
|
memset(e, 0, sizeof(Entry));
|
||||||
e->key = key;
|
e->key = key;
|
||||||
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
|
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
|
||||||
e->gamePhase = MaterialTable::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 material configuration. First we look for a fixed
|
// particular material configuration. First we look for a fixed
|
||||||
@@ -215,7 +265,7 @@ MaterialEntry* MaterialTable::probe(const Position& pos) {
|
|||||||
// 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", this allow us to be more flexible
|
||||||
// in defining bishop pair bonuses.
|
// in defining bishop pair bonuses.
|
||||||
const int pieceCount[2][8] = {
|
const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = {
|
||||||
{ pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT),
|
{ pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT),
|
||||||
pos.piece_count(WHITE, BISHOP) , pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) },
|
pos.piece_count(WHITE, BISHOP) , pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) },
|
||||||
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT),
|
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT),
|
||||||
@@ -226,47 +276,11 @@ MaterialEntry* MaterialTable::probe(const Position& pos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// MaterialTable::imbalance() calculates imbalance comparing piece count of each
|
/// Material::game_phase() calculates the phase given the current
|
||||||
/// piece type for both colors.
|
|
||||||
|
|
||||||
template<Color Us>
|
|
||||||
int MaterialTable::imbalance(const int pieceCount[][8]) {
|
|
||||||
|
|
||||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
|
||||||
|
|
||||||
int pt1, pt2, pc, v;
|
|
||||||
int value = 0;
|
|
||||||
|
|
||||||
// Redundancy of major pieces, formula based on Kaufman's paper
|
|
||||||
// "The Evaluation of Material Imbalances in Chess"
|
|
||||||
if (pieceCount[Us][ROOK] > 0)
|
|
||||||
value -= RedundantRookPenalty * (pieceCount[Us][ROOK] - 1)
|
|
||||||
+ RedundantQueenPenalty * pieceCount[Us][QUEEN];
|
|
||||||
|
|
||||||
// Second-degree polynomial material imbalance by Tord Romstad
|
|
||||||
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
|
|
||||||
{
|
|
||||||
pc = pieceCount[Us][pt1];
|
|
||||||
if (!pc)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
v = LinearCoefficients[pt1];
|
|
||||||
|
|
||||||
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
|
|
||||||
v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2]
|
|
||||||
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];
|
|
||||||
|
|
||||||
value += pc * v;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// MaterialTable::game_phase() calculates the phase given the current
|
|
||||||
/// position. Because the phase is strictly a function of the material, it
|
/// position. Because the phase is strictly a function of the material, it
|
||||||
/// is stored in MaterialEntry.
|
/// is stored in MaterialEntry.
|
||||||
|
|
||||||
Phase MaterialTable::game_phase(const Position& pos) {
|
Phase game_phase(const Position& pos) {
|
||||||
|
|
||||||
Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK);
|
Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK);
|
||||||
|
|
||||||
@@ -274,3 +288,5 @@ Phase MaterialTable::game_phase(const Position& pos) {
|
|||||||
: npm <= EndgameLimit ? PHASE_ENDGAME
|
: npm <= EndgameLimit ? PHASE_ENDGAME
|
||||||
: Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
|
: Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace Material
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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,96 +25,53 @@
|
|||||||
#include "position.h"
|
#include "position.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
const int MaterialTableSize = 8192;
|
namespace Material {
|
||||||
|
|
||||||
/// Game phase
|
/// Material::Entry contains various information about a material configuration.
|
||||||
enum Phase {
|
/// It contains a material balance evaluation, a function pointer to a special
|
||||||
PHASE_ENDGAME = 0,
|
/// endgame evaluation function (which in most cases is NULL, meaning that the
|
||||||
PHASE_MIDGAME = 128
|
/// standard evaluation function will be used), and "scale factors".
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// MaterialEntry is a class which contains various information about a
|
|
||||||
/// material configuration. It contains a material balance evaluation,
|
|
||||||
/// a function pointer to a special endgame evaluation function (which in
|
|
||||||
/// most cases is NULL, meaning that the standard evaluation function will
|
|
||||||
/// be used), and "scale factors" for black and white.
|
|
||||||
///
|
///
|
||||||
/// The scale factors are used to scale the evaluation score up or down.
|
/// The scale factors are used to scale the evaluation score up or down.
|
||||||
/// For instance, in KRB vs KR endgames, the score is scaled down by a factor
|
/// For instance, in KRB vs KR endgames, the score is scaled down by a factor
|
||||||
/// of 4, which will result in scores of absolute value less than one pawn.
|
/// of 4, which will result in scores of absolute value less than one pawn.
|
||||||
|
|
||||||
class MaterialEntry {
|
struct Entry {
|
||||||
|
|
||||||
friend struct MaterialTable;
|
Score material_value() const { return make_score(value, value); }
|
||||||
|
int space_weight() const { return spaceWeight; }
|
||||||
public:
|
Phase game_phase() const { return gamePhase; }
|
||||||
Score material_value() const;
|
bool specialized_eval_exists() const { return evaluationFunction != NULL; }
|
||||||
|
Value evaluate(const Position& p) const { return (*evaluationFunction)(p); }
|
||||||
ScaleFactor scale_factor(const Position& pos, Color c) const;
|
ScaleFactor scale_factor(const Position& pos, Color c) const;
|
||||||
int space_weight() const;
|
|
||||||
Phase game_phase() const;
|
|
||||||
bool specialized_eval_exists() const;
|
|
||||||
Value evaluate(const Position& pos) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Key key;
|
Key key;
|
||||||
int16_t value;
|
int16_t value;
|
||||||
uint8_t factor[2];
|
uint8_t factor[COLOR_NB];
|
||||||
EndgameBase<Value>* evaluationFunction;
|
EndgameBase<Value>* evaluationFunction;
|
||||||
EndgameBase<ScaleFactor>* scalingFunction[2];
|
EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB];
|
||||||
int spaceWeight;
|
int spaceWeight;
|
||||||
Phase gamePhase;
|
Phase gamePhase;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef HashTable<Entry, 8192> Table;
|
||||||
|
|
||||||
/// The MaterialTable class represents a material hash table. The most important
|
Entry* probe(const Position& pos, Table& entries, Endgames& endgames);
|
||||||
/// method is probe(), which returns a pointer to a MaterialEntry object.
|
Phase game_phase(const Position& pos);
|
||||||
|
|
||||||
struct MaterialTable {
|
/// Material::scale_factor takes a position and a color as input, and
|
||||||
|
|
||||||
MaterialEntry* probe(const Position& pos);
|
|
||||||
static Phase game_phase(const Position& pos);
|
|
||||||
template<Color Us> static int imbalance(const int pieceCount[][8]);
|
|
||||||
|
|
||||||
HashTable<MaterialEntry, MaterialTableSize> entries;
|
|
||||||
Endgames endgames;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// MaterialEntry::scale_factor takes a position and a color as input, and
|
|
||||||
/// returns a scale factor for the given color. We have to provide the
|
/// 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
|
/// 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
|
/// 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
|
/// the position. For instance, in KBP vs K endgames, a scaling function
|
||||||
/// which checks for draws with rook pawns and wrong-colored bishops.
|
/// which checks for draws with rook pawns and wrong-colored bishops.
|
||||||
|
|
||||||
inline ScaleFactor MaterialEntry::scale_factor(const Position& pos, Color c) const {
|
inline ScaleFactor Entry::scale_factor(const Position& pos, Color c) const {
|
||||||
|
|
||||||
if (!scalingFunction[c])
|
return !scalingFunction[c] || (*scalingFunction[c])(pos) == SCALE_FACTOR_NONE
|
||||||
return ScaleFactor(factor[c]);
|
? ScaleFactor(factor[c]) : (*scalingFunction[c])(pos);
|
||||||
|
|
||||||
ScaleFactor sf = (*scalingFunction[c])(pos);
|
|
||||||
return sf == SCALE_FACTOR_NONE ? ScaleFactor(factor[c]) : sf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Value MaterialEntry::evaluate(const Position& pos) const {
|
|
||||||
return (*evaluationFunction)(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Score MaterialEntry::material_value() const {
|
|
||||||
return make_score(value, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int MaterialEntry::space_weight() const {
|
|
||||||
return spaceWeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Phase MaterialEntry::game_phase() const {
|
|
||||||
return gamePhase;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool MaterialEntry::specialized_eval_exists() const {
|
|
||||||
return evaluationFunction != NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !defined(MATERIAL_H_INCLUDED)
|
#endif // !defined(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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -31,16 +31,16 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
/// Version number. If Version is left empty, then Tag plus current
|
/// Version number. If Version is left empty, then Tag plus current
|
||||||
/// date (in the format YYMMDD) is used as a version number.
|
/// date, in the format DD-MM-YY, are used as a version number.
|
||||||
|
|
||||||
static const string Version = "2.3.1";
|
static const string Version = "3";
|
||||||
static const string Tag = "";
|
static const string Tag = "";
|
||||||
|
|
||||||
|
|
||||||
/// engine_info() returns the full name of the current Stockfish version.
|
/// engine_info() returns the full name of the current Stockfish version. This
|
||||||
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when
|
/// will be either "Stockfish <Tag> DD-MM-YY" (where DD-MM-YY is the date when
|
||||||
/// the program was compiled) or "Stockfish <version number>", depending
|
/// the program was compiled) or "Stockfish <Version>", depending on whether
|
||||||
/// on whether Version is empty.
|
/// Version is empty.
|
||||||
|
|
||||||
const string engine_info(bool to_uci) {
|
const string engine_info(bool to_uci) {
|
||||||
|
|
||||||
@@ -57,8 +57,8 @@ const string engine_info(bool to_uci) {
|
|||||||
{
|
{
|
||||||
date >> month >> day >> year;
|
date >> month >> day >> year;
|
||||||
|
|
||||||
s << Tag << setfill('0') << " " << year.substr(2)
|
s << Tag << string(Tag.empty() ? "" : " ") << setfill('0') << setw(2) << day
|
||||||
<< setw(2) << (1 + months.find(month) / 4) << setw(2) << day;
|
<< "-" << setw(2) << (1 + months.find(month) / 4) << "-" << year.substr(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
s << cpu64 << popcnt << (to_uci ? "\nid author ": " by ")
|
s << cpu64 << popcnt << (to_uci ? "\nid author ": " by ")
|
||||||
@@ -227,18 +227,19 @@ void prefetch(char*) {}
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
# include <xmmintrin.h>
|
|
||||||
|
|
||||||
void prefetch(char* addr) {
|
void prefetch(char* addr) {
|
||||||
|
|
||||||
# if defined(__INTEL_COMPILER) || defined(__ICL)
|
# if defined(__INTEL_COMPILER)
|
||||||
// This hack prevents prefetches to be optimized away by
|
// This hack prevents prefetches to be optimized away by
|
||||||
// Intel compiler. Both MSVC and gcc seems not affected.
|
// Intel compiler. Both MSVC and gcc seems not affected.
|
||||||
__asm__ ("");
|
__asm__ ("");
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
_mm_prefetch(addr, _MM_HINT_T2);
|
# if defined(__INTEL_COMPILER) || defined(_MSC_VER)
|
||||||
_mm_prefetch(addr+64, _MM_HINT_T2); // 64 bytes ahead
|
_mm_prefetch(addr, _MM_HINT_T0);
|
||||||
|
# else
|
||||||
|
__builtin_prefetch(addr);
|
||||||
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -44,7 +44,7 @@ namespace {
|
|||||||
Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
|
Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
|
||||||
Bitboard enemies = pos.pieces(~us);
|
Bitboard enemies = pos.pieces(~us);
|
||||||
|
|
||||||
assert(!pos.in_check());
|
assert(!pos.checkers());
|
||||||
|
|
||||||
const int K = Chess960 ? kto > kfrom ? -1 : 1
|
const int K = Chess960 ? kto > kfrom ? -1 : 1
|
||||||
: Side == KING_SIDE ? -1 : 1;
|
: Side == KING_SIDE ? -1 : 1;
|
||||||
@@ -249,41 +249,38 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FORCE_INLINE MoveStack* generate_king_moves(const Position& pos, MoveStack* mlist,
|
|
||||||
Color us, Bitboard target) {
|
|
||||||
Square from = pos.king_square(us);
|
|
||||||
Bitboard b = pos.attacks_from<KING>(from) & target;
|
|
||||||
SERIALIZE(b);
|
|
||||||
return mlist;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<GenType Type> FORCE_INLINE
|
template<GenType Type> FORCE_INLINE
|
||||||
MoveStack* generate_all_moves(const Position& pos, MoveStack* mlist, Color us,
|
MoveStack* generate_all(const Position& pos, MoveStack* mlist, Color us,
|
||||||
Bitboard target, const CheckInfo* ci = NULL) {
|
Bitboard target, const CheckInfo* ci = NULL) {
|
||||||
|
|
||||||
|
const bool Checks = Type == QUIET_CHECKS;
|
||||||
|
|
||||||
mlist = (us == WHITE ? generate_pawn_moves<WHITE, Type>(pos, mlist, target, ci)
|
mlist = (us == WHITE ? generate_pawn_moves<WHITE, Type>(pos, mlist, target, ci)
|
||||||
: generate_pawn_moves<BLACK, Type>(pos, mlist, target, ci));
|
: generate_pawn_moves<BLACK, Type>(pos, mlist, target, ci));
|
||||||
|
|
||||||
mlist = generate_moves<KNIGHT, Type == QUIET_CHECKS>(pos, mlist, us, target, ci);
|
mlist = generate_moves<KNIGHT, Checks>(pos, mlist, us, target, ci);
|
||||||
mlist = generate_moves<BISHOP, Type == QUIET_CHECKS>(pos, mlist, us, target, ci);
|
mlist = generate_moves<BISHOP, Checks>(pos, mlist, us, target, ci);
|
||||||
mlist = generate_moves<ROOK, Type == QUIET_CHECKS>(pos, mlist, us, target, ci);
|
mlist = generate_moves<ROOK, Checks>(pos, mlist, us, target, ci);
|
||||||
mlist = generate_moves<QUEEN, Type == QUIET_CHECKS>(pos, mlist, us, target, ci);
|
mlist = generate_moves<QUEEN, Checks>(pos, mlist, us, target, ci);
|
||||||
|
|
||||||
if (Type != QUIET_CHECKS && Type != EVASIONS)
|
if (Type != QUIET_CHECKS && Type != EVASIONS)
|
||||||
mlist = generate_king_moves(pos, mlist, us, target);
|
{
|
||||||
|
Square from = pos.king_square(us);
|
||||||
|
Bitboard b = pos.attacks_from<KING>(from) & target;
|
||||||
|
SERIALIZE(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, Type == QUIET_CHECKS, true>(pos, mlist, us);
|
mlist = generate_castle<KING_SIDE, Checks, true>(pos, mlist, us);
|
||||||
mlist = generate_castle<QUEEN_SIDE, Type == QUIET_CHECKS, true>(pos, mlist, us);
|
mlist = generate_castle<QUEEN_SIDE, Checks, true>(pos, mlist, us);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mlist = generate_castle<KING_SIDE, Type == QUIET_CHECKS, false>(pos, mlist, us);
|
mlist = generate_castle<KING_SIDE, Checks, false>(pos, mlist, us);
|
||||||
mlist = generate_castle<QUEEN_SIDE, Type == QUIET_CHECKS, false>(pos, mlist, us);
|
mlist = generate_castle<QUEEN_SIDE, Checks, false>(pos, mlist, us);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,21 +304,15 @@ template<GenType Type>
|
|||||||
MoveStack* generate(const Position& pos, MoveStack* mlist) {
|
MoveStack* generate(const Position& pos, MoveStack* mlist) {
|
||||||
|
|
||||||
assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS);
|
assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS);
|
||||||
assert(!pos.in_check());
|
assert(!pos.checkers());
|
||||||
|
|
||||||
Color us = pos.side_to_move();
|
Color us = pos.side_to_move();
|
||||||
Bitboard target;
|
|
||||||
|
|
||||||
if (Type == CAPTURES)
|
Bitboard target = Type == CAPTURES ? pos.pieces(~us)
|
||||||
target = pos.pieces(~us);
|
: Type == QUIETS ? ~pos.pieces()
|
||||||
|
: Type == NON_EVASIONS ? ~pos.pieces(us) : 0;
|
||||||
|
|
||||||
else if (Type == QUIETS)
|
return generate_all<Type>(pos, mlist, us, target);
|
||||||
target = ~pos.pieces();
|
|
||||||
|
|
||||||
else if (Type == NON_EVASIONS)
|
|
||||||
target = ~pos.pieces(us);
|
|
||||||
|
|
||||||
return generate_all_moves<Type>(pos, mlist, us, target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicit template instantiations
|
// Explicit template instantiations
|
||||||
@@ -335,9 +326,8 @@ template MoveStack* generate<NON_EVASIONS>(const Position&, MoveStack*);
|
|||||||
template<>
|
template<>
|
||||||
MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
|
MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
|
||||||
|
|
||||||
assert(!pos.in_check());
|
assert(!pos.checkers());
|
||||||
|
|
||||||
Color us = pos.side_to_move();
|
|
||||||
CheckInfo ci(pos);
|
CheckInfo ci(pos);
|
||||||
Bitboard dc = ci.dcCandidates;
|
Bitboard dc = ci.dcCandidates;
|
||||||
|
|
||||||
@@ -357,7 +347,7 @@ MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
|
|||||||
SERIALIZE(b);
|
SERIALIZE(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
return generate_all_moves<QUIET_CHECKS>(pos, mlist, us, ~pos.pieces(), &ci);
|
return generate_all<QUIET_CHECKS>(pos, mlist, pos.side_to_move(), ~pos.pieces(), &ci);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -366,7 +356,7 @@ MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
|
|||||||
template<>
|
template<>
|
||||||
MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) {
|
MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) {
|
||||||
|
|
||||||
assert(pos.in_check());
|
assert(pos.checkers());
|
||||||
|
|
||||||
Square from, checksq;
|
Square from, checksq;
|
||||||
int checkersCnt = 0;
|
int checkersCnt = 0;
|
||||||
@@ -419,7 +409,7 @@ MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) {
|
|||||||
// Generate blocking evasions or captures of the checking piece
|
// Generate blocking evasions or captures of the checking piece
|
||||||
Bitboard target = between_bb(checksq, ksq) | pos.checkers();
|
Bitboard target = between_bb(checksq, ksq) | pos.checkers();
|
||||||
|
|
||||||
return generate_all_moves<EVASIONS>(pos, mlist, us, target);
|
return generate_all<EVASIONS>(pos, mlist, us, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -432,7 +422,7 @@ MoveStack* generate<LEGAL>(const Position& pos, MoveStack* mlist) {
|
|||||||
Bitboard pinned = pos.pinned_pieces();
|
Bitboard pinned = pos.pinned_pieces();
|
||||||
Square ksq = pos.king_square(pos.side_to_move());
|
Square ksq = pos.king_square(pos.side_to_move());
|
||||||
|
|
||||||
end = pos.in_check() ? generate<EVASIONS>(pos, mlist)
|
end = pos.checkers() ? generate<EVASIONS>(pos, mlist)
|
||||||
: generate<NON_EVASIONS>(pos, mlist);
|
: generate<NON_EVASIONS>(pos, mlist);
|
||||||
while (cur != end)
|
while (cur != end)
|
||||||
if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT)
|
if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT)
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -46,6 +46,10 @@ struct MoveList {
|
|||||||
bool end() const { return cur == last; }
|
bool end() const { return cur == last; }
|
||||||
Move move() const { return cur->move; }
|
Move move() const { return cur->move; }
|
||||||
size_t size() const { return last - mlist; }
|
size_t size() const { return last - mlist; }
|
||||||
|
bool contains(Move m) const {
|
||||||
|
for (const MoveStack* it(mlist); it != last; ++it) if (it->move == m) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MoveStack mlist[MAX_MOVES];
|
MoveStack mlist[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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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,10 +18,8 @@
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include "movegen.h"
|
|
||||||
#include "movepick.h"
|
#include "movepick.h"
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
|
|
||||||
@@ -37,6 +35,20 @@ namespace {
|
|||||||
STOP
|
STOP
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Our insertion sort, guaranteed to be stable, as is needed
|
||||||
|
void insertion_sort(MoveStack* begin, MoveStack* end)
|
||||||
|
{
|
||||||
|
MoveStack tmp, *p, *q;
|
||||||
|
|
||||||
|
for (p = begin + 1; p < end; ++p)
|
||||||
|
{
|
||||||
|
tmp = *p;
|
||||||
|
for (q = p; q != begin && *(q-1) < tmp; --q)
|
||||||
|
*q = *(q-1);
|
||||||
|
*q = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Unary predicate used by std::partition to split positive scores from remaining
|
// Unary predicate used by std::partition to split positive scores from remaining
|
||||||
// ones so to sort separately the two sets, and with the second sort delayed.
|
// ones so to sort separately the two sets, and with the second sort delayed.
|
||||||
inline bool has_positive_score(const MoveStack& ms) { return ms.score > 0; }
|
inline bool has_positive_score(const MoveStack& ms) { return ms.score > 0; }
|
||||||
@@ -59,7 +71,7 @@ namespace {
|
|||||||
/// move ordering is at the current node.
|
/// move ordering is at the current node.
|
||||||
|
|
||||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
||||||
Search::Stack* s, Value beta) : pos(p), H(h), depth(d) {
|
Search::Stack* s, Value beta) : pos(p), Hist(h), depth(d) {
|
||||||
|
|
||||||
assert(d > DEPTH_ZERO);
|
assert(d > DEPTH_ZERO);
|
||||||
|
|
||||||
@@ -68,7 +80,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
|||||||
endBadCaptures = moves + MAX_MOVES - 1;
|
endBadCaptures = moves + MAX_MOVES - 1;
|
||||||
ss = s;
|
ss = s;
|
||||||
|
|
||||||
if (p.in_check())
|
if (p.checkers())
|
||||||
phase = EVASION;
|
phase = EVASION;
|
||||||
|
|
||||||
else
|
else
|
||||||
@@ -79,12 +91,12 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
|||||||
killers[1].move = ss->killers[1];
|
killers[1].move = ss->killers[1];
|
||||||
|
|
||||||
// Consider sligtly negative captures as good if at low depth and far from beta
|
// Consider sligtly negative captures as good if at low depth and far from beta
|
||||||
if (ss && ss->eval < beta - PawnValueMg && d < 3 * ONE_PLY)
|
if (ss && ss->staticEval < beta - PawnValueMg && d < 3 * ONE_PLY)
|
||||||
captureThreshold = -PawnValueMg;
|
captureThreshold = -PawnValueMg;
|
||||||
|
|
||||||
// Consider negative captures as good if still enough to reach beta
|
// Consider negative captures as good if still enough to reach beta
|
||||||
else if (ss && ss->eval > beta)
|
else if (ss && ss->staticEval > beta)
|
||||||
captureThreshold = beta - ss->eval;
|
captureThreshold = beta - ss->staticEval;
|
||||||
}
|
}
|
||||||
|
|
||||||
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
||||||
@@ -92,11 +104,11 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
|||||||
}
|
}
|
||||||
|
|
||||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
||||||
Square sq) : pos(p), H(h), cur(moves), end(moves) {
|
Square sq) : pos(p), Hist(h), cur(moves), end(moves) {
|
||||||
|
|
||||||
assert(d <= DEPTH_ZERO);
|
assert(d <= DEPTH_ZERO);
|
||||||
|
|
||||||
if (p.in_check())
|
if (p.checkers())
|
||||||
phase = EVASION;
|
phase = EVASION;
|
||||||
|
|
||||||
else if (d > DEPTH_QS_NO_CHECKS)
|
else if (d > DEPTH_QS_NO_CHECKS)
|
||||||
@@ -124,14 +136,14 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
|||||||
}
|
}
|
||||||
|
|
||||||
MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType pt)
|
MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType pt)
|
||||||
: pos(p), H(h), cur(moves), end(moves) {
|
: pos(p), Hist(h), cur(moves), end(moves) {
|
||||||
|
|
||||||
assert(!pos.in_check());
|
assert(!pos.checkers());
|
||||||
|
|
||||||
phase = PROBCUT;
|
phase = PROBCUT;
|
||||||
|
|
||||||
// In ProbCut we generate only captures better than parent's captured piece
|
// In ProbCut we generate only captures better than parent's captured piece
|
||||||
captureThreshold = PieceValue[Mg][pt];
|
captureThreshold = PieceValue[MG][pt];
|
||||||
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
||||||
|
|
||||||
if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold))
|
if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold))
|
||||||
@@ -141,12 +153,10 @@ MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// MovePicker::score_captures(), MovePicker::score_noncaptures() and
|
/// score() assign a numerical move ordering score to each move in a move list.
|
||||||
/// MovePicker::score_evasions() assign a numerical move ordering score
|
/// The moves with highest scores will be picked first.
|
||||||
/// to each move in a move list. The moves with highest scores will be
|
template<>
|
||||||
/// picked first by next_move().
|
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.
|
||||||
// Suprisingly, this appears to perform slightly better than SEE based
|
// Suprisingly, this appears to perform slightly better than SEE based
|
||||||
// move ordering. The reason is probably that in a position with a winning
|
// move ordering. The reason is probably that in a position with a winning
|
||||||
@@ -165,51 +175,54 @@ void MovePicker::score_captures() {
|
|||||||
for (MoveStack* it = moves; it != end; ++it)
|
for (MoveStack* it = moves; it != end; ++it)
|
||||||
{
|
{
|
||||||
m = it->move;
|
m = it->move;
|
||||||
it->score = PieceValue[Mg][pos.piece_on(to_sq(m))]
|
it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
|
||||||
- type_of(pos.piece_moved(m));
|
- type_of(pos.piece_moved(m));
|
||||||
|
|
||||||
if (type_of(m) == PROMOTION)
|
if (type_of(m) == PROMOTION)
|
||||||
it->score += PieceValue[Mg][promotion_type(m)];
|
it->score += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN];
|
||||||
|
|
||||||
|
else if (type_of(m) == ENPASSANT)
|
||||||
|
it->score += PieceValue[MG][PAWN];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MovePicker::score_noncaptures() {
|
template<>
|
||||||
|
void MovePicker::score<QUIETS>() {
|
||||||
|
|
||||||
Move m;
|
Move m;
|
||||||
|
|
||||||
for (MoveStack* it = moves; it != end; ++it)
|
for (MoveStack* it = moves; it != end; ++it)
|
||||||
{
|
{
|
||||||
m = it->move;
|
m = it->move;
|
||||||
it->score = H.value(pos.piece_moved(m), to_sq(m));
|
it->score = Hist[pos.piece_moved(m)][to_sq(m)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MovePicker::score_evasions() {
|
template<>
|
||||||
|
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 score.
|
||||||
Move m;
|
Move m;
|
||||||
int seeScore;
|
int seeScore;
|
||||||
|
|
||||||
if (end < moves + 2)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (MoveStack* it = moves; it != end; ++it)
|
for (MoveStack* it = moves; it != end; ++it)
|
||||||
{
|
{
|
||||||
m = it->move;
|
m = it->move;
|
||||||
if ((seeScore = pos.see_sign(m)) < 0)
|
if ((seeScore = pos.see_sign(m)) < 0)
|
||||||
it->score = seeScore - History::MaxValue; // Be sure we are at the bottom
|
it->score = seeScore - History::Max; // At the bottom
|
||||||
|
|
||||||
else if (pos.is_capture(m))
|
else if (pos.is_capture(m))
|
||||||
it->score = PieceValue[Mg][pos.piece_on(to_sq(m))]
|
it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
|
||||||
- type_of(pos.piece_moved(m)) + History::MaxValue;
|
- type_of(pos.piece_moved(m)) + History::Max;
|
||||||
else
|
else
|
||||||
it->score = H.value(pos.piece_moved(m), to_sq(m));
|
it->score = Hist[pos.piece_moved(m)][to_sq(m)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// MovePicker::generate_next() generates, scores and sorts the next bunch of moves,
|
/// generate_next() generates, scores and sorts the next bunch of moves, when
|
||||||
/// when there are no more moves to try for the current phase.
|
/// there are no more moves to try for the current phase.
|
||||||
|
|
||||||
void MovePicker::generate_next() {
|
void MovePicker::generate_next() {
|
||||||
|
|
||||||
@@ -219,7 +232,7 @@ void MovePicker::generate_next() {
|
|||||||
|
|
||||||
case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6:
|
case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6:
|
||||||
end = generate<CAPTURES>(pos, moves);
|
end = generate<CAPTURES>(pos, moves);
|
||||||
score_captures();
|
score<CAPTURES>();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case KILLERS_S1:
|
case KILLERS_S1:
|
||||||
@@ -229,16 +242,16 @@ void MovePicker::generate_next() {
|
|||||||
|
|
||||||
case QUIETS_1_S1:
|
case QUIETS_1_S1:
|
||||||
endQuiets = end = generate<QUIETS>(pos, moves);
|
endQuiets = end = generate<QUIETS>(pos, moves);
|
||||||
score_noncaptures();
|
score<QUIETS>();
|
||||||
end = std::partition(cur, end, has_positive_score);
|
end = std::partition(cur, end, has_positive_score);
|
||||||
sort<MoveStack>(cur, end);
|
insertion_sort(cur, end);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case QUIETS_2_S1:
|
case QUIETS_2_S1:
|
||||||
cur = end;
|
cur = end;
|
||||||
end = endQuiets;
|
end = endQuiets;
|
||||||
if (depth >= 3 * ONE_PLY)
|
if (depth >= 3 * ONE_PLY)
|
||||||
sort<MoveStack>(cur, end);
|
insertion_sort(cur, end);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case BAD_CAPTURES_S1:
|
case BAD_CAPTURES_S1:
|
||||||
@@ -249,7 +262,8 @@ void MovePicker::generate_next() {
|
|||||||
|
|
||||||
case EVASIONS_S2:
|
case EVASIONS_S2:
|
||||||
end = generate<EVASIONS>(pos, moves);
|
end = generate<EVASIONS>(pos, moves);
|
||||||
score_evasions();
|
if (end > moves + 1)
|
||||||
|
score<EVASIONS>();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case QUIET_CHECKS_S3:
|
case QUIET_CHECKS_S3:
|
||||||
@@ -268,11 +282,10 @@ void MovePicker::generate_next() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// MovePicker::next_move() is the most important method of the MovePicker class.
|
/// next_move() is the most important method of the MovePicker class. It returns
|
||||||
/// It returns a new pseudo legal move every time it is called, until there
|
/// a new pseudo legal move every time is called, until there are no more moves
|
||||||
/// are no more moves left. It picks the move with the biggest score from a list
|
/// left. It picks the move with the biggest score from a list of generated moves
|
||||||
/// of generated moves taking care not to return the tt move if has already been
|
/// taking care not returning the ttMove if has already been searched previously.
|
||||||
/// searched previously.
|
|
||||||
template<>
|
template<>
|
||||||
Move MovePicker::next_move<false>() {
|
Move MovePicker::next_move<false>() {
|
||||||
|
|
||||||
@@ -359,6 +372,6 @@ Move MovePicker::next_move<false>() {
|
|||||||
|
|
||||||
/// Version of next_move() to use at split point nodes where the move is grabbed
|
/// Version of next_move() to use at split point nodes where the move is grabbed
|
||||||
/// from the split point's shared MovePicker object. This function is not thread
|
/// from the split point's shared MovePicker object. This function is not thread
|
||||||
/// safe so should be lock protected by the caller.
|
/// safe so must be lock protected by the caller.
|
||||||
template<>
|
template<>
|
||||||
Move MovePicker::next_move<true>() { return ss->sp->mp->next_move<false>(); }
|
Move MovePicker::next_move<true>() { return ss->splitPoint->movePicker->next_move<false>(); }
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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,12 +20,48 @@
|
|||||||
#if !defined MOVEPICK_H_INCLUDED
|
#if !defined MOVEPICK_H_INCLUDED
|
||||||
#define MOVEPICK_H_INCLUDED
|
#define MOVEPICK_H_INCLUDED
|
||||||
|
|
||||||
#include "history.h"
|
#include <algorithm> // For std::max
|
||||||
|
#include <cstring> // For memset
|
||||||
|
|
||||||
|
#include "movegen.h"
|
||||||
#include "position.h"
|
#include "position.h"
|
||||||
#include "search.h"
|
#include "search.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
|
|
||||||
|
/// The Stats struct stores moves statistics. According to the template parameter
|
||||||
|
/// the class can store both History and Gains type statistics. History records
|
||||||
|
/// how often different moves have been successful or unsuccessful during the
|
||||||
|
/// current search 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. Entries are stored according only to moving piece and
|
||||||
|
/// destination square, in particular two moves with different origin but same
|
||||||
|
/// destination and same piece will be considered identical.
|
||||||
|
template<bool Gain>
|
||||||
|
struct Stats {
|
||||||
|
|
||||||
|
static const Value Max = Value(2000);
|
||||||
|
|
||||||
|
const Value* operator[](Piece p) const { return &table[p][0]; }
|
||||||
|
void clear() { memset(table, 0, sizeof(table)); }
|
||||||
|
|
||||||
|
void update(Piece p, Square to, Value v) {
|
||||||
|
|
||||||
|
if (Gain)
|
||||||
|
table[p][to] = std::max(v, table[p][to] - 1);
|
||||||
|
|
||||||
|
else if (abs(table[p][to] + v) < Max)
|
||||||
|
table[p][to] += v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Value table[PIECE_NB][SQUARE_NB];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef Stats<false> History;
|
||||||
|
typedef Stats<true> Gains;
|
||||||
|
|
||||||
|
|
||||||
/// 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
|
||||||
/// current position. The most important method is next_move(), which returns a
|
/// current position. The most important method is next_move(), which returns a
|
||||||
/// new pseudo legal move each time it is called, until there are no moves left,
|
/// new pseudo legal move each time it is called, until there are no moves left,
|
||||||
@@ -44,13 +80,11 @@ public:
|
|||||||
template<bool SpNode> Move next_move();
|
template<bool SpNode> Move next_move();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void score_captures();
|
template<GenType> void score();
|
||||||
void score_noncaptures();
|
|
||||||
void score_evasions();
|
|
||||||
void generate_next();
|
void generate_next();
|
||||||
|
|
||||||
const Position& pos;
|
const Position& pos;
|
||||||
const History& H;
|
const History& Hist;
|
||||||
Search::Stack* ss;
|
Search::Stack* ss;
|
||||||
Depth depth;
|
Depth depth;
|
||||||
Move ttMove;
|
Move ttMove;
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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,7 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <stack>
|
||||||
|
|
||||||
#include "movegen.h"
|
#include "movegen.h"
|
||||||
#include "notation.h"
|
#include "notation.h"
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
static const char* PieceToChar = " PNBRQK pnbrqk";
|
static const char* PieceToChar[COLOR_NB] = { " PNBRQK", " pnbrqk" };
|
||||||
|
|
||||||
|
|
||||||
/// score_to_uci() converts a value to a string suitable for use with the UCI
|
/// score_to_uci() converts a value to a string suitable for use with the UCI
|
||||||
@@ -75,7 +75,7 @@ const string move_to_uci(Move m, bool chess960) {
|
|||||||
string move = square_to_string(from) + square_to_string(to);
|
string move = square_to_string(from) + square_to_string(to);
|
||||||
|
|
||||||
if (type_of(m) == PROMOTION)
|
if (type_of(m) == PROMOTION)
|
||||||
move += PieceToChar[make_piece(BLACK, promotion_type(m))]; // Lower case
|
move += PieceToChar[BLACK][promotion_type(m)]; // Lower case
|
||||||
|
|
||||||
return move;
|
return move;
|
||||||
}
|
}
|
||||||
@@ -108,10 +108,9 @@ const string move_to_san(Position& pos, Move m) {
|
|||||||
if (m == MOVE_NULL)
|
if (m == MOVE_NULL)
|
||||||
return "(null)";
|
return "(null)";
|
||||||
|
|
||||||
assert(pos.move_is_legal(m));
|
assert(MoveList<LEGAL>(pos).contains(m));
|
||||||
|
|
||||||
Bitboard attackers;
|
Bitboard others, b;
|
||||||
bool ambiguousMove, ambiguousFile, ambiguousRank;
|
|
||||||
string san;
|
string san;
|
||||||
Color us = pos.side_to_move();
|
Color us = pos.side_to_move();
|
||||||
Square from = from_sq(m);
|
Square from = from_sq(m);
|
||||||
@@ -125,33 +124,25 @@ const string move_to_san(Position& pos, Move m) {
|
|||||||
{
|
{
|
||||||
if (pt != PAWN)
|
if (pt != PAWN)
|
||||||
{
|
{
|
||||||
san = PieceToChar[pt]; // Upper case
|
san = PieceToChar[WHITE][pt]; // Upper case
|
||||||
|
|
||||||
// Disambiguation if we have more then one piece with destination 'to'
|
// Disambiguation if we have more then one piece of type 'pt' that can
|
||||||
// note that for pawns is not needed because starting file is explicit.
|
// reach 'to' with a legal move.
|
||||||
ambiguousMove = ambiguousFile = ambiguousRank = false;
|
others = b = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from;
|
||||||
|
|
||||||
attackers = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from;
|
while (b)
|
||||||
|
|
||||||
while (attackers)
|
|
||||||
{
|
{
|
||||||
Square sq = pop_lsb(&attackers);
|
Move move = make_move(pop_lsb(&b), to);
|
||||||
|
if (!pos.pl_move_is_legal(move, pos.pinned_pieces()))
|
||||||
// Pinned pieces are not included in the possible sub-set
|
others ^= from_sq(move);
|
||||||
if (!pos.pl_move_is_legal(make_move(sq, to), pos.pinned_pieces()))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ambiguousFile |= file_of(sq) == file_of(from);
|
|
||||||
ambiguousRank |= rank_of(sq) == rank_of(from);
|
|
||||||
ambiguousMove = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ambiguousMove)
|
if (others)
|
||||||
{
|
{
|
||||||
if (!ambiguousFile)
|
if (!(others & file_bb(from)))
|
||||||
san += file_to_char(file_of(from));
|
san += file_to_char(file_of(from));
|
||||||
|
|
||||||
else if (!ambiguousRank)
|
else if (!(others & rank_bb(from)))
|
||||||
san += rank_to_char(rank_of(from));
|
san += rank_to_char(rank_of(from));
|
||||||
|
|
||||||
else
|
else
|
||||||
@@ -167,7 +158,7 @@ const string move_to_san(Position& pos, Move m) {
|
|||||||
san += square_to_string(to);
|
san += square_to_string(to);
|
||||||
|
|
||||||
if (type_of(m) == PROMOTION)
|
if (type_of(m) == PROMOTION)
|
||||||
san += string("=") + PieceToChar[promotion_type(m)];
|
san += string("=") + PieceToChar[WHITE][promotion_type(m)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pos.move_gives_check(m, CheckInfo(pos)))
|
if (pos.move_gives_check(m, CheckInfo(pos)))
|
||||||
@@ -226,7 +217,7 @@ string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]
|
|||||||
const int64_t K = 1000;
|
const int64_t K = 1000;
|
||||||
const int64_t M = 1000000;
|
const int64_t M = 1000000;
|
||||||
|
|
||||||
StateInfo state[MAX_PLY_PLUS_2], *st = state;
|
std::stack<StateInfo> st;
|
||||||
Move* m = pv;
|
Move* m = pv;
|
||||||
string san, padding;
|
string san, padding;
|
||||||
size_t length;
|
size_t length;
|
||||||
@@ -261,7 +252,8 @@ string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]
|
|||||||
s << san << ' ';
|
s << san << ' ';
|
||||||
length += san.length() + 1;
|
length += san.length() + 1;
|
||||||
|
|
||||||
pos.do_move(*m++, *st++);
|
st.push(StateInfo());
|
||||||
|
pos.do_move(*m++, st.top());
|
||||||
}
|
}
|
||||||
|
|
||||||
while (m != pv)
|
while (m != pv)
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -30,34 +30,34 @@ namespace {
|
|||||||
#define S(mg, eg) make_score(mg, eg)
|
#define S(mg, eg) make_score(mg, eg)
|
||||||
|
|
||||||
// Doubled pawn penalty by opposed flag and file
|
// Doubled pawn penalty by opposed flag and file
|
||||||
const Score DoubledPawnPenalty[2][8] = {
|
const Score DoubledPawnPenalty[2][FILE_NB] = {
|
||||||
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48),
|
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48),
|
||||||
S(23, 48), S(23, 48), S(20, 48), S(13, 43) },
|
S(23, 48), S(23, 48), S(20, 48), S(13, 43) },
|
||||||
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48),
|
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48),
|
||||||
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }};
|
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }};
|
||||||
|
|
||||||
// Isolated pawn penalty by opposed flag and file
|
// Isolated pawn penalty by opposed flag and file
|
||||||
const Score IsolatedPawnPenalty[2][8] = {
|
const Score IsolatedPawnPenalty[2][FILE_NB] = {
|
||||||
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52),
|
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52),
|
||||||
S(60, 52), S(60, 52), S(54, 52), S(37, 45) },
|
S(60, 52), S(60, 52), S(54, 52), S(37, 45) },
|
||||||
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35),
|
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35),
|
||||||
S(40, 35), S(40, 35), S(36, 35), S(25, 30) }};
|
S(40, 35), S(40, 35), S(36, 35), S(25, 30) }};
|
||||||
|
|
||||||
// Backward pawn penalty by opposed flag and file
|
// Backward pawn penalty by opposed flag and file
|
||||||
const Score BackwardPawnPenalty[2][8] = {
|
const Score BackwardPawnPenalty[2][FILE_NB] = {
|
||||||
{ S(30, 42), S(43, 46), S(49, 46), S(49, 46),
|
{ S(30, 42), S(43, 46), S(49, 46), S(49, 46),
|
||||||
S(49, 46), S(49, 46), S(43, 46), S(30, 42) },
|
S(49, 46), S(49, 46), S(43, 46), S(30, 42) },
|
||||||
{ 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
|
// Pawn chain membership bonus by file
|
||||||
const Score ChainBonus[8] = {
|
const Score ChainBonus[FILE_NB] = {
|
||||||
S(11,-1), S(13,-1), S(13,-1), S(14,-1),
|
S(11,-1), S(13,-1), S(13,-1), S(14,-1),
|
||||||
S(14,-1), S(13,-1), S(13,-1), S(11,-1)
|
S(14,-1), S(13,-1), S(13,-1), S(11,-1)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Candidate passed pawn bonus by rank
|
// Candidate passed pawn bonus by rank
|
||||||
const Score CandidateBonus[8] = {
|
const Score CandidateBonus[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)
|
||||||
};
|
};
|
||||||
@@ -65,12 +65,12 @@ namespace {
|
|||||||
const Score PawnStructureWeight = S(233, 201);
|
const Score PawnStructureWeight = S(233, 201);
|
||||||
|
|
||||||
// Weakness of our pawn shelter in front of the king indexed by [king pawn][rank]
|
// Weakness of our pawn shelter in front of the king indexed by [king pawn][rank]
|
||||||
const Value ShelterWeakness[2][8] =
|
const Value ShelterWeakness[2][RANK_NB] =
|
||||||
{ { V(141), V(0), V(38), V(102), V(128), V(141), V(141) },
|
{ { V(141), V(0), V(38), V(102), V(128), V(141), V(141) },
|
||||||
{ V( 61), V(0), V(16), V( 44), V( 56), V( 61), V( 61) } };
|
{ V( 61), V(0), V(16), V( 44), V( 56), V( 61), V( 61) } };
|
||||||
|
|
||||||
// Danger of enemy pawns moving toward our king indexed by [pawn blocked][rank]
|
// Danger of enemy pawns moving toward our king indexed by [pawn blocked][rank]
|
||||||
const Value StormDanger[2][8] =
|
const Value StormDanger[2][RANK_NB] =
|
||||||
{ { V(26), V(0), V(128), V(51), V(26) },
|
{ { V(26), V(0), V(128), V(51), V(26) },
|
||||||
{ V(13), V(0), V( 64), V(25), V(13) } };
|
{ V(13), V(0), V( 64), V(25), V(13) } };
|
||||||
|
|
||||||
@@ -80,18 +80,122 @@ namespace {
|
|||||||
|
|
||||||
#undef S
|
#undef S
|
||||||
#undef V
|
#undef V
|
||||||
|
|
||||||
|
template<Color Us>
|
||||||
|
Score evaluate_pawns(const Position& pos, Bitboard ourPawns,
|
||||||
|
Bitboard theirPawns, Pawns::Entry* e) {
|
||||||
|
|
||||||
|
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||||
|
|
||||||
|
Bitboard b;
|
||||||
|
Square s;
|
||||||
|
File f;
|
||||||
|
Rank r;
|
||||||
|
bool passed, isolated, doubled, opposed, chain, backward, candidate;
|
||||||
|
Score value = SCORE_ZERO;
|
||||||
|
const Square* pl = pos.piece_list(Us, PAWN);
|
||||||
|
|
||||||
|
// Loop through all pawns of the current color and score each pawn
|
||||||
|
while ((s = *pl++) != SQ_NONE)
|
||||||
|
{
|
||||||
|
assert(pos.piece_on(s) == make_piece(Us, PAWN));
|
||||||
|
|
||||||
|
f = file_of(s);
|
||||||
|
r = rank_of(s);
|
||||||
|
|
||||||
|
// This file cannot be half open
|
||||||
|
e->halfOpenFiles[Us] &= ~(1 << f);
|
||||||
|
|
||||||
|
// Our rank plus previous one. Used for chain detection
|
||||||
|
b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1));
|
||||||
|
|
||||||
|
// Flag the pawn as passed, isolated, doubled or member of a pawn
|
||||||
|
// chain (but not the backward one).
|
||||||
|
chain = ourPawns & adjacent_files_bb(f) & b;
|
||||||
|
isolated = !(ourPawns & adjacent_files_bb(f));
|
||||||
|
doubled = ourPawns & forward_bb(Us, s);
|
||||||
|
opposed = theirPawns & forward_bb(Us, s);
|
||||||
|
passed = !(theirPawns & passed_pawn_mask(Us, s));
|
||||||
|
|
||||||
|
// Test for backward pawn
|
||||||
|
backward = false;
|
||||||
|
|
||||||
|
// If the pawn is passed, isolated, or member of a pawn chain it cannot
|
||||||
|
// be backward. If there are friendly pawns behind on adjacent files
|
||||||
|
// or if can capture an enemy pawn it cannot be backward either.
|
||||||
|
if ( !(passed | isolated | chain)
|
||||||
|
&& !(ourPawns & attack_span_mask(Them, s))
|
||||||
|
&& !(pos.attacks_from<PAWN>(s, Us) & theirPawns))
|
||||||
|
{
|
||||||
|
// We now know that there are no friendly pawns beside or behind this
|
||||||
|
// pawn on adjacent files. We now check whether the pawn is
|
||||||
|
// backward by looking in the forward direction on the adjacent
|
||||||
|
// files, and seeing whether we meet a friendly or an enemy pawn first.
|
||||||
|
b = pos.attacks_from<PAWN>(s, Us);
|
||||||
|
|
||||||
|
// Note that we are sure to find something because pawn is not passed
|
||||||
|
// nor isolated, so loop is potentially infinite, but it isn't.
|
||||||
|
while (!(b & (ourPawns | theirPawns)))
|
||||||
|
Us == WHITE ? b <<= 8 : b >>= 8;
|
||||||
|
|
||||||
|
// The friendly pawn needs to be at least two ranks closer than the
|
||||||
|
// enemy pawn in order to help the potentially backward pawn advance.
|
||||||
|
backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(opposed | passed | (attack_span_mask(Us, s) & theirPawns));
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// pawn on adjacent files is higher or equal than the number of
|
||||||
|
// enemy pawns in the forward direction on the adjacent files.
|
||||||
|
candidate = !(opposed | passed | backward | isolated)
|
||||||
|
&& (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != 0
|
||||||
|
&& popcount<Max15>(b) >= popcount<Max15>(attack_span_mask(Us, s) & theirPawns);
|
||||||
|
|
||||||
|
// Passed pawns will be properly scored in evaluation because we need
|
||||||
|
// full attack info to evaluate passed pawns. Only the frontmost passed
|
||||||
|
// pawn on each file is considered a true passed pawn.
|
||||||
|
if (passed && !doubled)
|
||||||
|
e->passedPawns[Us] |= s;
|
||||||
|
|
||||||
|
// Score this pawn
|
||||||
|
if (isolated)
|
||||||
|
value -= IsolatedPawnPenalty[opposed][f];
|
||||||
|
|
||||||
|
if (doubled)
|
||||||
|
value -= DoubledPawnPenalty[opposed][f];
|
||||||
|
|
||||||
|
if (backward)
|
||||||
|
value -= BackwardPawnPenalty[opposed][f];
|
||||||
|
|
||||||
|
if (chain)
|
||||||
|
value += ChainBonus[f];
|
||||||
|
|
||||||
|
if (candidate)
|
||||||
|
value += CandidateBonus[relative_rank(Us, s)];
|
||||||
|
}
|
||||||
|
|
||||||
|
e->pawnsOnSquares[Us][BLACK] = popcount<Max15>(ourPawns & BlackSquares);
|
||||||
|
e->pawnsOnSquares[Us][WHITE] = pos.piece_count(Us, PAWN) - e->pawnsOnSquares[Us][BLACK];
|
||||||
|
|
||||||
|
e->pawnsOnSquares[Them][BLACK] = popcount<Max15>(theirPawns & BlackSquares);
|
||||||
|
e->pawnsOnSquares[Them][WHITE] = pos.piece_count(Them, PAWN) - e->pawnsOnSquares[Them][BLACK];
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Pawns {
|
||||||
|
|
||||||
/// PawnTable::probe() takes a position object as input, computes a PawnEntry
|
/// probe() takes a position object as input, computes a Entry object, and returns
|
||||||
/// object, and returns a pointer to it. The result is also stored in a hash
|
/// a pointer to it. The result is also stored in a hash table, so we don't have
|
||||||
/// table, so we don't have to recompute everything when the same pawn structure
|
/// to recompute everything when the same pawn structure occurs again.
|
||||||
/// occurs again.
|
|
||||||
|
|
||||||
PawnEntry* PawnTable::probe(const Position& pos) {
|
Entry* probe(const Position& pos, Table& entries) {
|
||||||
|
|
||||||
Key key = pos.pawn_key();
|
Key key = pos.pawn_key();
|
||||||
PawnEntry* e = entries[key];
|
Entry* e = entries[key];
|
||||||
|
|
||||||
// If e->key matches the position's pawn hash key, it means that we
|
// If e->key matches the position's pawn hash key, it means that we
|
||||||
// have analysed this pawn structure before, and we can simply return
|
// have analysed this pawn structure before, and we can simply return
|
||||||
@@ -118,112 +222,11 @@ PawnEntry* PawnTable::probe(const Position& pos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// PawnTable::evaluate_pawns() evaluates each pawn of the given color
|
/// Entry::shelter_storm() calculates shelter and storm penalties for the file
|
||||||
|
|
||||||
template<Color Us>
|
|
||||||
Score PawnTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
|
|
||||||
Bitboard theirPawns, PawnEntry* e) {
|
|
||||||
|
|
||||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
|
||||||
|
|
||||||
Bitboard b;
|
|
||||||
Square s;
|
|
||||||
File f;
|
|
||||||
Rank r;
|
|
||||||
bool passed, isolated, doubled, opposed, chain, backward, candidate;
|
|
||||||
Score value = SCORE_ZERO;
|
|
||||||
const Square* pl = pos.piece_list(Us, PAWN);
|
|
||||||
|
|
||||||
// Loop through all pawns of the current color and score each pawn
|
|
||||||
while ((s = *pl++) != SQ_NONE)
|
|
||||||
{
|
|
||||||
assert(pos.piece_on(s) == make_piece(Us, PAWN));
|
|
||||||
|
|
||||||
f = file_of(s);
|
|
||||||
r = rank_of(s);
|
|
||||||
|
|
||||||
// This file cannot be half open
|
|
||||||
e->halfOpenFiles[Us] &= ~(1 << f);
|
|
||||||
|
|
||||||
// Our rank plus previous one. Used for chain detection
|
|
||||||
b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1));
|
|
||||||
|
|
||||||
// Flag the pawn as passed, isolated, doubled or member of a pawn
|
|
||||||
// chain (but not the backward one).
|
|
||||||
chain = ourPawns & adjacent_files_bb(f) & b;
|
|
||||||
isolated = !(ourPawns & adjacent_files_bb(f));
|
|
||||||
doubled = ourPawns & forward_bb(Us, s);
|
|
||||||
opposed = theirPawns & forward_bb(Us, s);
|
|
||||||
passed = !(theirPawns & passed_pawn_mask(Us, s));
|
|
||||||
|
|
||||||
// Test for backward pawn
|
|
||||||
backward = false;
|
|
||||||
|
|
||||||
// If the pawn is passed, isolated, or member of a pawn chain it cannot
|
|
||||||
// be backward. If there are friendly pawns behind on adjacent files
|
|
||||||
// or if can capture an enemy pawn it cannot be backward either.
|
|
||||||
if ( !(passed | isolated | chain)
|
|
||||||
&& !(ourPawns & attack_span_mask(Them, s))
|
|
||||||
&& !(pos.attacks_from<PAWN>(s, Us) & theirPawns))
|
|
||||||
{
|
|
||||||
// We now know that there are no friendly pawns beside or behind this
|
|
||||||
// pawn on adjacent files. We now check whether the pawn is
|
|
||||||
// backward by looking in the forward direction on the adjacent
|
|
||||||
// files, and seeing whether we meet a friendly or an enemy pawn first.
|
|
||||||
b = pos.attacks_from<PAWN>(s, Us);
|
|
||||||
|
|
||||||
// Note that we are sure to find something because pawn is not passed
|
|
||||||
// nor isolated, so loop is potentially infinite, but it isn't.
|
|
||||||
while (!(b & (ourPawns | theirPawns)))
|
|
||||||
Us == WHITE ? b <<= 8 : b >>= 8;
|
|
||||||
|
|
||||||
// The friendly pawn needs to be at least two ranks closer than the
|
|
||||||
// enemy pawn in order to help the potentially backward pawn advance.
|
|
||||||
backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(opposed | passed | (attack_span_mask(Us, s) & theirPawns));
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// pawn on adjacent files is higher or equal than the number of
|
|
||||||
// enemy pawns in the forward direction on the adjacent files.
|
|
||||||
candidate = !(opposed | passed | backward | isolated)
|
|
||||||
&& (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != 0
|
|
||||||
&& popcount<Max15>(b) >= popcount<Max15>(attack_span_mask(Us, s) & theirPawns);
|
|
||||||
|
|
||||||
// Passed pawns will be properly scored in evaluation because we need
|
|
||||||
// full attack info to evaluate passed pawns. Only the frontmost passed
|
|
||||||
// pawn on each file is considered a true passed pawn.
|
|
||||||
if (passed && !doubled)
|
|
||||||
e->passedPawns[Us] |= s;
|
|
||||||
|
|
||||||
// Score this pawn
|
|
||||||
if (isolated)
|
|
||||||
value -= IsolatedPawnPenalty[opposed][f];
|
|
||||||
|
|
||||||
if (doubled)
|
|
||||||
value -= DoubledPawnPenalty[opposed][f];
|
|
||||||
|
|
||||||
if (backward)
|
|
||||||
value -= BackwardPawnPenalty[opposed][f];
|
|
||||||
|
|
||||||
if (chain)
|
|
||||||
value += ChainBonus[f];
|
|
||||||
|
|
||||||
if (candidate)
|
|
||||||
value += CandidateBonus[relative_rank(Us, s)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// PawnEntry::shelter_storm() calculates shelter and storm penalties for the file
|
|
||||||
/// the king is on, as well as the two adjacent files.
|
/// the king is on, as well as the two adjacent files.
|
||||||
|
|
||||||
template<Color Us>
|
template<Color Us>
|
||||||
Value PawnEntry::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);
|
||||||
|
|
||||||
@@ -234,7 +237,7 @@ Value PawnEntry::shelter_storm(const Position& pos, Square ksq) {
|
|||||||
Rank rkUs, rkThem;
|
Rank rkUs, rkThem;
|
||||||
File kf = file_of(ksq);
|
File kf = file_of(ksq);
|
||||||
|
|
||||||
kf = (kf == FILE_A) ? kf++ : (kf == FILE_H) ? kf-- : kf;
|
kf = (kf == FILE_A) ? FILE_B : (kf == FILE_H) ? FILE_G : kf;
|
||||||
|
|
||||||
for (int f = kf - 1; f <= kf + 1; f++)
|
for (int f = kf - 1; f <= kf + 1; f++)
|
||||||
{
|
{
|
||||||
@@ -253,11 +256,11 @@ Value PawnEntry::shelter_storm(const Position& pos, Square ksq) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// PawnEntry::update_safety() calculates and caches a bonus for king safety. It is
|
/// Entry::update_safety() calculates and caches a bonus for king safety. It is
|
||||||
/// called only when king square changes, about 20% of total king_safety() calls.
|
/// called only when king square changes, about 20% of total king_safety() calls.
|
||||||
|
|
||||||
template<Color Us>
|
template<Color Us>
|
||||||
Score PawnEntry::update_safety(const Position& pos, Square ksq) {
|
Score Entry::update_safety(const Position& pos, Square ksq) {
|
||||||
|
|
||||||
kingSquares[Us] = ksq;
|
kingSquares[Us] = ksq;
|
||||||
castleRights[Us] = pos.can_castle(Us);
|
castleRights[Us] = pos.can_castle(Us);
|
||||||
@@ -283,5 +286,7 @@ Score PawnEntry::update_safety(const Position& pos, Square ksq) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Explicit template instantiation
|
// Explicit template instantiation
|
||||||
template Score PawnEntry::update_safety<WHITE>(const Position& pos, Square ksq);
|
template Score Entry::update_safety<WHITE>(const Position& pos, Square ksq);
|
||||||
template Score PawnEntry::update_safety<BLACK>(const Position& pos, Square ksq);
|
template Score Entry::update_safety<BLACK>(const Position& pos, Square ksq);
|
||||||
|
|
||||||
|
} // 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -24,31 +24,31 @@
|
|||||||
#include "position.h"
|
#include "position.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
const int PawnTableSize = 16384;
|
namespace Pawns {
|
||||||
|
|
||||||
/// PawnEntry is a class which contains various information about a pawn
|
/// Pawns::Entry contains various information about a pawn structure. Currently,
|
||||||
/// structure. Currently, it only includes a middle game and an end game
|
/// it only includes a middle game and end game pawn structure evaluation, and a
|
||||||
/// pawn structure evaluation, and a bitboard of passed pawns. We may want
|
/// bitboard of passed pawns. We may want to add further information in the future.
|
||||||
/// to add further information in the future. A lookup to the pawn hash
|
/// A lookup to the pawn hash table (performed by calling the probe function)
|
||||||
/// table (performed by calling the probe method in a PawnTable object)
|
/// returns a pointer to an Entry object.
|
||||||
/// returns a pointer to a PawnEntry object.
|
|
||||||
|
|
||||||
class PawnEntry {
|
struct Entry {
|
||||||
|
|
||||||
friend struct PawnTable;
|
Score pawns_value() const { return value; }
|
||||||
|
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
|
||||||
public:
|
Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
|
||||||
Score pawns_value() const;
|
int file_is_half_open(Color c, File f) const { return halfOpenFiles[c] & (1 << int(f)); }
|
||||||
Bitboard pawn_attacks(Color c) const;
|
int has_open_file_to_left(Color c, File f) const { return halfOpenFiles[c] & ((1 << int(f)) - 1); }
|
||||||
Bitboard passed_pawns(Color c) const;
|
int has_open_file_to_right(Color c, File f) const { return halfOpenFiles[c] & ~((1 << int(f+1)) - 1); }
|
||||||
int file_is_half_open(Color c, File f) const;
|
int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(BlackSquares & s)]; }
|
||||||
int has_open_file_to_left(Color c, File f) const;
|
|
||||||
int has_open_file_to_right(Color c, File f) const;
|
|
||||||
|
|
||||||
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 && castleRights[Us] == pos.can_castle(Us)
|
||||||
|
? kingSafety[Us] : update_safety<Us>(pos, ksq);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
template<Color Us>
|
template<Color Us>
|
||||||
Score update_safety(const Position& pos, Square ksq);
|
Score update_safety(const Position& pos, Square ksq);
|
||||||
|
|
||||||
@@ -56,60 +56,21 @@ private:
|
|||||||
Value shelter_storm(const Position& pos, Square ksq);
|
Value shelter_storm(const Position& pos, Square ksq);
|
||||||
|
|
||||||
Key key;
|
Key key;
|
||||||
Bitboard passedPawns[2];
|
Bitboard passedPawns[COLOR_NB];
|
||||||
Bitboard pawnAttacks[2];
|
Bitboard pawnAttacks[COLOR_NB];
|
||||||
Square kingSquares[2];
|
Square kingSquares[COLOR_NB];
|
||||||
int minKPdistance[2];
|
int minKPdistance[COLOR_NB];
|
||||||
int castleRights[2];
|
int castleRights[COLOR_NB];
|
||||||
Score value;
|
Score value;
|
||||||
int halfOpenFiles[2];
|
int halfOpenFiles[COLOR_NB];
|
||||||
Score kingSafety[2];
|
Score kingSafety[COLOR_NB];
|
||||||
|
int pawnsOnSquares[COLOR_NB][COLOR_NB];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef HashTable<Entry, 16384> Table;
|
||||||
|
|
||||||
/// The PawnTable class represents a pawn hash table. The most important
|
Entry* probe(const Position& pos, Table& entries);
|
||||||
/// method is probe, which returns a pointer to a PawnEntry object.
|
|
||||||
|
|
||||||
struct PawnTable {
|
|
||||||
|
|
||||||
PawnEntry* probe(const Position& pos);
|
|
||||||
|
|
||||||
template<Color Us>
|
|
||||||
static Score evaluate_pawns(const Position& pos, Bitboard ourPawns,
|
|
||||||
Bitboard theirPawns, PawnEntry* e);
|
|
||||||
|
|
||||||
HashTable<PawnEntry, PawnTableSize> entries;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
inline Score PawnEntry::pawns_value() const {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Bitboard PawnEntry::pawn_attacks(Color c) const {
|
|
||||||
return pawnAttacks[c];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Bitboard PawnEntry::passed_pawns(Color c) const {
|
|
||||||
return passedPawns[c];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int PawnEntry::file_is_half_open(Color c, File f) const {
|
|
||||||
return halfOpenFiles[c] & (1 << int(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int PawnEntry::has_open_file_to_left(Color c, File f) const {
|
|
||||||
return halfOpenFiles[c] & ((1 << int(f)) - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int PawnEntry::has_open_file_to_right(Color c, File f) const {
|
|
||||||
return halfOpenFiles[c] & ~((1 << int(f+1)) - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<Color Us>
|
|
||||||
inline Score PawnEntry::king_safety(const Position& pos, Square ksq) {
|
|
||||||
return kingSquares[Us] == ksq && castleRights[Us] == pos.can_castle(Us)
|
|
||||||
? kingSafety[Us] : update_safety<Us>(pos, ksq);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !defined(PAWNS_H_INCLUDED)
|
#endif // !defined(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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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,6 +40,7 @@ typedef unsigned __int64 uint64_t;
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
# include <inttypes.h>
|
# include <inttypes.h>
|
||||||
|
# include <unistd.h> // Used by sysconf(_SC_NPROCESSORS_ONLN)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(_WIN64) // Linux - Unix
|
#if !defined(_WIN32) && !defined(_WIN64) // Linux - Unix
|
||||||
@@ -92,6 +93,9 @@ 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
|
||||||
|
inline DWORD* dwWin9xKludge() { static DWORD dw; return &dw; }
|
||||||
|
|
||||||
# define lock_init(x) InitializeCriticalSection(&(x))
|
# define lock_init(x) InitializeCriticalSection(&(x))
|
||||||
# define lock_grab(x) EnterCriticalSection(&(x))
|
# define lock_grab(x) EnterCriticalSection(&(x))
|
||||||
# define lock_release(x) LeaveCriticalSection(&(x))
|
# define lock_release(x) LeaveCriticalSection(&(x))
|
||||||
@@ -101,7 +105,7 @@ typedef HANDLE NativeHandle;
|
|||||||
# define cond_signal(x) SetEvent(x)
|
# define cond_signal(x) SetEvent(x)
|
||||||
# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(x, INFINITE); lock_grab(y); }
|
# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(x, INFINITE); lock_grab(y); }
|
||||||
# define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(x,z); lock_grab(y); }
|
# define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(x,z); lock_grab(y); }
|
||||||
# define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,NULL), x != NULL)
|
# define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,dwWin9xKludge()), x != NULL)
|
||||||
# define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); }
|
# define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); }
|
||||||
|
|
||||||
#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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -40,16 +41,16 @@ static const string PieceToChar(" PNBRQK pnbrqk");
|
|||||||
|
|
||||||
CACHE_LINE_ALIGNMENT
|
CACHE_LINE_ALIGNMENT
|
||||||
|
|
||||||
Score pieceSquareTable[16][64]; // [piece][square]
|
Score pieceSquareTable[PIECE_NB][SQUARE_NB];
|
||||||
Value PieceValue[2][18] = { // [Mg / Eg][piece / pieceType]
|
Value PieceValue[PHASE_NB][PIECE_NB] = {
|
||||||
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
|
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
|
||||||
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
|
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
|
||||||
|
|
||||||
namespace Zobrist {
|
namespace Zobrist {
|
||||||
|
|
||||||
Key psq[2][8][64]; // [color][pieceType][square / piece count]
|
Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
|
||||||
Key enpassant[8]; // [file]
|
Key enpassant[FILE_NB];
|
||||||
Key castle[16]; // [castleRight]
|
Key castle[CASTLE_RIGHT_NB];
|
||||||
Key side;
|
Key side;
|
||||||
Key exclusion;
|
Key exclusion;
|
||||||
|
|
||||||
@@ -86,10 +87,10 @@ void init() {
|
|||||||
|
|
||||||
for (PieceType pt = PAWN; pt <= KING; pt++)
|
for (PieceType pt = PAWN; pt <= KING; pt++)
|
||||||
{
|
{
|
||||||
PieceValue[Mg][make_piece(BLACK, pt)] = PieceValue[Mg][pt];
|
PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt];
|
||||||
PieceValue[Eg][make_piece(BLACK, pt)] = PieceValue[Eg][pt];
|
PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt];
|
||||||
|
|
||||||
Score v = make_score(PieceValue[Mg][pt], PieceValue[Eg][pt]);
|
Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]);
|
||||||
|
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
||||||
{
|
{
|
||||||
@@ -172,11 +173,11 @@ Position& Position::operator=(const Position& pos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::from_fen() initializes the position object with the given FEN
|
/// Position::set() initializes the position object with the given FEN string.
|
||||||
/// string. This function is not very robust - make sure that input FENs are
|
/// This function is not very robust - make sure that input FENs are correct,
|
||||||
/// correct (this is assumed to be the responsibility of the GUI).
|
/// this is assumed to be the responsibility of the GUI.
|
||||||
|
|
||||||
void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
|
void Position::set(const string& fenStr, bool isChess960, Thread* th) {
|
||||||
/*
|
/*
|
||||||
A FEN string defines a particular position using only the ASCII character set.
|
A FEN string defines a particular position using only the ASCII character set.
|
||||||
|
|
||||||
@@ -214,13 +215,13 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
|
|||||||
char col, row, token;
|
char col, row, token;
|
||||||
size_t p;
|
size_t p;
|
||||||
Square sq = SQ_A8;
|
Square sq = SQ_A8;
|
||||||
std::istringstream fen(fenStr);
|
std::istringstream ss(fenStr);
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
fen >> std::noskipws;
|
ss >> std::noskipws;
|
||||||
|
|
||||||
// 1. Piece placement
|
// 1. Piece placement
|
||||||
while ((fen >> token) && !isspace(token))
|
while ((ss >> token) && !isspace(token))
|
||||||
{
|
{
|
||||||
if (isdigit(token))
|
if (isdigit(token))
|
||||||
sq += Square(token - '0'); // Advance the given number of files
|
sq += Square(token - '0'); // Advance the given number of files
|
||||||
@@ -236,16 +237,16 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. Active color
|
// 2. Active color
|
||||||
fen >> token;
|
ss >> token;
|
||||||
sideToMove = (token == 'w' ? WHITE : BLACK);
|
sideToMove = (token == 'w' ? WHITE : BLACK);
|
||||||
fen >> token;
|
ss >> token;
|
||||||
|
|
||||||
// 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
|
// 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
|
||||||
// Shredder-FEN that uses the letters of the columns on which the rooks began
|
// Shredder-FEN that uses the letters of the columns on which the rooks began
|
||||||
// the game instead of KQkq and also X-FEN standard that, in case of Chess960,
|
// the game instead of KQkq and also X-FEN standard that, in case of Chess960,
|
||||||
// if an inner rook is associated with the castling right, the castling tag is
|
// if an inner rook is associated with the castling right, the castling tag is
|
||||||
// replaced by the file letter of the involved rook, as for the Shredder-FEN.
|
// replaced by the file letter of the involved rook, as for the Shredder-FEN.
|
||||||
while ((fen >> token) && !isspace(token))
|
while ((ss >> token) && !isspace(token))
|
||||||
{
|
{
|
||||||
Square rsq;
|
Square rsq;
|
||||||
Color c = islower(token) ? BLACK : WHITE;
|
Color c = islower(token) ? BLACK : WHITE;
|
||||||
@@ -268,8 +269,8 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. En passant square. Ignore if no pawn capture is possible
|
// 4. En passant square. Ignore if no pawn capture is possible
|
||||||
if ( ((fen >> col) && (col >= 'a' && col <= 'h'))
|
if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
|
||||||
&& ((fen >> row) && (row == '3' || row == '6')))
|
&& ((ss >> row) && (row == '3' || row == '6')))
|
||||||
{
|
{
|
||||||
st->epSquare = File(col - 'a') | Rank(row - '1');
|
st->epSquare = File(col - 'a') | Rank(row - '1');
|
||||||
|
|
||||||
@@ -278,11 +279,11 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 5-6. Halfmove clock and fullmove number
|
// 5-6. Halfmove clock and fullmove number
|
||||||
fen >> std::skipws >> st->rule50 >> startPosPly;
|
ss >> std::skipws >> st->rule50 >> gamePly;
|
||||||
|
|
||||||
// Convert from fullmove starting from 1 to ply starting from 0,
|
// Convert from fullmove starting from 1 to ply starting from 0,
|
||||||
// handle also common incorrect FEN with fullmove = 0.
|
// handle also common incorrect FEN with fullmove = 0.
|
||||||
startPosPly = std::max(2 * (startPosPly - 1), 0) + int(sideToMove == BLACK);
|
gamePly = std::max(2 * (gamePly - 1), 0) + int(sideToMove == BLACK);
|
||||||
|
|
||||||
st->key = compute_key();
|
st->key = compute_key();
|
||||||
st->pawnKey = compute_pawn_key();
|
st->pawnKey = compute_pawn_key();
|
||||||
@@ -325,71 +326,64 @@ void Position::set_castle_right(Color c, Square rfrom) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::to_fen() returns a FEN representation of the position. In case
|
/// Position::fen() returns a FEN representation of the position. In case
|
||||||
/// of Chess960 the Shredder-FEN notation is used. Mainly a debugging function.
|
/// of Chess960 the Shredder-FEN notation is used. Mainly a debugging function.
|
||||||
|
|
||||||
const string Position::to_fen() const {
|
const string Position::fen() const {
|
||||||
|
|
||||||
std::ostringstream fen;
|
std::ostringstream ss;
|
||||||
Square sq;
|
|
||||||
int emptyCnt;
|
|
||||||
|
|
||||||
for (Rank rank = RANK_8; rank >= RANK_1; rank--)
|
for (Rank rank = RANK_8; rank >= RANK_1; rank--)
|
||||||
{
|
{
|
||||||
emptyCnt = 0;
|
|
||||||
|
|
||||||
for (File file = FILE_A; file <= FILE_H; file++)
|
for (File file = FILE_A; file <= FILE_H; file++)
|
||||||
{
|
{
|
||||||
sq = file | rank;
|
Square sq = file | rank;
|
||||||
|
|
||||||
if (is_empty(sq))
|
if (is_empty(sq))
|
||||||
emptyCnt++;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (emptyCnt > 0)
|
int emptyCnt = 1;
|
||||||
{
|
|
||||||
fen << emptyCnt;
|
for ( ; file < FILE_H && is_empty(sq++); file++)
|
||||||
emptyCnt = 0;
|
emptyCnt++;
|
||||||
}
|
|
||||||
fen << PieceToChar[piece_on(sq)];
|
ss << emptyCnt;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
ss << PieceToChar[piece_on(sq)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (emptyCnt > 0)
|
|
||||||
fen << emptyCnt;
|
|
||||||
|
|
||||||
if (rank > RANK_1)
|
if (rank > RANK_1)
|
||||||
fen << '/';
|
ss << '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
fen << (sideToMove == WHITE ? " w " : " b ");
|
ss << (sideToMove == WHITE ? " w " : " b ");
|
||||||
|
|
||||||
if (can_castle(WHITE_OO))
|
if (can_castle(WHITE_OO))
|
||||||
fen << (chess960 ? char(toupper(file_to_char(file_of(castle_rook_square(WHITE, KING_SIDE))))) : 'K');
|
ss << (chess960 ? file_to_char(file_of(castle_rook_square(WHITE, KING_SIDE)), false) : 'K');
|
||||||
|
|
||||||
if (can_castle(WHITE_OOO))
|
if (can_castle(WHITE_OOO))
|
||||||
fen << (chess960 ? char(toupper(file_to_char(file_of(castle_rook_square(WHITE, QUEEN_SIDE))))) : 'Q');
|
ss << (chess960 ? file_to_char(file_of(castle_rook_square(WHITE, QUEEN_SIDE)), false) : 'Q');
|
||||||
|
|
||||||
if (can_castle(BLACK_OO))
|
if (can_castle(BLACK_OO))
|
||||||
fen << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, KING_SIDE))) : 'k');
|
ss << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, KING_SIDE)), true) : 'k');
|
||||||
|
|
||||||
if (can_castle(BLACK_OOO))
|
if (can_castle(BLACK_OOO))
|
||||||
fen << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, QUEEN_SIDE))) : 'q');
|
ss << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, QUEEN_SIDE)), true) : 'q');
|
||||||
|
|
||||||
if (st->castleRights == CASTLES_NONE)
|
if (st->castleRights == CASTLES_NONE)
|
||||||
fen << '-';
|
ss << '-';
|
||||||
|
|
||||||
fen << (ep_square() == SQ_NONE ? " - " : " " + square_to_string(ep_square()) + " ")
|
ss << (ep_square() == SQ_NONE ? " - " : " " + square_to_string(ep_square()) + " ")
|
||||||
<< st->rule50 << " " << 1 + (startPosPly - int(sideToMove == BLACK)) / 2;
|
<< st->rule50 << " " << 1 + (gamePly - int(sideToMove == BLACK)) / 2;
|
||||||
|
|
||||||
return fen.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::print() prints an ASCII representation of the position to
|
/// Position::pretty() returns an ASCII representation of the position to be
|
||||||
/// the standard output. If a move is given then also the san is printed.
|
/// printed to the standard output together with the move's san notation.
|
||||||
|
|
||||||
void Position::print(Move move) const {
|
const string Position::pretty(Move move) const {
|
||||||
|
|
||||||
const string dottedLine = "\n+---+---+---+---+---+---+---+---+";
|
const string dottedLine = "\n+---+---+---+---+---+---+---+---+";
|
||||||
const string twoRows = dottedLine + "\n| | . | | . | | . | | . |"
|
const string twoRows = dottedLine + "\n| | . | | . | | . | | . |"
|
||||||
@@ -397,19 +391,27 @@ void Position::print(Move move) const {
|
|||||||
|
|
||||||
string brd = twoRows + twoRows + twoRows + twoRows + dottedLine;
|
string brd = twoRows + twoRows + twoRows + twoRows + dottedLine;
|
||||||
|
|
||||||
sync_cout;
|
std::ostringstream ss;
|
||||||
|
|
||||||
if (move)
|
if (move)
|
||||||
{
|
ss << "\nMove: " << (sideToMove == BLACK ? ".." : "")
|
||||||
Position p(*this);
|
<< move_to_san(*const_cast<Position*>(this), move);
|
||||||
cout << "\nMove is: " << (sideToMove == BLACK ? ".." : "") << move_to_san(p, move);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Square sq = SQ_A1; sq <= SQ_H8; sq++)
|
for (Square sq = SQ_A1; sq <= SQ_H8; sq++)
|
||||||
if (piece_on(sq) != NO_PIECE)
|
if (piece_on(sq) != NO_PIECE)
|
||||||
brd[513 - 68*rank_of(sq) + 4*file_of(sq)] = PieceToChar[piece_on(sq)];
|
brd[513 - 68*rank_of(sq) + 4*file_of(sq)] = PieceToChar[piece_on(sq)];
|
||||||
|
|
||||||
cout << brd << "\nFen is: " << to_fen() << "\nKey is: " << st->key << sync_endl;
|
ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase
|
||||||
|
<< std::setfill('0') << std::setw(16) << st->key << "\nCheckers: ";
|
||||||
|
|
||||||
|
for (Bitboard b = checkers(); b; )
|
||||||
|
ss << square_to_string(pop_lsb(&b)) << " ";
|
||||||
|
|
||||||
|
ss << "\nLegal moves: ";
|
||||||
|
for (MoveList<LEGAL> ml(*this); !ml.end(); ++ml)
|
||||||
|
ss << move_to_san(*const_cast<Position*>(this), ml.move()) << " ";
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -475,37 +477,6 @@ Bitboard Position::attacks_from(Piece p, Square s, Bitboard occ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::move_attacks_square() tests whether a move from the current
|
|
||||||
/// position attacks a given square.
|
|
||||||
|
|
||||||
bool Position::move_attacks_square(Move m, Square s) const {
|
|
||||||
|
|
||||||
assert(is_ok(m));
|
|
||||||
assert(is_ok(s));
|
|
||||||
|
|
||||||
Bitboard occ, xray;
|
|
||||||
Square from = from_sq(m);
|
|
||||||
Square to = to_sq(m);
|
|
||||||
Piece piece = piece_moved(m);
|
|
||||||
|
|
||||||
assert(!is_empty(from));
|
|
||||||
|
|
||||||
// Update occupancy as if the piece is moving
|
|
||||||
occ = pieces() ^ from ^ to;
|
|
||||||
|
|
||||||
// The piece moved in 'to' attacks the square 's' ?
|
|
||||||
if (attacks_from(piece, to, occ) & s)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Scan for possible X-ray attackers behind the moved piece
|
|
||||||
xray = (attacks_bb< ROOK>(s, occ) & pieces(color_of(piece), QUEEN, ROOK))
|
|
||||||
| (attacks_bb<BISHOP>(s, occ) & pieces(color_of(piece), QUEEN, BISHOP));
|
|
||||||
|
|
||||||
// Verify attackers are triggered by our move and not already existing
|
|
||||||
return xray && (xray ^ (xray & attacks_from<QUEEN>(s)));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Position::pl_move_is_legal() tests whether a pseudo-legal move is legal
|
/// Position::pl_move_is_legal() tests whether a pseudo-legal move is legal
|
||||||
|
|
||||||
bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
|
bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
|
||||||
@@ -553,20 +524,6 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::move_is_legal() takes a random move and tests whether the move
|
|
||||||
/// is legal. This version is not very fast and should be used only in non
|
|
||||||
/// time-critical paths.
|
|
||||||
|
|
||||||
bool Position::move_is_legal(const Move m) const {
|
|
||||||
|
|
||||||
for (MoveList<LEGAL> ml(*this); !ml.end(); ++ml)
|
|
||||||
if (ml.move() == m)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Position::is_pseudo_legal() takes a random move and tests whether the move
|
/// Position::is_pseudo_legal() takes a random move and tests whether the move
|
||||||
/// is pseudo legal. It is used to validate moves from TT that can be corrupted
|
/// is pseudo legal. It is used to validate moves from TT that can be corrupted
|
||||||
/// due to SMP concurrent access or hash position key aliasing.
|
/// due to SMP concurrent access or hash position key aliasing.
|
||||||
@@ -574,14 +531,13 @@ bool Position::move_is_legal(const Move m) const {
|
|||||||
bool Position::is_pseudo_legal(const Move m) const {
|
bool Position::is_pseudo_legal(const Move m) const {
|
||||||
|
|
||||||
Color us = sideToMove;
|
Color us = sideToMove;
|
||||||
Color them = ~sideToMove;
|
|
||||||
Square from = from_sq(m);
|
Square from = from_sq(m);
|
||||||
Square to = to_sq(m);
|
Square to = to_sq(m);
|
||||||
Piece pc = piece_moved(m);
|
Piece pc = piece_moved(m);
|
||||||
|
|
||||||
// Use a slower but simpler function for uncommon cases
|
// Use a slower but simpler function for uncommon cases
|
||||||
if (type_of(m) != NORMAL)
|
if (type_of(m) != NORMAL)
|
||||||
return move_is_legal(m);
|
return MoveList<LEGAL>(*this).contains(m);
|
||||||
|
|
||||||
// Is not a promotion, so promotion piece must be empty
|
// Is not a promotion, so promotion piece must be empty
|
||||||
if (promotion_type(m) - 2 != NO_PIECE_TYPE)
|
if (promotion_type(m) - 2 != NO_PIECE_TYPE)
|
||||||
@@ -593,7 +549,7 @@ bool Position::is_pseudo_legal(const Move m) const {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// The destination square cannot be occupied by a friendly piece
|
// The destination square cannot be occupied by a friendly piece
|
||||||
if (color_of(piece_on(to)) == us)
|
if (piece_on(to) != NO_PIECE && color_of(piece_on(to)) == us)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Handle the special case of a pawn move
|
// Handle the special case of a pawn move
|
||||||
@@ -619,7 +575,7 @@ bool Position::is_pseudo_legal(const Move m) const {
|
|||||||
case DELTA_SE:
|
case DELTA_SE:
|
||||||
// Capture. The destination square must be occupied by an enemy
|
// Capture. The destination square must be occupied by an enemy
|
||||||
// piece (en passant captures was handled earlier).
|
// piece (en passant captures was handled earlier).
|
||||||
if (color_of(piece_on(to)) != them)
|
if (piece_on(to) == NO_PIECE || color_of(piece_on(to)) != ~us)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// From and to files must be one file apart, avoids a7h5
|
// From and to files must be one file apart, avoids a7h5
|
||||||
@@ -664,18 +620,16 @@ bool Position::is_pseudo_legal(const Move m) const {
|
|||||||
// Evasions generator already takes care to avoid some kind of illegal moves
|
// Evasions generator already takes care to avoid some kind of illegal moves
|
||||||
// and pl_move_is_legal() relies on this. So we have to take care that the
|
// and pl_move_is_legal() relies on this. So we have to take care that the
|
||||||
// same kind of moves are filtered out here.
|
// same kind of moves are filtered out here.
|
||||||
if (in_check())
|
if (checkers())
|
||||||
{
|
{
|
||||||
if (type_of(pc) != KING)
|
if (type_of(pc) != KING)
|
||||||
{
|
{
|
||||||
Bitboard b = checkers();
|
// Double check? In this case a king move is required
|
||||||
Square checksq = pop_lsb(&b);
|
if (more_than_one(checkers()))
|
||||||
|
|
||||||
if (b) // double check ? In this case a king move is required
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Our move must be a blocking evasion or a capture of the checking piece
|
// Our move must be a blocking evasion or a capture of the checking piece
|
||||||
if (!((between_bb(checksq, king_square(us)) | checkers()) & to))
|
if (!((between_bb(lsb(checkers()), king_square(us)) | checkers()) & to))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// In case of king moves under check we have to remove king so to catch
|
// In case of king moves under check we have to remove king so to catch
|
||||||
@@ -720,15 +674,16 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
|
|||||||
Color us = sideToMove;
|
Color us = sideToMove;
|
||||||
Square ksq = king_square(~us);
|
Square ksq = king_square(~us);
|
||||||
|
|
||||||
// Promotion with check ?
|
switch (type_of(m))
|
||||||
if (type_of(m) == PROMOTION)
|
{
|
||||||
|
case PROMOTION:
|
||||||
return attacks_from(Piece(promotion_type(m)), to, pieces() ^ from) & ksq;
|
return attacks_from(Piece(promotion_type(m)), to, pieces() ^ from) & ksq;
|
||||||
|
|
||||||
// En passant capture with check ? We have already handled the case
|
// En passant capture with check ? We have already handled the case
|
||||||
// of direct checks and ordinary discovered check, the only case we
|
// of direct checks and ordinary discovered check, the only case we
|
||||||
// need to handle is the unusual case of a discovered check through
|
// need to handle is the unusual case of a discovered check through
|
||||||
// the captured pawn.
|
// the captured pawn.
|
||||||
if (type_of(m) == ENPASSANT)
|
case ENPASSANT:
|
||||||
{
|
{
|
||||||
Square capsq = file_of(to) | rank_of(from);
|
Square capsq = file_of(to) | rank_of(from);
|
||||||
Bitboard b = (pieces() ^ from ^ capsq) | to;
|
Bitboard b = (pieces() ^ from ^ capsq) | to;
|
||||||
@@ -736,9 +691,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
|
|||||||
return (attacks_bb< ROOK>(ksq, b) & pieces(us, QUEEN, ROOK))
|
return (attacks_bb< ROOK>(ksq, b) & pieces(us, QUEEN, ROOK))
|
||||||
| (attacks_bb<BISHOP>(ksq, b) & pieces(us, QUEEN, BISHOP));
|
| (attacks_bb<BISHOP>(ksq, b) & pieces(us, QUEEN, BISHOP));
|
||||||
}
|
}
|
||||||
|
case CASTLE:
|
||||||
// Castling with check ?
|
|
||||||
if (type_of(m) == CASTLE)
|
|
||||||
{
|
{
|
||||||
Square kfrom = from;
|
Square kfrom = from;
|
||||||
Square rfrom = to; // 'King captures the rook' notation
|
Square rfrom = to; // 'King captures the rook' notation
|
||||||
@@ -748,8 +701,10 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
|
|||||||
|
|
||||||
return attacks_bb<ROOK>(rto, b) & ksq;
|
return attacks_bb<ROOK>(rto, b) & ksq;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
return false;
|
assert(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -772,9 +727,9 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||||||
Key k = st->key;
|
Key k = st->key;
|
||||||
|
|
||||||
// Copy some fields of old state to our new StateInfo object except the ones
|
// Copy some fields of old state to our new StateInfo object except the ones
|
||||||
// which are recalculated from scratch anyway, then switch our state pointer
|
// which are going to be recalculated from scratch anyway, then switch our state
|
||||||
// to point to the new, ready to be updated, state.
|
// pointer to point to the new, ready to be updated, state.
|
||||||
memcpy(&newSt, st, sizeof(ReducedStateInfo));
|
memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t));
|
||||||
|
|
||||||
newSt.previous = st;
|
newSt.previous = st;
|
||||||
st = &newSt;
|
st = &newSt;
|
||||||
@@ -782,18 +737,12 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||||||
// Update side to move
|
// Update side to move
|
||||||
k ^= Zobrist::side;
|
k ^= Zobrist::side;
|
||||||
|
|
||||||
// Increment the 50 moves rule draw counter. Resetting it to zero in the
|
// Increment ply counters.In particular rule50 will be later reset it to zero
|
||||||
// case of a capture or a pawn move is taken care of later.
|
// in case of a capture or a pawn move.
|
||||||
|
gamePly++;
|
||||||
st->rule50++;
|
st->rule50++;
|
||||||
st->pliesFromNull++;
|
st->pliesFromNull++;
|
||||||
|
|
||||||
if (type_of(m) == CASTLE)
|
|
||||||
{
|
|
||||||
st->key = k;
|
|
||||||
do_castle_move<true>(m);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Color us = sideToMove;
|
Color us = sideToMove;
|
||||||
Color them = ~us;
|
Color them = ~us;
|
||||||
Square from = from_sq(m);
|
Square from = from_sq(m);
|
||||||
@@ -803,9 +752,25 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||||||
PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to));
|
PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to));
|
||||||
|
|
||||||
assert(color_of(piece) == us);
|
assert(color_of(piece) == us);
|
||||||
assert(color_of(piece_on(to)) != us);
|
assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE);
|
||||||
assert(capture != KING);
|
assert(capture != KING);
|
||||||
|
|
||||||
|
if (type_of(m) == CASTLE)
|
||||||
|
{
|
||||||
|
assert(piece == make_piece(us, KING));
|
||||||
|
|
||||||
|
bool kingSide = to > from;
|
||||||
|
Square rfrom = to; // Castle is encoded as "king captures friendly rook"
|
||||||
|
Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
|
||||||
|
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
|
||||||
|
capture = NO_PIECE_TYPE;
|
||||||
|
|
||||||
|
do_castle(from, to, rfrom, rto);
|
||||||
|
|
||||||
|
st->psqScore += psq_delta(make_piece(us, ROOK), rfrom, rto);
|
||||||
|
k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
|
||||||
|
}
|
||||||
|
|
||||||
if (capture)
|
if (capture)
|
||||||
{
|
{
|
||||||
Square capsq = to;
|
Square capsq = to;
|
||||||
@@ -830,7 +795,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||||||
st->pawnKey ^= Zobrist::psq[them][PAWN][capsq];
|
st->pawnKey ^= Zobrist::psq[them][PAWN][capsq];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
st->npMaterial[them] -= PieceValue[Mg][capture];
|
st->npMaterial[them] -= PieceValue[MG][capture];
|
||||||
|
|
||||||
// Remove the captured piece
|
// Remove the captured piece
|
||||||
byTypeBB[ALL_PIECES] ^= capsq;
|
byTypeBB[ALL_PIECES] ^= capsq;
|
||||||
@@ -840,7 +805,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||||||
// Update piece list, move the last piece at index[capsq] position and
|
// Update piece list, move the last piece at index[capsq] position and
|
||||||
// shrink the list.
|
// shrink the list.
|
||||||
//
|
//
|
||||||
// WARNING: This is a not revresible operation. When we will reinsert the
|
// WARNING: This is a not reversible operation. When we will reinsert the
|
||||||
// captured piece in undo_move() we will put it at the end of the list and
|
// captured piece in undo_move() we will put it at the end of the list and
|
||||||
// not in its original place, it means index[] and pieceList[] are not
|
// not in its original place, it means index[] and pieceList[] are not
|
||||||
// guaranteed to be invariant to a do_move() + undo_move() sequence.
|
// guaranteed to be invariant to a do_move() + undo_move() sequence.
|
||||||
@@ -849,9 +814,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||||||
pieceList[them][capture][index[lastSquare]] = lastSquare;
|
pieceList[them][capture][index[lastSquare]] = lastSquare;
|
||||||
pieceList[them][capture][pieceCount[them][capture]] = SQ_NONE;
|
pieceList[them][capture][pieceCount[them][capture]] = SQ_NONE;
|
||||||
|
|
||||||
// Update hash keys
|
// Update material hash key and prefetch access to materialTable
|
||||||
k ^= Zobrist::psq[them][capture][capsq];
|
k ^= Zobrist::psq[them][capture][capsq];
|
||||||
st->materialKey ^= Zobrist::psq[them][capture][pieceCount[them][capture]];
|
st->materialKey ^= Zobrist::psq[them][capture][pieceCount[them][capture]];
|
||||||
|
prefetch((char*)thisThread->materialTable[st->materialKey]);
|
||||||
|
|
||||||
// Update incremental scores
|
// Update incremental scores
|
||||||
st->psqScore -= pieceSquareTable[make_piece(them, capture)][capsq];
|
st->psqScore -= pieceSquareTable[make_piece(them, capture)][capsq];
|
||||||
@@ -878,22 +844,25 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||||||
st->castleRights &= ~cr;
|
st->castleRights &= ~cr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefetch TT access as soon as we know key is updated
|
// Prefetch TT access as soon as we know the new hash key
|
||||||
prefetch((char*)TT.first_entry(k));
|
prefetch((char*)TT.first_entry(k));
|
||||||
|
|
||||||
// Move the piece
|
// Move the piece. The tricky Chess960 castle is handled earlier
|
||||||
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
|
if (type_of(m) != CASTLE)
|
||||||
byTypeBB[ALL_PIECES] ^= from_to_bb;
|
{
|
||||||
byTypeBB[pt] ^= from_to_bb;
|
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
|
||||||
byColorBB[us] ^= from_to_bb;
|
byTypeBB[ALL_PIECES] ^= from_to_bb;
|
||||||
|
byTypeBB[pt] ^= from_to_bb;
|
||||||
|
byColorBB[us] ^= from_to_bb;
|
||||||
|
|
||||||
board[to] = board[from];
|
board[from] = NO_PIECE;
|
||||||
board[from] = NO_PIECE;
|
board[to] = piece;
|
||||||
|
|
||||||
// Update piece lists, index[from] is not updated and becomes stale. This
|
// Update piece lists, index[from] is not updated and becomes stale. This
|
||||||
// works as long as index[] is accessed just by known occupied squares.
|
// works as long as index[] is accessed just by known occupied squares.
|
||||||
index[to] = index[from];
|
index[to] = index[from];
|
||||||
pieceList[us][pt][index[to]] = to;
|
pieceList[us][pt][index[to]] = to;
|
||||||
|
}
|
||||||
|
|
||||||
// If the moving piece is a pawn do some special extra work
|
// If the moving piece is a pawn do some special extra work
|
||||||
if (pt == PAWN)
|
if (pt == PAWN)
|
||||||
@@ -938,20 +907,17 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||||||
- pieceSquareTable[make_piece(us, PAWN)][to];
|
- pieceSquareTable[make_piece(us, PAWN)][to];
|
||||||
|
|
||||||
// Update material
|
// Update material
|
||||||
st->npMaterial[us] += PieceValue[Mg][promotion];
|
st->npMaterial[us] += PieceValue[MG][promotion];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update pawn hash key
|
// Update pawn hash key and prefetch access to pawnsTable
|
||||||
st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to];
|
st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to];
|
||||||
|
prefetch((char*)thisThread->pawnsTable[st->pawnKey]);
|
||||||
|
|
||||||
// Reset rule 50 draw counter
|
// Reset rule 50 draw counter
|
||||||
st->rule50 = 0;
|
st->rule50 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefetch pawn and material hash tables
|
|
||||||
prefetch((char*)thisThread->pawnTable.entries[st->pawnKey]);
|
|
||||||
prefetch((char*)thisThread->materialTable.entries[st->materialKey]);
|
|
||||||
|
|
||||||
// Update incremental scores
|
// Update incremental scores
|
||||||
st->psqScore += psq_delta(piece, from, to);
|
st->psqScore += psq_delta(piece, from, to);
|
||||||
|
|
||||||
@@ -1001,22 +967,14 @@ void Position::undo_move(Move m) {
|
|||||||
|
|
||||||
sideToMove = ~sideToMove;
|
sideToMove = ~sideToMove;
|
||||||
|
|
||||||
if (type_of(m) == CASTLE)
|
|
||||||
{
|
|
||||||
do_castle_move<false>(m);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Color us = sideToMove;
|
Color us = sideToMove;
|
||||||
Color them = ~us;
|
Color them = ~us;
|
||||||
Square from = from_sq(m);
|
Square from = from_sq(m);
|
||||||
Square to = to_sq(m);
|
Square to = to_sq(m);
|
||||||
Piece piece = piece_on(to);
|
PieceType pt = type_of(piece_on(to));
|
||||||
PieceType pt = type_of(piece);
|
|
||||||
PieceType capture = st->capturedType;
|
PieceType capture = st->capturedType;
|
||||||
|
|
||||||
assert(is_empty(from));
|
assert(is_empty(from) || type_of(m) == CASTLE);
|
||||||
assert(color_of(piece) == us);
|
|
||||||
assert(capture != KING);
|
assert(capture != KING);
|
||||||
|
|
||||||
if (type_of(m) == PROMOTION)
|
if (type_of(m) == PROMOTION)
|
||||||
@@ -1044,19 +1002,32 @@ void Position::undo_move(Move m) {
|
|||||||
pt = PAWN;
|
pt = PAWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put the piece back at the source square
|
if (type_of(m) == CASTLE)
|
||||||
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
|
{
|
||||||
byTypeBB[ALL_PIECES] ^= from_to_bb;
|
bool kingSide = to > from;
|
||||||
byTypeBB[pt] ^= from_to_bb;
|
Square rfrom = to; // Castle is encoded as "king captures friendly rook"
|
||||||
byColorBB[us] ^= from_to_bb;
|
Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
|
||||||
|
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
|
||||||
|
capture = NO_PIECE_TYPE;
|
||||||
|
pt = KING;
|
||||||
|
do_castle(to, from, rto, rfrom);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Put the piece back at the source square
|
||||||
|
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
|
||||||
|
byTypeBB[ALL_PIECES] ^= from_to_bb;
|
||||||
|
byTypeBB[pt] ^= from_to_bb;
|
||||||
|
byColorBB[us] ^= from_to_bb;
|
||||||
|
|
||||||
board[from] = board[to];
|
board[to] = NO_PIECE;
|
||||||
board[to] = NO_PIECE;
|
board[from] = make_piece(us, pt);
|
||||||
|
|
||||||
// Update piece lists, index[to] is not updated and becomes stale. This
|
// Update piece lists, index[to] is not updated and becomes stale. This
|
||||||
// works as long as index[] is accessed just by known occupied squares.
|
// works as long as index[] is accessed just by known occupied squares.
|
||||||
index[from] = index[to];
|
index[from] = index[to];
|
||||||
pieceList[us][pt][index[from]] = from;
|
pieceList[us][pt][index[from]] = from;
|
||||||
|
}
|
||||||
|
|
||||||
if (capture)
|
if (capture)
|
||||||
{
|
{
|
||||||
@@ -1086,49 +1057,18 @@ void Position::undo_move(Move m) {
|
|||||||
|
|
||||||
// Finally point our state pointer back to the previous state
|
// Finally point our state pointer back to the previous state
|
||||||
st = st->previous;
|
st = st->previous;
|
||||||
|
gamePly--;
|
||||||
|
|
||||||
assert(pos_is_ok());
|
assert(pos_is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::do_castle_move() is a private method used to do/undo a castling
|
/// Position::do_castle() is a helper used to do/undo a castling move. This
|
||||||
/// move. Note that castling moves are encoded as "king captures friendly rook"
|
/// is a bit tricky, especially in Chess960.
|
||||||
/// moves, for instance white short castling in a non-Chess960 game is encoded
|
|
||||||
/// as e1h1.
|
|
||||||
template<bool Do>
|
|
||||||
void Position::do_castle_move(Move m) {
|
|
||||||
|
|
||||||
assert(is_ok(m));
|
void Position::do_castle(Square kfrom, Square kto, Square rfrom, Square rto) {
|
||||||
assert(type_of(m) == CASTLE);
|
|
||||||
|
|
||||||
Square kto, kfrom, rfrom, rto, kAfter, rAfter;
|
|
||||||
|
|
||||||
Color us = sideToMove;
|
Color us = sideToMove;
|
||||||
Square kBefore = from_sq(m);
|
|
||||||
Square rBefore = to_sq(m);
|
|
||||||
|
|
||||||
// Find after-castle squares for king and rook
|
|
||||||
if (rBefore > kBefore) // O-O
|
|
||||||
{
|
|
||||||
kAfter = relative_square(us, SQ_G1);
|
|
||||||
rAfter = relative_square(us, SQ_F1);
|
|
||||||
}
|
|
||||||
else // O-O-O
|
|
||||||
{
|
|
||||||
kAfter = relative_square(us, SQ_C1);
|
|
||||||
rAfter = relative_square(us, SQ_D1);
|
|
||||||
}
|
|
||||||
|
|
||||||
kfrom = Do ? kBefore : kAfter;
|
|
||||||
rfrom = Do ? rBefore : rAfter;
|
|
||||||
|
|
||||||
kto = Do ? kAfter : kBefore;
|
|
||||||
rto = Do ? rAfter : rBefore;
|
|
||||||
|
|
||||||
assert(piece_on(kfrom) == make_piece(us, KING));
|
|
||||||
assert(piece_on(rfrom) == make_piece(us, ROOK));
|
|
||||||
|
|
||||||
// Move the pieces, with some care; in chess960 could be kto == rfrom
|
|
||||||
Bitboard k_from_to_bb = SquareBB[kfrom] ^ SquareBB[kto];
|
Bitboard k_from_to_bb = SquareBB[kfrom] ^ SquareBB[kto];
|
||||||
Bitboard r_from_to_bb = SquareBB[rfrom] ^ SquareBB[rto];
|
Bitboard r_from_to_bb = SquareBB[rfrom] ^ SquareBB[rto];
|
||||||
byTypeBB[KING] ^= k_from_to_bb;
|
byTypeBB[KING] ^= k_from_to_bb;
|
||||||
@@ -1136,105 +1076,63 @@ void Position::do_castle_move(Move m) {
|
|||||||
byTypeBB[ALL_PIECES] ^= k_from_to_bb ^ r_from_to_bb;
|
byTypeBB[ALL_PIECES] ^= k_from_to_bb ^ r_from_to_bb;
|
||||||
byColorBB[us] ^= k_from_to_bb ^ r_from_to_bb;
|
byColorBB[us] ^= k_from_to_bb ^ r_from_to_bb;
|
||||||
|
|
||||||
// Update board
|
// Could be from == to, so first set NO_PIECE then KING and ROOK
|
||||||
Piece king = make_piece(us, KING);
|
|
||||||
Piece rook = make_piece(us, ROOK);
|
|
||||||
board[kfrom] = board[rfrom] = NO_PIECE;
|
board[kfrom] = board[rfrom] = NO_PIECE;
|
||||||
board[kto] = king;
|
board[kto] = make_piece(us, KING);
|
||||||
board[rto] = rook;
|
board[rto] = make_piece(us, ROOK);
|
||||||
|
|
||||||
// Update piece lists
|
// Could be kfrom == rto, so use a 'tmp' variable
|
||||||
pieceList[us][KING][index[kfrom]] = kto;
|
int tmp = index[kfrom];
|
||||||
pieceList[us][ROOK][index[rfrom]] = rto;
|
index[rto] = index[rfrom];
|
||||||
int tmp = index[rfrom]; // In Chess960 could be kto == rfrom
|
index[kto] = tmp;
|
||||||
index[kto] = index[kfrom];
|
pieceList[us][KING][index[kto]] = kto;
|
||||||
index[rto] = tmp;
|
pieceList[us][ROOK][index[rto]] = rto;
|
||||||
|
|
||||||
if (Do)
|
|
||||||
{
|
|
||||||
// Reset capture field
|
|
||||||
st->capturedType = NO_PIECE_TYPE;
|
|
||||||
|
|
||||||
// Update incremental scores
|
|
||||||
st->psqScore += psq_delta(king, kfrom, kto);
|
|
||||||
st->psqScore += psq_delta(rook, rfrom, rto);
|
|
||||||
|
|
||||||
// Update hash key
|
|
||||||
st->key ^= Zobrist::psq[us][KING][kfrom] ^ Zobrist::psq[us][KING][kto];
|
|
||||||
st->key ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
|
|
||||||
|
|
||||||
// Clear en passant square
|
|
||||||
if (st->epSquare != SQ_NONE)
|
|
||||||
{
|
|
||||||
st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
|
|
||||||
st->epSquare = SQ_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update castling rights
|
|
||||||
st->key ^= Zobrist::castle[st->castleRights & castleRightsMask[kfrom]];
|
|
||||||
st->castleRights &= ~castleRightsMask[kfrom];
|
|
||||||
|
|
||||||
// Update checkers BB
|
|
||||||
st->checkersBB = attackers_to(king_square(~us)) & pieces(us);
|
|
||||||
|
|
||||||
sideToMove = ~sideToMove;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
// Undo: point our state pointer back to the previous state
|
|
||||||
st = st->previous;
|
|
||||||
|
|
||||||
assert(pos_is_ok());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::do_null_move() is used to do/undo a "null move": It flips the side
|
/// Position::do(undo)_null_move() is used to do(undo) a "null move": It flips
|
||||||
/// to move and updates the hash key without executing any move on the board.
|
/// the side to move without executing any move on the board.
|
||||||
template<bool Do>
|
|
||||||
void Position::do_null_move(StateInfo& backupSt) {
|
|
||||||
|
|
||||||
assert(!in_check());
|
void Position::do_null_move(StateInfo& newSt) {
|
||||||
|
|
||||||
// Back up the information necessary to undo the null move to the supplied
|
assert(!checkers());
|
||||||
// StateInfo object. Note that differently from normal case here backupSt
|
|
||||||
// is actually used as a backup storage not as the new state. This reduces
|
|
||||||
// the number of fields to be copied.
|
|
||||||
StateInfo* src = Do ? st : &backupSt;
|
|
||||||
StateInfo* dst = Do ? &backupSt : st;
|
|
||||||
|
|
||||||
dst->key = src->key;
|
memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here
|
||||||
dst->epSquare = src->epSquare;
|
|
||||||
dst->psqScore = src->psqScore;
|
newSt.previous = st;
|
||||||
dst->rule50 = src->rule50;
|
st = &newSt;
|
||||||
dst->pliesFromNull = src->pliesFromNull;
|
|
||||||
|
if (st->epSquare != SQ_NONE)
|
||||||
|
{
|
||||||
|
st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
|
||||||
|
st->epSquare = SQ_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
st->key ^= Zobrist::side;
|
||||||
|
prefetch((char*)TT.first_entry(st->key));
|
||||||
|
|
||||||
|
st->rule50++;
|
||||||
|
st->pliesFromNull = 0;
|
||||||
|
|
||||||
sideToMove = ~sideToMove;
|
sideToMove = ~sideToMove;
|
||||||
|
|
||||||
if (Do)
|
|
||||||
{
|
|
||||||
if (st->epSquare != SQ_NONE)
|
|
||||||
st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
|
|
||||||
|
|
||||||
st->key ^= Zobrist::side;
|
|
||||||
prefetch((char*)TT.first_entry(st->key));
|
|
||||||
|
|
||||||
st->epSquare = SQ_NONE;
|
|
||||||
st->rule50++;
|
|
||||||
st->pliesFromNull = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(pos_is_ok());
|
assert(pos_is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicit template instantiations
|
void Position::undo_null_move() {
|
||||||
template void Position::do_null_move<false>(StateInfo& backupSt);
|
|
||||||
template void Position::do_null_move<true>(StateInfo& backupSt);
|
assert(!checkers());
|
||||||
|
|
||||||
|
st = st->previous;
|
||||||
|
sideToMove = ~sideToMove;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::see() is a static exchange evaluator: It tries to estimate the
|
/// Position::see() is a static exchange evaluator: It tries to estimate the
|
||||||
/// material gain or loss resulting from a move. There are three versions of
|
/// material gain or loss resulting from a move. Parameter 'asymmThreshold' takes
|
||||||
/// this function: One which takes a destination square as input, one takes a
|
/// tempi into account. If the side who initiated the capturing sequence does the
|
||||||
/// move, and one which takes a 'from' and a 'to' square. The function does
|
/// last capture, he loses a tempo and if the result is below 'asymmThreshold'
|
||||||
/// not yet understand promotions captures.
|
/// the capturing sequence is considered bad.
|
||||||
|
|
||||||
int Position::see_sign(Move m) const {
|
int Position::see_sign(Move m) const {
|
||||||
|
|
||||||
@@ -1243,13 +1141,13 @@ int Position::see_sign(Move m) const {
|
|||||||
// Early return if SEE cannot be negative because captured piece value
|
// Early return if SEE cannot be negative because captured piece value
|
||||||
// is not less then capturing one. Note that king moves always return
|
// is not less then capturing one. Note that king moves always return
|
||||||
// here because king midgame value is set to 0.
|
// here because king midgame value is set to 0.
|
||||||
if (PieceValue[Mg][piece_on(to_sq(m))] >= PieceValue[Mg][piece_moved(m)])
|
if (PieceValue[MG][piece_on(to_sq(m))] >= PieceValue[MG][piece_moved(m)])
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
return see(m);
|
return see(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Position::see(Move m) const {
|
int Position::see(Move m, int asymmThreshold) const {
|
||||||
|
|
||||||
Square from, to;
|
Square from, to;
|
||||||
Bitboard occupied, attackers, stmAttackers;
|
Bitboard occupied, attackers, stmAttackers;
|
||||||
@@ -1290,7 +1188,7 @@ int Position::see(Move m) const {
|
|||||||
stm = ~color_of(piece_on(from));
|
stm = ~color_of(piece_on(from));
|
||||||
stmAttackers = attackers & pieces(stm);
|
stmAttackers = attackers & pieces(stm);
|
||||||
if (!stmAttackers)
|
if (!stmAttackers)
|
||||||
return PieceValue[Mg][captured];
|
return PieceValue[MG][captured];
|
||||||
|
|
||||||
// The destination square is defended, which makes things rather more
|
// The destination square is defended, which makes things rather more
|
||||||
// difficult to compute. We proceed by building up a "swap list" containing
|
// difficult to compute. We proceed by building up a "swap list" containing
|
||||||
@@ -1298,14 +1196,14 @@ int Position::see(Move m) const {
|
|||||||
// destination square, where the sides alternately capture, and always
|
// destination square, where the sides alternately capture, and always
|
||||||
// capture with the least valuable piece. After each capture, we look for
|
// capture with the least valuable piece. After each capture, we look for
|
||||||
// new X-ray attacks from behind the capturing piece.
|
// new X-ray attacks from behind the capturing piece.
|
||||||
swapList[0] = PieceValue[Mg][captured];
|
swapList[0] = PieceValue[MG][captured];
|
||||||
captured = type_of(piece_on(from));
|
captured = type_of(piece_on(from));
|
||||||
|
|
||||||
do {
|
do {
|
||||||
assert(slIndex < 32);
|
assert(slIndex < 32);
|
||||||
|
|
||||||
// Add the new entry to the swap list
|
// Add the new entry to the swap list
|
||||||
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[Mg][captured];
|
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured];
|
||||||
slIndex++;
|
slIndex++;
|
||||||
|
|
||||||
// Locate and remove from 'occupied' the next least valuable attacker
|
// Locate and remove from 'occupied' the next least valuable attacker
|
||||||
@@ -1326,6 +1224,15 @@ int Position::see(Move m) const {
|
|||||||
|
|
||||||
} while (stmAttackers);
|
} while (stmAttackers);
|
||||||
|
|
||||||
|
// If we are doing asymmetric SEE evaluation and the same side does the first
|
||||||
|
// and the last capture, he loses a tempo and gain must be at least worth
|
||||||
|
// 'asymmThreshold', otherwise we replace the score with a very low value,
|
||||||
|
// before negamaxing.
|
||||||
|
if (asymmThreshold)
|
||||||
|
for (int i = 0; i < slIndex; i += 2)
|
||||||
|
if (swapList[i] < asymmThreshold)
|
||||||
|
swapList[i] = - QueenValueMg * 16;
|
||||||
|
|
||||||
// Having built the swap list, we negamax through it to find the best
|
// Having built the swap list, we negamax through it to find the best
|
||||||
// achievable score from the point of view of the side to move.
|
// achievable score from the point of view of the side to move.
|
||||||
while (--slIndex)
|
while (--slIndex)
|
||||||
@@ -1347,9 +1254,6 @@ void Position::clear() {
|
|||||||
for (int i = 0; i < 8; i++)
|
for (int i = 0; i < 8; i++)
|
||||||
for (int j = 0; j < 16; j++)
|
for (int j = 0; j < 16; j++)
|
||||||
pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE;
|
pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE;
|
||||||
|
|
||||||
for (Square sq = SQ_A1; sq <= SQ_H8; sq++)
|
|
||||||
board[sq] = NO_PIECE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1463,7 +1367,7 @@ Value Position::compute_non_pawn_material(Color c) const {
|
|||||||
Value value = VALUE_ZERO;
|
Value value = VALUE_ZERO;
|
||||||
|
|
||||||
for (PieceType pt = KNIGHT; pt <= QUEEN; pt++)
|
for (PieceType pt = KNIGHT; pt <= QUEEN; pt++)
|
||||||
value += piece_count(c, pt) * PieceValue[Mg][pt];
|
value += piece_count(c, pt) * PieceValue[MG][pt];
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -1472,7 +1376,6 @@ Value Position::compute_non_pawn_material(Color c) const {
|
|||||||
/// Position::is_draw() tests whether the position is drawn by material,
|
/// Position::is_draw() tests whether the position is drawn by material,
|
||||||
/// repetition, or the 50 moves rule. It does not detect stalemates, this
|
/// repetition, or the 50 moves rule. It does not detect stalemates, this
|
||||||
/// must be done by the search.
|
/// must be done by the search.
|
||||||
template<bool SkipRepetition>
|
|
||||||
bool Position::is_draw() const {
|
bool Position::is_draw() const {
|
||||||
|
|
||||||
// Draw by material?
|
// Draw by material?
|
||||||
@@ -1481,37 +1384,30 @@ bool Position::is_draw() const {
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Draw by the 50 moves rule?
|
// Draw by the 50 moves rule?
|
||||||
if (st->rule50 > 99 && (!in_check() || MoveList<LEGAL>(*this).size()))
|
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Draw by repetition?
|
// Draw by repetition?
|
||||||
if (!SkipRepetition)
|
int i = 4, e = std::min(st->rule50, st->pliesFromNull);
|
||||||
|
|
||||||
|
if (i <= e)
|
||||||
{
|
{
|
||||||
int i = 4, e = std::min(st->rule50, st->pliesFromNull);
|
StateInfo* stp = st->previous->previous;
|
||||||
|
|
||||||
if (i <= e)
|
do {
|
||||||
{
|
stp = stp->previous->previous;
|
||||||
StateInfo* stp = st->previous->previous;
|
|
||||||
|
|
||||||
do {
|
if (stp->key == st->key)
|
||||||
stp = stp->previous->previous;
|
return true;
|
||||||
|
|
||||||
if (stp->key == st->key)
|
i += 2;
|
||||||
return true;
|
|
||||||
|
|
||||||
i +=2;
|
} while (i <= e);
|
||||||
|
|
||||||
} while (i <= e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicit template instantiations
|
|
||||||
template bool Position::is_draw<false>() const;
|
|
||||||
template bool Position::is_draw<true>() const;
|
|
||||||
|
|
||||||
|
|
||||||
/// Position::flip() flips position with the white and black sides reversed. This
|
/// Position::flip() flips position with the white and black sides reversed. This
|
||||||
/// is only useful for debugging especially for finding evaluation symmetry bugs.
|
/// is only useful for debugging especially for finding evaluation symmetry bugs.
|
||||||
@@ -1526,7 +1422,7 @@ void Position::flip() {
|
|||||||
thisThread = pos.this_thread();
|
thisThread = pos.this_thread();
|
||||||
nodes = pos.nodes_searched();
|
nodes = pos.nodes_searched();
|
||||||
chess960 = pos.is_chess960();
|
chess960 = pos.is_chess960();
|
||||||
startPosPly = pos.startpos_ply_counter();
|
gamePly = pos.game_ply();
|
||||||
|
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
||||||
if (!pos.is_empty(s))
|
if (!pos.is_empty(s))
|
||||||
@@ -1593,7 +1489,7 @@ bool Position::pos_is_ok(int* failedStep) const {
|
|||||||
|
|
||||||
if ((*step)++, debugKingCount)
|
if ((*step)++, debugKingCount)
|
||||||
{
|
{
|
||||||
int kingCount[2] = {};
|
int kingCount[COLOR_NB] = {};
|
||||||
|
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
||||||
if (type_of(piece_on(s)) == KING)
|
if (type_of(piece_on(s)) == KING)
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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,6 +21,7 @@
|
|||||||
#define POSITION_H_INCLUDED
|
#define POSITION_H_INCLUDED
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
#include "bitboard.h"
|
#include "bitboard.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
@@ -29,7 +30,7 @@
|
|||||||
/// The checkInfo struct is initialized at c'tor time and keeps info used
|
/// The checkInfo struct is initialized at c'tor time and keeps info used
|
||||||
/// to detect if a move gives check.
|
/// to detect if a move gives check.
|
||||||
class Position;
|
class Position;
|
||||||
class Thread;
|
struct Thread;
|
||||||
|
|
||||||
struct CheckInfo {
|
struct CheckInfo {
|
||||||
|
|
||||||
@@ -37,19 +38,19 @@ struct CheckInfo {
|
|||||||
|
|
||||||
Bitboard dcCandidates;
|
Bitboard dcCandidates;
|
||||||
Bitboard pinned;
|
Bitboard pinned;
|
||||||
Bitboard checkSq[8];
|
Bitboard checkSq[PIECE_TYPE_NB];
|
||||||
Square ksq;
|
Square ksq;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// The StateInfo struct stores information we need to restore a Position
|
/// The StateInfo struct stores information we need to restore a Position
|
||||||
/// object to its previous state when we retract a move. Whenever a move
|
/// object to its previous state when we retract a move. Whenever a move
|
||||||
/// is made on the board (by calling Position::do_move), an StateInfo object
|
/// is made on the board (by calling Position::do_move), a StateInfo object
|
||||||
/// must be passed as a parameter.
|
/// must be passed as a parameter.
|
||||||
|
|
||||||
struct StateInfo {
|
struct StateInfo {
|
||||||
Key pawnKey, materialKey;
|
Key pawnKey, materialKey;
|
||||||
Value npMaterial[2];
|
Value npMaterial[COLOR_NB];
|
||||||
int castleRights, rule50, pliesFromNull;
|
int castleRights, rule50, pliesFromNull;
|
||||||
Score psqScore;
|
Score psqScore;
|
||||||
Square epSquare;
|
Square epSquare;
|
||||||
@@ -60,13 +61,10 @@ struct StateInfo {
|
|||||||
StateInfo* previous;
|
StateInfo* previous;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ReducedStateInfo {
|
|
||||||
Key pawnKey, materialKey;
|
/// When making a move the current StateInfo up to 'key' excluded is copied to
|
||||||
Value npMaterial[2];
|
/// the new one. Here we calculate the quad words (64bits) needed to be copied.
|
||||||
int castleRights, rule50, pliesFromNull;
|
const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1;
|
||||||
Score psqScore;
|
|
||||||
Square epSquare;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// The position data structure. A position consists of the following data:
|
/// The position data structure. A position consists of the following data:
|
||||||
@@ -95,13 +93,13 @@ class Position {
|
|||||||
public:
|
public:
|
||||||
Position() {}
|
Position() {}
|
||||||
Position(const Position& p, Thread* t) { *this = p; thisThread = t; }
|
Position(const Position& p, Thread* t) { *this = p; thisThread = t; }
|
||||||
Position(const std::string& f, bool c960, Thread* t) { from_fen(f, c960, t); }
|
Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); }
|
||||||
Position& operator=(const Position&);
|
Position& operator=(const Position&);
|
||||||
|
|
||||||
// Text input/output
|
// Text input/output
|
||||||
void from_fen(const std::string& fen, bool isChess960, Thread* th);
|
void set(const std::string& fen, bool isChess960, Thread* th);
|
||||||
const std::string to_fen() const;
|
const std::string fen() const;
|
||||||
void print(Move m = MOVE_NONE) const;
|
const std::string pretty(Move m = MOVE_NONE) const;
|
||||||
|
|
||||||
// Position representation
|
// Position representation
|
||||||
Bitboard pieces() const;
|
Bitboard pieces() const;
|
||||||
@@ -124,7 +122,6 @@ public:
|
|||||||
Square castle_rook_square(Color c, CastlingSide s) const;
|
Square castle_rook_square(Color c, CastlingSide s) const;
|
||||||
|
|
||||||
// Checking
|
// Checking
|
||||||
bool in_check() const;
|
|
||||||
Bitboard checkers() const;
|
Bitboard checkers() const;
|
||||||
Bitboard discovered_check_candidates() const;
|
Bitboard discovered_check_candidates() const;
|
||||||
Bitboard pinned_pieces() const;
|
Bitboard pinned_pieces() const;
|
||||||
@@ -139,8 +136,6 @@ public:
|
|||||||
|
|
||||||
// Properties of moves
|
// Properties of moves
|
||||||
bool move_gives_check(Move m, const CheckInfo& ci) const;
|
bool move_gives_check(Move m, const CheckInfo& ci) const;
|
||||||
bool move_attacks_square(Move m, Square s) const;
|
|
||||||
bool move_is_legal(const Move m) const;
|
|
||||||
bool pl_move_is_legal(Move m, Bitboard pinned) const;
|
bool pl_move_is_legal(Move m, Bitboard pinned) const;
|
||||||
bool is_pseudo_legal(const Move m) const;
|
bool is_pseudo_legal(const Move m) const;
|
||||||
bool is_capture(Move m) const;
|
bool is_capture(Move m) const;
|
||||||
@@ -159,10 +154,11 @@ public:
|
|||||||
void do_move(Move m, StateInfo& st);
|
void do_move(Move m, StateInfo& st);
|
||||||
void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck);
|
void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck);
|
||||||
void undo_move(Move m);
|
void undo_move(Move m);
|
||||||
template<bool Do> void do_null_move(StateInfo& st);
|
void do_null_move(StateInfo& st);
|
||||||
|
void undo_null_move();
|
||||||
|
|
||||||
// Static exchange evaluation
|
// Static exchange evaluation
|
||||||
int see(Move m) const;
|
int see(Move m, int asymmThreshold = 0) const;
|
||||||
int see_sign(Move m) const;
|
int see_sign(Move m) const;
|
||||||
|
|
||||||
// Accessing hash keys
|
// Accessing hash keys
|
||||||
@@ -178,12 +174,12 @@ public:
|
|||||||
|
|
||||||
// Other properties of the position
|
// Other properties of the position
|
||||||
Color side_to_move() const;
|
Color side_to_move() const;
|
||||||
int startpos_ply_counter() 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;
|
int64_t nodes_searched() const;
|
||||||
void set_nodes_searched(int64_t n);
|
void set_nodes_searched(int64_t n);
|
||||||
template<bool SkipRepetition> 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* failedStep = NULL) const;
|
||||||
@@ -195,8 +191,8 @@ private:
|
|||||||
void put_piece(Piece p, Square s);
|
void put_piece(Piece p, Square s);
|
||||||
void set_castle_right(Color c, Square rfrom);
|
void set_castle_right(Color c, Square rfrom);
|
||||||
|
|
||||||
// Helper template functions
|
// Helper functions
|
||||||
template<bool Do> void do_castle_move(Move m);
|
void do_castle(Square kfrom, Square kto, Square rfrom, Square rto);
|
||||||
template<bool FindPinned> Bitboard hidden_checkers() const;
|
template<bool FindPinned> Bitboard hidden_checkers() const;
|
||||||
|
|
||||||
// Computing hash keys from scratch (for initialization and debugging)
|
// Computing hash keys from scratch (for initialization and debugging)
|
||||||
@@ -209,20 +205,20 @@ private:
|
|||||||
Value compute_non_pawn_material(Color c) const;
|
Value compute_non_pawn_material(Color c) const;
|
||||||
|
|
||||||
// Board and pieces
|
// Board and pieces
|
||||||
Piece board[64]; // [square]
|
Piece board[SQUARE_NB];
|
||||||
Bitboard byTypeBB[8]; // [pieceType]
|
Bitboard byTypeBB[PIECE_TYPE_NB];
|
||||||
Bitboard byColorBB[2]; // [color]
|
Bitboard byColorBB[COLOR_NB];
|
||||||
int pieceCount[2][8]; // [color][pieceType]
|
int pieceCount[COLOR_NB][PIECE_TYPE_NB];
|
||||||
Square pieceList[2][8][16]; // [color][pieceType][index]
|
Square pieceList[COLOR_NB][PIECE_TYPE_NB][16];
|
||||||
int index[64]; // [square]
|
int index[SQUARE_NB];
|
||||||
|
|
||||||
// Other info
|
// Other info
|
||||||
int castleRightsMask[64]; // [square]
|
int castleRightsMask[SQUARE_NB];
|
||||||
Square castleRookSquare[2][2]; // [color][side]
|
Square castleRookSquare[COLOR_NB][CASTLING_SIDE_NB];
|
||||||
Bitboard castlePath[2][2]; // [color][side]
|
Bitboard castlePath[COLOR_NB][CASTLING_SIDE_NB];
|
||||||
StateInfo startState;
|
StateInfo startState;
|
||||||
int64_t nodes;
|
int64_t nodes;
|
||||||
int startPosPly;
|
int gamePly;
|
||||||
Color sideToMove;
|
Color sideToMove;
|
||||||
Thread* thisThread;
|
Thread* thisThread;
|
||||||
StateInfo* st;
|
StateInfo* st;
|
||||||
@@ -334,10 +330,6 @@ inline Bitboard Position::checkers() const {
|
|||||||
return st->checkersBB;
|
return st->checkersBB;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Position::in_check() const {
|
|
||||||
return st->checkersBB != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Bitboard Position::discovered_check_candidates() const {
|
inline Bitboard Position::discovered_check_candidates() const {
|
||||||
return hidden_checkers<false>();
|
return hidden_checkers<false>();
|
||||||
}
|
}
|
||||||
@@ -384,8 +376,8 @@ inline bool Position::is_passed_pawn_push(Move m) const {
|
|||||||
&& pawn_is_passed(sideToMove, to_sq(m));
|
&& pawn_is_passed(sideToMove, to_sq(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int Position::startpos_ply_counter() const {
|
inline int Position::game_ply() const {
|
||||||
return startPosPly + st->pliesFromNull; // HACK
|
return gamePly;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Position::opposite_bishops() const {
|
inline bool Position::opposite_bishops() 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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,7 +29,7 @@
|
|||||||
/// a given square a (midgame, endgame) score pair is assigned. PSQT is defined
|
/// a given square a (midgame, endgame) score pair is assigned. PSQT is defined
|
||||||
/// for white side, for black side the tables are symmetric.
|
/// for white side, for black side the tables are symmetric.
|
||||||
|
|
||||||
static const Score PSQT[][64] = {
|
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),
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -61,17 +61,15 @@ class RKISS {
|
|||||||
return s.d = e + s.a;
|
return s.d = e + s.a;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init seed and scramble a few rounds
|
public:
|
||||||
void raninit() {
|
RKISS(int seed = 73) {
|
||||||
|
|
||||||
s.a = 0xf1ea5eed;
|
s.a = 0xf1ea5eed;
|
||||||
s.b = s.c = s.d = 0xd4e12c77;
|
s.b = s.c = s.d = 0xd4e12c77;
|
||||||
for (int i = 0; i < 73; i++)
|
for (int i = 0; i < seed; i++) // Scramble a few rounds
|
||||||
rand64();
|
rand64();
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
RKISS() { raninit(); }
|
|
||||||
template<typename T> T rand() { return T(rand64()); }
|
template<typename T> T rand() { return T(rand64()); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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 @@ namespace Search {
|
|||||||
/// has its own array of Stack objects, indexed by the current ply.
|
/// has its own array of Stack objects, indexed by the current ply.
|
||||||
|
|
||||||
struct Stack {
|
struct Stack {
|
||||||
SplitPoint* sp;
|
SplitPoint* splitPoint;
|
||||||
int ply;
|
int ply;
|
||||||
Move currentMove;
|
Move currentMove;
|
||||||
Move excludedMove;
|
Move excludedMove;
|
||||||
Move killers[2];
|
Move killers[2];
|
||||||
Depth reduction;
|
Depth reduction;
|
||||||
Value eval;
|
Value staticEval;
|
||||||
Value evalMargin;
|
Value evalMargin;
|
||||||
int skipNullMove;
|
int skipNullMove;
|
||||||
|
int futilityMoveCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -56,12 +57,11 @@ struct Stack {
|
|||||||
/// all non-pv moves.
|
/// all non-pv moves.
|
||||||
struct RootMove {
|
struct RootMove {
|
||||||
|
|
||||||
RootMove(){} // Needed by sort()
|
|
||||||
RootMove(Move m) : score(-VALUE_INFINITE), prevScore(-VALUE_INFINITE) {
|
RootMove(Move m) : score(-VALUE_INFINITE), prevScore(-VALUE_INFINITE) {
|
||||||
pv.push_back(m); pv.push_back(MOVE_NONE);
|
pv.push_back(m); pv.push_back(MOVE_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator<(const RootMove& m) const { return score < m.score; }
|
bool operator<(const RootMove& m) const { return score > m.score; } // Ascending sort
|
||||||
bool operator==(const Move& m) const { return pv[0] == m; }
|
bool operator==(const Move& m) const { return pv[0] == m; }
|
||||||
|
|
||||||
void extract_pv_from_tt(Position& pos);
|
void extract_pv_from_tt(Position& pos);
|
||||||
@@ -80,9 +80,9 @@ struct RootMove {
|
|||||||
struct LimitsType {
|
struct LimitsType {
|
||||||
|
|
||||||
LimitsType() { memset(this, 0, sizeof(LimitsType)); }
|
LimitsType() { memset(this, 0, sizeof(LimitsType)); }
|
||||||
bool use_time_management() const { return !(movetime | depth | nodes | infinite); }
|
bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); }
|
||||||
|
|
||||||
int time[2], inc[2], movestogo, depth, nodes, movetime, infinite, ponder;
|
int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -98,7 +98,8 @@ typedef std::auto_ptr<std::stack<StateInfo> > StateStackPtr;
|
|||||||
extern volatile SignalsType Signals;
|
extern volatile SignalsType Signals;
|
||||||
extern LimitsType Limits;
|
extern LimitsType Limits;
|
||||||
extern std::vector<RootMove> RootMoves;
|
extern std::vector<RootMove> RootMoves;
|
||||||
extern Position RootPosition;
|
extern Position RootPos;
|
||||||
|
extern Color RootColor;
|
||||||
extern Time::point SearchTime;
|
extern Time::point SearchTime;
|
||||||
extern StateStackPtr SetupStates;
|
extern StateStackPtr SetupStates;
|
||||||
|
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <algorithm> // For std::count
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
@@ -32,26 +33,24 @@ ThreadPool Threads; // Global object
|
|||||||
namespace { extern "C" {
|
namespace { extern "C" {
|
||||||
|
|
||||||
// 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
|
||||||
// is launched. It is a wrapper to member function pointed by start_fn.
|
// is launched. It is a wrapper to the virtual function idle_loop().
|
||||||
|
|
||||||
long start_routine(Thread* th) { (th->*(th->start_fn))(); return 0; }
|
long start_routine(Thread* th) { th->idle_loop(); return 0; }
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
|
||||||
|
|
||||||
// Thread c'tor starts a newly-created thread of execution that will call
|
// Thread c'tor starts a newly-created thread of execution that will call
|
||||||
// the idle loop function pointed by start_fn going immediately to sleep.
|
// the the virtual function idle_loop(), going immediately to sleep.
|
||||||
|
|
||||||
Thread::Thread(Fn fn) {
|
Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC
|
||||||
|
|
||||||
is_searching = do_exit = false;
|
searching = exit = false;
|
||||||
maxPly = splitPointsCnt = 0;
|
maxPly = splitPointsSize = 0;
|
||||||
curSplitPoint = NULL;
|
activeSplitPoint = NULL;
|
||||||
start_fn = fn;
|
activePosition = NULL;
|
||||||
idx = Threads.size();
|
idx = Threads.size();
|
||||||
|
|
||||||
do_sleep = (fn != &Thread::main_loop); // Avoid a race with start_searching()
|
|
||||||
|
|
||||||
if (!thread_create(handle, start_routine, this))
|
if (!thread_create(handle, start_routine, this))
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to create thread number " << idx << std::endl;
|
std::cerr << "Failed to create thread number " << idx << std::endl;
|
||||||
@@ -60,47 +59,49 @@ Thread::Thread(Fn fn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Thread d'tor waits for thread termination before to return.
|
// Thread d'tor waits for thread termination before to return
|
||||||
|
|
||||||
Thread::~Thread() {
|
Thread::~Thread() {
|
||||||
|
|
||||||
assert(do_sleep);
|
exit = true; // Search must be already finished
|
||||||
|
notify_one();
|
||||||
do_exit = true; // Search must be already finished
|
|
||||||
wake_up();
|
|
||||||
thread_join(handle); // Wait for thread termination
|
thread_join(handle); // Wait for thread termination
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Thread::timer_loop() is where the timer thread waits maxPly milliseconds and
|
// TimerThread::idle_loop() is where the timer thread waits msec milliseconds
|
||||||
// then calls check_time(). If maxPly is 0 thread sleeps until is woken up.
|
// and then calls check_time(). If msec is 0 thread sleeps until is woken up.
|
||||||
extern void check_time();
|
extern void check_time();
|
||||||
|
|
||||||
void Thread::timer_loop() {
|
void TimerThread::idle_loop() {
|
||||||
|
|
||||||
while (!do_exit)
|
while (!exit)
|
||||||
{
|
{
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
sleepCondition.wait_for(mutex, maxPly ? maxPly : INT_MAX);
|
|
||||||
|
if (!exit)
|
||||||
|
sleepCondition.wait_for(mutex, msec ? msec : INT_MAX);
|
||||||
|
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
check_time();
|
|
||||||
|
if (msec)
|
||||||
|
check_time();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Thread::main_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. Main thread will launch all the slave threads.
|
||||||
|
|
||||||
void Thread::main_loop() {
|
void MainThread::idle_loop() {
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
|
|
||||||
do_sleep = true; // Always return to sleep after a search
|
thinking = false;
|
||||||
is_searching = false;
|
|
||||||
|
|
||||||
while (do_sleep && !do_exit)
|
while (!thinking && !exit)
|
||||||
{
|
{
|
||||||
Threads.sleepCondition.notify_one(); // Wake up UI thread if needed
|
Threads.sleepCondition.notify_one(); // Wake up UI thread if needed
|
||||||
sleepCondition.wait(mutex);
|
sleepCondition.wait(mutex);
|
||||||
@@ -108,22 +109,23 @@ void Thread::main_loop() {
|
|||||||
|
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
|
|
||||||
if (do_exit)
|
if (exit)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
is_searching = true;
|
searching = true;
|
||||||
|
|
||||||
Search::think();
|
Search::think();
|
||||||
|
|
||||||
assert(is_searching);
|
assert(searching);
|
||||||
|
|
||||||
|
searching = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Thread::wake_up() wakes up the thread, normally at the beginning of the search
|
// Thread::notify_one() wakes up the thread when there is some search to do
|
||||||
// or, if "sleeping threads" is used at split time.
|
|
||||||
|
|
||||||
void Thread::wake_up() {
|
void Thread::notify_one() {
|
||||||
|
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
sleepCondition.notify_one();
|
sleepCondition.notify_one();
|
||||||
@@ -131,19 +133,12 @@ void Thread::wake_up() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Thread::wait_for_stop_or_ponderhit() is called when the maximum depth is
|
// Thread::wait_for() set the thread to sleep until condition 'b' turns true
|
||||||
// reached while the program is pondering. The point is to work around a wrinkle
|
|
||||||
// in the UCI protocol: When pondering, the engine is not allowed to give a
|
|
||||||
// "bestmove" before the GUI sends it a "stop" or "ponderhit" command. We simply
|
|
||||||
// wait here until one of these commands (that raise StopRequest) is sent and
|
|
||||||
// then return, after which the bestmove and pondermove will be printed.
|
|
||||||
|
|
||||||
void Thread::wait_for_stop_or_ponderhit() {
|
void Thread::wait_for(volatile const bool& b) {
|
||||||
|
|
||||||
Signals.stopOnPonderhit = true;
|
|
||||||
|
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
while (!Signals.stop) sleepCondition.wait(mutex);;
|
while (!b) sleepCondition.wait(mutex);
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +148,7 @@ void Thread::wait_for_stop_or_ponderhit() {
|
|||||||
|
|
||||||
bool Thread::cutoff_occurred() const {
|
bool Thread::cutoff_occurred() const {
|
||||||
|
|
||||||
for (SplitPoint* sp = curSplitPoint; sp; sp = sp->parent)
|
for (SplitPoint* sp = activeSplitPoint; sp; sp = sp->parentSplitPoint)
|
||||||
if (sp->cutoff)
|
if (sp->cutoff)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -164,46 +159,47 @@ bool Thread::cutoff_occurred() const {
|
|||||||
// Thread::is_available_to() checks whether the thread is available to help the
|
// Thread::is_available_to() checks whether the thread is available to help the
|
||||||
// thread 'master' at a split point. An obvious requirement is that thread must
|
// 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
|
// be idle. With more than two threads, this is not sufficient: If the thread is
|
||||||
// the master of some active split point, it is only available as a slave to the
|
// the master of some split point, it is only available as a slave to the slaves
|
||||||
// slaves which are busy searching the split point at the top of slaves split
|
// which are busy searching the split point at the top of slaves split point
|
||||||
// point stack (the "helpful master concept" in YBWC terminology).
|
// stack (the "helpful master concept" in YBWC terminology).
|
||||||
|
|
||||||
bool Thread::is_available_to(Thread* master) const {
|
bool Thread::is_available_to(Thread* master) const {
|
||||||
|
|
||||||
if (is_searching)
|
if (searching)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Make a local copy to be sure doesn't become zero under our feet while
|
// 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.
|
// testing next condition and so leading to an out of bound access.
|
||||||
int spCnt = splitPointsCnt;
|
int size = splitPointsSize;
|
||||||
|
|
||||||
// No active split points means that the thread is available as a slave for any
|
// No split points means that the thread is available as a slave for any
|
||||||
// other thread otherwise apply the "helpful master" concept if possible.
|
// other thread otherwise apply the "helpful master" concept if possible.
|
||||||
return !spCnt || (splitPoints[spCnt - 1].slavesMask & (1ULL << master->idx));
|
return !size || (splitPoints[size - 1].slavesMask & (1ULL << master->idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// init() is called at startup. Initializes lock and condition variable and
|
// init() is called at startup to create and launch requested threads, that will
|
||||||
// launches requested threads sending them immediately to sleep. We cannot use
|
// go immediately to sleep due to 'sleepWhileIdle' set to true. We cannot use
|
||||||
// a c'tor becuase Threads is a static object and we need a fully initialized
|
// a c'tor becuase Threads is a static object and we need a fully initialized
|
||||||
// engine at this point due to allocation of endgames in Thread c'tor.
|
// engine at this point due to allocation of Endgames in Thread c'tor.
|
||||||
|
|
||||||
void ThreadPool::init() {
|
void ThreadPool::init() {
|
||||||
|
|
||||||
timer = new Thread(&Thread::timer_loop);
|
sleepWhileIdle = true;
|
||||||
threads.push_back(new Thread(&Thread::main_loop));
|
timer = new TimerThread();
|
||||||
|
push_back(new 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
|
||||||
|
|
||||||
void ThreadPool::exit() {
|
void ThreadPool::exit() {
|
||||||
|
|
||||||
for (size_t i = 0; i < threads.size(); i++)
|
delete timer; // As first because check_time() accesses threads data
|
||||||
delete threads[i];
|
|
||||||
|
|
||||||
delete timer;
|
for (iterator it = begin(); it != end(); ++it)
|
||||||
|
delete *it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -216,221 +212,170 @@ void ThreadPool::read_uci_options() {
|
|||||||
|
|
||||||
maxThreadsPerSplitPoint = Options["Max Threads per Split Point"];
|
maxThreadsPerSplitPoint = Options["Max Threads per Split Point"];
|
||||||
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
|
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
|
||||||
useSleepingThreads = Options["Use Sleeping Threads"];
|
|
||||||
size_t requested = Options["Threads"];
|
size_t requested = Options["Threads"];
|
||||||
|
|
||||||
assert(requested > 0);
|
assert(requested > 0);
|
||||||
|
|
||||||
while (threads.size() < requested)
|
while (size() < requested)
|
||||||
threads.push_back(new Thread(&Thread::idle_loop));
|
push_back(new Thread());
|
||||||
|
|
||||||
while (threads.size() > requested)
|
while (size() > requested)
|
||||||
{
|
{
|
||||||
delete threads.back();
|
delete back();
|
||||||
threads.pop_back();
|
pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// wake_up() is called before a new search to start the threads that are waiting
|
// slave_available() tries to find an idle thread which is available as a slave
|
||||||
// on the sleep condition and to reset maxPly. When useSleepingThreads is set
|
// for the thread 'master'.
|
||||||
// threads will be woken up at split time.
|
|
||||||
|
|
||||||
void ThreadPool::wake_up() const {
|
Thread* ThreadPool::available_slave(Thread* master) const {
|
||||||
|
|
||||||
for (size_t i = 0; i < threads.size(); i++)
|
for (const_iterator it = begin(); it != end(); ++it)
|
||||||
{
|
if ((*it)->is_available_to(master))
|
||||||
threads[i]->maxPly = 0;
|
return *it;
|
||||||
threads[i]->do_sleep = false;
|
|
||||||
|
|
||||||
if (!useSleepingThreads)
|
return NULL;
|
||||||
threads[i]->wake_up();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// sleep() is called after the search finishes to ask all the threads but the
|
|
||||||
// main one to go waiting on a sleep condition.
|
|
||||||
|
|
||||||
void ThreadPool::sleep() const {
|
|
||||||
|
|
||||||
// Main thread will go to sleep by itself to avoid a race with start_searching()
|
|
||||||
for (size_t i = 1; i < threads.size(); i++)
|
|
||||||
threads[i]->do_sleep = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// available_slave_exists() tries to find an idle thread which is available as
|
|
||||||
// a slave for the thread 'master'.
|
|
||||||
|
|
||||||
bool ThreadPool::available_slave_exists(Thread* master) const {
|
|
||||||
|
|
||||||
for (size_t i = 0; i < threads.size(); i++)
|
|
||||||
if (threads[i]->is_available_to(master))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// split() does the actual work of distributing the work at a node between
|
// split() does the actual work of distributing the work at a node between
|
||||||
// several available threads. If it does not succeed in splitting the node
|
// several available threads. If it does not succeed in splitting the node
|
||||||
// (because no idle threads are available, or because we have no unused split
|
// (because no idle threads are available), the function immediately returns.
|
||||||
// point objects), the function immediately returns. If splitting is possible, a
|
// If splitting is possible, a SplitPoint object is initialized with all the
|
||||||
// SplitPoint object is initialized with all the data that must be copied to the
|
// data that must be copied to the helper threads and then helper threads are
|
||||||
// helper threads and then helper threads are told that they have been assigned
|
// told that they have been assigned work. This will cause them to instantly
|
||||||
// work. This will cause them to instantly leave their idle loops and call
|
// leave their idle loops and call search(). When all threads have returned from
|
||||||
// search(). When all threads have returned from search() then split() returns.
|
// search() then split() returns.
|
||||||
|
|
||||||
template <bool Fake>
|
template <bool Fake>
|
||||||
Value ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta,
|
void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bestValue,
|
||||||
Value bestValue, Move* bestMove, Depth depth,
|
Move* bestMove, Depth depth, Move threatMove, int moveCount,
|
||||||
Move threatMove, int moveCount, MovePicker* mp, int nodeType) {
|
MovePicker* movePicker, int nodeType) {
|
||||||
|
|
||||||
assert(pos.pos_is_ok());
|
assert(pos.pos_is_ok());
|
||||||
assert(bestValue > -VALUE_INFINITE);
|
assert(*bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE);
|
||||||
assert(bestValue <= alpha);
|
assert(*bestValue > -VALUE_INFINITE);
|
||||||
assert(alpha < beta);
|
assert(depth >= Threads.minimumSplitDepth);
|
||||||
assert(beta <= VALUE_INFINITE);
|
assert(searching);
|
||||||
assert(depth > DEPTH_ZERO);
|
assert(splitPointsSize < MAX_SPLITPOINTS_PER_THREAD);
|
||||||
|
|
||||||
Thread* master = pos.this_thread();
|
|
||||||
|
|
||||||
if (master->splitPointsCnt >= MAX_SPLITPOINTS_PER_THREAD)
|
|
||||||
return bestValue;
|
|
||||||
|
|
||||||
// Pick the next available split point from the split point stack
|
// Pick the next available split point from the split point stack
|
||||||
SplitPoint& sp = master->splitPoints[master->splitPointsCnt];
|
SplitPoint& sp = splitPoints[splitPointsSize];
|
||||||
|
|
||||||
sp.parent = master->curSplitPoint;
|
sp.masterThread = this;
|
||||||
sp.master = master;
|
sp.parentSplitPoint = activeSplitPoint;
|
||||||
sp.cutoff = false;
|
sp.slavesMask = 1ULL << idx;
|
||||||
sp.slavesMask = 1ULL << master->idx;
|
|
||||||
sp.depth = depth;
|
sp.depth = depth;
|
||||||
|
sp.bestValue = *bestValue;
|
||||||
sp.bestMove = *bestMove;
|
sp.bestMove = *bestMove;
|
||||||
sp.threatMove = threatMove;
|
sp.threatMove = threatMove;
|
||||||
sp.alpha = alpha;
|
sp.alpha = alpha;
|
||||||
sp.beta = beta;
|
sp.beta = beta;
|
||||||
sp.nodeType = nodeType;
|
sp.nodeType = nodeType;
|
||||||
sp.bestValue = bestValue;
|
sp.movePicker = movePicker;
|
||||||
sp.mp = mp;
|
|
||||||
sp.moveCount = moveCount;
|
sp.moveCount = moveCount;
|
||||||
sp.pos = &pos;
|
sp.pos = &pos;
|
||||||
sp.nodes = 0;
|
sp.nodes = 0;
|
||||||
|
sp.cutoff = false;
|
||||||
sp.ss = ss;
|
sp.ss = ss;
|
||||||
|
|
||||||
assert(master->is_searching);
|
|
||||||
|
|
||||||
master->curSplitPoint = &sp;
|
|
||||||
int slavesCnt = 0;
|
|
||||||
|
|
||||||
// Try to allocate available threads and ask them to start searching setting
|
// Try to allocate available threads and ask them to start searching setting
|
||||||
// is_searching flag. This must be done under lock protection to avoid concurrent
|
// 'searching' flag. This must be done under lock protection to avoid concurrent
|
||||||
// allocation of the same slave by another master.
|
// allocation of the same slave by another master.
|
||||||
|
Threads.mutex.lock();
|
||||||
sp.mutex.lock();
|
sp.mutex.lock();
|
||||||
mutex.lock();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < threads.size() && !Fake; ++i)
|
splitPointsSize++;
|
||||||
if (threads[i]->is_available_to(master))
|
activeSplitPoint = &sp;
|
||||||
{
|
activePosition = NULL;
|
||||||
sp.slavesMask |= 1ULL << i;
|
|
||||||
threads[i]->curSplitPoint = &sp;
|
|
||||||
threads[i]->is_searching = true; // Slave leaves idle_loop()
|
|
||||||
|
|
||||||
if (useSleepingThreads)
|
size_t slavesCnt = 1; // This thread is always included
|
||||||
threads[i]->wake_up();
|
Thread* slave;
|
||||||
|
|
||||||
if (++slavesCnt + 1 >= maxThreadsPerSplitPoint) // Master is always included
|
while ( (slave = Threads.available_slave(this)) != NULL
|
||||||
break;
|
&& ++slavesCnt <= Threads.maxThreadsPerSplitPoint && !Fake)
|
||||||
}
|
{
|
||||||
|
sp.slavesMask |= 1ULL << slave->idx;
|
||||||
master->splitPointsCnt++;
|
slave->activeSplitPoint = &sp;
|
||||||
|
slave->searching = true; // Slave leaves idle_loop()
|
||||||
mutex.unlock();
|
slave->notify_one(); // Could be sleeping
|
||||||
sp.mutex.unlock();
|
}
|
||||||
|
|
||||||
// Everything is set up. The master thread enters the idle loop, from which
|
// Everything is set up. The master thread enters the idle loop, from which
|
||||||
// it will instantly launch a search, because its is_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 || Fake)
|
if (slavesCnt > 1 || Fake)
|
||||||
{
|
{
|
||||||
master->idle_loop();
|
sp.mutex.unlock();
|
||||||
|
Threads.mutex.unlock();
|
||||||
|
|
||||||
|
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 helpful master concept a master can help only a sub-tree of its split
|
||||||
// point, and because here is all finished is not possible master is booked.
|
// point, and because here is all finished is not possible master is booked.
|
||||||
assert(!master->is_searching);
|
assert(!searching);
|
||||||
|
assert(!activePosition);
|
||||||
|
|
||||||
|
// We have returned from the idle loop, which means that all threads are
|
||||||
|
// finished. Note that setting 'searching' and decreasing splitPointsSize is
|
||||||
|
// done under lock protection to avoid a race with Thread::is_available_to().
|
||||||
|
Threads.mutex.lock();
|
||||||
|
sp.mutex.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have returned from the idle loop, which means that all threads are
|
searching = true;
|
||||||
// finished. Note that setting is_searching and decreasing splitPointsCnt is
|
splitPointsSize--;
|
||||||
// done under lock protection to avoid a race with Thread::is_available_to().
|
activeSplitPoint = sp.parentSplitPoint;
|
||||||
sp.mutex.lock(); // To protect sp.nodes
|
activePosition = &pos;
|
||||||
mutex.lock();
|
|
||||||
|
|
||||||
master->is_searching = true;
|
|
||||||
master->splitPointsCnt--;
|
|
||||||
master->curSplitPoint = sp.parent;
|
|
||||||
pos.set_nodes_searched(pos.nodes_searched() + sp.nodes);
|
pos.set_nodes_searched(pos.nodes_searched() + sp.nodes);
|
||||||
*bestMove = sp.bestMove;
|
*bestMove = sp.bestMove;
|
||||||
|
*bestValue = sp.bestValue;
|
||||||
|
|
||||||
mutex.unlock();
|
|
||||||
sp.mutex.unlock();
|
sp.mutex.unlock();
|
||||||
|
Threads.mutex.unlock();
|
||||||
return sp.bestValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicit template instantiations
|
// Explicit template instantiations
|
||||||
template Value ThreadPool::split<false>(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int);
|
template void Thread::split<false>(Position&, Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int);
|
||||||
template Value ThreadPool::split<true>(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int);
|
template void Thread::split< true>(Position&, Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int);
|
||||||
|
|
||||||
|
|
||||||
// set_timer() is used to set the timer to trigger after msec milliseconds.
|
// wait_for_think_finished() waits for main thread to go to sleep then returns
|
||||||
// If msec is 0 then timer is stopped.
|
|
||||||
|
|
||||||
void ThreadPool::set_timer(int msec) {
|
void ThreadPool::wait_for_think_finished() {
|
||||||
|
|
||||||
timer->mutex.lock();
|
MainThread* t = main_thread();
|
||||||
timer->maxPly = msec;
|
|
||||||
timer->sleepCondition.notify_one(); // Wake up and restart the timer
|
|
||||||
timer->mutex.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// wait_for_search_finished() waits for main thread to go to sleep, this means
|
|
||||||
// search is finished. Then returns.
|
|
||||||
|
|
||||||
void ThreadPool::wait_for_search_finished() {
|
|
||||||
|
|
||||||
Thread* t = main_thread();
|
|
||||||
t->mutex.lock();
|
t->mutex.lock();
|
||||||
t->sleepCondition.notify_one(); // In case is waiting for stop or ponderhit
|
while (t->thinking) sleepCondition.wait(t->mutex);
|
||||||
while (!t->do_sleep) sleepCondition.wait(t->mutex);
|
|
||||||
t->mutex.unlock();
|
t->mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// start_searching() wakes up the main thread sleeping in main_loop() so to start
|
// start_thinking() wakes up the main thread sleeping in MainThread::idle_loop()
|
||||||
// a new search, then returns immediately.
|
// so to start a new search, then returns immediately.
|
||||||
|
|
||||||
void ThreadPool::start_searching(const Position& pos, const LimitsType& limits,
|
void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
|
||||||
const std::vector<Move>& searchMoves, StateStackPtr& states) {
|
const std::vector<Move>& searchMoves, StateStackPtr& states) {
|
||||||
wait_for_search_finished();
|
wait_for_think_finished();
|
||||||
|
|
||||||
SearchTime = Time::now(); // As early as possible
|
SearchTime = Time::now(); // As early as possible
|
||||||
|
|
||||||
Signals.stopOnPonderhit = Signals.firstRootMove = false;
|
Signals.stopOnPonderhit = Signals.firstRootMove = false;
|
||||||
Signals.stop = Signals.failedLowAtRoot = false;
|
Signals.stop = Signals.failedLowAtRoot = false;
|
||||||
|
|
||||||
RootPosition = pos;
|
RootPos = pos;
|
||||||
Limits = limits;
|
Limits = limits;
|
||||||
SetupStates = states; // Ownership transfer here
|
SetupStates = states; // Ownership transfer here
|
||||||
RootMoves.clear();
|
RootMoves.clear();
|
||||||
|
|
||||||
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
|
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
|
||||||
if (searchMoves.empty() || std::count(searchMoves.begin(), searchMoves.end(), ml.move()))
|
if ( searchMoves.empty()
|
||||||
|
|| std::count(searchMoves.begin(), searchMoves.end(), ml.move()))
|
||||||
RootMoves.push_back(RootMove(ml.move()));
|
RootMoves.push_back(RootMove(ml.move()));
|
||||||
|
|
||||||
main_thread()->do_sleep = false;
|
main_thread()->thinking = true;
|
||||||
main_thread()->wake_up();
|
main_thread()->notify_one(); // Starts main thread
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
#include "position.h"
|
#include "position.h"
|
||||||
#include "search.h"
|
#include "search.h"
|
||||||
|
|
||||||
const int MAX_THREADS = 32;
|
const int MAX_THREADS = 64; // Because SplitPoint::slavesMask is a uint64_t
|
||||||
const int MAX_SPLITPOINTS_PER_THREAD = 8;
|
const int MAX_SPLITPOINTS_PER_THREAD = 8;
|
||||||
|
|
||||||
struct Mutex {
|
struct Mutex {
|
||||||
@@ -56,22 +56,22 @@ private:
|
|||||||
WaitCondition c;
|
WaitCondition c;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Thread;
|
struct Thread;
|
||||||
|
|
||||||
struct SplitPoint {
|
struct SplitPoint {
|
||||||
|
|
||||||
// Const data after split point has been setup
|
// Const data after split point has been setup
|
||||||
const Position* pos;
|
const Position* pos;
|
||||||
const Search::Stack* ss;
|
const Search::Stack* ss;
|
||||||
|
Thread* masterThread;
|
||||||
Depth depth;
|
Depth depth;
|
||||||
Value beta;
|
Value beta;
|
||||||
int nodeType;
|
int nodeType;
|
||||||
Thread* master;
|
|
||||||
Move threatMove;
|
Move threatMove;
|
||||||
|
|
||||||
// Const pointers to shared data
|
// Const pointers to shared data
|
||||||
MovePicker* mp;
|
MovePicker* movePicker;
|
||||||
SplitPoint* parent;
|
SplitPoint* parentSplitPoint;
|
||||||
|
|
||||||
// Shared data
|
// Shared data
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
@@ -90,77 +90,76 @@ struct SplitPoint {
|
|||||||
/// tables so that once we get a pointer to an entry its life time is unlimited
|
/// tables so that once we get a pointer to an entry its life time is unlimited
|
||||||
/// and we don't have to care about someone changing the entry under our feet.
|
/// and we don't have to care about someone changing the entry under our feet.
|
||||||
|
|
||||||
class Thread {
|
struct Thread {
|
||||||
|
|
||||||
typedef void (Thread::* Fn) (); // Pointer to member function
|
Thread();
|
||||||
|
virtual ~Thread();
|
||||||
|
|
||||||
public:
|
virtual void idle_loop();
|
||||||
Thread(Fn fn);
|
void notify_one();
|
||||||
~Thread();
|
|
||||||
|
|
||||||
void wake_up();
|
|
||||||
bool cutoff_occurred() const;
|
bool cutoff_occurred() const;
|
||||||
bool is_available_to(Thread* master) const;
|
bool is_available_to(Thread* master) const;
|
||||||
void idle_loop();
|
void wait_for(volatile const bool& b);
|
||||||
void main_loop();
|
|
||||||
void timer_loop();
|
template <bool Fake>
|
||||||
void wait_for_stop_or_ponderhit();
|
void split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove,
|
||||||
|
Depth depth, Move threatMove, int moveCount, MovePicker* movePicker, int nodeType);
|
||||||
|
|
||||||
SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD];
|
SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD];
|
||||||
MaterialTable materialTable;
|
Material::Table materialTable;
|
||||||
PawnTable pawnTable;
|
Endgames endgames;
|
||||||
|
Pawns::Table pawnsTable;
|
||||||
|
Position* activePosition;
|
||||||
size_t idx;
|
size_t idx;
|
||||||
int maxPly;
|
int maxPly;
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
ConditionVariable sleepCondition;
|
ConditionVariable sleepCondition;
|
||||||
NativeHandle handle;
|
NativeHandle handle;
|
||||||
Fn start_fn;
|
SplitPoint* volatile activeSplitPoint;
|
||||||
SplitPoint* volatile curSplitPoint;
|
volatile int splitPointsSize;
|
||||||
volatile int splitPointsCnt;
|
volatile bool searching;
|
||||||
volatile bool is_searching;
|
volatile bool exit;
|
||||||
volatile bool do_sleep;
|
|
||||||
volatile bool do_exit;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// ThreadPool class handles all the threads related stuff like init, starting,
|
/// MainThread and TimerThread are sublassed from Thread to characterize the two
|
||||||
|
/// special threads: the main one and the recurring timer.
|
||||||
|
|
||||||
|
struct MainThread : public Thread {
|
||||||
|
MainThread() : thinking(true) {} // Avoid a race with start_thinking()
|
||||||
|
virtual void idle_loop();
|
||||||
|
volatile bool thinking;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TimerThread : public Thread {
|
||||||
|
TimerThread() : msec(0) {}
|
||||||
|
virtual void idle_loop();
|
||||||
|
int msec;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// 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, the most important, 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.
|
||||||
|
|
||||||
class ThreadPool {
|
struct ThreadPool : public std::vector<Thread*> {
|
||||||
|
|
||||||
public:
|
|
||||||
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 valid during the whole thread lifetime.
|
||||||
|
|
||||||
Thread& operator[](size_t id) { return *threads[id]; }
|
MainThread* main_thread() { return static_cast<MainThread*>((*this)[0]); }
|
||||||
bool use_sleeping_threads() const { return useSleepingThreads; }
|
|
||||||
int min_split_depth() const { return minimumSplitDepth; }
|
|
||||||
size_t size() const { return threads.size(); }
|
|
||||||
Thread* main_thread() { return threads[0]; }
|
|
||||||
|
|
||||||
void wake_up() const;
|
|
||||||
void sleep() const;
|
|
||||||
void read_uci_options();
|
void read_uci_options();
|
||||||
bool available_slave_exists(Thread* master) const;
|
Thread* available_slave(Thread* master) const;
|
||||||
void set_timer(int msec);
|
void wait_for_think_finished();
|
||||||
void wait_for_search_finished();
|
void start_thinking(const Position&, const Search::LimitsType&,
|
||||||
void start_searching(const Position&, const Search::LimitsType&,
|
const std::vector<Move>&, Search::StateStackPtr&);
|
||||||
const std::vector<Move>&, Search::StateStackPtr&);
|
|
||||||
|
|
||||||
template <bool Fake>
|
bool sleepWhileIdle;
|
||||||
Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue, Move* bestMove,
|
Depth minimumSplitDepth;
|
||||||
Depth depth, Move threatMove, int moveCount, MovePicker* mp, int nodeType);
|
size_t maxThreadsPerSplitPoint;
|
||||||
private:
|
|
||||||
friend class Thread;
|
|
||||||
|
|
||||||
std::vector<Thread*> threads;
|
|
||||||
Thread* timer;
|
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
ConditionVariable sleepCondition;
|
ConditionVariable sleepCondition;
|
||||||
Depth minimumSplitDepth;
|
TimerThread* timer;
|
||||||
int maxThreadsPerSplitPoint;
|
|
||||||
bool useSleepingThreads;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern ThreadPool Threads;
|
extern ThreadPool Threads;
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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,7 +29,7 @@ namespace {
|
|||||||
/// Constants
|
/// Constants
|
||||||
|
|
||||||
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 float MaxRatio = 3.0f; // When in trouble, we can step over reserved time with this ratio
|
const float MaxRatio = 7.0f; // When in trouble, we can step over reserved time with this ratio
|
||||||
const float StealRatio = 0.33f; // However we must not steal time from remaining moves over this ratio
|
const float StealRatio = 0.33f; // However we must not steal time from remaining moves over this ratio
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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,42 +25,32 @@
|
|||||||
|
|
||||||
TranspositionTable TT; // Our global transposition table
|
TranspositionTable TT; // Our global transposition table
|
||||||
|
|
||||||
TranspositionTable::TranspositionTable() {
|
|
||||||
|
|
||||||
size = generation = 0;
|
|
||||||
entries = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
TranspositionTable::~TranspositionTable() {
|
|
||||||
|
|
||||||
delete [] entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// TranspositionTable::set_size() sets the size of the transposition table,
|
/// TranspositionTable::set_size() sets the size of the transposition table,
|
||||||
/// measured in megabytes. Transposition table consists of a power of 2 number of
|
/// measured in megabytes. Transposition table consists of a power of 2 number
|
||||||
/// TTCluster and each cluster consists of ClusterSize number of TTEntries. Each
|
/// of clusters and each cluster consists of ClusterSize number of TTEntry.
|
||||||
/// non-empty entry contains information of exactly one position.
|
|
||||||
|
|
||||||
void TranspositionTable::set_size(size_t mbSize) {
|
void TranspositionTable::set_size(size_t mbSize) {
|
||||||
|
|
||||||
size_t newSize = 1ULL << msb((mbSize << 20) / sizeof(TTCluster));
|
assert(msb((mbSize << 20) / sizeof(TTEntry)) < 32);
|
||||||
|
|
||||||
if (newSize == size)
|
uint32_t size = ClusterSize << msb((mbSize << 20) / sizeof(TTEntry[ClusterSize]));
|
||||||
|
|
||||||
|
if (hashMask == size - ClusterSize)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
size = newSize;
|
free(mem);
|
||||||
delete [] entries;
|
mem = malloc(size * sizeof(TTEntry) + (CACHE_LINE_SIZE - 1));
|
||||||
entries = new (std::nothrow) TTCluster[size];
|
if (!mem)
|
||||||
|
|
||||||
if (!entries)
|
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to allocate " << mbSize
|
std::cerr << "Failed to allocate " << mbSize
|
||||||
<< "MB for transposition table." << std::endl;
|
<< "MB for transposition table." << std::endl;
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
clear(); // Operator new is not guaranteed to initialize memory to zero
|
table = (TTEntry*)((size_t(mem) + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1));
|
||||||
|
hashMask = size - ClusterSize;
|
||||||
|
clear(); // Newly allocated block of memory is not initialized
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -70,7 +60,7 @@ void TranspositionTable::set_size(size_t mbSize) {
|
|||||||
|
|
||||||
void TranspositionTable::clear() {
|
void TranspositionTable::clear() {
|
||||||
|
|
||||||
memset(entries, 0, size * sizeof(TTCluster));
|
memset(table, 0, (hashMask + ClusterSize) * sizeof(TTEntry));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -82,23 +72,23 @@ void TranspositionTable::clear() {
|
|||||||
/// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from
|
/// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from
|
||||||
/// a previous search, or if the depth of t1 is bigger than the depth of t2.
|
/// a previous search, or if the depth of t1 is bigger than the depth of t2.
|
||||||
|
|
||||||
void TranspositionTable::store(const Key posKey, Value v, Bound t, Depth d, Move m, Value statV, Value kingD) {
|
void TranspositionTable::store(const Key key, Value v, Bound t, Depth d, Move m, Value statV, Value kingD) {
|
||||||
|
|
||||||
int c1, c2, c3;
|
int c1, c2, c3;
|
||||||
TTEntry *tte, *replace;
|
TTEntry *tte, *replace;
|
||||||
uint32_t posKey32 = posKey >> 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
|
||||||
|
|
||||||
tte = replace = first_entry(posKey);
|
tte = replace = first_entry(key);
|
||||||
|
|
||||||
for (int i = 0; i < ClusterSize; i++, tte++)
|
for (unsigned i = 0; i < ClusterSize; i++, tte++)
|
||||||
{
|
{
|
||||||
if (!tte->key() || tte->key() == posKey32) // Empty or overwrite old
|
if (!tte->key() || tte->key() == key32) // Empty or overwrite old
|
||||||
{
|
{
|
||||||
// Preserve any existing ttMove
|
// Preserve any existing ttMove
|
||||||
if (m == MOVE_NONE)
|
if (m == MOVE_NONE)
|
||||||
m = tte->move();
|
m = tte->move();
|
||||||
|
|
||||||
tte->save(posKey32, v, t, d, m, generation, statV, kingD);
|
tte->save(key32, v, t, d, m, generation, statV, kingD);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +100,7 @@ void TranspositionTable::store(const Key posKey, Value v, Bound t, Depth d, Move
|
|||||||
if (c1 + c2 + c3 > 0)
|
if (c1 + c2 + c3 > 0)
|
||||||
replace = tte;
|
replace = tte;
|
||||||
}
|
}
|
||||||
replace->save(posKey32, v, t, d, m, generation, statV, kingD);
|
replace->save(key32, v, t, d, m, generation, statV, kingD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -118,24 +108,14 @@ void TranspositionTable::store(const Key posKey, Value v, Bound t, Depth d, Move
|
|||||||
/// transposition table. Returns a pointer to the TTEntry or NULL if
|
/// transposition table. Returns a pointer to the TTEntry or NULL if
|
||||||
/// position is not found.
|
/// position is not found.
|
||||||
|
|
||||||
TTEntry* TranspositionTable::probe(const Key posKey) const {
|
TTEntry* TranspositionTable::probe(const Key key) const {
|
||||||
|
|
||||||
uint32_t posKey32 = posKey >> 32;
|
TTEntry* tte = first_entry(key);
|
||||||
TTEntry* tte = first_entry(posKey);
|
uint32_t key32 = key >> 32;
|
||||||
|
|
||||||
for (int i = 0; i < ClusterSize; i++, tte++)
|
for (unsigned i = 0; i < ClusterSize; i++, tte++)
|
||||||
if (tte->key() == posKey32)
|
if (tte->key() == key32)
|
||||||
return tte;
|
return tte;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// TranspositionTable::new_search() is called at the beginning of every new
|
|
||||||
/// search. It increments the "generation" variable, which is used to
|
|
||||||
/// distinguish transposition table entries from previous searches from
|
|
||||||
/// entries from the current search.
|
|
||||||
|
|
||||||
void TranspositionTable::new_search() {
|
|
||||||
generation++;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
class TTEntry {
|
class TTEntry {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value statV, Value statM) {
|
void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev, Value em) {
|
||||||
|
|
||||||
key32 = (uint32_t)k;
|
key32 = (uint32_t)k;
|
||||||
move16 = (uint16_t)m;
|
move16 = (uint16_t)m;
|
||||||
@@ -52,63 +52,53 @@ public:
|
|||||||
generation8 = (uint8_t)g;
|
generation8 = (uint8_t)g;
|
||||||
value16 = (int16_t)v;
|
value16 = (int16_t)v;
|
||||||
depth16 = (int16_t)d;
|
depth16 = (int16_t)d;
|
||||||
staticValue = (int16_t)statV;
|
evalValue = (int16_t)ev;
|
||||||
staticMargin = (int16_t)statM;
|
evalMargin = (int16_t)em;
|
||||||
}
|
}
|
||||||
void set_generation(int g) { generation8 = (uint8_t)g; }
|
void set_generation(int g) { generation8 = (uint8_t)g; }
|
||||||
|
|
||||||
uint32_t key() const { return key32; }
|
uint32_t key() const { return key32; }
|
||||||
Depth depth() const { return (Depth)depth16; }
|
Depth depth() const { return (Depth)depth16; }
|
||||||
Move move() const { return (Move)move16; }
|
Move move() const { return (Move)move16; }
|
||||||
Value value() const { return (Value)value16; }
|
Value value() const { return (Value)value16; }
|
||||||
Bound type() const { return (Bound)bound; }
|
Bound type() const { return (Bound)bound; }
|
||||||
int generation() const { return (int)generation8; }
|
int generation() const { return (int)generation8; }
|
||||||
Value static_value() const { return (Value)staticValue; }
|
Value eval_value() const { return (Value)evalValue; }
|
||||||
Value static_value_margin() const { return (Value)staticMargin; }
|
Value eval_margin() const { return (Value)evalMargin; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t key32;
|
uint32_t key32;
|
||||||
uint16_t move16;
|
uint16_t move16;
|
||||||
uint8_t bound, generation8;
|
uint8_t bound, generation8;
|
||||||
int16_t value16, depth16, staticValue, staticMargin;
|
int16_t value16, depth16, evalValue, evalMargin;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// This is the number of TTEntry slots for each cluster
|
/// A TranspositionTable consists of a power of 2 number of clusters and each
|
||||||
const int ClusterSize = 4;
|
/// cluster consists of ClusterSize number of TTEntry. Each non-empty entry
|
||||||
|
/// contains information of exactly one position. Size of a cluster shall not be
|
||||||
|
/// bigger than a cache line size. In case it is less, it should be padded to
|
||||||
/// TTCluster consists of ClusterSize number of TTEntries. Size of TTCluster
|
/// guarantee always aligned accesses.
|
||||||
/// must not be bigger than a cache line size. In case it is less, it should
|
|
||||||
/// be padded to guarantee always aligned accesses.
|
|
||||||
|
|
||||||
struct TTCluster {
|
|
||||||
TTEntry data[ClusterSize];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// The transposition table class. This is basically just a huge array containing
|
|
||||||
/// TTCluster objects, and a few methods for writing and reading entries.
|
|
||||||
|
|
||||||
class TranspositionTable {
|
class TranspositionTable {
|
||||||
|
|
||||||
TranspositionTable(const TranspositionTable&);
|
static const unsigned ClusterSize = 4; // A cluster is 64 Bytes
|
||||||
TranspositionTable& operator=(const TranspositionTable&);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TranspositionTable();
|
~TranspositionTable() { free(mem); }
|
||||||
~TranspositionTable();
|
void new_search() { generation++; }
|
||||||
|
|
||||||
|
TTEntry* probe(const Key key) const;
|
||||||
|
TTEntry* first_entry(const Key key) const;
|
||||||
|
void refresh(const TTEntry* tte) const;
|
||||||
void set_size(size_t mbSize);
|
void set_size(size_t mbSize);
|
||||||
void clear();
|
void clear();
|
||||||
void store(const Key posKey, Value v, Bound type, Depth d, Move m, Value statV, Value kingD);
|
void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV, Value kingD);
|
||||||
TTEntry* probe(const Key posKey) const;
|
|
||||||
void new_search();
|
|
||||||
TTEntry* first_entry(const Key posKey) const;
|
|
||||||
void refresh(const TTEntry* tte) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t size;
|
uint32_t hashMask;
|
||||||
TTCluster* entries;
|
TTEntry* table;
|
||||||
|
void* mem;
|
||||||
uint8_t generation; // Size must be not bigger then TTEntry::generation8
|
uint8_t generation; // Size must be not bigger then TTEntry::generation8
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -119,9 +109,9 @@ extern TranspositionTable TT;
|
|||||||
/// a cluster given a position. The lowest order bits of the key are used to
|
/// a cluster given a position. The lowest order bits of the key are used to
|
||||||
/// get the index of the cluster.
|
/// get the index of the cluster.
|
||||||
|
|
||||||
inline TTEntry* TranspositionTable::first_entry(const Key posKey) const {
|
inline TTEntry* TranspositionTable::first_entry(const Key key) const {
|
||||||
|
|
||||||
return entries[((uint32_t)posKey) & (size - 1)].data;
|
return table + ((uint32_t)key & hashMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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,13 +35,14 @@
|
|||||||
/// | only in 64-bit mode. For compiling requires hardware with
|
/// | only in 64-bit mode. For compiling requires hardware with
|
||||||
/// | popcnt support.
|
/// | popcnt support.
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64) && !defined(IS_64BIT)
|
||||||
# include <intrin.h> // MSVC popcnt and bsfq instrinsics
|
# include <intrin.h> // MSVC popcnt and bsfq instrinsics
|
||||||
# define IS_64BIT
|
# define IS_64BIT
|
||||||
# define USE_BSFQ
|
# define USE_BSFQ
|
||||||
@@ -51,10 +52,15 @@
|
|||||||
# include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic
|
# include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
# if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER))
|
||||||
|
# include <xmmintrin.h> // Intel and Microsoft header for _mm_prefetch()
|
||||||
|
# endif
|
||||||
|
|
||||||
|
#define CACHE_LINE_SIZE 64
|
||||||
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
|
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
|
||||||
# define CACHE_LINE_ALIGNMENT __declspec(align(64))
|
# define CACHE_LINE_ALIGNMENT __declspec(align(CACHE_LINE_SIZE))
|
||||||
#else
|
#else
|
||||||
# define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64)))
|
# define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(CACHE_LINE_SIZE)))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
@@ -132,12 +138,20 @@ enum CastleRight { // Defined as in PolyGlot book hash key
|
|||||||
WHITE_OOO = 2,
|
WHITE_OOO = 2,
|
||||||
BLACK_OO = 4,
|
BLACK_OO = 4,
|
||||||
BLACK_OOO = 8,
|
BLACK_OOO = 8,
|
||||||
ALL_CASTLES = 15
|
ALL_CASTLES = 15,
|
||||||
|
CASTLE_RIGHT_NB = 16
|
||||||
};
|
};
|
||||||
|
|
||||||
enum CastlingSide {
|
enum CastlingSide {
|
||||||
KING_SIDE,
|
KING_SIDE,
|
||||||
QUEEN_SIDE
|
QUEEN_SIDE,
|
||||||
|
CASTLING_SIDE_NB = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Phase {
|
||||||
|
PHASE_ENDGAME = 0,
|
||||||
|
PHASE_MIDGAME = 128,
|
||||||
|
MG = 0, EG = 1, PHASE_NB = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ScaleFactor {
|
enum ScaleFactor {
|
||||||
@@ -168,8 +182,6 @@ enum Value {
|
|||||||
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
|
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
|
||||||
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN,
|
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN,
|
||||||
|
|
||||||
Mg = 0, Eg = 1,
|
|
||||||
|
|
||||||
PawnValueMg = 198, PawnValueEg = 258,
|
PawnValueMg = 198, PawnValueEg = 258,
|
||||||
KnightValueMg = 817, KnightValueEg = 846,
|
KnightValueMg = 817, KnightValueEg = 846,
|
||||||
BishopValueMg = 836, BishopValueEg = 857,
|
BishopValueMg = 836, BishopValueEg = 857,
|
||||||
@@ -179,17 +191,19 @@ enum Value {
|
|||||||
|
|
||||||
enum PieceType {
|
enum PieceType {
|
||||||
NO_PIECE_TYPE = 0, ALL_PIECES = 0,
|
NO_PIECE_TYPE = 0, ALL_PIECES = 0,
|
||||||
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
|
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6,
|
||||||
|
PIECE_TYPE_NB = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Piece {
|
enum Piece {
|
||||||
NO_PIECE = 16, // color_of(NO_PIECE) == NO_COLOR
|
NO_PIECE = 0,
|
||||||
W_PAWN = 1, W_KNIGHT = 2, W_BISHOP = 3, W_ROOK = 4, W_QUEEN = 5, W_KING = 6,
|
W_PAWN = 1, W_KNIGHT = 2, W_BISHOP = 3, W_ROOK = 4, W_QUEEN = 5, W_KING = 6,
|
||||||
B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14
|
B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14,
|
||||||
|
PIECE_NB = 16
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Color {
|
enum Color {
|
||||||
WHITE, BLACK, NO_COLOR
|
WHITE, BLACK, NO_COLOR, COLOR_NB = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Depth {
|
enum Depth {
|
||||||
@@ -215,6 +229,8 @@ enum Square {
|
|||||||
SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8,
|
SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8,
|
||||||
SQ_NONE,
|
SQ_NONE,
|
||||||
|
|
||||||
|
SQUARE_NB = 64,
|
||||||
|
|
||||||
DELTA_N = 8,
|
DELTA_N = 8,
|
||||||
DELTA_E = 1,
|
DELTA_E = 1,
|
||||||
DELTA_S = -8,
|
DELTA_S = -8,
|
||||||
@@ -229,11 +245,11 @@ enum Square {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum File {
|
enum File {
|
||||||
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H
|
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Rank {
|
enum Rank {
|
||||||
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8
|
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -320,9 +336,9 @@ inline Score apply_weight(Score v, Score w) {
|
|||||||
|
|
||||||
namespace Zobrist {
|
namespace Zobrist {
|
||||||
|
|
||||||
extern Key psq[2][8][64]; // [color][pieceType][square / piece count]
|
extern Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
|
||||||
extern Key enpassant[8]; // [file]
|
extern Key enpassant[FILE_NB];
|
||||||
extern Key castle[16]; // [castleRight]
|
extern Key castle[CASTLE_RIGHT_NB];
|
||||||
extern Key side;
|
extern Key side;
|
||||||
extern Key exclusion;
|
extern Key exclusion;
|
||||||
|
|
||||||
@@ -331,9 +347,9 @@ namespace Zobrist {
|
|||||||
|
|
||||||
CACHE_LINE_ALIGNMENT
|
CACHE_LINE_ALIGNMENT
|
||||||
|
|
||||||
extern Score pieceSquareTable[16][64]; // [piece][square]
|
extern Score pieceSquareTable[PIECE_NB][SQUARE_NB];
|
||||||
extern Value PieceValue[2][18]; // [Mg / Eg][piece / pieceType]
|
extern Value PieceValue[PHASE_NB][PIECE_NB];
|
||||||
extern int SquareDistance[64][64]; // [square][square]
|
extern int SquareDistance[SQUARE_NB][SQUARE_NB];
|
||||||
|
|
||||||
struct MoveStack {
|
struct MoveStack {
|
||||||
Move move;
|
Move move;
|
||||||
@@ -377,6 +393,7 @@ inline PieceType type_of(Piece p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline Color color_of(Piece p) {
|
inline Color color_of(Piece p) {
|
||||||
|
assert(p != NO_PIECE);
|
||||||
return Color(p >> 3);
|
return Color(p >> 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,12 +442,12 @@ inline int square_distance(Square s1, Square s2) {
|
|||||||
return SquareDistance[s1][s2];
|
return SquareDistance[s1][s2];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline char file_to_char(File f) {
|
inline char file_to_char(File f, bool tolower = true) {
|
||||||
return char(f - FILE_A + int('a'));
|
return char(f - FILE_A + (tolower ? 'a' : 'A'));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline char rank_to_char(Rank r) {
|
inline char rank_to_char(Rank r) {
|
||||||
return char(r - RANK_1 + int('1'));
|
return char(r - RANK_1 + '1');
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Square pawn_push(Color c) {
|
inline Square pawn_push(Color c) {
|
||||||
@@ -459,7 +476,7 @@ inline Move make_move(Square from, Square to) {
|
|||||||
|
|
||||||
template<MoveType T>
|
template<MoveType T>
|
||||||
inline Move make(Square from, Square to, PieceType pt = KNIGHT) {
|
inline Move make(Square from, Square to, PieceType pt = KNIGHT) {
|
||||||
return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12)) ;
|
return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool is_ok(Move m) {
|
inline bool is_ok(Move m) {
|
||||||
@@ -473,21 +490,4 @@ inline const std::string square_to_string(Square s) {
|
|||||||
return ch;
|
return ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Our insertion sort implementation, works with pointers and iterators and is
|
|
||||||
/// guaranteed to be stable, as is needed.
|
|
||||||
template<typename T, typename K>
|
|
||||||
void sort(K first, K last)
|
|
||||||
{
|
|
||||||
T tmp;
|
|
||||||
K p, q;
|
|
||||||
|
|
||||||
for (p = first + 1; p < last; p++)
|
|
||||||
{
|
|
||||||
tmp = *p;
|
|
||||||
for (q = p; q != first && *(q-1) < tmp; --q)
|
|
||||||
*q = *(q-1);
|
|
||||||
*q = tmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // !defined(TYPES_H_INCLUDED)
|
#endif // !defined(TYPES_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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -43,7 +44,7 @@ namespace {
|
|||||||
|
|
||||||
void set_option(istringstream& up);
|
void set_option(istringstream& up);
|
||||||
void set_position(Position& pos, istringstream& up);
|
void set_position(Position& pos, istringstream& up);
|
||||||
void go(Position& pos, istringstream& up);
|
void go(const Position& pos, istringstream& up);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -55,78 +56,32 @@ namespace {
|
|||||||
void UCI::loop(const string& args) {
|
void UCI::loop(const string& args) {
|
||||||
|
|
||||||
Position pos(StartFEN, false, Threads.main_thread()); // The root position
|
Position pos(StartFEN, false, Threads.main_thread()); // The root position
|
||||||
string cmd, token;
|
string token, cmd = args;
|
||||||
|
|
||||||
while (token != "quit")
|
do {
|
||||||
{
|
if (args.empty() && !getline(cin, cmd)) // Block here waiting for input
|
||||||
if (!args.empty())
|
|
||||||
cmd = args;
|
|
||||||
|
|
||||||
else if (!getline(cin, cmd)) // Block here waiting for input
|
|
||||||
cmd = "quit";
|
cmd = "quit";
|
||||||
|
|
||||||
istringstream is(cmd);
|
istringstream is(cmd);
|
||||||
|
|
||||||
is >> skipws >> token;
|
is >> skipws >> token;
|
||||||
|
|
||||||
if (token == "quit" || token == "stop")
|
if (token == "quit" || token == "stop" || token == "ponderhit")
|
||||||
{
|
{
|
||||||
Search::Signals.stop = true;
|
// GUI sends 'ponderhit' to tell us to ponder on the same move the
|
||||||
Threads.wait_for_search_finished(); // Cannot quit while threads are running
|
// 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
|
||||||
else if (token == "ponderhit")
|
// switching from pondering to normal search.
|
||||||
{
|
if (token != "ponderhit" || Search::Signals.stopOnPonderhit)
|
||||||
// The opponent has played the expected move. GUI sends "ponderhit" if
|
|
||||||
// we were told to ponder on the same move the opponent has played. We
|
|
||||||
// should continue searching but switching from pondering to normal search.
|
|
||||||
Search::Limits.ponder = false;
|
|
||||||
|
|
||||||
if (Search::Signals.stopOnPonderhit)
|
|
||||||
{
|
{
|
||||||
Search::Signals.stop = true;
|
Search::Signals.stop = true;
|
||||||
Threads.main_thread()->wake_up(); // Could be sleeping
|
Threads.main_thread()->notify_one(); // Could be sleeping
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
Search::Limits.ponder = false;
|
||||||
}
|
}
|
||||||
|
else if (token == "perft" && (is >> token)) // Read perft depth
|
||||||
else if (token == "go")
|
|
||||||
go(pos, is);
|
|
||||||
|
|
||||||
else if (token == "ucinewgame")
|
|
||||||
{ /* Avoid returning "Unknown command" */ }
|
|
||||||
|
|
||||||
else if (token == "isready")
|
|
||||||
sync_cout << "readyok" << sync_endl;
|
|
||||||
|
|
||||||
else if (token == "position")
|
|
||||||
set_position(pos, is);
|
|
||||||
|
|
||||||
else if (token == "setoption")
|
|
||||||
set_option(is);
|
|
||||||
|
|
||||||
else if (token == "d")
|
|
||||||
pos.print();
|
|
||||||
|
|
||||||
else if (token == "flip")
|
|
||||||
pos.flip();
|
|
||||||
|
|
||||||
else if (token == "eval")
|
|
||||||
sync_cout << Eval::trace(pos) << sync_endl;
|
|
||||||
|
|
||||||
else if (token == "bench")
|
|
||||||
benchmark(pos, is);
|
|
||||||
|
|
||||||
else if (token == "key")
|
|
||||||
sync_cout << "key: " << hex << pos.key()
|
|
||||||
<< "\nmaterial key: " << pos.material_key()
|
|
||||||
<< "\npawn key: " << pos.pawn_key() << sync_endl;
|
|
||||||
|
|
||||||
else if (token == "uci")
|
|
||||||
sync_cout << "id name " << engine_info(true)
|
|
||||||
<< "\n" << Options
|
|
||||||
<< "\nuciok" << sync_endl;
|
|
||||||
|
|
||||||
else if (token == "perft" && (is >> token)) // Read depth
|
|
||||||
{
|
{
|
||||||
stringstream ss;
|
stringstream ss;
|
||||||
|
|
||||||
@@ -135,16 +90,33 @@ void UCI::loop(const string& args) {
|
|||||||
|
|
||||||
benchmark(pos, ss);
|
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 == "ucinewgame") { /* Avoid returning "Unknown command" */ }
|
||||||
|
else if (token == "go") go(pos, is);
|
||||||
|
else if (token == "position") set_position(pos, is);
|
||||||
|
else if (token == "setoption") set_option(is);
|
||||||
|
else if (token == "flip") pos.flip();
|
||||||
|
else if (token == "bench") benchmark(pos, is);
|
||||||
|
else if (token == "d") sync_cout << pos.pretty() << sync_endl;
|
||||||
|
else if (token == "isready") sync_cout << "readyok" << sync_endl;
|
||||||
|
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
|
||||||
else
|
else
|
||||||
sync_cout << "Unknown command: " << cmd << sync_endl;
|
sync_cout << "Unknown command: " << cmd << sync_endl;
|
||||||
|
|
||||||
if (!args.empty()) // Command line arguments have one-shot behaviour
|
} while (token != "quit" && args.empty()); // Args have one-shot behaviour
|
||||||
{
|
|
||||||
Threads.wait_for_search_finished();
|
Threads.wait_for_think_finished(); // Cannot quit while search is running
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -173,7 +145,7 @@ namespace {
|
|||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pos.from_fen(fen, Options["UCI_Chess960"], Threads.main_thread());
|
pos.set(fen, Options["UCI_Chess960"], Threads.main_thread());
|
||||||
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>());
|
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>());
|
||||||
|
|
||||||
// Parse move list (if any)
|
// Parse move list (if any)
|
||||||
@@ -210,10 +182,10 @@ namespace {
|
|||||||
|
|
||||||
|
|
||||||
// go() is called when engine receives the "go" UCI command. The function sets
|
// go() is called when engine receives the "go" UCI command. The function sets
|
||||||
// the thinking time and other parameters from the input string, and then starts
|
// the thinking time and other parameters from the input string, and starts
|
||||||
// the search.
|
// the search.
|
||||||
|
|
||||||
void go(Position& pos, istringstream& is) {
|
void go(const Position& pos, istringstream& is) {
|
||||||
|
|
||||||
Search::LimitsType limits;
|
Search::LimitsType limits;
|
||||||
vector<Move> searchMoves;
|
vector<Move> searchMoves;
|
||||||
@@ -221,31 +193,23 @@ namespace {
|
|||||||
|
|
||||||
while (is >> token)
|
while (is >> token)
|
||||||
{
|
{
|
||||||
if (token == "wtime")
|
if (token == "searchmoves")
|
||||||
is >> limits.time[WHITE];
|
|
||||||
else if (token == "btime")
|
|
||||||
is >> limits.time[BLACK];
|
|
||||||
else if (token == "winc")
|
|
||||||
is >> limits.inc[WHITE];
|
|
||||||
else if (token == "binc")
|
|
||||||
is >> limits.inc[BLACK];
|
|
||||||
else if (token == "movestogo")
|
|
||||||
is >> limits.movestogo;
|
|
||||||
else if (token == "depth")
|
|
||||||
is >> limits.depth;
|
|
||||||
else if (token == "nodes")
|
|
||||||
is >> limits.nodes;
|
|
||||||
else if (token == "movetime")
|
|
||||||
is >> limits.movetime;
|
|
||||||
else if (token == "infinite")
|
|
||||||
limits.infinite = true;
|
|
||||||
else if (token == "ponder")
|
|
||||||
limits.ponder = true;
|
|
||||||
else if (token == "searchmoves")
|
|
||||||
while (is >> token)
|
while (is >> token)
|
||||||
searchMoves.push_back(move_from_uci(pos, token));
|
searchMoves.push_back(move_from_uci(pos, token));
|
||||||
|
|
||||||
|
else if (token == "wtime") is >> limits.time[WHITE];
|
||||||
|
else if (token == "btime") is >> limits.time[BLACK];
|
||||||
|
else if (token == "winc") is >> limits.inc[WHITE];
|
||||||
|
else if (token == "binc") is >> limits.inc[BLACK];
|
||||||
|
else if (token == "movestogo") is >> limits.movestogo;
|
||||||
|
else if (token == "depth") is >> limits.depth;
|
||||||
|
else if (token == "nodes") is >> limits.nodes;
|
||||||
|
else if (token == "movetime") is >> limits.movetime;
|
||||||
|
else if (token == "mate") is >> limits.mate;
|
||||||
|
else if (token == "infinite") limits.infinite = true;
|
||||||
|
else if (token == "ponder") limits.ponder = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Threads.start_searching(pos, limits, searchMoves, SetupStates);
|
Threads.start_thinking(pos, limits, searchMoves, SetupStates);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
@@ -64,6 +64,7 @@ void init(OptionsMap& o) {
|
|||||||
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["Mobility (Middle Game)"] = Option(100, 0, 200, on_eval);
|
o["Mobility (Middle Game)"] = 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["Passed Pawns (Middle Game)"] = Option(100, 0, 200, on_eval);
|
o["Passed Pawns (Middle Game)"] = Option(100, 0, 200, on_eval);
|
||||||
@@ -71,11 +72,11 @@ void init(OptionsMap& o) {
|
|||||||
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(msd, 4, 7, on_threads);
|
o["Min Split Depth"] = Option(msd, 4, 12, on_threads);
|
||||||
o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads);
|
o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads);
|
||||||
o["Threads"] = Option(cpus, 1, MAX_THREADS, on_threads);
|
o["Threads"] = Option(cpus, 1, MAX_THREADS, on_threads);
|
||||||
o["Use Sleeping Threads"] = Option(true, on_threads);
|
o["Use Sleeping Threads"] = Option(true);
|
||||||
o["Hash"] = Option(32, 4, 8192, on_hash_size);
|
o["Hash"] = Option(32, 1, 8192, on_hash_size);
|
||||||
o["Clear Hash"] = Option(on_clear_hash);
|
o["Clear Hash"] = Option(on_clear_hash);
|
||||||
o["Ponder"] = Option(true);
|
o["Ponder"] = Option(true);
|
||||||
o["OwnBook"] = Option(false);
|
o["OwnBook"] = Option(false);
|
||||||
|
|||||||
@@ -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-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
Copyright (C) 2008-2013 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
|
||||||
|
|||||||
Reference in New Issue
Block a user