DroidFish: Updated stockfish engine to version 4.

This commit is contained in:
Peter Osterlund
2013-08-20 18:02:33 +00:00
parent 96a8ec79ac
commit bfc5782f52
41 changed files with 1477 additions and 1673 deletions

View File

@@ -4,12 +4,9 @@ include $(CLEAR_VARS)
LOCAL_MODULE := stockfish
LOCAL_SRC_FILES := \
evaluate.cpp notation.cpp search.cpp \
benchmark.cpp movegen.cpp tt.cpp \
bitbase.cpp main.cpp movepick.cpp uci.cpp \
bitboard.cpp pawns.cpp ucioption.cpp \
book.cpp material.cpp position.cpp \
endgame.cpp misc.cpp timeman.cpp thread.cpp
benchmark.cpp book.cpp main.cpp movegen.cpp pawns.cpp thread.cpp uci.cpp \
bitbase.cpp endgame.cpp material.cpp movepick.cpp position.cpp timeman.cpp ucioption.cpp \
bitboard.cpp evaluate.cpp misc.cpp notation.cpp search.cpp tt.cpp
LOCAL_CFLAGS := -DNO_PREFETCH=1 -O2

View File

@@ -118,7 +118,7 @@ void benchmark(const Position& current, istream& is) {
for (size_t i = 0; i < fens.size(); i++)
{
Position pos(fens[i], Options["UCI_Chess960"], Threads.main_thread());
Position pos(fens[i], Options["UCI_Chess960"], Threads.main());
cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl;

View File

@@ -39,9 +39,9 @@ namespace {
// 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)
// bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - 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);
return wksq + (bksq << 6) + (us << 12) + (file_of(psq) << 13) + ((RANK_7 - rank_of(psq)) << 15);
}
enum Result {
@@ -110,7 +110,7 @@ namespace {
wksq = Square((idx >> 0) & 0x3F);
bksq = Square((idx >> 6) & 0x3F);
us = Color ((idx >> 12) & 0x01);
psq = File((idx >> 13) & 3) | Rank(6 - (idx >> 15));
psq = File ((idx >> 13) & 0x03) | Rank(RANK_7 - (idx >> 15));
// Check if two pieces are on the same square or if a king can be captured
if ( wksq == psq || wksq == bksq || bksq == psq
@@ -148,12 +148,14 @@ namespace {
// as WIN, the position is classified WIN otherwise the current position is
// classified UNKNOWN.
const Color Them = (Us == WHITE ? BLACK : WHITE);
Result r = INVALID;
Bitboard b = StepAttacksBB[KING][Us == WHITE ? wksq : bksq];
while (b)
r |= Us == WHITE ? db[index(~Us, bksq, pop_lsb(&b), psq)]
: db[index(~Us, pop_lsb(&b), wksq, psq)];
r |= Us == WHITE ? db[index(Them, bksq, pop_lsb(&b), psq)]
: db[index(Them, pop_lsb(&b), wksq, psq)];
if (Us == WHITE && rank_of(psq) < RANK_7)
{

View File

@@ -42,14 +42,13 @@ Bitboard SquareBB[SQUARE_NB];
Bitboard FileBB[FILE_NB];
Bitboard RankBB[RANK_NB];
Bitboard AdjacentFilesBB[FILE_NB];
Bitboard ThisAndAdjacentFilesBB[FILE_NB];
Bitboard InFrontBB[COLOR_NB][RANK_NB];
Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard DistanceRingsBB[SQUARE_NB][8];
Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
Bitboard AttackSpanMask[COLOR_NB][SQUARE_NB];
Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
int SquareDistance[SQUARE_NB][SQUARE_NB];
@@ -84,7 +83,7 @@ namespace {
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard.
/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard.
#if !defined(USE_BSFQ)
#ifndef USE_BSFQ
Square lsb(Bitboard b) { return BSFTable[bsf_index(b)]; }
@@ -123,7 +122,7 @@ Square msb(Bitboard b) {
return (Square)(result + MS1BTable[b32]);
}
#endif // !defined(USE_BSFQ)
#endif // ifndef USE_BSFQ
/// Bitboards::print() prints a bitboard in an easily readable format to the
@@ -171,10 +170,7 @@ void Bitboards::init() {
}
for (File f = FILE_A; f <= FILE_H; f++)
{
AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
ThisAndAdjacentFilesBB[f] = FileBB[f] | AdjacentFilesBB[f];
}
for (Rank r = RANK_1; r < RANK_8; r++)
InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]);
@@ -183,19 +179,17 @@ void Bitboards::init() {
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)];
PassedPawnMask[c][s] = InFrontBB[c][rank_of(s)] & ThisAndAdjacentFilesBB[file_of(s)];
AttackSpanMask[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)];
PawnAttackSpan[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)];
PassedPawnMask[c][s] = ForwardBB[c][s] | PawnAttackSpan[c][s];
}
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
{
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (int d = 1; d < 8; d++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (SquareDistance[s1][s2] == d)
DistanceRingsBB[s1][d - 1] |= s2;
if (s1 != s2)
DistanceRingsBB[s1][SquareDistance[s1][s2] - 1] |= s2;
}
int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 },
{}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } };
@@ -322,7 +316,7 @@ namespace {
do magics[s] = pick_random(rk, booster);
while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6);
memset(attacks[s], 0, size * sizeof(Bitboard));
std::memset(attacks[s], 0, size * sizeof(Bitboard));
// A good magic must map every possible occupancy to an index that
// looks up the correct sliding attack in the attacks[s] database.

View File

@@ -18,7 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(BITBOARD_H_INCLUDED)
#ifndef BITBOARD_H_INCLUDED
#define BITBOARD_H_INCLUDED
#include "types.h"
@@ -37,6 +37,24 @@ bool probe_kpk(Square wksq, Square wpsq, Square bksq, Color us);
}
const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = FileABB << 1;
const Bitboard FileCBB = FileABB << 2;
const Bitboard FileDBB = FileABB << 3;
const Bitboard FileEBB = FileABB << 4;
const Bitboard FileFBB = FileABB << 5;
const Bitboard FileGBB = FileABB << 6;
const Bitboard FileHBB = FileABB << 7;
const Bitboard Rank1BB = 0xFF;
const Bitboard Rank2BB = Rank1BB << (8 * 1);
const Bitboard Rank3BB = Rank1BB << (8 * 2);
const Bitboard Rank4BB = Rank1BB << (8 * 3);
const Bitboard Rank5BB = Rank1BB << (8 * 4);
const Bitboard Rank6BB = Rank1BB << (8 * 5);
const Bitboard Rank7BB = Rank1BB << (8 * 6);
const Bitboard Rank8BB = Rank1BB << (8 * 7);
CACHE_LINE_ALIGNMENT
extern Bitboard RMasks[SQUARE_NB];
@@ -53,17 +71,18 @@ extern Bitboard SquareBB[SQUARE_NB];
extern Bitboard FileBB[FILE_NB];
extern Bitboard RankBB[RANK_NB];
extern Bitboard AdjacentFilesBB[FILE_NB];
extern Bitboard ThisAndAdjacentFilesBB[FILE_NB];
extern Bitboard InFrontBB[COLOR_NB][RANK_NB];
extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
extern Bitboard DistanceRingsBB[SQUARE_NB][8];
extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
extern Bitboard AttackSpanMask[COLOR_NB][SQUARE_NB];
extern Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
const Bitboard BlackSquares = 0xAA55AA55AA55AA55ULL;
extern int SquareDistance[SQUARE_NB][SQUARE_NB];
const Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
/// 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.
@@ -88,13 +107,34 @@ inline Bitboard operator^(Bitboard b, Square s) {
return b ^ SquareBB[s];
}
/// more_than_one() returns true if in 'b' there is more than one bit set
inline bool more_than_one(Bitboard b) {
return b & (b - 1);
}
inline int square_distance(Square s1, Square s2) {
return SquareDistance[s1][s2];
}
inline int file_distance(Square s1, Square s2) {
return abs(file_of(s1) - file_of(s2));
}
inline int rank_distance(Square s1, Square s2) {
return abs(rank_of(s1) - rank_of(s2));
}
/// shift_bb() moves bitboard one step along direction Delta. Mainly for pawns.
template<Square Delta>
inline Bitboard shift_bb(Bitboard b) {
return Delta == DELTA_N ? b << 8 : Delta == DELTA_S ? b >> 8
: Delta == DELTA_NE ? (b & ~FileHBB) << 9 : Delta == DELTA_SE ? (b & ~FileHBB) >> 7
: Delta == DELTA_NW ? (b & ~FileABB) << 7 : Delta == DELTA_SW ? (b & ~FileABB) >> 9
: 0;
}
/// rank_bb() and file_bb() take a file or a square as input and return
/// a bitboard representing all squares on the given file or rank.
@@ -116,7 +156,7 @@ inline Bitboard file_bb(Square s) {
}
/// adjacent_files_bb takes a file as input and returns a bitboard representing
/// adjacent_files_bb() takes a file as input and returns a bitboard representing
/// all squares on the adjacent files.
inline Bitboard adjacent_files_bb(File f) {
@@ -124,30 +164,17 @@ inline Bitboard adjacent_files_bb(File f) {
}
/// this_and_adjacent_files_bb takes a file as input and returns a bitboard
/// representing all squares on the given and adjacent files.
inline Bitboard this_and_adjacent_files_bb(File f) {
return ThisAndAdjacentFilesBB[f];
}
/// in_front_bb() takes a color and a rank or square as input, and returns a
/// bitboard representing all the squares on all ranks in front of the rank
/// (or square), from the given color's point of view. For instance,
/// in_front_bb(WHITE, RANK_5) will give all squares on ranks 6, 7 and 8, while
/// in_front_bb(BLACK, SQ_D3) will give all squares on ranks 1 and 2.
/// in_front_bb() takes a color and a rank as input, and returns a bitboard
/// representing all the squares on all ranks in front of the rank, from the
/// given color's point of view. For instance, in_front_bb(BLACK, RANK_3) will
/// give all squares on ranks 1 and 2.
inline Bitboard in_front_bb(Color c, Rank r) {
return InFrontBB[c][r];
}
inline Bitboard in_front_bb(Color c, Square s) {
return InFrontBB[c][rank_of(s)];
}
/// between_bb returns a bitboard representing all squares between two squares.
/// between_bb() returns a bitboard representing all squares between two squares.
/// For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with the bits for
/// square d5 and e6 set. If s1 and s2 are not on the same line, file or diagonal,
/// 0 is returned.
@@ -157,7 +184,7 @@ inline Bitboard between_bb(Square s1, Square s2) {
}
/// forward_bb takes a color and a square as input, and returns a bitboard
/// forward_bb() takes a color and a square as input, and returns a bitboard
/// representing all squares along the line in front of the square, from the
/// point of view of the given color. Definition of the table is:
/// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s)
@@ -167,27 +194,35 @@ inline Bitboard forward_bb(Color c, Square s) {
}
/// passed_pawn_mask takes a color and a square as input, and returns a
/// pawn_attack_span() takes a color and a square as input, and returns a bitboard
/// representing all squares that can be attacked by a pawn of the given color
/// when it moves along its file starting from the given square. Definition is:
/// PawnAttackSpan[c][s] = in_front_bb(c, s) & adjacent_files_bb(s);
inline Bitboard pawn_attack_span(Color c, Square s) {
return PawnAttackSpan[c][s];
}
/// passed_pawn_mask() takes a color and a square as input, and returns a
/// bitboard mask which can be used to test if a pawn of the given color on
/// the given square is a passed pawn. Definition of the table is:
/// PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_adjacent_files_bb(s)
/// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_bb(c, s)
inline Bitboard passed_pawn_mask(Color c, Square s) {
return PassedPawnMask[c][s];
}
/// attack_span_mask takes a color and a square as input, and returns a bitboard
/// representing all squares that can be attacked by a pawn of the given color
/// when it moves along its file starting from the given square. Definition is:
/// AttackSpanMask[c][s] = in_front_bb(c, s) & adjacent_files_bb(s);
/// squares_of_color() returns a bitboard representing all squares with the same
/// color of the given square.
inline Bitboard attack_span_mask(Color c, Square s) {
return AttackSpanMask[c][s];
inline Bitboard squares_of_color(Square s) {
return DarkSquares & s ? DarkSquares : ~DarkSquares;
}
/// squares_aligned returns true if the squares s1, s2 and s3 are aligned
/// squares_aligned() returns true if the squares s1, s2 and s3 are aligned
/// either on a straight or on a diagonal line.
inline bool squares_aligned(Square s1, Square s2, Square s3) {
@@ -196,14 +231,6 @@ inline bool squares_aligned(Square s1, Square s2, Square s3) {
}
/// same_color_squares() returns a bitboard representing all squares with
/// the same color of the given square.
inline Bitboard same_color_squares(Square s) {
return BlackSquares & s ? BlackSquares : ~BlackSquares;
}
/// Functions for computing sliding attack bitboards. Function attacks_bb() takes
/// a square and a bitboard of occupied squares as input, and returns a bitboard
/// representing all squares attacked by Pt (bishop or rook) on the given square.
@@ -231,7 +258,7 @@ inline Bitboard attacks_bb(Square s, Bitboard occ) {
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard.
/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard.
#if defined(USE_BSFQ)
#ifdef USE_BSFQ
# if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
@@ -284,7 +311,7 @@ FORCE_INLINE Square pop_lsb(Bitboard* b) {
return s;
}
#else // if !defined(USE_BSFQ)
#else // if defined(USE_BSFQ)
extern Square msb(Bitboard b);
extern Square lsb(Bitboard b);
@@ -292,4 +319,4 @@ extern Square pop_lsb(Bitboard* b);
#endif
#endif // !defined(BITBOARD_H_INCLUDED)
#endif // #ifndef BITBOARD_H_INCLUDED

View File

@@ -18,7 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(BITCOUNT_H_INCLUDED)
#ifndef BITCOUNT_H_INCLUDED
#define BITCOUNT_H_INCLUDED
#include <cassert>
@@ -81,7 +81,7 @@ inline int popcount<CNT_32_MAX15>(Bitboard b) {
template<>
inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#if !defined(USE_POPCNT)
#ifndef USE_POPCNT
assert(false);
return b != 0; // Avoid 'b not used' warning
@@ -102,4 +102,4 @@ inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#endif
}
#endif // !defined(BITCOUNT_H_INCLUDED)
#endif // #ifndef BITCOUNT_H_INCLUDED

View File

@@ -436,9 +436,9 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
move = make<PROMOTION>(from_sq(move), to_sq(move), PieceType(pt + 1));
// Add 'special move' flags and verify it is legal
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
if (move == (ml.move() ^ type_of(ml.move())))
return ml.move();
for (MoveList<LEGAL> it(pos); *it; ++it)
if (move == (*it ^ type_of(*it)))
return *it;
return MOVE_NONE;
}

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(BOOK_H_INCLUDED)
#ifndef BOOK_H_INCLUDED
#define BOOK_H_INCLUDED
#include <fstream>
@@ -42,4 +42,4 @@ private:
std::string fileName;
};
#endif // !defined(BOOK_H_INCLUDED)
#endif // #ifndef BOOK_H_INCLUDED

View File

@@ -89,7 +89,10 @@ namespace {
Endgames::Endgames() {
add<KK>("KK");
add<KPK>("KPK");
add<KBK>("KBK");
add<KNK>("KNK");
add<KNNK>("KNNK");
add<KBNK>("KBNK");
add<KRKP>("KRKP");
@@ -130,28 +133,25 @@ template<>
Value Endgame<KXK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO);
assert(!pos.count<PAWN>(weakerSide));
assert(!pos.checkers()); // Eval is never called when in check
// Stalemate detection with lone king
if ( pos.side_to_move() == weakerSide
&& !pos.checkers()
&& !MoveList<LEGAL>(pos).size()) {
if (pos.side_to_move() == weakerSide && !MoveList<LEGAL>(pos).size())
return VALUE_DRAW;
}
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
Value result = pos.non_pawn_material(strongerSide)
+ pos.piece_count(strongerSide, PAWN) * PawnValueEg
+ pos.count<PAWN>(strongerSide) * PawnValueEg
+ MateTable[loserKSq]
+ DistanceBonus[square_distance(winnerKSq, loserKSq)];
if ( pos.piece_count(strongerSide, QUEEN)
|| pos.piece_count(strongerSide, ROOK)
|| pos.bishop_pair(strongerSide)) {
if ( pos.count<QUEEN>(strongerSide)
|| pos.count<ROOK>(strongerSide)
|| pos.bishop_pair(strongerSide))
result += VALUE_KNOWN_WIN;
}
return strongerSide == pos.side_to_move() ? result : -result;
}
@@ -162,16 +162,16 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
template<>
Value Endgame<KBNK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO);
assert(pos.non_pawn_material(strongerSide) == KnightValueMg + BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, KNIGHT) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.count<BISHOP>(strongerSide) == 1);
assert(pos.count<KNIGHT>(strongerSide) == 1);
assert(pos.count< PAWN>(strongerSide) == 0);
assert(pos.count< PAWN>(weakerSide ) == 0);
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0];
Square bishopSq = pos.list<BISHOP>(strongerSide)[0];
// kbnk_mate_table() tries to drive toward corners A1 or H8,
// if we have a bishop that cannot reach the above squares we
@@ -196,8 +196,8 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.count<PAWN>(strongerSide) == 1);
assert(pos.count<PAWN>(weakerSide ) == 0);
Square wksq, bksq, wpsq;
Color us;
@@ -206,14 +206,14 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
{
wksq = pos.king_square(WHITE);
bksq = pos.king_square(BLACK);
wpsq = pos.piece_list(WHITE, PAWN)[0];
wpsq = pos.list<PAWN>(WHITE)[0];
us = pos.side_to_move();
}
else
{
wksq = ~pos.king_square(BLACK);
bksq = ~pos.king_square(WHITE);
wpsq = ~pos.piece_list(BLACK, PAWN)[0];
wpsq = ~pos.list<PAWN>(BLACK)[0];
us = ~pos.side_to_move();
}
@@ -241,17 +241,17 @@ template<>
Value Endgame<KRKP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide) == 0);
assert(pos.piece_count(weakerSide, PAWN) == 1);
assert(pos.count<PAWN>(strongerSide) == 0);
assert(pos.count<PAWN>(weakerSide ) == 1);
Square wksq, wrsq, bksq, bpsq;
int tempo = (pos.side_to_move() == strongerSide);
wksq = pos.king_square(strongerSide);
wrsq = pos.piece_list(strongerSide, ROOK)[0];
bksq = pos.king_square(weakerSide);
bpsq = pos.piece_list(weakerSide, PAWN)[0];
wrsq = pos.list<ROOK>(strongerSide)[0];
bpsq = pos.list<PAWN>(weakerSide)[0];
if (strongerSide == BLACK)
{
@@ -298,10 +298,10 @@ template<>
Value Endgame<KRKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide ) == BishopValueMg);
assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.piece_count(weakerSide, BISHOP) == 1);
assert(pos.count<BISHOP>(weakerSide ) == 1);
assert(pos.count< PAWN>(weakerSide ) == 0);
assert(pos.count< PAWN>(strongerSide) == 0);
Value result = Value(MateTable[pos.king_square(weakerSide)]);
return strongerSide == pos.side_to_move() ? result : -result;
@@ -314,15 +314,15 @@ template<>
Value Endgame<KRKN>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide ) == KnightValueMg);
assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
assert(pos.count<KNIGHT>(weakerSide ) == 1);
assert(pos.count< PAWN>(weakerSide ) == 0);
assert(pos.count< PAWN>(strongerSide) == 0);
const int penalty[8] = { 0, 10, 14, 20, 30, 42, 58, 80 };
Square bksq = pos.king_square(weakerSide);
Square bnsq = pos.piece_list(weakerSide, KNIGHT)[0];
Square bnsq = pos.list<KNIGHT>(weakerSide)[0];
Value result = Value(MateTable[bksq] + penalty[square_distance(bksq, bnsq)]);
return strongerSide == pos.side_to_move() ? result : -result;
}
@@ -335,13 +335,13 @@ 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);
assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO);
assert(pos.count<PAWN>(strongerSide) == 0);
assert(pos.count<PAWN>(weakerSide ) == 1);
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
Square pawnSq = pos.piece_list(weakerSide, PAWN)[0];
Square pawnSq = pos.list<PAWN>(weakerSide)[0];
Value result = QueenValueEg
- PawnValueEg
@@ -368,9 +368,9 @@ template<>
Value Endgame<KQKR>::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 ) == RookValueMg);
assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.count<PAWN>(strongerSide) == 0);
assert(pos.count<PAWN>(weakerSide ) == 0);
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
@@ -386,16 +386,16 @@ Value Endgame<KQKR>::operator()(const Position& pos) const {
template<>
Value Endgame<KBBKN>::operator()(const Position& pos) const {
assert(pos.piece_count(strongerSide, BISHOP) == 2);
assert(pos.non_pawn_material(strongerSide) == 2 * BishopValueMg);
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
assert(pos.non_pawn_material(weakerSide ) == KnightValueMg);
assert(pos.count<BISHOP>(strongerSide) == 2);
assert(pos.count<KNIGHT>(weakerSide ) == 1);
assert(!pos.pieces(PAWN));
Value result = BishopValueEg;
Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide);
Square nsq = pos.piece_list(weakerSide, KNIGHT)[0];
Square nsq = pos.list<KNIGHT>(weakerSide)[0];
// Bonus for attacking king close to defending king
result += Value(DistanceBonus[square_distance(wksq, bksq)]);
@@ -410,17 +410,13 @@ Value Endgame<KBBKN>::operator()(const Position& pos) const {
}
/// K and two minors vs K and one or two minors or K and two knights against
/// king alone are always draw.
template<>
Value Endgame<KmmKm>::operator()(const Position&) const {
return VALUE_DRAW;
}
/// Some cases of trivial draws
template<> Value Endgame<KK>::operator()(const Position&) const { return VALUE_DRAW; }
template<> Value Endgame<KBK>::operator()(const Position&) const { return VALUE_DRAW; }
template<> Value Endgame<KNK>::operator()(const Position&) const { return VALUE_DRAW; }
template<> Value Endgame<KNNK>::operator()(const Position&) const { return VALUE_DRAW; }
template<> Value Endgame<KmmKm>::operator()(const Position&) const { return VALUE_DRAW; }
template<>
Value Endgame<KNNK>::operator()(const Position&) const {
return VALUE_DRAW;
}
/// K, bishop and one or more pawns vs K. It checks for draws with rook pawns and
/// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW
@@ -430,20 +426,20 @@ template<>
ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, PAWN) >= 1);
assert(pos.count<BISHOP>(strongerSide) == 1);
assert(pos.count< PAWN>(strongerSide) >= 1);
// No assertions about the material of weakerSide, because we want draws to
// be detected even when the weaker side has some pawns.
Bitboard pawns = pos.pieces(strongerSide, PAWN);
File pawnFile = file_of(pos.piece_list(strongerSide, PAWN)[0]);
File pawnFile = file_of(pos.list<PAWN>(strongerSide)[0]);
// All pawns are on a single rook file ?
if ( (pawnFile == FILE_A || pawnFile == FILE_H)
&& !(pawns & ~file_bb(pawnFile)))
{
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0];
Square bishopSq = pos.list<BISHOP>(strongerSide)[0];
Square queeningSq = relative_square(strongerSide, pawnFile | RANK_8);
Square kingSq = pos.king_square(weakerSide);
@@ -477,7 +473,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
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)
&& pos.count<PAWN>(weakerSide) >= 1)
{
// Get weaker pawn closest to opponent's queening square
Bitboard wkPawns = pos.pieces(weakerSide, PAWN);
@@ -485,7 +481,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
Square strongerKingSq = pos.king_square(strongerSide);
Square weakerKingSq = pos.king_square(weakerSide);
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0];
Square bishopSq = pos.list<BISHOP>(strongerSide)[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.
@@ -505,10 +501,10 @@ template<>
ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
assert(pos.piece_count(strongerSide, QUEEN) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.piece_count(weakerSide, ROOK) == 1);
assert(pos.piece_count(weakerSide, PAWN) >= 1);
assert(pos.count<QUEEN>(strongerSide) == 1);
assert(pos.count< PAWN>(strongerSide) == 0);
assert(pos.count< ROOK>(weakerSide ) == 1);
assert(pos.count< PAWN>(weakerSide ) >= 1);
Square kingSq = pos.king_square(weakerSide);
if ( relative_rank(weakerSide, kingSq) <= RANK_2
@@ -517,7 +513,7 @@ ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
&& (pos.pieces(weakerSide, PAWN) & rank_bb(relative_rank(weakerSide, RANK_2)))
&& (pos.attacks_from<KING>(kingSq) & pos.pieces(weakerSide, PAWN)))
{
Square rsq = pos.piece_list(weakerSide, ROOK)[0];
Square rsq = pos.list<ROOK>(weakerSide)[0];
if (pos.attacks_from<PAWN>(rsq, strongerSide) & pos.pieces(weakerSide, PAWN))
return SCALE_FACTOR_DRAW;
}
@@ -535,15 +531,15 @@ template<>
ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide) == RookValueMg);
assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.count<PAWN>(strongerSide) == 1);
assert(pos.count<PAWN>(weakerSide ) == 0);
Square wksq = pos.king_square(strongerSide);
Square wrsq = pos.piece_list(strongerSide, ROOK)[0];
Square wpsq = pos.piece_list(strongerSide, PAWN)[0];
Square bksq = pos.king_square(weakerSide);
Square brsq = pos.piece_list(weakerSide, ROOK)[0];
Square wrsq = pos.list<ROOK>(strongerSide)[0];
Square wpsq = pos.list<PAWN>(strongerSide)[0];
Square brsq = pos.list<ROOK>(weakerSide)[0];
// Orient the board in such a way that the stronger side is white, and the
// pawn is on the left half of the board.
@@ -653,12 +649,12 @@ template<>
ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 2);
assert(pos.non_pawn_material(weakerSide) == RookValueMg);
assert(pos.piece_count(weakerSide, PAWN) == 1);
assert(pos.count<PAWN>(strongerSide) == 2);
assert(pos.count<PAWN>(weakerSide ) == 1);
Square wpsq1 = pos.piece_list(strongerSide, PAWN)[0];
Square wpsq2 = pos.piece_list(strongerSide, PAWN)[1];
Square wpsq1 = pos.list<PAWN>(strongerSide)[0];
Square wpsq2 = pos.list<PAWN>(strongerSide)[1];
Square bksq = pos.king_square(weakerSide);
// Does the stronger side have a passed pawn?
@@ -691,9 +687,9 @@ template<>
ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.piece_count(strongerSide, PAWN) >= 2);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.count<PAWN>(strongerSide) >= 2);
assert(pos.count<PAWN>(weakerSide ) == 0);
Square ksq = pos.king_square(weakerSide);
Bitboard pawns = pos.pieces(strongerSide, PAWN);
@@ -704,7 +700,7 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
// Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1
|| ( file_of(ksq) == FILE_A
&& !(in_front_bb(strongerSide, ksq) & pawns)))
&& !(in_front_bb(strongerSide, rank_of(ksq)) & pawns)))
return SCALE_FACTOR_DRAW;
}
// Are all pawns on the 'h' file?
@@ -713,7 +709,7 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
// Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1
|| ( file_of(ksq) == FILE_H
&& !(in_front_bb(strongerSide, ksq) & pawns)))
&& !(in_front_bb(strongerSide, rank_of(ksq)) & pawns)))
return SCALE_FACTOR_DRAW;
}
return SCALE_FACTOR_NONE;
@@ -728,15 +724,15 @@ template<>
ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide ) == BishopValueMg);
assert(pos.piece_count(weakerSide, BISHOP) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.count<BISHOP>(strongerSide) == 1);
assert(pos.count<BISHOP>(weakerSide ) == 1);
assert(pos.count< PAWN>(strongerSide) == 1);
assert(pos.count< PAWN>(weakerSide ) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0];
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP)[0];
Square weakerBishopSq = pos.piece_list(weakerSide, BISHOP)[0];
Square pawnSq = pos.list<PAWN>(strongerSide)[0];
Square strongerBishopSq = pos.list<BISHOP>(strongerSide)[0];
Square weakerBishopSq = pos.list<BISHOP>(weakerSide)[0];
Square weakerKingSq = pos.king_square(weakerSide);
// Case 1: Defending king blocks the pawn, and cannot be driven away
@@ -783,21 +779,21 @@ template<>
ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 2);
assert(pos.non_pawn_material(weakerSide ) == BishopValueMg);
assert(pos.piece_count(weakerSide, BISHOP) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.count<BISHOP>(strongerSide) == 1);
assert(pos.count<BISHOP>(weakerSide ) == 1);
assert(pos.count< PAWN>(strongerSide) == 2);
assert(pos.count< PAWN>(weakerSide ) == 0);
Square wbsq = pos.piece_list(strongerSide, BISHOP)[0];
Square bbsq = pos.piece_list(weakerSide, BISHOP)[0];
Square wbsq = pos.list<BISHOP>(strongerSide)[0];
Square bbsq = pos.list<BISHOP>(weakerSide)[0];
if (!opposite_colors(wbsq, bbsq))
return SCALE_FACTOR_NONE;
Square ksq = pos.king_square(weakerSide);
Square psq1 = pos.piece_list(strongerSide, PAWN)[0];
Square psq2 = pos.piece_list(strongerSide, PAWN)[1];
Square psq1 = pos.list<PAWN>(strongerSide)[0];
Square psq2 = pos.list<PAWN>(strongerSide)[1];
Rank r1 = rank_of(psq1);
Rank r2 = rank_of(psq2);
Square blockSq1, blockSq2;
@@ -858,14 +854,14 @@ template<>
ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide ) == KnightValueMg);
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.count<BISHOP>(strongerSide) == 1);
assert(pos.count<KNIGHT>(weakerSide ) == 1);
assert(pos.count< PAWN>(strongerSide) == 1);
assert(pos.count< PAWN>(weakerSide ) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0];
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP)[0];
Square pawnSq = pos.list<PAWN>(strongerSide)[0];
Square strongerBishopSq = pos.list<BISHOP>(strongerSide)[0];
Square weakerKingSq = pos.king_square(weakerSide);
if ( file_of(weakerKingSq) == file_of(pawnSq)
@@ -885,12 +881,12 @@ template<>
ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == KnightValueMg);
assert(pos.piece_count(strongerSide, KNIGHT) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.count<KNIGHT>(strongerSide) == 1);
assert(pos.count< PAWN>(strongerSide) == 1);
assert(pos.count< PAWN>(weakerSide ) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0];
Square pawnSq = pos.list<PAWN>(strongerSide)[0];
Square weakerKingSq = pos.king_square(weakerSide);
if ( pawnSq == relative_square(strongerSide, SQ_A7)
@@ -910,8 +906,8 @@ ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
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 pawnSq = pos.list<PAWN>(strongerSide)[0];
Square bishopSq = pos.list<BISHOP>(weakerSide)[0];
Square weakerKingSq = pos.king_square(weakerSide);
// King needs to get close to promoting pawn to prevent knight from blocking.
@@ -933,12 +929,12 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO);
assert(pos.piece_count(WHITE, PAWN) == 1);
assert(pos.piece_count(BLACK, PAWN) == 1);
assert(pos.count<PAWN>(WHITE) == 1);
assert(pos.count<PAWN>(BLACK) == 1);
Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide);
Square wpsq = pos.piece_list(strongerSide, PAWN)[0];
Square wpsq = pos.list<PAWN>(strongerSide)[0];
Color us = pos.side_to_move();
if (strongerSide == BLACK)

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(ENDGAME_H_INCLUDED)
#ifndef ENDGAME_H_INCLUDED
#define ENDGAME_H_INCLUDED
#include <map>
@@ -33,6 +33,10 @@ enum EndgameType {
// Evaluation functions
KK, // K vs K
KBK, // KB vs K
KNK, // KN vs K
KNNK, // KNN vs K
KXK, // Generic "mate lone king" eval
KBNK, // KBN vs K
KPK, // KP vs K
@@ -42,7 +46,6 @@ enum EndgameType {
KQKP, // KQ vs KP
KQKR, // KQ vs KR
KBBKN, // KBB vs KN
KNNK, // KNN vs K
KmmKm, // K and two minors vs K and one or two minors
@@ -119,4 +122,4 @@ public:
{ return eg = map(eg).count(key) ? map(eg)[key] : NULL; }
};
#endif // !defined(ENDGAME_H_INCLUDED)
#endif // #ifndef ENDGAME_H_INCLUDED

View File

@@ -31,6 +31,20 @@
namespace {
enum ExtendedPieceType { // Used for tracing
PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, UNSTOPPABLE, SPACE, TOTAL
};
namespace Tracing {
Score scores[COLOR_NB][TOTAL + 1];
std::stringstream stream;
void add(int idx, Score term_w, Score term_b = SCORE_ZERO);
void row(const char* name, int idx);
std::string do_trace(const Position& pos);
}
// Struct EvalInfo contains various information computed and collected
// by the evaluation functions.
struct EvalInfo {
@@ -72,10 +86,10 @@ namespace {
};
// Evaluation grain size, must be a power of 2
const int GrainSize = 8;
const int GrainSize = 4;
// Evaluation weights, initialized from UCI options
enum { Mobility, PassedPawns, Space, KingDangerUs, KingDangerThem };
enum { Mobility, PawnStructure, PassedPawns, Space, KingDangerUs, KingDangerThem };
Score Weights[6];
typedef Value V;
@@ -88,33 +102,33 @@ namespace {
//
// Values modified by Joona Kiiski
const Score WeightsInternal[] = {
S(289, 344), S(221, 273), S(46, 0), S(271, 0), S(307, 0)
S(289, 344), S(233, 201), S(221, 273), S(46, 0), S(271, 0), S(307, 0)
};
// MobilityBonus[PieceType][attacked] contains mobility bonuses for middle and
// end game, indexed by piece type and number of attacked squares not occupied
// by friendly pieces.
// MobilityBonus[PieceType][attacked] contains bonuses for middle and end
// game, indexed by piece type and number of attacked squares not occupied by
// friendly pieces.
const Score MobilityBonus[][32] = {
{}, {},
{ S(-38,-33), S(-25,-23), S(-12,-13), S( 0, -3), S(12, 7), S(25, 17), // Knights
S( 31, 22), S( 38, 27), S( 38, 27) },
{ S(-25,-30), S(-11,-16), S( 3, -2), S(17, 12), S(31, 26), S(45, 40), // Bishops
S( 57, 52), S( 65, 60), S( 71, 65), S(74, 69), S(76, 71), S(78, 73),
S( 79, 74), S( 80, 75), S( 81, 76), S(81, 76) },
{ S(-20,-36), S(-14,-19), S( -8, -3), S(-2, 13), S( 4, 29), S(10, 46), // Rooks
S( 14, 62), S( 19, 79), S( 23, 95), S(26,106), S(27,111), S(28,114),
S( 29,116), S( 30,117), S( 31,118), S(32,118) },
{ S(-10,-18), S( -8,-13), S( -6, -7), S(-3, -2), S(-1, 3), S( 1, 8), // Queens
S( 3, 13), S( 5, 19), S( 8, 23), S(10, 27), S(12, 32), S(15, 34),
S( 16, 35), S( 17, 35), S( 18, 35), S(20, 35), S(20, 35), S(20, 35),
S( 20, 35), S( 20, 35), S( 20, 35), S(20, 35), S(20, 35), S(20, 35),
S( 20, 35), S( 20, 35), S( 20, 35), S(20, 35), S(20, 35), S(20, 35),
S( 20, 35), S( 20, 35) }
{ S(-35,-30), S(-22,-20), S(-9,-10), S( 3, 0), S(15, 10), S(27, 20), // Knights
S( 37, 28), S( 42, 31), S(44, 33) },
{ S(-22,-27), S( -8,-13), S( 6, 1), S(20, 15), S(34, 29), S(48, 43), // Bishops
S( 60, 55), S( 68, 63), S(74, 68), S(77, 72), S(80, 75), S(82, 77),
S( 84, 79), S( 86, 81), S(87, 82), S(87, 82) },
{ S(-17,-33), S(-11,-16), S(-5, 0), S( 1, 16), S( 7, 32), S(13, 48), // Rooks
S( 18, 64), S( 22, 80), S(26, 96), S(29,109), S(31,115), S(33,119),
S( 35,122), S( 36,123), S(37,124), S(38,124) },
{ S(-12,-20), S( -8,-13), S(-5, -7), S(-2, -1), S( 1, 5), S( 4, 11), // Queens
S( 7, 17), S( 10, 23), S(13, 29), S(16, 34), S(18, 38), S(20, 40),
S( 22, 41), S( 23, 41), S(24, 41), S(25, 41), S(25, 41), S(25, 41),
S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41),
S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41),
S( 25, 41), S( 25, 41) }
};
// OutpostBonus[PieceType][Square] contains outpost bonuses of knights and
// bishops, indexed by piece type and square (from white's point of view).
const Value OutpostBonus[][SQUARE_NB] = {
// Outpost[PieceType][Square] contains bonuses of knights and bishops, indexed
// by piece type and square (from white's point of view).
const Value Outpost[][SQUARE_NB] = {
{
// 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
@@ -132,9 +146,9 @@ namespace {
V(0), V(5), V(8), V(8), V(8), V(8), V(5), V(0) }
};
// ThreatBonus[attacking][attacked] contains threat bonuses according to
// which piece type attacks which one.
const Score ThreatBonus[][PIECE_TYPE_NB] = {
// Threat[attacking][attacked] contains bonuses according to which piece
// type attacks which one.
const Score Threat[][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(24, 49), S( 0, 0), S(41,100), S(41,100) }, // BISHOP
@@ -142,45 +156,31 @@ namespace {
{ S(0, 0), S(15, 39), S(15, 39), S(15, 39), S(15, 39), S( 0, 0) } // QUEEN
};
// ThreatenedByPawnPenalty[PieceType] contains a penalty according to which
// piece type is attacked by an enemy pawn.
const Score ThreatenedByPawnPenalty[] = {
// ThreatenedByPawn[PieceType] contains a penalty according to which piece
// type is attacked by an enemy pawn.
const Score ThreatenedByPawn[] = {
S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118)
};
#undef S
const Score BishopPinBonus = make_score(66, 11);
// Bonus for having the side to move (modified by Joona Kiiski)
const Score Tempo = make_score(24, 11);
// Rooks and queens on the 7th rank
const Score RookOn7thBonus = make_score(11, 20);
const Score QueenOn7thBonus = make_score( 3, 8);
// Rooks and queens attacking pawns on the same rank
const Score RookOnPawnBonus = make_score(10, 28);
const Score QueenOnPawnBonus = make_score( 4, 20);
// Rooks on open files (modified by Joona Kiiski)
const Score RookOpenFileBonus = make_score(43, 21);
const Score RookHalfOpenFileBonus = make_score(19, 10);
// Penalty for rooks trapped inside a friendly king which has lost the
// right to castle.
const Value TrappedRookPenalty = Value(180);
// Penalty for bishop with pawns on the same coloured squares
const Score BishopPawnsPenalty = make_score(8, 12);
const Score BishopPin = make_score(66, 11);
const Score RookOn7th = make_score(11, 20);
const Score QueenOn7th = make_score( 3, 8);
const Score RookOnPawn = make_score(10, 28);
const Score QueenOnPawn = make_score( 4, 20);
const Score RookOpenFile = make_score(43, 21);
const Score RookSemiopenFile = make_score(19, 10);
const Score BishopPawns = make_score( 8, 12);
const Score MinorBehindPawn = make_score(16, 0);
const Score UndefendedMinor = make_score(25, 10);
const Score TrappedRook = make_score(90, 0);
// 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
// happen in Chess960 games.
const Score TrappedBishopA1H1Penalty = make_score(100, 100);
// Penalty for an undefended bishop or knight
const Score UndefendedMinorPenalty = make_score(25, 10);
const Score TrappedBishopA1H1 = make_score(50, 50);
// The SpaceMask[Color] contains the area of the board which is considered
// by the space evaluation. In the middle game, each side is given a bonus
@@ -196,24 +196,24 @@ namespace {
};
// King danger constants and variables. The king danger scores are taken
// from the KingDangerTable[]. Various little "meta-bonuses" measuring
// from the KingDanger[]. Various little "meta-bonuses" measuring
// the strength of the enemy attack are added up into an integer, which
// is used as an index to KingDangerTable[].
// is used as an index to KingDanger[].
//
// KingAttackWeights[PieceType] contains king attack weights by piece type
const int KingAttackWeights[] = { 0, 0, 2, 2, 3, 5 };
// Bonuses for enemy's safe checks
const int QueenContactCheckBonus = 6;
const int RookContactCheckBonus = 4;
const int QueenCheckBonus = 3;
const int RookCheckBonus = 2;
const int BishopCheckBonus = 1;
const int KnightCheckBonus = 1;
const int QueenContactCheck = 6;
const int RookContactCheck = 4;
const int QueenCheck = 3;
const int RookCheck = 2;
const int BishopCheck = 1;
const int KnightCheck = 1;
// InitKingDanger[Square] contains penalties based on the position of the
// KingExposed[Square] contains penalties based on the position of the
// defending king, indexed by king's square (from white's point of view).
const int InitKingDanger[] = {
const int KingExposed[] = {
2, 0, 2, 5, 5, 2, 0, 2,
2, 2, 4, 8, 8, 4, 2, 2,
7, 10, 12, 12, 12, 12, 10, 7,
@@ -224,19 +224,9 @@ namespace {
15, 15, 15, 15, 15, 15, 15, 15
};
// KingDangerTable[Color][attackUnits] contains the actual king danger
// weighted scores, indexed by color and by a calculated integer number.
Score KingDangerTable[COLOR_NB][128];
// TracedTerms[Color][PieceType || TracedType] contains a breakdown of the
// evaluation terms, used when tracing.
Score TracedScores[COLOR_NB][16];
std::stringstream TraceStream;
enum TracedType {
PST = 8, IMBALANCE = 9, MOBILITY = 10, THREAT = 11,
PASSED = 12, UNSTOPPABLE = 13, SPACE = 14, TOTAL = 15
};
// KingDanger[Color][attackUnits] contains the actual king danger weighted
// scores, indexed by color and by a calculated integer number.
Score KingDanger[COLOR_NB][128];
// Function prototypes
template<bool Trace>
@@ -249,24 +239,23 @@ namespace {
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility);
template<Color Us, bool Trace>
Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]);
Score evaluate_king(const Position& pos, const EvalInfo& ei, Value margins[]);
template<Color Us, bool Trace>
Score evaluate_threats(const Position& pos, const EvalInfo& ei);
template<Color Us, bool Trace>
Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei);
template<Color Us>
Score evaluate_threats(const Position& pos, EvalInfo& ei);
int evaluate_space(const Position& pos, const EvalInfo& ei);
template<Color Us>
int evaluate_space(const Position& pos, EvalInfo& ei);
template<Color Us>
Score evaluate_passed_pawns(const Position& pos, EvalInfo& ei);
Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei);
Score evaluate_unstoppable_pawns(const Position& pos, const EvalInfo& ei);
Value interpolate(const Score& v, Phase ph, ScaleFactor sf);
Score apply_weight(Score v, Score w);
Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight);
double to_cp(Value v);
void trace_add(int idx, Score term_w, Score term_b = SCORE_ZERO);
void trace_row(const char* name, int idx);
}
@@ -281,13 +270,22 @@ namespace Eval {
}
/// trace() is like evaluate() but instead of a value returns a string suitable
/// to be print on stdout with the detailed descriptions and values of each
/// evaluation term. Used mainly for debugging.
std::string trace(const Position& pos) {
return Tracing::do_trace(pos);
}
/// init() computes evaluation weights from the corresponding UCI parameters
/// and setup king tables.
void init() {
Weights[Mobility] = weight_option("Mobility (Middle Game)", "Mobility (Endgame)", WeightsInternal[Mobility]);
Weights[PassedPawns] = weight_option("Passed Pawns (Middle Game)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]);
Weights[Mobility] = weight_option("Mobility (Midgame)", "Mobility (Endgame)", WeightsInternal[Mobility]);
Weights[PawnStructure] = weight_option("Pawn Structure (Midgame)", "Pawn Structure (Endgame)", WeightsInternal[PawnStructure]);
Weights[PassedPawns] = weight_option("Passed Pawns (Midgame)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]);
Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]);
Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]);
Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]);
@@ -299,57 +297,11 @@ namespace Eval {
{
t = std::min(Peak, std::min(int(0.4 * i * i), t + MaxSlope));
KingDangerTable[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]);
KingDangerTable[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]);
KingDanger[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]);
KingDanger[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]);
}
}
/// trace() is like evaluate() but instead of a value returns a string suitable
/// to be print on stdout with the detailed descriptions and values of each
/// evaluation term. Used mainly for debugging.
std::string trace(const Position& pos) {
Value margin;
std::string totals;
Search::RootColor = pos.side_to_move();
TraceStream.str("");
TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
memset(TracedScores, 0, 2 * 16 * sizeof(Score));
do_evaluate<true>(pos, margin);
totals = TraceStream.str();
TraceStream.str("");
TraceStream << std::setw(21) << "Eval term " << "| White | Black | Total \n"
<< " | MG EG | MG EG | MG EG \n"
<< "---------------------+-------------+-------------+---------------\n";
trace_row("Material, PST, Tempo", PST);
trace_row("Material imbalance", IMBALANCE);
trace_row("Pawns", PAWN);
trace_row("Knights", KNIGHT);
trace_row("Bishops", BISHOP);
trace_row("Rooks", ROOK);
trace_row("Queens", QUEEN);
trace_row("Mobility", MOBILITY);
trace_row("King safety", KING);
trace_row("Threats", THREAT);
trace_row("Passed pawns", PASSED);
trace_row("Unstoppable pawns", UNSTOPPABLE);
trace_row("Space", SPACE);
TraceStream << "---------------------+-------------+-------------+---------------\n";
trace_row("Total", TOTAL);
TraceStream << totals;
return TraceStream.str();
}
} // namespace Eval
@@ -388,7 +340,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
// Probe the pawn hash table
ei.pi = Pawns::probe(pos, th->pawnsTable);
score += ei.pi->pawns_value();
score += apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]);
// Initialize attack and king safety bitboards
init_eval_info<WHITE>(pos, ei);
@@ -406,12 +358,12 @@ Value do_evaluate(const Position& pos, Value& margin) {
- evaluate_king<BLACK, Trace>(pos, ei, margins);
// Evaluate tactical threats, we need full attack information including king
score += evaluate_threats<WHITE>(pos, ei)
- evaluate_threats<BLACK>(pos, ei);
score += evaluate_threats<WHITE, Trace>(pos, ei)
- evaluate_threats<BLACK, Trace>(pos, ei);
// Evaluate passed pawns, we need full attack information including king
score += evaluate_passed_pawns<WHITE>(pos, ei)
- evaluate_passed_pawns<BLACK>(pos, ei);
score += evaluate_passed_pawns<WHITE, Trace>(pos, ei)
- evaluate_passed_pawns<BLACK, Trace>(pos, ei);
// If one side has only a king, check whether exists any unstoppable passed pawn
if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK))
@@ -421,7 +373,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
if (ei.mi->space_weight())
{
int s = evaluate_space<WHITE>(pos, ei) - evaluate_space<BLACK>(pos, ei);
score += apply_weight(make_score(s * ei.mi->space_weight(), 0), Weights[Space]);
score += apply_weight(s * ei.mi->space_weight(), Weights[Space]);
}
// Scale winning side if position is more drawish that what it appears
@@ -440,7 +392,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
{
// Check for KBP vs KB with only a single pawn that is almost
// certainly a draw or at least two pawns.
bool one_pawn = (pos.piece_count(WHITE, PAWN) + pos.piece_count(BLACK, PAWN) == 1);
bool one_pawn = (pos.count<PAWN>(WHITE) + pos.count<PAWN>(BLACK) == 1);
sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32);
}
else
@@ -455,18 +407,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
// In case of tracing add all single evaluation contributions for both white and black
if (Trace)
{
trace_add(PST, pos.psq_score());
trace_add(IMBALANCE, ei.mi->material_value());
trace_add(PAWN, ei.pi->pawns_value());
trace_add(MOBILITY, apply_weight(mobilityWhite, Weights[Mobility]), apply_weight(mobilityBlack, Weights[Mobility]));
trace_add(THREAT, evaluate_threats<WHITE>(pos, ei), evaluate_threats<BLACK>(pos, ei));
trace_add(PASSED, evaluate_passed_pawns<WHITE>(pos, ei), evaluate_passed_pawns<BLACK>(pos, ei));
trace_add(UNSTOPPABLE, evaluate_unstoppable_pawns(pos, ei));
Score w = make_score(ei.mi->space_weight() * evaluate_space<WHITE>(pos, ei), 0);
Score b = make_score(ei.mi->space_weight() * evaluate_space<BLACK>(pos, ei), 0);
trace_add(SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space]));
trace_add(TOTAL, score);
TraceStream << "\nUncertainty margin: White: " << to_cp(margins[WHITE])
Tracing::add(PST, pos.psq_score());
Tracing::add(IMBALANCE, ei.mi->material_value());
Tracing::add(PAWN, ei.pi->pawns_value());
Tracing::add(UNSTOPPABLE, evaluate_unstoppable_pawns(pos, ei));
Score w = ei.mi->space_weight() * evaluate_space<WHITE>(pos, ei);
Score b = ei.mi->space_weight() * evaluate_space<BLACK>(pos, ei);
Tracing::add(SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space]));
Tracing::add(TOTAL, score);
Tracing::stream << "\nUncertainty margin: White: " << to_cp(margins[WHITE])
<< ", Black: " << to_cp(margins[BLACK])
<< "\nScaling: " << std::noshowpos
<< std::setw(6) << 100.0 * ei.mi->game_phase() / 128.0 << "% MG, "
@@ -486,15 +435,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
void init_eval_info(const Position& pos, EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Down = (Us == WHITE ? DELTA_S : DELTA_N);
Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them));
ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us);
// Init king safety tables only if we are going to use them
if ( pos.piece_count(Us, QUEEN)
&& pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg)
if (pos.count<QUEEN>(Us) && pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg)
{
ei.kingRing[Them] = (b | (Us == WHITE ? b >> 8 : b << 8));
ei.kingRing[Them] = b | shift_bb<Down>(b);
b &= ei.attackedBy[Us][PAWN];
ei.kingAttackersCount[Us] = b ? popcount<Max15>(b) / 2 : 0;
ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0;
@@ -513,14 +462,14 @@ Value do_evaluate(const Position& pos, Value& margin) {
assert (Piece == BISHOP || Piece == KNIGHT);
// Initial bonus based on square
Value bonus = OutpostBonus[Piece == BISHOP][relative_square(Us, s)];
Value bonus = Outpost[Piece == BISHOP][relative_square(Us, s)];
// Increase bonus if supported by pawn, especially if the opponent has
// no minor piece which can exchange the outpost piece.
if (bonus && (ei.attackedBy[Us][PAWN] & s))
{
if ( !pos.pieces(Them, KNIGHT)
&& !(same_color_squares(s) & pos.pieces(Them, BISHOP)))
&& !(squares_of_color(s) & pos.pieces(Them, BISHOP)))
bonus += bonus + bonus / 2;
else
bonus += bonus / 2;
@@ -535,27 +484,20 @@ Value do_evaluate(const Position& pos, Value& margin) {
Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score& mobility, Bitboard mobilityArea) {
Bitboard b;
Square s, ksq;
int mob;
File f;
Square s;
Score score = SCORE_ZERO;
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square* pl = pos.piece_list(Us, Piece);
const Square* pl = pos.list<Piece>(Us);
ei.attackedBy[Us][Piece] = 0;
while ((s = *pl++) != SQ_NONE)
{
// Find attacked squares, including x-ray attacks for bishops and rooks
if (Piece == KNIGHT || Piece == QUEEN)
b = pos.attacks_from<Piece>(s);
else if (Piece == BISHOP)
b = attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(Us, QUEEN));
else if (Piece == ROOK)
b = attacks_bb<ROOK>(s, pos.pieces() ^ pos.pieces(Us, ROOK, QUEEN));
else
assert(false);
b = Piece == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(Us, QUEEN))
: Piece == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(Us, ROOK, QUEEN))
: pos.attacks_from<Piece>(s);
ei.attackedBy[Us][Piece] |= b;
@@ -568,110 +510,91 @@ Value do_evaluate(const Position& pos, Value& margin) {
ei.kingAdjacentZoneAttacksCount[Us] += popcount<Max15>(bb);
}
mob = (Piece != QUEEN ? popcount<Max15>(b & mobilityArea)
: popcount<Full >(b & mobilityArea));
int mob = Piece != QUEEN ? popcount<Max15>(b & mobilityArea)
: popcount<Full >(b & mobilityArea);
mobility += MobilityBonus[Piece][mob];
// 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.
if (ei.attackedBy[Them][PAWN] & s)
score -= ThreatenedByPawnPenalty[Piece];
score -= ThreatenedByPawn[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.
// 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;
score += BishopPin;
// Penalty for bishop with same coloured pawns
if (Piece == BISHOP)
score -= BishopPawnsPenalty * ei.pi->pawns_on_same_color_squares(Us, s);
score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s);
if (Piece == BISHOP || Piece == KNIGHT)
{
// Bishop and knight outposts squares
if ( (Piece == BISHOP || Piece == KNIGHT)
&& !(pos.pieces(Them, PAWN) & attack_span_mask(Us, s)))
if (!(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s)))
score += evaluate_outposts<Piece, Us>(pos, ei, s);
if ((Piece == ROOK || Piece == QUEEN) && relative_rank(Us, s) >= RANK_5)
// Bishop or knight behind a pawn
if ( relative_rank(Us, s) < RANK_5
&& (pos.pieces(PAWN) & (s + pawn_push(Us))))
score += MinorBehindPawn;
}
if ( (Piece == ROOK || Piece == QUEEN)
&& relative_rank(Us, s) >= RANK_5)
{
// Major piece on 7th rank
// Major piece on 7th rank and enemy king trapped on 8th
if ( relative_rank(Us, s) == RANK_7
&& relative_rank(Us, pos.king_square(Them)) == RANK_8)
score += (Piece == ROOK ? RookOn7thBonus : QueenOn7thBonus);
score += Piece == ROOK ? RookOn7th : QueenOn7th;
// Major piece attacking pawns on the same rank
Bitboard pawns = pos.pieces(Them, PAWN) & rank_bb(s);
// Major piece attacking enemy pawns on the same rank/file
Bitboard pawns = pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s];
if (pawns)
score += (Piece == ROOK ? RookOnPawnBonus
: QueenOnPawnBonus) * popcount<Max15>(pawns);
}
// Special extra evaluation for bishops
if (Piece == BISHOP && pos.is_chess960())
{
// An important Chess960 pattern: A cornered bishop blocked by
// a friendly pawn diagonally in front of it is a very serious
// problem, especially when that pawn is also blocked.
if (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))
{
Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W);
if (pos.piece_on(s + d) == make_piece(Us, PAWN))
{
if (!pos.is_empty(s + d + pawn_push(Us)))
score -= 2*TrappedBishopA1H1Penalty;
else if (pos.piece_on(s + 2*d) == make_piece(Us, PAWN))
score -= TrappedBishopA1H1Penalty;
else
score -= TrappedBishopA1H1Penalty / 2;
}
}
score += popcount<Max15>(pawns) * (Piece == ROOK ? RookOnPawn : QueenOnPawn);
}
// Special extra evaluation for rooks
if (Piece == ROOK)
{
// Open and half-open files
f = file_of(s);
if (ei.pi->file_is_half_open(Us, f))
{
if (ei.pi->file_is_half_open(Them, f))
score += RookOpenFileBonus;
else
score += RookHalfOpenFileBonus;
}
// Give a bonus for a rook on a open or semi-open file
if (ei.pi->semiopen(Us, file_of(s)))
score += ei.pi->semiopen(Them, file_of(s)) ? RookOpenFile : RookSemiopenFile;
if (mob > 3 || ei.pi->semiopen(Us, file_of(s)))
continue;
Square ksq = pos.king_square(Us);
// Penalize rooks which are trapped inside a king. Penalize more if
// king has lost right to castle.
if (mob > 6 || ei.pi->file_is_half_open(Us, f))
continue;
ksq = pos.king_square(Us);
if ( file_of(ksq) >= FILE_E
&& file_of(s) > file_of(ksq)
&& (relative_rank(Us, ksq) == RANK_1 || rank_of(ksq) == rank_of(s)))
{
// Is there a half-open file between the king and the edge of the board?
if (!ei.pi->has_open_file_to_right(Us, file_of(ksq)))
score -= make_score(pos.can_castle(Us) ? (TrappedRookPenalty - mob * 16) / 2
: (TrappedRookPenalty - mob * 16), 0);
if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq)))
&& (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1)
&& !ei.pi->semiopen_on_side(Us, file_of(ksq), file_of(ksq) < FILE_E))
score -= (TrappedRook - make_score(mob * 8, 0)) * (pos.can_castle(Us) ? 1 : 2);
}
else if ( file_of(ksq) <= FILE_D
&& file_of(s) < file_of(ksq)
&& (relative_rank(Us, ksq) == RANK_1 || rank_of(ksq) == rank_of(s)))
// An important Chess960 pattern: A cornered bishop blocked by a friendly
// pawn diagonally in front of it is a very serious problem, especially
// when that pawn is also blocked.
if ( Piece == BISHOP
&& pos.is_chess960()
&& (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
{
// Is there a half-open file between the king and the edge of the board?
if (!ei.pi->has_open_file_to_left(Us, file_of(ksq)))
score -= make_score(pos.can_castle(Us) ? (TrappedRookPenalty - mob * 16) / 2
: (TrappedRookPenalty - mob * 16), 0);
}
const enum Piece P = make_piece(Us, PAWN);
Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W);
if (pos.piece_on(s + d) == P)
score -= !pos.is_empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4
: pos.piece_on(s + d + d) == P ? TrappedBishopA1H1 * 2
: TrappedBishopA1H1;
}
}
if (Trace)
TracedScores[Us][Piece] = score;
Tracing::scores[Us][Piece] = score;
return score;
}
@@ -680,8 +603,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
// evaluate_threats<>() assigns bonuses according to the type of attacking piece
// and the type of attacked one.
template<Color Us>
Score evaluate_threats(const Position& pos, EvalInfo& ei) {
template<Color Us, bool Trace>
Score evaluate_threats(const Position& pos, const EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -689,32 +612,33 @@ Value do_evaluate(const Position& pos, Value& margin) {
Score score = SCORE_ZERO;
// Undefended minors get penalized even if not under attack
undefendedMinors = pos.pieces(Them)
& (pos.pieces(BISHOP) | pos.pieces(KNIGHT))
undefendedMinors = pos.pieces(Them, BISHOP, KNIGHT)
& ~ei.attackedBy[Them][ALL_PIECES];
if (undefendedMinors)
score += UndefendedMinorPenalty;
score += UndefendedMinor;
// Enemy pieces not defended by a pawn and under our attack
weakEnemies = pos.pieces(Them)
& ~ei.attackedBy[Them][PAWN]
& ei.attackedBy[Us][ALL_PIECES];
if (!weakEnemies)
return score;
// Add bonus according to type of attacked enemy piece and to the
// type of attacking piece, from knights to queens. Kings are not
// considered because are already handled in king evaluation.
if (weakEnemies)
for (PieceType pt1 = KNIGHT; pt1 < KING; pt1++)
{
b = ei.attackedBy[Us][pt1] & weakEnemies;
if (b)
for (PieceType pt2 = PAWN; pt2 < KING; pt2++)
if (b & pos.pieces(pt2))
score += ThreatBonus[pt1][pt2];
score += Threat[pt1][pt2];
}
if (Trace)
Tracing::scores[Us][THREAT] = score;
return score;
}
@@ -730,7 +654,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
Score score = mobility = SCORE_ZERO;
// Do not include in mobility squares protected by enemy pawns or occupied by our pieces
const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces(Us));
const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces(Us, PAWN, KING));
score += evaluate_pieces<KNIGHT, Us, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<BISHOP, Us, Trace>(pos, ei, mobility, mobilityArea);
@@ -741,6 +665,9 @@ Value do_evaluate(const Position& pos, Value& margin) {
ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
| ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING];
if (Trace)
Tracing::scores[Us][MOBILITY] = apply_weight(mobility, Weights[Mobility]);
return score;
}
@@ -748,7 +675,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
// evaluate_king<>() assigns bonuses and penalties to a king of a given color
template<Color Us, bool Trace>
Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]) {
Score evaluate_king(const Position& pos, const EvalInfo& ei, Value margins[]) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -772,13 +699,13 @@ Value do_evaluate(const Position& pos, Value& margin) {
| ei.attackedBy[Us][QUEEN]);
// Initialize the 'attackUnits' variable, which is used later on as an
// index to the KingDangerTable[] array. The initial value is based on
// the number and types of the enemy's attacking pieces, the number of
// index to the KingDanger[] array. The initial value is based on the
// number and types of the enemy's attacking pieces, the number of
// attacked and undefended squares around our king, the square of the
// king, and the quality of the pawn shelter.
attackUnits = std::min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
+ 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount<Max15>(undefended))
+ InitKingDanger[relative_square(Us, ksq)]
+ KingExposed[relative_square(Us, ksq)]
- mg_value(score) / 32;
// Analyse enemy's safe queen contact checks. First find undefended
@@ -790,7 +717,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT]
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]);
if (b)
attackUnits += QueenContactCheckBonus
attackUnits += QueenContactCheck
* popcount<Max15>(b)
* (Them == pos.side_to_move() ? 2 : 1);
}
@@ -808,7 +735,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT]
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]);
if (b)
attackUnits += RookContactCheckBonus
attackUnits += RookContactCheck
* popcount<Max15>(b)
* (Them == pos.side_to_move() ? 2 : 1);
}
@@ -822,37 +749,37 @@ Value do_evaluate(const Position& pos, Value& margin) {
// Enemy queen safe checks
b = (b1 | b2) & ei.attackedBy[Them][QUEEN];
if (b)
attackUnits += QueenCheckBonus * popcount<Max15>(b);
attackUnits += QueenCheck * popcount<Max15>(b);
// Enemy rooks safe checks
b = b1 & ei.attackedBy[Them][ROOK];
if (b)
attackUnits += RookCheckBonus * popcount<Max15>(b);
attackUnits += RookCheck * popcount<Max15>(b);
// Enemy bishops safe checks
b = b2 & ei.attackedBy[Them][BISHOP];
if (b)
attackUnits += BishopCheckBonus * popcount<Max15>(b);
attackUnits += BishopCheck * popcount<Max15>(b);
// Enemy knights safe checks
b = pos.attacks_from<KNIGHT>(ksq) & ei.attackedBy[Them][KNIGHT] & safe;
if (b)
attackUnits += KnightCheckBonus * popcount<Max15>(b);
attackUnits += KnightCheck * popcount<Max15>(b);
// To index KingDangerTable[] attackUnits must be in [0, 99] range
// To index KingDanger[] attackUnits must be in [0, 99] range
attackUnits = std::min(99, std::max(0, attackUnits));
// Finally, extract the king danger score from the KingDangerTable[]
// Finally, extract the king danger score from the KingDanger[]
// array and subtract the score from evaluation. Set also margins[]
// value that will be used for pruning because this value can sometimes
// 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.
score -= KingDangerTable[Us == Search::RootColor][attackUnits];
margins[Us] += mg_value(KingDangerTable[Us == Search::RootColor][attackUnits]);
score -= KingDanger[Us == Search::RootColor][attackUnits];
margins[Us] += mg_value(KingDanger[Us == Search::RootColor][attackUnits]);
}
if (Trace)
TracedScores[Us][KING] = score;
Tracing::scores[Us][KING] = score;
return score;
}
@@ -860,8 +787,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
// evaluate_passed_pawns<>() evaluates the passed pawns of the given color
template<Color Us>
Score evaluate_passed_pawns(const Position& pos, EvalInfo& ei) {
template<Color Us, bool Trace>
Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -870,10 +797,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
b = ei.pi->passed_pawns(Us);
if (!b)
return SCORE_ZERO;
do {
while (b)
{
Square s = pop_lsb(&b);
assert(pos.pawn_is_passed(Us, s));
@@ -882,8 +807,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
int rr = r * (r - 1);
// Base bonus based on rank
Value mbonus = Value(20 * rr);
Value ebonus = Value(10 * (rr + r + 1));
Value mbonus = Value(17 * rr);
Value ebonus = Value(7 * (rr + r + 1));
if (rr)
{
@@ -906,22 +831,25 @@ Value do_evaluate(const Position& pos, Value& margin) {
// 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
// the squares in the pawn's path attacked or occupied by the enemy.
if ( (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN))
if ( unlikely(forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN))
&& (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN) & pos.attacks_from<ROOK>(s)))
unsafeSquares = squaresToQueen;
else
unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
// 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.
if (!unsafeSquares)
ebonus += Value(rr * (squaresToQueen == defendedSquares ? 17 : 15));
else
// OK, there are enemy attacks or pieces (but not pawns). Are those
// squares which are attacked by the enemy also attacked by us ?
// If yes, big bonus (but smaller than when there are no enemy attacks),
// if no, somewhat smaller bonus.
ebonus += Value(rr * ((unsafeSquares & defendedSquares) == unsafeSquares ? 13 : 8));
// If there aren't enemy attacks huge bonus, a bit smaller if at
// least block square is not attacked, otherwise smallest bonus.
int k = !unsafeSquares ? 15 : !(unsafeSquares & blockSq) ? 9 : 3;
// Big bonus if the path to queen is fully defended, a bit less
// if at least block square is defended.
if (defendedSquares == squaresToQueen)
k += 6;
else if (defendedSquares & blockSq)
k += (unsafeSquares & defendedSquares) == unsafeSquares ? 4 : 2;
mbonus += Value(k * rr), ebonus += Value(k * rr);
}
} // rr != 0
@@ -949,7 +877,10 @@ Value do_evaluate(const Position& pos, Value& margin) {
}
score += make_score(mbonus, ebonus);
} while (b);
}
if (Trace)
Tracing::scores[Us][PASSED] = apply_weight(score, Weights[PassedPawns]);
// Add the scores to the middle game and endgame eval
return apply_weight(score, Weights[PassedPawns]);
@@ -959,7 +890,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
// evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides, this is quite
// conservative and returns a winning score only when we are very sure that the pawn is winning.
Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei) {
Score evaluate_unstoppable_pawns(const Position& pos, const EvalInfo& ei) {
Bitboard b, b2, blockers, supporters, queeningPath, candidates;
Square s, blockSq, queeningSquare;
@@ -1069,7 +1000,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
// black pawns: a4, b4 white: b2 then pawn in b4 is giving support.
if (!opposed)
{
b2 = supporters & in_front_bb(winnerSide, blockSq + pawn_push(winnerSide));
b2 = supporters & in_front_bb(winnerSide, rank_of(blockSq + pawn_push(winnerSide)));
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
{
@@ -1079,7 +1010,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
}
// Check pawns that can be sacrificed against the blocking pawn
b2 = attack_span_mask(winnerSide, blockSq) & candidates & ~(1ULL << s);
b2 = pawn_attack_span(winnerSide, blockSq) & candidates & ~(1ULL << s);
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
{
@@ -1124,7 +1055,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
// twice. Finally, the space bonus is scaled by a weight taken from the
// material hash table. The aim is to improve play on game opening.
template<Color Us>
int evaluate_space(const Position& pos, EvalInfo& ei) {
int evaluate_space(const Position& pos, const EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -1160,9 +1091,14 @@ Value do_evaluate(const Position& pos, Value& margin) {
int ev = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL;
int result = (mg_value(v) * int(ph) + ev * int(128 - ph)) / 128;
return Value((result + GrainSize / 2) & ~(GrainSize - 1));
return Value((result / GrainSize) * GrainSize); // Sign independent
}
// apply_weight() weights score v by score w trying to prevent overflow
Score apply_weight(Score v, Score w) {
return make_score((int(mg_value(v)) * mg_value(w)) / 0x100,
(int(eg_value(v)) * eg_value(w)) / 0x100);
}
// weight_option() computes the value of an evaluation weight, by combining
// two UCI-configurable weights (midgame and endgame) with an internal weight.
@@ -1177,34 +1113,29 @@ Value do_evaluate(const Position& pos, Value& margin) {
}
// A couple of little helpers used by tracing code, to_cp() converts a value to
// a double in centipawns scale, trace_add() stores white and black scores.
// Tracing functions definitions
double to_cp(Value v) { return double(v) / double(PawnValueMg); }
void trace_add(int idx, Score wScore, Score bScore) {
void Tracing::add(int idx, Score wScore, Score bScore) {
TracedScores[WHITE][idx] = wScore;
TracedScores[BLACK][idx] = bScore;
scores[WHITE][idx] = wScore;
scores[BLACK][idx] = bScore;
}
void Tracing::row(const char* name, int idx) {
// trace_row() is an helper function used by tracing code to register the
// values of a single evaluation term.
void trace_row(const char* name, int idx) {
Score wScore = TracedScores[WHITE][idx];
Score bScore = TracedScores[BLACK][idx];
Score wScore = scores[WHITE][idx];
Score bScore = scores[BLACK][idx];
switch (idx) {
case PST: case IMBALANCE: case PAWN: case UNSTOPPABLE: case TOTAL:
TraceStream << std::setw(20) << name << " | --- --- | --- --- | "
stream << std::setw(20) << name << " | --- --- | --- --- | "
<< std::setw(6) << to_cp(mg_value(wScore)) << " "
<< std::setw(6) << to_cp(eg_value(wScore)) << " \n";
break;
default:
TraceStream << std::setw(20) << name << " | " << std::noshowpos
stream << std::setw(20) << name << " | " << std::noshowpos
<< std::setw(5) << to_cp(mg_value(wScore)) << " "
<< std::setw(5) << to_cp(eg_value(wScore)) << " | "
<< std::setw(5) << to_cp(mg_value(bScore)) << " "
@@ -1214,4 +1145,41 @@ Value do_evaluate(const Position& pos, Value& margin) {
<< std::setw(6) << to_cp(eg_value(wScore - bScore)) << " \n";
}
}
std::string Tracing::do_trace(const Position& pos) {
stream.str("");
stream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
std::memset(scores, 0, 2 * (TOTAL + 1) * sizeof(Score));
Value margin;
do_evaluate<true>(pos, margin);
std::string totals = stream.str();
stream.str("");
stream << std::setw(21) << "Eval term " << "| White | Black | Total \n"
<< " | MG EG | MG EG | MG EG \n"
<< "---------------------+-------------+-------------+---------------\n";
row("Material, PST, Tempo", PST);
row("Material imbalance", IMBALANCE);
row("Pawns", PAWN);
row("Knights", KNIGHT);
row("Bishops", BISHOP);
row("Rooks", ROOK);
row("Queens", QUEEN);
row("Mobility", MOBILITY);
row("King safety", KING);
row("Threats", THREAT);
row("Passed pawns", PASSED);
row("Unstoppable pawns", UNSTOPPABLE);
row("Space", SPACE);
stream << "---------------------+-------------+-------------+---------------\n";
row("Total", TOTAL);
stream << totals;
return stream.str();
}
}

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(EVALUATE_H_INCLUDED)
#ifndef EVALUATE_H_INCLUDED
#define EVALUATE_H_INCLUDED
#include "types.h"
@@ -32,4 +32,4 @@ extern std::string trace(const Position& pos);
}
#endif // !defined(EVALUATE_H_INCLUDED)
#endif // #ifndef EVALUATE_H_INCLUDED

View File

@@ -34,7 +34,7 @@ int main(int argc, char* argv[]) {
UCI::init(Options);
Bitboards::init();
Zobrist::init();
Position::init();
Bitbases::init_kpk();
Search::init();
Eval::init();

View File

@@ -35,8 +35,8 @@ namespace {
const int NoPawnsSF[4] = { 6, 12, 32 };
// Polynomial material balance parameters
const Value RedundantQueenPenalty = Value(320);
const Value RedundantRookPenalty = Value(554);
const Value RedundantQueen = Value(320);
const Value RedundantRook = Value(554);
// pair pawn knight bishop rook queen
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
@@ -75,24 +75,24 @@ namespace {
// Helper templates used to detect a given material distribution
template<Color Us> bool is_KXK(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
return pos.non_pawn_material(Them) == VALUE_ZERO
&& pos.piece_count(Them, PAWN) == 0
return !pos.count<PAWN>(Them)
&& pos.non_pawn_material(Them) == VALUE_ZERO
&& pos.non_pawn_material(Us) >= RookValueMg;
}
template<Color Us> bool is_KBPsKs(const Position& pos) {
return pos.non_pawn_material(Us) == BishopValueMg
&& pos.piece_count(Us, BISHOP) == 1
&& pos.piece_count(Us, PAWN) >= 1;
&& pos.count<BISHOP>(Us) == 1
&& pos.count<PAWN >(Us) >= 1;
}
template<Color Us> bool is_KQKRPs(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
return pos.piece_count(Us, PAWN) == 0
return !pos.count<PAWN>(Us)
&& pos.non_pawn_material(Us) == QueenValueMg
&& pos.piece_count(Us, QUEEN) == 1
&& pos.piece_count(Them, ROOK) == 1
&& pos.piece_count(Them, PAWN) >= 1;
&& pos.count<QUEEN>(Us) == 1
&& pos.count<ROOK>(Them) == 1
&& pos.count<PAWN>(Them) >= 1;
}
/// imbalance() calculates imbalance comparing piece count of each
@@ -109,8 +109,8 @@ namespace {
// 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];
value -= RedundantRook * (pieceCount[Us][ROOK] - 1)
+ RedundantQueen * pieceCount[Us][QUEEN];
// Second-degree polynomial material imbalance by Tord Romstad
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
@@ -150,7 +150,7 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
if (e->key == key)
return e;
memset(e, 0, sizeof(Entry));
std::memset(e, 0, sizeof(Entry));
e->key = key;
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
e->gamePhase = game_phase(pos);
@@ -180,8 +180,8 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
assert((pos.pieces(WHITE, KNIGHT) | pos.pieces(WHITE, BISHOP)));
assert((pos.pieces(BLACK, KNIGHT) | pos.pieces(BLACK, BISHOP)));
if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2
&& pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2)
if ( pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE) <= 2
&& pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK) <= 2)
{
e->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()];
return e;
@@ -221,17 +221,17 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
if (npm_w + npm_b == VALUE_ZERO)
{
if (pos.piece_count(BLACK, PAWN) == 0)
if (!pos.count<PAWN>(BLACK))
{
assert(pos.piece_count(WHITE, PAWN) >= 2);
assert(pos.count<PAWN>(WHITE) >= 2);
e->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
}
else if (pos.piece_count(WHITE, PAWN) == 0)
else if (!pos.count<PAWN>(WHITE))
{
assert(pos.piece_count(BLACK, PAWN) >= 2);
assert(pos.count<PAWN>(BLACK) >= 2);
e->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
}
else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1)
else if (pos.count<PAWN>(WHITE) == 1 && pos.count<PAWN>(BLACK) == 1)
{
// This is a special case because we set scaling functions
// for both colors instead of only one.
@@ -241,35 +241,35 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
}
// No pawns makes it difficult to win, even with a material advantage
if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMg)
if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg)
{
e->factor[WHITE] = (uint8_t)
(npm_w == npm_b || npm_w < RookValueMg ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]);
(npm_w == npm_b || npm_w < RookValueMg ? 0 : NoPawnsSF[std::min(pos.count<BISHOP>(WHITE), 2)]);
}
if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMg)
if (!pos.count<PAWN>(BLACK) && npm_b - npm_w <= BishopValueMg)
{
e->factor[BLACK] = (uint8_t)
(npm_w == npm_b || npm_b < RookValueMg ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]);
(npm_w == npm_b || npm_b < RookValueMg ? 0 : NoPawnsSF[std::min(pos.count<BISHOP>(BLACK), 2)]);
}
// Compute the space weight
if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg)
{
int minorPieceCount = pos.piece_count(WHITE, KNIGHT) + pos.piece_count(WHITE, BISHOP)
+ pos.piece_count(BLACK, KNIGHT) + pos.piece_count(BLACK, BISHOP);
int minorPieceCount = pos.count<KNIGHT>(WHITE) + pos.count<BISHOP>(WHITE)
+ pos.count<KNIGHT>(BLACK) + pos.count<BISHOP>(BLACK);
e->spaceWeight = minorPieceCount * minorPieceCount;
e->spaceWeight = make_score(minorPieceCount * minorPieceCount, 0);
}
// 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
// in defining bishop pair bonuses.
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) , 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) , pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } };
{ pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE),
pos.count<BISHOP>(WHITE) , pos.count<ROOK>(WHITE), pos.count<QUEEN >(WHITE) },
{ pos.count<BISHOP>(BLACK) > 1, pos.count<PAWN>(BLACK), pos.count<KNIGHT>(BLACK),
pos.count<BISHOP>(BLACK) , pos.count<ROOK>(BLACK), pos.count<QUEEN >(BLACK) } };
e->value = (int16_t)((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16);
return e;

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(MATERIAL_H_INCLUDED)
#ifndef MATERIAL_H_INCLUDED
#define MATERIAL_H_INCLUDED
#include "endgame.h"
@@ -39,7 +39,7 @@ namespace Material {
struct Entry {
Score material_value() const { return make_score(value, value); }
int space_weight() const { return spaceWeight; }
Score space_weight() const { return spaceWeight; }
Phase game_phase() const { return gamePhase; }
bool specialized_eval_exists() const { return evaluationFunction != NULL; }
Value evaluate(const Position& p) const { return (*evaluationFunction)(p); }
@@ -50,7 +50,7 @@ struct Entry {
uint8_t factor[COLOR_NB];
EndgameBase<Value>* evaluationFunction;
EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB];
int spaceWeight;
Score spaceWeight;
Phase gamePhase;
};
@@ -74,4 +74,4 @@ inline ScaleFactor Entry::scale_factor(const Position& pos, Color c) const {
}
#endif // !defined(MATERIAL_H_INCLUDED)
#endif // #ifndef MATERIAL_H_INCLUDED

View File

@@ -24,17 +24,11 @@
#include "misc.h"
#include "thread.h"
#if defined(__hpux)
# include <sys/pstat.h>
#endif
using namespace std;
/// Version number. If Version is left empty, then Tag plus current
/// date, in the format DD-MM-YY, are used as a version number.
static const string Version = "3";
static const string Tag = "";
/// Version number. If Version is left empty, then compile date, in the
/// format DD-MM-YY, is shown in engine_info.
static const string Version = "4";
/// engine_info() returns the full name of the current Stockfish version. This
@@ -45,36 +39,26 @@ static const string Tag = "";
const string engine_info(bool to_uci) {
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
const string cpu64(Is64Bit ? " 64bit" : "");
const string popcnt(HasPopCnt ? " SSE4.2" : "");
string month, day, year;
stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008"
s << "Stockfish " << Version;
s << "Stockfish " << Version << setfill('0');
if (Version.empty())
{
date >> month >> day >> year;
s << Tag << string(Tag.empty() ? "" : " ") << setfill('0') << setw(2) << day
<< "-" << setw(2) << (1 + months.find(month) / 4) << "-" << year.substr(2);
s << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
}
s << cpu64 << popcnt << (to_uci ? "\nid author ": " by ")
s << (Is64Bit ? " 64" : "")
<< (HasPopCnt ? " SSE4.2" : "")
<< (to_uci ? "\nid author ": " by ")
<< "Tord Romstad, Marco Costalba and Joona Kiiski";
return s.str();
}
/// Convert system time to milliseconds. That's all we need.
Time::point Time::now() {
sys_time_t t; system_time(&t); return time_to_msec(t);
}
/// Debug functions used mainly to collect run-time statistics
static uint64_t hits[2], means[2];
@@ -174,37 +158,12 @@ std::ostream& operator<<(std::ostream& os, SyncCout sc) {
void start_logger(bool b) { Logger::start(b); }
/// cpu_count() tries to detect the number of CPU cores
int cpu_count() {
#if defined(_WIN32) || defined(_WIN64)
SYSTEM_INFO s;
GetSystemInfo(&s);
return s.dwNumberOfProcessors;
#else
# if defined(_SC_NPROCESSORS_ONLN)
return sysconf(_SC_NPROCESSORS_ONLN);
# elif defined(__hpux)
struct pst_dynamic psd;
if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) == -1)
return 1;
return psd.psd_proc_cnt;
# else
return 1;
# endif
#endif
}
/// timed_wait() waits for msec milliseconds. It is mainly an helper to wrap
/// conversion from milliseconds to struct timespec, as used by pthreads.
void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) {
#if defined(_WIN32) || defined(_WIN64)
#ifdef _WIN32
int tm = msec;
#else
timespec ts, *tm = &ts;
@@ -221,7 +180,7 @@ void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) {
/// prefetch() preloads the given address in L1/L2 cache. This is a non
/// blocking function and do not stalls the CPU waiting for data to be
/// loaded from memory, that can be quite slow.
#if defined(NO_PREFETCH)
#ifdef NO_PREFETCH
void prefetch(char*) {}

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(MISC_H_INCLUDED)
#ifndef MISC_H_INCLUDED
#define MISC_H_INCLUDED
#include <fstream>
@@ -27,7 +27,6 @@
#include "types.h"
extern const std::string engine_info(bool to_uci = false);
extern int cpu_count();
extern void timed_wait(WaitCondition&, Lock&, int);
extern void prefetch(char* addr);
extern void start_logger(bool b);
@@ -46,7 +45,7 @@ struct Log : public std::ofstream {
namespace Time {
typedef int64_t point;
point now();
inline point now() { return system_time_to_msec(); }
}
@@ -66,4 +65,4 @@ std::ostream& operator<<(std::ostream&, SyncCout);
#define sync_cout std::cout << io_lock
#define sync_endl std::endl << io_unlock
#endif // !defined(MISC_H_INCLUDED)
#endif // #ifndef MISC_H_INCLUDED

View File

@@ -24,15 +24,15 @@
/// Simple macro to wrap a very common while loop, no facny, no flexibility,
/// hardcoded names 'mlist' and 'from'.
#define SERIALIZE(b) while (b) (*mlist++).move = make_move(from, pop_lsb(&b))
#define SERIALIZE(b) while (b) (mlist++)->move = make_move(from, pop_lsb(&b))
/// Version used for pawns, where the 'from' square is given as a delta from the 'to' square
#define SERIALIZE_PAWNS(b, d) while (b) { Square to = pop_lsb(&b); \
(*mlist++).move = make_move(to - (d), to); }
(mlist++)->move = make_move(to - (d), to); }
namespace {
template<CastlingSide Side, bool Checks, bool Chess960>
MoveStack* generate_castle(const Position& pos, MoveStack* mlist, Color us) {
ExtMove* generate_castle(const Position& pos, ExtMove* mlist, Color us) {
if (pos.castle_impeded(us, Side) || !pos.can_castle(make_castle_right(us, Side)))
return mlist;
@@ -59,7 +59,7 @@ namespace {
if (Chess960 && (pos.attackers_to(kto, pos.pieces() ^ rfrom) & enemies))
return mlist;
(*mlist++).move = make<CASTLE>(kfrom, rfrom);
(mlist++)->move = make<CASTLE>(kfrom, rfrom);
if (Checks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos)))
mlist--;
@@ -68,42 +68,30 @@ namespace {
}
template<Square Delta>
inline Bitboard move_pawns(Bitboard p) {
return Delta == DELTA_N ? p << 8
: Delta == DELTA_S ? p >> 8
: Delta == DELTA_NE ? (p & ~FileHBB) << 9
: Delta == DELTA_SE ? (p & ~FileHBB) >> 7
: Delta == DELTA_NW ? (p & ~FileABB) << 7
: Delta == DELTA_SW ? (p & ~FileABB) >> 9 : 0;
}
template<GenType Type, Square Delta>
inline MoveStack* generate_promotions(MoveStack* mlist, Bitboard pawnsOn7,
inline ExtMove* generate_promotions(ExtMove* mlist, Bitboard pawnsOn7,
Bitboard target, const CheckInfo* ci) {
Bitboard b = move_pawns<Delta>(pawnsOn7) & target;
Bitboard b = shift_bb<Delta>(pawnsOn7) & target;
while (b)
{
Square to = pop_lsb(&b);
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
(*mlist++).move = make<PROMOTION>(to - Delta, to, QUEEN);
(mlist++)->move = make<PROMOTION>(to - Delta, to, QUEEN);
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
{
(*mlist++).move = make<PROMOTION>(to - Delta, to, ROOK);
(*mlist++).move = make<PROMOTION>(to - Delta, to, BISHOP);
(*mlist++).move = make<PROMOTION>(to - Delta, to, KNIGHT);
(mlist++)->move = make<PROMOTION>(to - Delta, to, ROOK);
(mlist++)->move = make<PROMOTION>(to - Delta, to, BISHOP);
(mlist++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
}
// Knight-promotion is the only one that can give a direct check not
// already included in the queen-promotion.
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq))
(*mlist++).move = make<PROMOTION>(to - Delta, to, KNIGHT);
(mlist++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
else
(void)ci; // Silence a warning under MSVC
}
@@ -113,7 +101,7 @@ namespace {
template<Color Us, GenType Type>
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist,
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* mlist,
Bitboard target, const CheckInfo* ci) {
// Compute our parametrized parameters at compile time, named according to
@@ -122,9 +110,9 @@ namespace {
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const Square UP = (Us == WHITE ? DELTA_N : DELTA_S);
const Square RIGHT = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square LEFT = (Us == WHITE ? DELTA_NW : DELTA_SE);
const Square Up = (Us == WHITE ? DELTA_N : DELTA_S);
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
Bitboard b1, b2, dc1, dc2, emptySquares;
@@ -139,8 +127,8 @@ namespace {
{
emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces());
b1 = move_pawns<UP>(pawnsNotOn7) & emptySquares;
b2 = move_pawns<UP>(b1 & TRank3BB) & emptySquares;
b1 = shift_bb<Up>(pawnsNotOn7) & emptySquares;
b2 = shift_bb<Up>(b1 & TRank3BB) & emptySquares;
if (Type == EVASIONS) // Consider only blocking squares
{
@@ -159,16 +147,16 @@ namespace {
// promotion has been already generated among captures.
if (pawnsNotOn7 & ci->dcCandidates)
{
dc1 = move_pawns<UP>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq);
dc2 = move_pawns<UP>(dc1 & TRank3BB) & emptySquares;
dc1 = shift_bb<Up>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq);
dc2 = shift_bb<Up>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1;
b2 |= dc2;
}
}
SERIALIZE_PAWNS(b1, UP);
SERIALIZE_PAWNS(b2, UP + UP);
SERIALIZE_PAWNS(b1, Up);
SERIALIZE_PAWNS(b2, Up + Up);
}
// Promotions and underpromotions
@@ -180,19 +168,19 @@ namespace {
if (Type == EVASIONS)
emptySquares &= target;
mlist = generate_promotions<Type, RIGHT>(mlist, pawnsOn7, enemies, ci);
mlist = generate_promotions<Type, LEFT>(mlist, pawnsOn7, enemies, ci);
mlist = generate_promotions<Type, UP>(mlist, pawnsOn7, emptySquares, ci);
mlist = generate_promotions<Type, Right>(mlist, pawnsOn7, enemies, ci);
mlist = generate_promotions<Type, Left >(mlist, pawnsOn7, enemies, ci);
mlist = generate_promotions<Type, Up>(mlist, pawnsOn7, emptySquares, ci);
}
// Standard and en-passant captures
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
{
b1 = move_pawns<RIGHT>(pawnsNotOn7) & enemies;
b2 = move_pawns<LEFT >(pawnsNotOn7) & enemies;
b1 = shift_bb<Right>(pawnsNotOn7) & enemies;
b2 = shift_bb<Left >(pawnsNotOn7) & enemies;
SERIALIZE_PAWNS(b1, RIGHT);
SERIALIZE_PAWNS(b2, LEFT);
SERIALIZE_PAWNS(b1, Right);
SERIALIZE_PAWNS(b2, Left);
if (pos.ep_square() != SQ_NONE)
{
@@ -201,7 +189,7 @@ namespace {
// An en passant capture can be an evasion only if the checking piece
// is the double pushed pawn and so is in the target. Otherwise this
// is a discovery check and we are forced to do otherwise.
if (Type == EVASIONS && !(target & (pos.ep_square() - UP)))
if (Type == EVASIONS && !(target & (pos.ep_square() - Up)))
return mlist;
b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them);
@@ -209,7 +197,7 @@ namespace {
assert(b1);
while (b1)
(*mlist++).move = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
(mlist++)->move = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
}
}
@@ -218,12 +206,12 @@ namespace {
template<PieceType Pt, bool Checks> FORCE_INLINE
MoveStack* generate_moves(const Position& pos, MoveStack* mlist, Color us,
ExtMove* generate_moves(const Position& pos, ExtMove* mlist, Color us,
Bitboard target, const CheckInfo* ci) {
assert(Pt != KING && Pt != PAWN);
const Square* pl = pos.piece_list(us, Pt);
const Square* pl = pos.list<Pt>(us);
for (Square from = *pl; from != SQ_NONE; from = *++pl)
{
@@ -233,7 +221,7 @@ namespace {
&& !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt]))
continue;
if (ci->dcCandidates && (ci->dcCandidates & from))
if (unlikely(ci->dcCandidates) && (ci->dcCandidates & from))
continue;
}
@@ -249,38 +237,36 @@ namespace {
}
template<GenType Type> FORCE_INLINE
MoveStack* generate_all(const Position& pos, MoveStack* mlist, Color us,
Bitboard target, const CheckInfo* ci = NULL) {
template<Color Us, GenType Type> FORCE_INLINE
ExtMove* generate_all(const Position& pos, ExtMove* mlist, Bitboard target,
const CheckInfo* ci = NULL) {
const bool Checks = Type == QUIET_CHECKS;
mlist = (us == WHITE ? generate_pawn_moves<WHITE, Type>(pos, mlist, target, ci)
: generate_pawn_moves<BLACK, Type>(pos, mlist, target, ci));
mlist = generate_moves<KNIGHT, Checks>(pos, mlist, us, target, ci);
mlist = generate_moves<BISHOP, Checks>(pos, mlist, us, target, ci);
mlist = generate_moves<ROOK, Checks>(pos, mlist, us, target, ci);
mlist = generate_moves<QUEEN, Checks>(pos, mlist, us, target, ci);
mlist = generate_pawn_moves<Us, Type>(pos, mlist, target, ci);
mlist = generate_moves<KNIGHT, Checks>(pos, mlist, Us, target, ci);
mlist = generate_moves<BISHOP, Checks>(pos, mlist, Us, target, ci);
mlist = generate_moves< ROOK, Checks>(pos, mlist, Us, target, ci);
mlist = generate_moves< QUEEN, Checks>(pos, mlist, Us, target, ci);
if (Type != QUIET_CHECKS && Type != EVASIONS)
{
Square from = pos.king_square(us);
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())
{
mlist = generate_castle<KING_SIDE, Checks, true>(pos, mlist, us);
mlist = generate_castle<QUEEN_SIDE, Checks, true>(pos, mlist, us);
mlist = generate_castle< KING_SIDE, Checks, true>(pos, mlist, Us);
mlist = generate_castle<QUEEN_SIDE, Checks, true>(pos, mlist, Us);
}
else
{
mlist = generate_castle<KING_SIDE, Checks, false>(pos, mlist, us);
mlist = generate_castle<QUEEN_SIDE, Checks, false>(pos, mlist, us);
mlist = generate_castle< KING_SIDE, Checks, false>(pos, mlist, Us);
mlist = generate_castle<QUEEN_SIDE, Checks, false>(pos, mlist, Us);
}
}
@@ -301,7 +287,7 @@ namespace {
/// non-captures. Returns a pointer to the end of the move list.
template<GenType Type>
MoveStack* generate(const Position& pos, MoveStack* mlist) {
ExtMove* generate(const Position& pos, ExtMove* mlist) {
assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS);
assert(!pos.checkers());
@@ -312,22 +298,24 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) {
: Type == QUIETS ? ~pos.pieces()
: Type == NON_EVASIONS ? ~pos.pieces(us) : 0;
return generate_all<Type>(pos, mlist, us, target);
return us == WHITE ? generate_all<WHITE, Type>(pos, mlist, target)
: generate_all<BLACK, Type>(pos, mlist, target);
}
// Explicit template instantiations
template MoveStack* generate<CAPTURES>(const Position&, MoveStack*);
template MoveStack* generate<QUIETS>(const Position&, MoveStack*);
template MoveStack* generate<NON_EVASIONS>(const Position&, MoveStack*);
template ExtMove* generate<CAPTURES>(const Position&, ExtMove*);
template ExtMove* generate<QUIETS>(const Position&, ExtMove*);
template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
/// generate<QUIET_CHECKS> generates all pseudo-legal non-captures and knight
/// underpromotions that give check. Returns a pointer to the end of the move list.
template<>
MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* mlist) {
assert(!pos.checkers());
Color us = pos.side_to_move();
CheckInfo ci(pos);
Bitboard dc = ci.dcCandidates;
@@ -347,21 +335,21 @@ MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
SERIALIZE(b);
}
return generate_all<QUIET_CHECKS>(pos, mlist, pos.side_to_move(), ~pos.pieces(), &ci);
return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, mlist, ~pos.pieces(), &ci)
: generate_all<BLACK, QUIET_CHECKS>(pos, mlist, ~pos.pieces(), &ci);
}
/// generate<EVASIONS> generates all pseudo-legal check evasions when the side
/// to move is in check. Returns a pointer to the end of the move list.
template<>
MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) {
ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* mlist) {
assert(pos.checkers());
Square from, checksq;
int checkersCnt = 0;
Color us = pos.side_to_move();
Square ksq = pos.king_square(us);
Square ksq = pos.king_square(us), from = ksq /* For SERIALIZE */, checksq;
Bitboard sliderAttacks = 0;
Bitboard b = pos.checkers();
@@ -400,25 +388,25 @@ MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) {
// Generate evasions for king, capture and non capture moves
b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
from = ksq;
SERIALIZE(b);
if (checkersCnt > 1)
return mlist; // Double check, only a king move can save the day
// Generate blocking evasions or captures of the checking piece
Bitboard target = between_bb(checksq, ksq) | pos.checkers();
Bitboard target = between_bb(checksq, ksq) | checksq;
return generate_all<EVASIONS>(pos, mlist, us, target);
return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, mlist, target)
: generate_all<BLACK, EVASIONS>(pos, mlist, target);
}
/// generate<LEGAL> generates all the legal moves in the given position
template<>
MoveStack* generate<LEGAL>(const Position& pos, MoveStack* mlist) {
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* mlist) {
MoveStack *end, *cur = mlist;
ExtMove *end, *cur = mlist;
Bitboard pinned = pos.pinned_pieces();
Square ksq = pos.king_square(pos.side_to_move());

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(MOVEGEN_H_INCLUDED)
#ifndef MOVEGEN_H_INCLUDED
#define MOVEGEN_H_INCLUDED
#include "types.h"
@@ -34,26 +34,25 @@ enum GenType {
class Position;
template<GenType>
MoveStack* generate(const Position& pos, MoveStack* mlist);
ExtMove* generate(const Position& pos, ExtMove* mlist);
/// The MoveList struct is a simple wrapper around generate(), sometimes comes
/// handy to use this class instead of the low level generate() function.
template<GenType T>
struct MoveList {
explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) {}
explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) { last->move = MOVE_NONE; }
void operator++() { cur++; }
bool end() const { return cur == last; }
Move move() const { return cur->move; }
Move operator*() const { return cur->move; }
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;
for (const ExtMove* it(mlist); it != last; ++it) if (it->move == m) return true;
return false;
}
private:
MoveStack mlist[MAX_MOVES];
MoveStack *cur, *last;
ExtMove mlist[MAX_MOVES];
ExtMove *cur, *last;
};
#endif // !defined(MOVEGEN_H_INCLUDED)
#endif // #ifndef MOVEGEN_H_INCLUDED

View File

@@ -25,7 +25,7 @@
namespace {
enum Sequencer {
enum Stages {
MAIN_SEARCH, CAPTURES_S1, KILLERS_S1, QUIETS_1_S1, QUIETS_2_S1, BAD_CAPTURES_S1,
EVASION, EVASIONS_S2,
QSEARCH_0, CAPTURES_S3, QUIET_CHECKS_S3,
@@ -36,9 +36,9 @@ namespace {
};
// Our insertion sort, guaranteed to be stable, as is needed
void insertion_sort(MoveStack* begin, MoveStack* end)
void insertion_sort(ExtMove* begin, ExtMove* end)
{
MoveStack tmp, *p, *q;
ExtMove tmp, *p, *q;
for (p = begin + 1; p < end; ++p)
{
@@ -51,12 +51,12 @@ namespace {
// 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.
inline bool has_positive_score(const MoveStack& ms) { return ms.score > 0; }
inline bool has_positive_score(const ExtMove& ms) { return ms.score > 0; }
// Picks and moves to the front the best move in the range [begin, end),
// it is faster than sorting all the moves in advance when moves are few, as
// normally are the possible captures.
inline MoveStack* pick_best(MoveStack* begin, MoveStack* end)
inline ExtMove* pick_best(ExtMove* begin, ExtMove* end)
{
std::swap(*begin, *std::max_element(begin, end));
return begin;
@@ -70,53 +70,40 @@ namespace {
/// search captures, promotions and some checks) and about how important good
/// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
Search::Stack* s, Value beta) : pos(p), Hist(h), depth(d) {
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h,
Move* cm, Search::Stack* s) : pos(p), history(h), depth(d) {
assert(d > DEPTH_ZERO);
captureThreshold = 0;
cur = end = moves;
endBadCaptures = moves + MAX_MOVES - 1;
countermoves = cm;
ss = s;
if (p.checkers())
phase = EVASION;
stage = EVASION;
else
{
phase = MAIN_SEARCH;
killers[0].move = ss->killers[0];
killers[1].move = ss->killers[1];
// Consider sligtly negative captures as good if at low depth and far from beta
if (ss && ss->staticEval < beta - PawnValueMg && d < 3 * ONE_PLY)
captureThreshold = -PawnValueMg;
// Consider negative captures as good if still enough to reach beta
else if (ss && ss->staticEval > beta)
captureThreshold = beta - ss->staticEval;
}
stage = MAIN_SEARCH;
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
end += (ttMove != MOVE_NONE);
}
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
Square sq) : pos(p), Hist(h), cur(moves), end(moves) {
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h,
Square sq) : pos(p), history(h), cur(moves), end(moves) {
assert(d <= DEPTH_ZERO);
if (p.checkers())
phase = EVASION;
stage = EVASION;
else if (d > DEPTH_QS_NO_CHECKS)
phase = QSEARCH_0;
stage = QSEARCH_0;
else if (d > DEPTH_QS_RECAPTURES)
{
phase = QSEARCH_1;
stage = QSEARCH_1;
// Skip TT move if is not a capture or a promotion, this avoids qsearch
// tree explosion due to a possible perpetual check or similar rare cases
@@ -126,7 +113,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
}
else
{
phase = RECAPTURE;
stage = RECAPTURE;
recaptureSquare = sq;
ttm = MOVE_NONE;
}
@@ -135,12 +122,12 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
end += (ttMove != MOVE_NONE);
}
MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType pt)
: pos(p), Hist(h), cur(moves), end(moves) {
MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, PieceType pt)
: pos(p), history(h), cur(moves), end(moves) {
assert(!pos.checkers());
phase = PROBCUT;
stage = PROBCUT;
// In ProbCut we generate only captures better than parent's captured piece
captureThreshold = PieceValue[MG][pt];
@@ -172,7 +159,7 @@ void MovePicker::score<CAPTURES>() {
// some SEE calls in case we get a cutoff (idea from Pablo Vazquez).
Move m;
for (MoveStack* it = moves; it != end; ++it)
for (ExtMove* it = moves; it != end; ++it)
{
m = it->move;
it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
@@ -191,10 +178,10 @@ void MovePicker::score<QUIETS>() {
Move m;
for (MoveStack* it = moves; it != end; ++it)
for (ExtMove* it = moves; it != end; ++it)
{
m = it->move;
it->score = Hist[pos.piece_moved(m)][to_sq(m)];
it->score = history[pos.piece_moved(m)][to_sq(m)];
}
}
@@ -206,17 +193,17 @@ void MovePicker::score<EVASIONS>() {
Move m;
int seeScore;
for (MoveStack* it = moves; it != end; ++it)
for (ExtMove* it = moves; it != end; ++it)
{
m = it->move;
if ((seeScore = pos.see_sign(m)) < 0)
it->score = seeScore - History::Max; // At the bottom
it->score = seeScore - HistoryStats::Max; // At the bottom
else if (pos.is_capture(m))
it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
- type_of(pos.piece_moved(m)) + History::Max;
- type_of(pos.piece_moved(m)) + HistoryStats::Max;
else
it->score = Hist[pos.piece_moved(m)][to_sq(m)];
it->score = history[pos.piece_moved(m)][to_sq(m)];
}
}
@@ -228,7 +215,7 @@ void MovePicker::generate_next() {
cur = moves;
switch (++phase) {
switch (++stage) {
case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6:
end = generate<CAPTURES>(pos, moves);
@@ -238,6 +225,19 @@ void MovePicker::generate_next() {
case KILLERS_S1:
cur = killers;
end = cur + 2;
killers[0].move = ss->killers[0];
killers[1].move = ss->killers[1];
killers[2].move = killers[3].move = MOVE_NONE;
// Be sure countermoves are different from killers
for (int i = 0; i < 2; i++)
if (countermoves[i] != cur->move && countermoves[i] != (cur+1)->move)
(end++)->move = countermoves[i];
if (countermoves[1] && countermoves[1] == countermoves[0]) // Due to SMP races
killers[3].move = MOVE_NONE;
return;
case QUIETS_1_S1:
@@ -271,7 +271,7 @@ void MovePicker::generate_next() {
return;
case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE:
phase = STOP;
stage = STOP;
case STOP:
end = cur + 1; // Avoid another next_phase() call
return;
@@ -296,7 +296,7 @@ Move MovePicker::next_move<false>() {
while (cur == end)
generate_next();
switch (phase) {
switch (stage) {
case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT:
cur++;
@@ -306,9 +306,7 @@ Move MovePicker::next_move<false>() {
move = pick_best(cur++, end)->move;
if (move != ttMove)
{
assert(captureThreshold <= 0); // Otherwise we cannot use see_sign()
if (pos.see_sign(move) >= captureThreshold)
if (pos.see_sign(move) >= 0)
return move;
// Losing capture, move it to the tail of the array
@@ -329,7 +327,9 @@ Move MovePicker::next_move<false>() {
move = (cur++)->move;
if ( move != ttMove
&& move != killers[0].move
&& move != killers[1].move)
&& move != killers[1].move
&& move != killers[2].move
&& move != killers[3].move)
return move;
break;

View File

@@ -17,11 +17,11 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined MOVEPICK_H_INCLUDED
#ifndef MOVEPICK_H_INCLUDED
#define MOVEPICK_H_INCLUDED
#include <algorithm> // For std::max
#include <cstring> // For memset
#include <cstring> // For std::memset
#include "movegen.h"
#include "position.h"
@@ -30,20 +30,29 @@
/// 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>
/// the class can store History, Gains and Countermoves. 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.
/// Countermoves store the move that refute a previous one. Entries are stored
/// according only to moving piece and destination square, hence two moves with
/// different origin but same destination and piece will be considered identical.
template<bool Gain, typename T>
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)); }
const T* operator[](Piece p) const { return table[p]; }
void clear() { std::memset(table, 0, sizeof(table)); }
void update(Piece p, Square to, Move m) {
if (m == table[p][to].first)
return;
table[p][to].second = table[p][to].first;
table[p][to].first = m;
}
void update(Piece p, Square to, Value v) {
@@ -55,11 +64,12 @@ struct Stats {
}
private:
Value table[PIECE_NB][SQUARE_NB];
T table[PIECE_NB][SQUARE_NB];
};
typedef Stats<false> History;
typedef Stats<true> Gains;
typedef Stats< true, Value> GainsStats;
typedef Stats<false, Value> HistoryStats;
typedef Stats<false, std::pair<Move, Move> > CountermovesStats;
/// MovePicker class is used to pick one pseudo legal move at a time from the
@@ -74,9 +84,10 @@ class MovePicker {
MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC
public:
MovePicker(const Position&, Move, Depth, const History&, Search::Stack*, Value);
MovePicker(const Position&, Move, Depth, const History&, Square);
MovePicker(const Position&, Move, const History&, PieceType);
MovePicker(const Position&, Move, Depth, const HistoryStats&, Square);
MovePicker(const Position&, Move, const HistoryStats&, PieceType);
MovePicker(const Position&, Move, Depth, const HistoryStats&, Move*, Search::Stack*);
template<bool SpNode> Move next_move();
private:
@@ -84,15 +95,16 @@ private:
void generate_next();
const Position& pos;
const History& Hist;
const HistoryStats& history;
Search::Stack* ss;
Move* countermoves;
Depth depth;
Move ttMove;
MoveStack killers[2];
ExtMove killers[4];
Square recaptureSquare;
int captureThreshold, phase;
MoveStack *cur, *end, *endQuiets, *endBadCaptures;
MoveStack moves[MAX_MOVES];
int captureThreshold, stage;
ExtMove *cur, *end, *endQuiets, *endBadCaptures;
ExtMove moves[MAX_MOVES];
};
#endif // !defined(MOVEPICK_H_INCLUDED)
#endif // #ifndef MOVEPICK_H_INCLUDED

View File

@@ -89,9 +89,9 @@ Move move_from_uci(const Position& pos, string& str) {
if (str.length() == 5) // Junior could send promotion piece in uppercase
str[4] = char(tolower(str[4]));
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
if (str == move_to_uci(ml.move(), pos.is_chess960()))
return ml.move();
for (MoveList<LEGAL> it(pos); *it; ++it)
if (str == move_to_uci(*it, pos.is_chess960()))
return *it;
return MOVE_NONE;
}

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(NOTATION_H_INCLUDED)
#ifndef NOTATION_H_INCLUDED
#define NOTATION_H_INCLUDED
#include <string>
@@ -32,4 +32,4 @@ const std::string move_to_uci(Move m, bool chess960);
const std::string move_to_san(Position& pos, Move m);
std::string pretty_pv(Position& pos, int depth, Value score, int64_t msecs, Move pv[]);
#endif // !defined(NOTATION_H_INCLUDED)
#endif // #ifndef NOTATION_H_INCLUDED

View File

@@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include "bitboard.h"
@@ -30,49 +31,48 @@ namespace {
#define S(mg, eg) make_score(mg, eg)
// Doubled pawn penalty by opposed flag and file
const Score DoubledPawnPenalty[2][FILE_NB] = {
const Score Doubled[2][FILE_NB] = {
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) },
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }};
// Isolated pawn penalty by opposed flag and file
const Score IsolatedPawnPenalty[2][FILE_NB] = {
const Score Isolated[2][FILE_NB] = {
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52),
S(60, 52), S(60, 52), S(54, 52), S(37, 45) },
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35),
S(40, 35), S(40, 35), S(36, 35), S(25, 30) }};
// Backward pawn penalty by opposed flag and file
const Score BackwardPawnPenalty[2][FILE_NB] = {
const Score Backward[2][FILE_NB] = {
{ 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(20, 28), S(29, 31), S(33, 31), S(33, 31),
S(33, 31), S(33, 31), S(29, 31), S(20, 28) }};
// Pawn chain membership bonus by file
const Score ChainBonus[FILE_NB] = {
const Score ChainMember[FILE_NB] = {
S(11,-1), S(13,-1), S(13,-1), S(14,-1),
S(14,-1), S(13,-1), S(13,-1), S(11,-1)
};
// Candidate passed pawn bonus by rank
const Score CandidateBonus[RANK_NB] = {
const Score CandidatePassed[RANK_NB] = {
S( 0, 0), S( 6, 13), S(6,13), S(14,29),
S(34,68), S(83,166), S(0, 0), S( 0, 0)
};
const Score PawnStructureWeight = S(233, 201);
// Weakness of our pawn shelter in front of the king indexed by [rank]
const Value ShelterWeakness[RANK_NB] =
{ V(100), V(0), V(27), V(73), V(92), V(101), V(101) };
// Weakness of our pawn shelter in front of the king indexed by [king pawn][rank]
const Value ShelterWeakness[2][RANK_NB] =
{ { 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) } };
// Danger of enemy pawns moving toward our king indexed by [pawn blocked][rank]
const Value StormDanger[2][RANK_NB] =
{ { V(26), V(0), V(128), V(51), V(26) },
{ V(13), V(0), V( 64), V(25), V(13) } };
// Danger of enemy pawns moving toward our king indexed by
// [no friendly pawn | pawn unblocked | pawn blocked][rank of enemy pawn]
const Value StormDanger[3][RANK_NB] = {
{ V( 0), V(64), V(128), V(51), V(26) },
{ V(26), V(32), V( 96), V(38), V(20) },
{ V( 0), V( 0), V( 64), V(25), V(13) }};
// Max bonus for king safety. Corresponds to start position with all the pawns
// in front of the king and no enemy pawn on the horizont.
@@ -82,10 +82,12 @@ namespace {
#undef V
template<Color Us>
Score evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, Pawns::Entry* e) {
Score evaluate(const Position& pos, Pawns::Entry* e) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Up = (Us == WHITE ? DELTA_N : DELTA_S);
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
Bitboard b;
Square s;
@@ -93,7 +95,17 @@ namespace {
Rank r;
bool passed, isolated, doubled, opposed, chain, backward, candidate;
Score value = SCORE_ZERO;
const Square* pl = pos.piece_list(Us, PAWN);
const Square* pl = pos.list<PAWN>(Us);
Bitboard ourPawns = pos.pieces(Us, PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN);
e->passedPawns[Us] = 0;
e->kingSquares[Us] = SQ_NONE;
e->semiopenFiles[Us] = 0xFF;
e->pawnAttacks[Us] = shift_bb<Right>(ourPawns) | shift_bb<Left>(ourPawns);
e->pawnsOnSquares[Us][BLACK] = popcount<Max15>(ourPawns & DarkSquares);
e->pawnsOnSquares[Us][WHITE] = pos.count<PAWN>(Us) - e->pawnsOnSquares[Us][BLACK];
// Loop through all pawns of the current color and score each pawn
while ((s = *pl++) != SQ_NONE)
@@ -103,8 +115,8 @@ namespace {
f = file_of(s);
r = rank_of(s);
// This file cannot be half open
e->halfOpenFiles[Us] &= ~(1 << f);
// This file cannot be semi-open
e->semiopenFiles[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));
@@ -124,7 +136,7 @@ namespace {
// 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))
&& !(ourPawns & pawn_attack_span(Them, s))
&& !(pos.attacks_from<PAWN>(s, Us) & theirPawns))
{
// We now know that there are no friendly pawns beside or behind this
@@ -136,22 +148,22 @@ namespace {
// 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;
b = shift_bb<Up>(b);
// 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;
backward = (b | shift_bb<Up>(b)) & theirPawns;
}
assert(opposed | passed | (attack_span_mask(Us, s) & theirPawns));
assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns));
// A not passed pawn is a candidate to become passed if it is free to
// 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);
&& (b = pawn_attack_span(Them, s + pawn_push(Us)) & ourPawns) != 0
&& popcount<Max15>(b) >= popcount<Max15>(pawn_attack_span(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
@@ -161,30 +173,25 @@ namespace {
// Score this pawn
if (isolated)
value -= IsolatedPawnPenalty[opposed][f];
value -= Isolated[opposed][f];
if (doubled)
value -= DoubledPawnPenalty[opposed][f];
value -= Doubled[opposed][f];
if (backward)
value -= BackwardPawnPenalty[opposed][f];
value -= Backward[opposed][f];
if (chain)
value += ChainBonus[f];
value += ChainMember[f];
if (candidate)
value += CandidateBonus[relative_rank(Us, s)];
value += CandidatePassed[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
namespace Pawns {
@@ -197,27 +204,11 @@ Entry* probe(const Position& pos, Table& entries) {
Key key = pos.pawn_key();
Entry* e = entries[key];
// 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
// the information we found the last time instead of recomputing it.
if (e->key == key)
return e;
e->key = key;
e->passedPawns[WHITE] = e->passedPawns[BLACK] = 0;
e->kingSquares[WHITE] = e->kingSquares[BLACK] = SQ_NONE;
e->halfOpenFiles[WHITE] = e->halfOpenFiles[BLACK] = 0xFF;
Bitboard wPawns = pos.pieces(WHITE, PAWN);
Bitboard bPawns = pos.pieces(BLACK, PAWN);
e->pawnAttacks[WHITE] = ((wPawns & ~FileHBB) << 9) | ((wPawns & ~FileABB) << 7);
e->pawnAttacks[BLACK] = ((bPawns & ~FileHBB) >> 7) | ((bPawns & ~FileABB) >> 9);
e->value = evaluate_pawns<WHITE>(pos, wPawns, bPawns, e)
- evaluate_pawns<BLACK>(pos, bPawns, wPawns, e);
e->value = apply_weight(e->value, PawnStructureWeight);
e->value = evaluate<WHITE>(pos, e) - evaluate<BLACK>(pos, e);
return e;
}
@@ -231,8 +222,8 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
Value safety = MaxSafetyBonus;
Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, ksq) | rank_bb(ksq));
Bitboard ourPawns = b & pos.pieces(Us) & ~rank_bb(ksq);
Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq));
Bitboard ourPawns = b & pos.pieces(Us);
Bitboard theirPawns = b & pos.pieces(Them);
Rank rkUs, rkThem;
File kf = file_of(ksq);
@@ -241,15 +232,13 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) {
for (int f = kf - 1; f <= kf + 1; f++)
{
// Shelter penalty is higher for the pawn in front of the king
b = ourPawns & FileBB[f];
rkUs = b ? rank_of(Us == WHITE ? lsb(b) : ~msb(b)) : RANK_1;
safety -= ShelterWeakness[f != kf][rkUs];
rkUs = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1;
safety -= ShelterWeakness[rkUs];
// Storm danger is smaller if enemy pawn is blocked
b = theirPawns & FileBB[f];
rkThem = b ? rank_of(Us == WHITE ? lsb(b) : ~msb(b)) : RANK_1;
safety -= StormDanger[rkThem == rkUs + 1][rkThem];
rkThem = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1;
safety -= StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem];
}
return safety;

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(PAWNS_H_INCLUDED)
#ifndef PAWNS_H_INCLUDED
#define PAWNS_H_INCLUDED
#include "misc.h"
@@ -37,10 +37,12 @@ struct Entry {
Score pawns_value() const { return value; }
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
int file_is_half_open(Color c, File f) const { return halfOpenFiles[c] & (1 << int(f)); }
int has_open_file_to_left(Color c, File f) const { return halfOpenFiles[c] & ((1 << int(f)) - 1); }
int has_open_file_to_right(Color c, File f) const { return halfOpenFiles[c] & ~((1 << int(f+1)) - 1); }
int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(BlackSquares & s)]; }
int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(DarkSquares & s)]; }
int semiopen(Color c, File f) const { return semiopenFiles[c] & (1 << int(f)); }
int semiopen_on_side(Color c, File f, bool left) const {
return semiopenFiles[c] & (left ? ((1 << int(f)) - 1) : ~((1 << int(f+1)) - 1));
}
template<Color Us>
Score king_safety(const Position& pos, Square ksq) {
@@ -62,7 +64,7 @@ struct Entry {
int minKPdistance[COLOR_NB];
int castleRights[COLOR_NB];
Score value;
int halfOpenFiles[COLOR_NB];
int semiopenFiles[COLOR_NB];
Score kingSafety[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB];
};
@@ -73,4 +75,4 @@ Entry* probe(const Position& pos, Table& entries);
}
#endif // !defined(PAWNS_H_INCLUDED)
#endif // #ifndef PAWNS_H_INCLUDED

View File

@@ -17,10 +17,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(PLATFORM_H_INCLUDED)
#ifndef PLATFORM_H_INCLUDED
#define PLATFORM_H_INCLUDED
#if defined(_MSC_VER)
#ifdef _MSC_VER
// Disable some silly and noisy warning from MSVC compiler
#pragma warning(disable: 4127) // Conditional expression is constant
@@ -40,16 +40,17 @@ typedef unsigned __int64 uint64_t;
#else
# include <inttypes.h>
# include <unistd.h> // Used by sysconf(_SC_NPROCESSORS_ONLN)
#endif
#if !defined(_WIN32) && !defined(_WIN64) // Linux - Unix
#ifndef _WIN32 // Linux - Unix
# include <sys/time.h>
typedef timeval sys_time_t;
inline void system_time(sys_time_t* t) { gettimeofday(t, NULL); }
inline int64_t time_to_msec(const sys_time_t& t) { return t.tv_sec * 1000LL + t.tv_usec / 1000; }
inline int64_t system_time_to_msec() {
timeval t;
gettimeofday(&t, NULL);
return t.tv_sec * 1000LL + t.tv_usec / 1000;
}
# include <pthread.h>
typedef pthread_mutex_t Lock;
@@ -66,18 +67,20 @@ typedef void*(*pt_start_fn)(void*);
# define cond_signal(x) pthread_cond_signal(&(x))
# define cond_wait(x,y) pthread_cond_wait(&(x),&(y))
# define cond_timedwait(x,y,z) pthread_cond_timedwait(&(x),&(y),z)
# define thread_create(x,f,t) !pthread_create(&(x),NULL,(pt_start_fn)f,t)
# define thread_create(x,f,t) pthread_create(&(x),NULL,(pt_start_fn)f,t)
# define thread_join(x) pthread_join(x, NULL)
#else // Windows and MinGW
# include <sys/timeb.h>
typedef _timeb sys_time_t;
inline void system_time(sys_time_t* t) { _ftime(t); }
inline int64_t time_to_msec(const sys_time_t& t) { return t.time * 1000LL + t.millitm; }
inline int64_t system_time_to_msec() {
_timeb t;
_ftime(&t);
return t.time * 1000LL + t.millitm;
}
#if !defined(NOMINMAX)
#ifndef NOMINMAX
# define NOMINMAX // disable macros min() and max()
#endif
@@ -105,9 +108,9 @@ inline DWORD* dwWin9xKludge() { static DWORD dw; return &dw; }
# define cond_signal(x) SetEvent(x)
# 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 thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,dwWin9xKludge()), x != NULL)
# define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,dwWin9xKludge()))
# define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); }
#endif
#endif // !defined(PLATFORM_H_INCLUDED)
#endif // #ifndef PLATFORM_H_INCLUDED

View File

@@ -17,12 +17,12 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <algorithm>
#include "bitcount.h"
#include "movegen.h"
@@ -41,7 +41,7 @@ static const string PieceToChar(" PNBRQK pnbrqk");
CACHE_LINE_ALIGNMENT
Score pieceSquareTable[PIECE_NB][SQUARE_NB];
Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
Value PieceValue[PHASE_NB][PIECE_NB] = {
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
@@ -53,69 +53,24 @@ Key enpassant[FILE_NB];
Key castle[CASTLE_RIGHT_NB];
Key side;
Key exclusion;
/// init() initializes at startup the various arrays used to compute hash keys
/// and the piece square tables. The latter is a two-step operation: First, the
/// white halves of the tables are copied from PSQT[] tables. Second, the black
/// halves of the tables are initialized by flipping and changing the sign of
/// the white scores.
void init() {
RKISS rk;
for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= KING; pt++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
psq[c][pt][s] = rk.rand<Key>();
for (File f = FILE_A; f <= FILE_H; f++)
enpassant[f] = rk.rand<Key>();
for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; cr++)
{
Bitboard b = cr;
while (b)
{
Key k = castle[1ULL << pop_lsb(&b)];
castle[cr] ^= k ? k : rk.rand<Key>();
}
}
side = rk.rand<Key>();
exclusion = rk.rand<Key>();
for (PieceType pt = PAWN; pt <= KING; pt++)
{
PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt];
PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt];
Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]);
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
pieceSquareTable[make_piece(WHITE, pt)][ s] = (v + PSQT[pt][s]);
pieceSquareTable[make_piece(BLACK, pt)][~s] = -(v + PSQT[pt][s]);
}
}
}
} // namespace Zobrist
Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion;}
namespace {
/// next_attacker() is an helper function used by see() to locate the least
/// valuable attacker for the side to move, remove the attacker we just found
/// from the 'occupied' bitboard and scan for new X-ray attacks behind it.
// min_attacker() is an helper function used by see() to locate the least
// valuable attacker for the side to move, remove the attacker we just found
// from the bitboards and scan for new X-ray attacks behind it.
template<int Pt> FORCE_INLINE
PieceType next_attacker(const Bitboard* bb, const Square& to, const Bitboard& stmAttackers,
PieceType min_attacker(const Bitboard* bb, const Square& to, const Bitboard& stmAttackers,
Bitboard& occupied, Bitboard& attackers) {
if (stmAttackers & bb[Pt])
{
Bitboard b = stmAttackers & bb[Pt];
if (!b)
return min_attacker<Pt+1>(bb, to, stmAttackers, occupied, attackers);
occupied ^= b & ~(b - 1);
if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN)
@@ -124,13 +79,12 @@ PieceType next_attacker(const Bitboard* bb, const Square& to, const Bitboard& st
if (Pt == ROOK || Pt == QUEEN)
attackers |= attacks_bb<ROOK>(to, occupied) & (bb[ROOK] | bb[QUEEN]);
attackers &= occupied; // After X-ray that may add already processed pieces
return (PieceType)Pt;
}
return next_attacker<Pt+1>(bb, to, stmAttackers, occupied, attackers);
}
template<> FORCE_INLINE
PieceType next_attacker<KING>(const Bitboard*, const Square&, const Bitboard&, Bitboard&, Bitboard&) {
PieceType min_attacker<KING>(const Bitboard*, const Square&, const Bitboard&, Bitboard&, Bitboard&) {
return KING; // No need to update bitboards, it is the last cycle
}
@@ -156,13 +110,60 @@ CheckInfo::CheckInfo(const Position& pos) {
}
/// Position::init() initializes at startup the various arrays used to compute
/// hash keys and the piece square tables. The latter is a two-step operation:
/// First, the white halves of the tables are copied from PSQT[] tables. Second,
/// the black halves of the tables are initialized by flipping and changing the
/// sign of the white scores.
void Position::init() {
RKISS rk;
for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= KING; pt++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
Zobrist::psq[c][pt][s] = rk.rand<Key>();
for (File f = FILE_A; f <= FILE_H; f++)
Zobrist::enpassant[f] = rk.rand<Key>();
for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; cr++)
{
Bitboard b = cr;
while (b)
{
Key k = Zobrist::castle[1ULL << pop_lsb(&b)];
Zobrist::castle[cr] ^= k ? k : rk.rand<Key>();
}
}
Zobrist::side = rk.rand<Key>();
Zobrist::exclusion = rk.rand<Key>();
for (PieceType pt = PAWN; pt <= KING; pt++)
{
PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt];
PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt];
Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]);
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
psq[WHITE][pt][ s] = (v + PSQT[pt][s]);
psq[BLACK][pt][~s] = -(v + PSQT[pt][s]);
}
}
}
/// Position::operator=() creates a copy of 'pos'. We want the new born Position
/// object do not depend on any external data so we detach state pointer from
/// the source one.
Position& Position::operator=(const Position& pos) {
memcpy(this, &pos, sizeof(Position));
std::memcpy(this, &pos, sizeof(Position));
startState = *st;
st = &startState;
nodes = 0;
@@ -231,7 +232,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
else if ((p = PieceToChar.find(token)) != string::npos)
{
put_piece(Piece(p), sq);
put_piece(sq, color_of(Piece(p)), type_of(Piece(p)));
sq++;
}
}
@@ -288,7 +289,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
st->key = compute_key();
st->pawnKey = compute_pawn_key();
st->materialKey = compute_material_key();
st->psqScore = compute_psq_score();
st->psq = compute_psq_score();
st->npMaterial[WHITE] = compute_non_pawn_material(WHITE);
st->npMaterial[BLACK] = compute_non_pawn_material(BLACK);
st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove);
@@ -391,16 +392,18 @@ const string Position::pretty(Move move) const {
string brd = twoRows + twoRows + twoRows + twoRows + dottedLine;
for (Bitboard b = pieces(); b; )
{
Square s = pop_lsb(&b);
brd[513 - 68 * rank_of(s) + 4 * file_of(s)] = PieceToChar[piece_on(s)];
}
std::ostringstream ss;
if (move)
ss << "\nMove: " << (sideToMove == BLACK ? ".." : "")
<< move_to_san(*const_cast<Position*>(this), move);
for (Square sq = SQ_A1; sq <= SQ_H8; sq++)
if (piece_on(sq) != NO_PIECE)
brd[513 - 68*rank_of(sq) + 4*file_of(sq)] = PieceToChar[piece_on(sq)];
ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase
<< std::setfill('0') << std::setw(16) << st->key << "\nCheckers: ";
@@ -408,43 +411,35 @@ const string Position::pretty(Move move) const {
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()) << " ";
for (MoveList<LEGAL> it(*this); *it; ++it)
ss << move_to_san(*const_cast<Position*>(this), *it) << " ";
return ss.str();
}
/// Position:hidden_checkers<>() returns a bitboard of all pinned (against the
/// king) pieces for the given color. Or, when template parameter FindPinned is
/// false, the function return the pieces of the given color candidate for a
/// discovery check against the enemy king.
template<bool FindPinned>
Bitboard Position::hidden_checkers() const {
/// Position:hidden_checkers() returns a bitboard of all pinned / discovery check
/// pieces, according to the call parameters. Pinned pieces protect our king,
/// discovery check pieces attack the enemy king.
// Pinned pieces protect our king, dicovery checks attack the enemy king
Bitboard b, result = 0;
Bitboard pinners = pieces(FindPinned ? ~sideToMove : sideToMove);
Square ksq = king_square(FindPinned ? sideToMove : ~sideToMove);
Bitboard Position::hidden_checkers(Square ksq, Color c) const {
// Pinners are sliders, that give check when candidate pinned is removed
pinners &= (pieces(ROOK, QUEEN) & PseudoAttacks[ROOK][ksq])
| (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq]);
Bitboard b, pinners, result = 0;
// Pinners are sliders that give check when pinned piece is removed
pinners = ( (pieces( ROOK, QUEEN) & PseudoAttacks[ROOK ][ksq])
| (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq])) & pieces(c);
while (pinners)
{
b = between_bb(ksq, pop_lsb(&pinners)) & pieces();
if (b && !more_than_one(b) && (b & pieces(sideToMove)))
result |= b;
if (!more_than_one(b))
result |= b & pieces(sideToMove);
}
return result;
}
// Explicit template instantiations
template Bitboard Position::hidden_checkers<true>() const;
template Bitboard Position::hidden_checkers<false>() const;
/// Position::attackers_to() computes a bitboard of all pieces which attack a
/// given square. Slider attacks use occ bitboard as occupancy.
@@ -549,7 +544,7 @@ bool Position::is_pseudo_legal(const Move m) const {
return false;
// The destination square cannot be occupied by a friendly piece
if (piece_on(to) != NO_PIECE && color_of(piece_on(to)) == us)
if (pieces(us) & to)
return false;
// Handle the special case of a pawn move
@@ -659,7 +654,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
return true;
// Discovery check ?
if (ci.dcCandidates && (ci.dcCandidates & from))
if (unlikely(ci.dcCandidates) && (ci.dcCandidates & from))
{
// For pawn and king moves we need to verify also direction
if ( (pt != PAWN && pt != KING)
@@ -697,9 +692,9 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
Square rfrom = to; // 'King captures the rook' notation
Square kto = relative_square(us, rfrom > kfrom ? SQ_G1 : SQ_C1);
Square rto = relative_square(us, rfrom > kfrom ? SQ_F1 : SQ_D1);
Bitboard b = (pieces() ^ kfrom ^ rfrom) | rto | kto;
return attacks_bb<ROOK>(rto, b) & ksq;
return (PseudoAttacks[ROOK][rto] & ksq)
&& (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & ksq);
}
default:
assert(false);
@@ -729,7 +724,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Copy some fields of old state to our new StateInfo object except the ones
// which are going to be recalculated from scratch anyway, then switch our state
// pointer to point to the new, ready to be updated, state.
memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t));
std::memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t));
newSt.previous = st;
st = &newSt;
@@ -747,17 +742,17 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
Color them = ~us;
Square from = from_sq(m);
Square to = to_sq(m);
Piece piece = piece_on(from);
PieceType pt = type_of(piece);
Piece pc = piece_on(from);
PieceType pt = type_of(pc);
PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to));
assert(color_of(piece) == us);
assert(color_of(pc) == us);
assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE);
assert(capture != KING);
if (type_of(m) == CASTLE)
{
assert(piece == make_piece(us, KING));
assert(pc == make_piece(us, KING));
bool kingSide = to > from;
Square rfrom = to; // Castle is encoded as "king captures friendly rook"
@@ -767,7 +762,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
do_castle(from, to, rfrom, rto);
st->psqScore += psq_delta(make_piece(us, ROOK), rfrom, rto);
st->psq += psq[us][ROOK][rto] - psq[us][ROOK][rfrom];
k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
}
@@ -797,22 +792,8 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
else
st->npMaterial[them] -= PieceValue[MG][capture];
// Remove the captured piece
byTypeBB[ALL_PIECES] ^= capsq;
byTypeBB[capture] ^= capsq;
byColorBB[them] ^= capsq;
// Update piece list, move the last piece at index[capsq] position and
// shrink the list.
//
// 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
// not in its original place, it means index[] and pieceList[] are not
// guaranteed to be invariant to a do_move() + undo_move() sequence.
Square lastSquare = pieceList[them][capture][--pieceCount[them][capture]];
index[lastSquare] = index[capsq];
pieceList[them][capture][index[lastSquare]] = lastSquare;
pieceList[them][capture][pieceCount[them][capture]] = SQ_NONE;
// Update board and piece lists
remove_piece(capsq, them, capture);
// Update material hash key and prefetch access to materialTable
k ^= Zobrist::psq[them][capture][capsq];
@@ -820,7 +801,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
prefetch((char*)thisThread->materialTable[st->materialKey]);
// Update incremental scores
st->psqScore -= pieceSquareTable[make_piece(them, capture)][capsq];
st->psq -= psq[them][capture][capsq];
// Reset rule 50 counter
st->rule50 = 0;
@@ -849,20 +830,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Move the piece. The tricky Chess960 castle is handled earlier
if (type_of(m) != CASTLE)
{
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] = NO_PIECE;
board[to] = piece;
// Update piece lists, index[from] is not updated and becomes stale. This
// works as long as index[] is accessed just by known occupied squares.
index[to] = index[from];
pieceList[us][pt][index[to]] = to;
}
move_piece(from, to, us, pt);
// If the moving piece is a pawn do some special extra work
if (pt == PAWN)
@@ -882,29 +850,17 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
assert(relative_rank(us, to) == RANK_8);
assert(promotion >= KNIGHT && promotion <= QUEEN);
// Replace the pawn with the promoted piece
byTypeBB[PAWN] ^= to;
byTypeBB[promotion] |= to;
board[to] = make_piece(us, promotion);
// Update piece lists, move the last pawn at index[to] position
// and shrink the list. Add a new promotion piece to the list.
Square lastSquare = pieceList[us][PAWN][--pieceCount[us][PAWN]];
index[lastSquare] = index[to];
pieceList[us][PAWN][index[lastSquare]] = lastSquare;
pieceList[us][PAWN][pieceCount[us][PAWN]] = SQ_NONE;
index[to] = pieceCount[us][promotion];
pieceList[us][promotion][index[to]] = to;
remove_piece(to, us, PAWN);
put_piece(to, us, promotion);
// Update hash keys
k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to];
st->pawnKey ^= Zobrist::psq[us][PAWN][to];
st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion]++]
st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion]-1]
^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]];
// Update incremental score
st->psqScore += pieceSquareTable[make_piece(us, promotion)][to]
- pieceSquareTable[make_piece(us, PAWN)][to];
st->psq += psq[us][promotion][to] - psq[us][PAWN][to];
// Update material
st->npMaterial[us] += PieceValue[MG][promotion];
@@ -919,7 +875,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
}
// Update incremental scores
st->psqScore += psq_delta(piece, from, to);
st->psq += psq[us][pt][to] - psq[us][pt][from];
// Set capture piece
st->capturedType = capture;
@@ -985,20 +941,8 @@ void Position::undo_move(Move m) {
assert(relative_rank(us, to) == RANK_8);
assert(promotion >= KNIGHT && promotion <= QUEEN);
// Replace the promoted piece with the pawn
byTypeBB[promotion] ^= to;
byTypeBB[PAWN] |= to;
board[to] = make_piece(us, PAWN);
// Update piece lists, move the last promoted piece at index[to] position
// and shrink the list. Add a new pawn to the list.
Square lastSquare = pieceList[us][promotion][--pieceCount[us][promotion]];
index[lastSquare] = index[to];
pieceList[us][promotion][index[lastSquare]] = lastSquare;
pieceList[us][promotion][pieceCount[us][promotion]] = SQ_NONE;
index[to] = pieceCount[us][PAWN]++;
pieceList[us][PAWN][index[to]] = to;
remove_piece(to, us, promotion);
put_piece(to, us, PAWN);
pt = PAWN;
}
@@ -1013,21 +957,7 @@ void Position::undo_move(Move m) {
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[to] = NO_PIECE;
board[from] = make_piece(us, pt);
// Update piece lists, index[to] is not updated and becomes stale. This
// works as long as index[] is accessed just by known occupied squares.
index[from] = index[to];
pieceList[us][pt][index[from]] = from;
}
move_piece(to, from, us, pt); // Put the piece back at the source square
if (capture)
{
@@ -1043,16 +973,7 @@ void Position::undo_move(Move m) {
assert(piece_on(capsq) == NO_PIECE);
}
// Restore the captured piece
byTypeBB[ALL_PIECES] |= capsq;
byTypeBB[capture] |= capsq;
byColorBB[them] |= capsq;
board[capsq] = make_piece(them, capture);
// Update piece list, add a new captured piece in capsq square
index[capsq] = pieceCount[them][capture]++;
pieceList[them][capture][index[capsq]] = capsq;
put_piece(capsq, them, capture); // Restore the captured piece
}
// Finally point our state pointer back to the previous state
@@ -1068,25 +989,12 @@ void Position::undo_move(Move m) {
void Position::do_castle(Square kfrom, Square kto, Square rfrom, Square rto) {
Color us = sideToMove;
Bitboard k_from_to_bb = SquareBB[kfrom] ^ SquareBB[kto];
Bitboard r_from_to_bb = SquareBB[rfrom] ^ SquareBB[rto];
byTypeBB[KING] ^= k_from_to_bb;
byTypeBB[ROOK] ^= 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;
// Could be from == to, so first set NO_PIECE then KING and ROOK
board[kfrom] = board[rfrom] = NO_PIECE;
board[kto] = make_piece(us, KING);
board[rto] = make_piece(us, ROOK);
// Could be kfrom == rto, so use a 'tmp' variable
int tmp = index[kfrom];
index[rto] = index[rfrom];
index[kto] = tmp;
pieceList[us][KING][index[kto]] = kto;
pieceList[us][ROOK][index[rto]] = rto;
// Remove both pieces first since squares could overlap in Chess960
remove_piece(kfrom, sideToMove, KING);
remove_piece(rfrom, sideToMove, ROOK);
board[kfrom] = board[rfrom] = NO_PIECE; // Since remove_piece doesn't do it for us
put_piece(kto, sideToMove, KING);
put_piece(rto, sideToMove, ROOK);
}
@@ -1097,7 +1005,7 @@ void Position::do_null_move(StateInfo& newSt) {
assert(!checkers());
memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here
std::memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here
newSt.previous = st;
st = &newSt;
@@ -1141,7 +1049,7 @@ int Position::see_sign(Move m) const {
// Early return if SEE cannot be negative because captured piece value
// is not less then capturing one. Note that king moves always return
// 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_moved(m)] <= PieceValue[MG][piece_on(to_sq(m))])
return 1;
return see(m);
@@ -1159,36 +1067,31 @@ int Position::see(Move m, int asymmThreshold) const {
from = from_sq(m);
to = to_sq(m);
captured = type_of(piece_on(to));
swapList[0] = PieceValue[MG][type_of(piece_on(to))];
stm = color_of(piece_on(from));
occupied = pieces() ^ from;
// Handle en passant moves
if (type_of(m) == ENPASSANT)
{
Square capQq = to - pawn_push(sideToMove);
assert(!captured);
assert(type_of(piece_on(capQq)) == PAWN);
// Remove the captured pawn
occupied ^= capQq;
captured = PAWN;
}
else if (type_of(m) == CASTLE)
// Castle moves are implemented as king capturing the rook so cannot be
// handled correctly. Simply return 0 that is always the correct value
// unless the rook is ends up under attack.
// unless in the rare case the rook ends up under attack.
if (type_of(m) == CASTLE)
return 0;
if (type_of(m) == ENPASSANT)
{
occupied ^= to - pawn_push(stm); // Remove the captured pawn
swapList[0] = PieceValue[MG][PAWN];
}
// Find all attackers to the destination square, with the moving piece
// removed, but possibly an X-ray attacker added behind it.
attackers = attackers_to(to, occupied);
attackers = attackers_to(to, occupied) & occupied;
// If the opponent has no attackers we are finished
stm = ~color_of(piece_on(from));
stm = ~stm;
stmAttackers = attackers & pieces(stm);
if (!stmAttackers)
return PieceValue[MG][captured];
return swapList[0];
// The destination square is defended, which makes things rather more
// difficult to compute. We proceed by building up a "swap list" containing
@@ -1196,7 +1099,6 @@ int Position::see(Move m, int asymmThreshold) const {
// destination square, where the sides alternately capture, and always
// capture with the least valuable piece. After each capture, we look for
// new X-ray attacks from behind the capturing piece.
swapList[0] = PieceValue[MG][captured];
captured = type_of(piece_on(from));
do {
@@ -1206,19 +1108,15 @@ int Position::see(Move m, int asymmThreshold) const {
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured];
slIndex++;
// Locate and remove from 'occupied' the next least valuable attacker
captured = next_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
attackers &= occupied; // Remove the just found attacker
// Locate and remove the next least valuable attacker
captured = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
stm = ~stm;
stmAttackers = attackers & pieces(stm);
if (captured == KING)
{
// Stop before processing a king capture
if (stmAttackers)
if (captured == KING && stmAttackers)
{
swapList[slIndex++] = QueenValueMg * 16;
break;
}
@@ -1247,7 +1145,7 @@ int Position::see(Move m, int asymmThreshold) const {
void Position::clear() {
memset(this, 0, sizeof(Position));
std::memset(this, 0, sizeof(Position));
startState.epSquare = SQ_NONE;
st = &startState;
@@ -1257,24 +1155,6 @@ void Position::clear() {
}
/// Position::put_piece() puts a piece on the given square of the board,
/// updating the board array, pieces list, bitboards, and piece counts.
void Position::put_piece(Piece p, Square s) {
Color c = color_of(p);
PieceType pt = type_of(p);
board[s] = p;
index[s] = pieceCount[c][pt]++;
pieceList[c][pt][index[s]] = s;
byTypeBB[ALL_PIECES] |= s;
byTypeBB[pt] |= s;
byColorBB[c] |= s;
}
/// Position::compute_key() computes the hash key of the position. The hash
/// key is usually updated incrementally as moves are made and unmade, the
/// compute_key() function is only used when a new position is set up, and
@@ -1332,7 +1212,7 @@ Key Position::compute_material_key() const {
for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= QUEEN; pt++)
for (int cnt = 0; cnt < piece_count(c, pt); cnt++)
for (int cnt = 0; cnt < pieceCount[c][pt]; cnt++)
k ^= Zobrist::psq[c][pt][cnt];
return k;
@@ -1350,7 +1230,8 @@ Score Position::compute_psq_score() const {
for (Bitboard b = pieces(); b; )
{
Square s = pop_lsb(&b);
score += pieceSquareTable[piece_on(s)][s];
Piece pc = piece_on(s);
score += psq[color_of(pc)][type_of(pc)][s];
}
return score;
@@ -1367,7 +1248,7 @@ Value Position::compute_non_pawn_material(Color c) const {
Value value = VALUE_ZERO;
for (PieceType pt = KNIGHT; pt <= QUEEN; pt++)
value += piece_count(c, pt) * PieceValue[MG][pt];
value += pieceCount[c][pt] * PieceValue[MG][pt];
return value;
}
@@ -1412,42 +1293,36 @@ bool Position::is_draw() const {
/// Position::flip() flips position with the white and black sides reversed. This
/// is only useful for debugging especially for finding evaluation symmetry bugs.
static char toggle_case(char c) {
return char(islower(c) ? toupper(c) : tolower(c));
}
void Position::flip() {
const Position pos(*this);
string f, token;
std::stringstream ss(fen());
clear();
for (Rank rank = RANK_8; rank >= RANK_1; rank--) // Piece placement
{
std::getline(ss, token, rank > RANK_1 ? '/' : ' ');
f.insert(0, token + (f.empty() ? " " : "/"));
}
sideToMove = ~pos.side_to_move();
thisThread = pos.this_thread();
nodes = pos.nodes_searched();
chess960 = pos.is_chess960();
gamePly = pos.game_ply();
ss >> token; // Active color
f += (token == "w" ? "B " : "W "); // Will be lowercased later
for (Square s = SQ_A1; s <= SQ_H8; s++)
if (!pos.is_empty(s))
put_piece(Piece(pos.piece_on(s) ^ 8), ~s);
ss >> token; // Castling availability
f += token + " ";
if (pos.can_castle(WHITE_OO))
set_castle_right(BLACK, ~pos.castle_rook_square(WHITE, KING_SIDE));
if (pos.can_castle(WHITE_OOO))
set_castle_right(BLACK, ~pos.castle_rook_square(WHITE, QUEEN_SIDE));
if (pos.can_castle(BLACK_OO))
set_castle_right(WHITE, ~pos.castle_rook_square(BLACK, KING_SIDE));
if (pos.can_castle(BLACK_OOO))
set_castle_right(WHITE, ~pos.castle_rook_square(BLACK, QUEEN_SIDE));
std::transform(f.begin(), f.end(), f.begin(), toggle_case);
if (pos.st->epSquare != SQ_NONE)
st->epSquare = ~pos.st->epSquare;
ss >> token; // En passant square
f += (token == "-" ? token : token.replace(1, 1, token[1] == '3' ? "6" : "3"));
st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove);
std::getline(ss, token); // Half and full moves
f += token;
st->key = compute_key();
st->pawnKey = compute_pawn_key();
st->materialKey = compute_material_key();
st->psqScore = compute_psq_score();
st->npMaterial[WHITE] = compute_non_pawn_material(WHITE);
st->npMaterial[BLACK] = compute_non_pawn_material(BLACK);
set(f, is_chess960(), this_thread());
assert(pos_is_ok());
}
@@ -1536,15 +1411,13 @@ bool Position::pos_is_ok(int* failedStep) const {
if ((*step)++, debugMaterialKey && st->materialKey != compute_material_key())
return false;
if ((*step)++, debugIncrementalEval && st->psqScore != compute_psq_score())
if ((*step)++, debugIncrementalEval && st->psq != compute_psq_score())
return false;
if ((*step)++, debugNonPawnMaterial)
{
if ( st->npMaterial[WHITE] != compute_non_pawn_material(WHITE)
|| st->npMaterial[BLACK] != compute_non_pawn_material(BLACK))
return false;
}
if ((*step)++, debugPieceCounts)
for (Color c = WHITE; c <= BLACK; c++)
@@ -1556,14 +1429,10 @@ bool Position::pos_is_ok(int* failedStep) const {
for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= KING; pt++)
for (int i = 0; i < pieceCount[c][pt]; i++)
{
if (piece_on(piece_list(c, pt)[i]) != make_piece(c, pt))
if ( board[pieceList[c][pt][i]] != make_piece(c, pt)
|| index[pieceList[c][pt][i]] != i)
return false;
if (index[piece_list(c, pt)[i]] != i)
return false;
}
if ((*step)++, debugCastleSquares)
for (Color c = WHITE; c <= BLACK; c++)
for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
@@ -1573,10 +1442,8 @@ bool Position::pos_is_ok(int* failedStep) const {
if (!can_castle(cr))
continue;
if ((castleRightsMask[king_square(c)] & cr) != cr)
return false;
if ( piece_on(castleRookSquare[c][s]) != make_piece(c, ROOK)
if ( (castleRightsMask[king_square(c)] & cr) != cr
|| piece_on(castleRookSquare[c][s]) != make_piece(c, ROOK)
|| castleRightsMask[castleRookSquare[c][s]] != cr)
return false;
}

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(POSITION_H_INCLUDED)
#ifndef POSITION_H_INCLUDED
#define POSITION_H_INCLUDED
#include <cassert>
@@ -52,7 +52,7 @@ struct StateInfo {
Key pawnKey, materialKey;
Value npMaterial[COLOR_NB];
int castleRights, rule50, pliesFromNull;
Score psqScore;
Score psq;
Square epSquare;
Key key;
@@ -95,6 +95,7 @@ public:
Position(const Position& p, Thread* t) { *this = p; thisThread = t; }
Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); }
Position& operator=(const Position&);
static void init();
// Text input/output
void set(const std::string& fen, bool isChess960, Thread* th);
@@ -112,8 +113,8 @@ public:
Square king_square(Color c) const;
Square ep_square() const;
bool is_empty(Square s) const;
const Square* piece_list(Color c, PieceType pt) const;
int piece_count(Color c, PieceType pt) const;
template<PieceType Pt> int count(Color c) const;
template<PieceType Pt> const Square* list(Color c) const;
// Castling
int can_castle(CastleRight f) const;
@@ -169,7 +170,6 @@ public:
// Incremental piece-square evaluation
Score psq_score() const;
Score psq_delta(Piece p, Square from, Square to) const;
Value non_pawn_material(Color c) const;
// Other properties of the position
@@ -188,12 +188,14 @@ public:
private:
// Initialization helpers (used while setting up a position)
void clear();
void put_piece(Piece p, Square s);
void set_castle_right(Color c, Square rfrom);
// Helper functions
void do_castle(Square kfrom, Square kto, Square rfrom, Square rto);
template<bool FindPinned> Bitboard hidden_checkers() const;
Bitboard hidden_checkers(Square ksq, Color c) const;
void put_piece(Square s, Color c, PieceType pt);
void remove_piece(Square s, Color c, PieceType pt);
void move_piece(Square from, Square to, Color c, PieceType pt);
// Computing hash keys from scratch (for initialization and debugging)
Key compute_key() const;
@@ -273,12 +275,12 @@ inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const {
return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]);
}
inline int Position::piece_count(Color c, PieceType pt) const {
return pieceCount[c][pt];
template<PieceType Pt> inline int Position::count(Color c) const {
return pieceCount[c][Pt];
}
inline const Square* Position::piece_list(Color c, PieceType pt) const {
return pieceList[c][pt];
template<PieceType Pt> inline const Square* Position::list(Color c) const {
return pieceList[c][Pt];
}
inline Square Position::ep_square() const {
@@ -331,11 +333,11 @@ inline Bitboard Position::checkers() const {
}
inline Bitboard Position::discovered_check_candidates() const {
return hidden_checkers<false>();
return hidden_checkers(king_square(~sideToMove), sideToMove);
}
inline Bitboard Position::pinned_pieces() const {
return hidden_checkers<true>();
return hidden_checkers(king_square(sideToMove), ~sideToMove);
}
inline bool Position::pawn_is_passed(Color c, Square s) const {
@@ -346,10 +348,6 @@ inline Key Position::key() const {
return st->key;
}
inline Key Position::exclusion_key() const {
return st->key ^ Zobrist::exclusion;
}
inline Key Position::pawn_key() const {
return st->pawnKey;
}
@@ -358,12 +356,8 @@ inline Key Position::material_key() const {
return st->materialKey;
}
inline Score Position::psq_delta(Piece p, Square from, Square to) const {
return pieceSquareTable[p][to] - pieceSquareTable[p][from];
}
inline Score Position::psq_score() const {
return st->psqScore;
return st->psq;
}
inline Value Position::non_pawn_material(Color c) const {
@@ -422,4 +416,44 @@ inline Thread* Position::this_thread() const {
return thisThread;
}
#endif // !defined(POSITION_H_INCLUDED)
inline void Position::put_piece(Square s, Color c, PieceType pt) {
board[s] = make_piece(c, pt);
byTypeBB[ALL_PIECES] |= s;
byTypeBB[pt] |= s;
byColorBB[c] |= s;
index[s] = pieceCount[c][pt]++;
pieceList[c][pt][index[s]] = s;
}
inline void Position::move_piece(Square from, Square to, Color c, PieceType pt) {
// index[from] is not updated and becomes stale. This works as long
// as index[] is accessed just by known occupied squares.
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb;
byColorBB[c] ^= from_to_bb;
board[from] = NO_PIECE;
board[to] = make_piece(c, pt);
index[to] = index[from];
pieceList[c][pt][index[to]] = to;
}
inline void Position::remove_piece(Square s, Color c, PieceType pt) {
// WARNING: This is not a reversible operation. If we remove a piece in
// do_move() and then replace it 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 guaranteed to be invariant to a do_move() + undo_move() sequence.
byTypeBB[ALL_PIECES] ^= s;
byTypeBB[pt] ^= s;
byColorBB[c] ^= s;
/* board[s] = NO_PIECE; */ // Not needed, will be overwritten by capturing
Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]];
index[lastSquare] = index[s];
pieceList[c][pt][index[lastSquare]] = lastSquare;
pieceList[c][pt][pieceCount[c][pt]] = SQ_NONE;
}
#endif // #ifndef POSITION_H_INCLUDED

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(PSQTAB_H_INCLUDED)
#ifndef PSQTAB_H_INCLUDED
#define PSQTAB_H_INCLUDED
#include "types.h"
@@ -33,12 +33,12 @@ static const Score PSQT[][SQUARE_NB] = {
{ },
{ // 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(-28,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-28,-8),
S(-28,-8), S(-6,-8), S( 9,-8), S(36,-8), S(36,-8), S( 9,-8), S(-6,-8), S(-28,-8),
S(-28,-8), S(-6,-8), S(17,-8), S(58,-8), S(58,-8), S(17,-8), S(-6,-8), S(-28,-8),
S(-28,-8), S(-6,-8), S(17,-8), S(36,-8), S(36,-8), S(17,-8), S(-6,-8), S(-28,-8),
S(-28,-8), S(-6,-8), S( 9,-8), S(14,-8), S(14,-8), S( 9,-8), S(-6,-8), S(-28,-8),
S(-28,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-28,-8),
S(-20,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-20,-8),
S(-20,-8), S(-6,-8), S( 9,-8), S(34,-8), S(34,-8), S( 9,-8), S(-6,-8), S(-20,-8),
S(-20,-8), S(-6,-8), S(17,-8), S(54,-8), S(54,-8), S(17,-8), S(-6,-8), S(-20,-8),
S(-20,-8), S(-6,-8), S(17,-8), S(34,-8), S(34,-8), S(17,-8), S(-6,-8), S(-20,-8),
S(-20,-8), S(-6,-8), S( 9,-8), S(14,-8), S(14,-8), S( 9,-8), S(-6,-8), S(-20,-8),
S(-20,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-20,-8),
S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0)
},
{ // Knight
@@ -95,4 +95,4 @@ static const Score PSQT[][SQUARE_NB] = {
#undef S
#endif // !defined(PSQTAB_H_INCLUDED)
#endif // #ifndef PSQTAB_H_INCLUDED

View File

@@ -22,7 +22,7 @@
(at your option) any later version.
*/
#if !defined(RKISS_H_INCLUDED)
#ifndef RKISS_H_INCLUDED
#define RKISS_H_INCLUDED
#include "types.h"
@@ -43,14 +43,12 @@
class RKISS {
// Keep variables always together
struct S { uint64_t a, b, c, d; } s;
struct S { uint64_t a, b, c, d; } s; // Keep variables always together
uint64_t rotate(uint64_t x, uint64_t k) const {
return (x << k) | (x >> (64 - k));
}
// Return 64 bit unsigned integer in between [0, 2^64 - 1]
uint64_t rand64() {
const uint64_t
@@ -73,4 +71,4 @@ public:
template<typename T> T rand() { return T(rand64()); }
};
#endif // !defined(RKISS_H_INCLUDED)
#endif // #ifndef RKISS_H_INCLUDED

View File

@@ -66,7 +66,7 @@ namespace {
// Futility lookup tables (initialized at startup) and their access functions
Value FutilityMargins[16][64]; // [depth][moveNumber]
int FutilityMoveCounts[32]; // [depth]
int FutilityMoveCounts[2][32]; // [improving][depth]
inline Value futility_margin(Depth d, int mn) {
@@ -75,22 +75,23 @@ namespace {
}
// Reduction lookup tables (initialized at startup) and their access function
int8_t Reductions[2][64][64]; // [pv][depth][moveNumber]
int8_t Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber]
template <bool PvNode> inline Depth reduction(Depth d, int mn) {
template <bool PvNode> inline Depth reduction(bool i, Depth d, int mn) {
return (Depth) Reductions[PvNode][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)];
return (Depth) Reductions[PvNode][i][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)];
}
size_t PVSize, PVIdx;
TimeManager TimeMgr;
int BestMoveChanges;
Value DrawValue[COLOR_NB];
History Hist;
Gains Gain;
HistoryStats History;
GainsStats Gains;
CountermovesStats Countermoves;
template <NodeType NT>
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth);
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode);
template <NodeType NT, bool InCheck>
Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth);
@@ -135,8 +136,14 @@ void Search::init() {
{
double pvRed = log(double(hd)) * log(double(mc)) / 3.0;
double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25;
Reductions[1][hd][mc] = (int8_t) ( pvRed >= 1.0 ? floor( pvRed * int(ONE_PLY)) : 0);
Reductions[0][hd][mc] = (int8_t) (nonPVRed >= 1.0 ? floor(nonPVRed * int(ONE_PLY)) : 0);
Reductions[1][1][hd][mc] = (int8_t) ( pvRed >= 1.0 ? floor( pvRed * int(ONE_PLY)) : 0);
Reductions[0][1][hd][mc] = (int8_t) (nonPVRed >= 1.0 ? floor(nonPVRed * int(ONE_PLY)) : 0);
Reductions[1][0][hd][mc] = Reductions[1][1][hd][mc];
Reductions[0][0][hd][mc] = Reductions[0][1][hd][mc];
if (Reductions[0][0][hd][mc] > 2 * ONE_PLY)
Reductions[0][0][hd][mc] += ONE_PLY;
}
// Init futility margins array
@@ -145,33 +152,35 @@ void Search::init() {
// Init futility move count array
for (d = 0; d < 32; d++)
FutilityMoveCounts[d] = int(3.001 + 0.25 * pow(double(d), 2.0));
{
FutilityMoveCounts[0][d] = int(3.001 + 0.3 * pow(double(d ), 1.8)) * (d < 5 ? 4 : 3) / 4;
FutilityMoveCounts[1][d] = int(3.001 + 0.3 * pow(double(d + 0.98), 1.8));
}
}
/// Search::perft() is our utility to verify move generation. All the leaf nodes
/// up to the given depth are generated and counted and the sum returned.
size_t Search::perft(Position& pos, Depth depth) {
// At the last ply just return the number of legal moves (leaf nodes)
if (depth == ONE_PLY)
return MoveList<LEGAL>(pos).size();
static size_t perft(Position& pos, Depth depth) {
StateInfo st;
size_t cnt = 0;
CheckInfo ci(pos);
const bool leaf = depth == 2 * ONE_PLY;
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
for (MoveList<LEGAL> it(pos); *it; ++it)
{
pos.do_move(ml.move(), st, ci, pos.move_gives_check(ml.move(), ci));
cnt += perft(pos, depth - ONE_PLY);
pos.undo_move(ml.move());
pos.do_move(*it, st, ci, pos.move_gives_check(*it, ci));
cnt += leaf ? MoveList<LEGAL>(pos).size() : ::perft(pos, depth - ONE_PLY);
pos.undo_move(*it);
}
return cnt;
}
size_t Search::perft(Position& pos, Depth depth) {
return depth > ONE_PLY ? ::perft(pos, depth) : MoveList<LEGAL>(pos).size();
}
/// Search::think() is the external interface to Stockfish's search, and is
/// called by the main thread when the program receives the UCI 'go' command. It
@@ -215,7 +224,7 @@ void Search::think() {
else
DrawValue[WHITE] = DrawValue[BLACK] = VALUE_DRAW;
if (Options["Use Search Log"])
if (Options["Write Search Log"])
{
Log log(Options["Search Log Filename"]);
log << "\nSearching: " << RootPos.fen()
@@ -231,7 +240,7 @@ void Search::think() {
for (size_t i = 0; i < Threads.size(); i++)
Threads[i]->maxPly = 0;
Threads.sleepWhileIdle = Options["Use Sleeping Threads"];
Threads.sleepWhileIdle = Options["Idle Threads Sleep"];
// Set best timer interval to avoid lagging under time pressure. Timer is
// used to check for remaining available thinking time.
@@ -247,7 +256,7 @@ void Search::think() {
Threads.timer->msec = 0; // Stop the timer
Threads.sleepWhileIdle = true; // Send idle threads to sleep
if (Options["Use Search Log"])
if (Options["Write Search Log"])
{
Time::point elapsed = Time::now() - SearchTime + 1;
@@ -264,6 +273,10 @@ void Search::think() {
finalize:
// When search is stopped this info is not printed
sync_cout << "info nodes " << RootPos.nodes_searched()
<< " time " << Time::now() - SearchTime + 1 << sync_endl;
// When we reach max depth we arrive here even without Signals.stop is raised,
// but if we are pondering or in infinite search, according to UCI protocol,
// we shouldn't print the best move before the GUI sends a "stop" or "ponderhit"
@@ -290,17 +303,21 @@ namespace {
void id_loop(Position& pos) {
Stack ss[MAX_PLY_PLUS_2];
Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2)
int depth, prevBestMoveChanges;
Value bestValue, alpha, beta, delta;
memset(ss, 0, 4 * sizeof(Stack));
std::memset(ss-2, 0, 5 * sizeof(Stack));
(ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains
depth = BestMoveChanges = 0;
bestValue = delta = -VALUE_INFINITE;
ss->currentMove = MOVE_NULL; // Hack to skip update gains
bestValue = delta = alpha = -VALUE_INFINITE;
beta = VALUE_INFINITE;
TT.new_search();
Hist.clear();
Gain.clear();
History.clear();
Gains.clear();
Countermoves.clear();
PVSize = Options["MultiPV"];
Skill skill(Options["Skill Level"]);
@@ -326,26 +343,19 @@ namespace {
// MultiPV loop. We perform a full root search for each PV line
for (PVIdx = 0; PVIdx < PVSize; PVIdx++)
{
// Set aspiration window default width
if (depth >= 5 && abs(RootMoves[PVIdx].prevScore) < VALUE_KNOWN_WIN)
// Reset aspiration window starting size
if (depth >= 5)
{
delta = Value(16);
alpha = RootMoves[PVIdx].prevScore - delta;
beta = RootMoves[PVIdx].prevScore + delta;
}
else
{
alpha = -VALUE_INFINITE;
beta = VALUE_INFINITE;
alpha = std::max(RootMoves[PVIdx].prevScore - delta,-VALUE_INFINITE);
beta = std::min(RootMoves[PVIdx].prevScore + delta, VALUE_INFINITE);
}
// Start with a small aspiration window and, in case of fail high/low,
// research with bigger window until not failing high/low anymore.
while (true)
{
// Search starts from ss+1 to allow referencing (ss-1). This is
// needed by update gains and ss copy when splitting at Root.
bestValue = search<Root>(pos, ss+1, alpha, beta, depth * ONE_PLY);
bestValue = search<Root>(pos, ss, alpha, beta, depth * ONE_PLY, false);
// Bring to front the best move. It is critical that sorting is
// done with a stable algorithm because all the values but the first
@@ -366,33 +376,28 @@ namespace {
if (Signals.stop)
return;
// In case of failing high/low increase aspiration window and
// research, otherwise exit the loop.
if (bestValue > alpha && bestValue < beta)
break;
// Give some update (without cluttering the UI) before to research
if (Time::now() - SearchTime > 3000)
// When failing high/low give some update (without cluttering
// the UI) before to research.
if ( (bestValue <= alpha || bestValue >= beta)
&& Time::now() - SearchTime > 3000)
sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl;
if (abs(bestValue) >= VALUE_KNOWN_WIN)
{
alpha = -VALUE_INFINITE;
beta = VALUE_INFINITE;
}
else if (bestValue >= beta)
{
beta += delta;
delta += delta / 2;
}
else
// In case of failing low/high increase aspiration window and
// research, otherwise exit the loop.
if (bestValue <= alpha)
{
alpha = std::max(bestValue - delta, -VALUE_INFINITE);
Signals.failedLowAtRoot = true;
Signals.stopOnPonderhit = false;
alpha -= delta;
delta += delta / 2;
}
else if (bestValue >= beta)
beta = std::min(bestValue + delta, VALUE_INFINITE);
else
break;
delta += delta / 2;
assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
}
@@ -408,10 +413,14 @@ namespace {
if (skill.enabled() && skill.time_to_pick(depth))
skill.pick_move();
if (Options["Use Search Log"])
if (Options["Write Search Log"])
{
RootMove& rm = RootMoves[0];
if (skill.best != MOVE_NONE)
rm = *std::find(RootMoves.begin(), RootMoves.end(), skill.best);
Log log(Options["Search Log Filename"]);
log << pretty_pv(pos, depth, bestValue, Time::now() - SearchTime, &RootMoves[0].pv[0])
log << pretty_pv(pos, depth, rm.score, Time::now() - SearchTime, &rm.pv[0])
<< std::endl;
}
@@ -445,11 +454,11 @@ namespace {
|| Time::now() - SearchTime > (TimeMgr.available_time() * 20) / 100))
{
Value rBeta = bestValue - 2 * PawnValueMg;
(ss+1)->excludedMove = RootMoves[0].pv[0];
(ss+1)->skipNullMove = true;
Value v = search<NonPV>(pos, ss+1, rBeta - 1, rBeta, (depth - 3) * ONE_PLY);
(ss+1)->skipNullMove = false;
(ss+1)->excludedMove = MOVE_NONE;
ss->excludedMove = RootMoves[0].pv[0];
ss->skipNullMove = true;
Value v = search<NonPV>(pos, ss, rBeta - 1, rBeta, (depth - 3) * ONE_PLY, true);
ss->skipNullMove = false;
ss->excludedMove = MOVE_NONE;
if (v < rBeta)
stop = true;
@@ -477,7 +486,7 @@ namespace {
// here: This is taken care of after we return from the split point.
template <NodeType NT>
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) {
const bool PvNode = (NT == PV || NT == Root || NT == SplitPointPV || NT == SplitPointRoot);
const bool SpNode = (NT == SplitPointPV || NT == SplitPointNonPV || NT == SplitPointRoot);
@@ -487,7 +496,7 @@ namespace {
assert(PvNode || (alpha == beta - 1));
assert(depth > DEPTH_ZERO);
Move movesSearched[64];
Move quietsSearched[64];
StateInfo st;
const TTEntry *tte;
SplitPoint* splitPoint;
@@ -496,13 +505,12 @@ namespace {
Depth ext, newDepth;
Value bestValue, value, ttValue;
Value eval, nullValue, futilityValue;
bool inCheck, givesCheck, pvMove, singularExtensionNode;
bool inCheck, givesCheck, pvMove, singularExtensionNode, improving;
bool captureOrPromotion, dangerous, doFullDepthSearch;
int moveCount, playedMoveCount;
int moveCount, quietCount;
// Step 1. Initialize node
Thread* thisThread = pos.this_thread();
moveCount = playedMoveCount = 0;
inCheck = pos.checkers();
if (SpNode)
@@ -517,9 +525,10 @@ namespace {
assert(splitPoint->bestValue > -VALUE_INFINITE && splitPoint->moveCount > 0);
goto split_point_start;
goto moves_loop;
}
moveCount = quietCount = 0;
bestValue = -VALUE_INFINITE;
ss->currentMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
ss->ply = (ss-1)->ply + 1;
@@ -566,9 +575,9 @@ namespace {
&& tte
&& tte->depth() >= depth
&& ttValue != VALUE_NONE // Only in case of TT access race
&& ( PvNode ? tte->type() == BOUND_EXACT
: ttValue >= beta ? (tte->type() & BOUND_LOWER)
: (tte->type() & BOUND_UPPER)))
&& ( PvNode ? tte->bound() == BOUND_EXACT
: ttValue >= beta ? (tte->bound() & BOUND_LOWER)
: (tte->bound() & BOUND_UPPER)))
{
TT.refresh(tte);
ss->currentMove = ttMove; // Can be MOVE_NONE
@@ -586,7 +595,10 @@ namespace {
// Step 5. Evaluate the position statically and update parent's gain statistics
if (inCheck)
{
ss->staticEval = ss->evalMargin = eval = VALUE_NONE;
goto moves_loop;
}
else if (tte)
{
@@ -597,8 +609,8 @@ namespace {
// Can ttValue be used as a better position evaluation?
if (ttValue != VALUE_NONE)
if ( ((tte->type() & BOUND_LOWER) && ttValue > eval)
|| ((tte->type() & BOUND_UPPER) && ttValue < eval))
if ( ((tte->bound() & BOUND_LOWER) && ttValue > eval)
|| ((tte->bound() & BOUND_UPPER) && ttValue < eval))
eval = ttValue;
}
else
@@ -610,20 +622,19 @@ namespace {
// Update gain for the parent non-capture move given the static position
// evaluation before and after the move.
if ( (move = (ss-1)->currentMove) != MOVE_NULL
&& (ss-1)->staticEval != VALUE_NONE
if ( !pos.captured_piece_type()
&& ss->staticEval != VALUE_NONE
&& !pos.captured_piece_type()
&& (ss-1)->staticEval != VALUE_NONE
&& (move = (ss-1)->currentMove) != MOVE_NULL
&& type_of(move) == NORMAL)
{
Square to = to_sq(move);
Gain.update(pos.piece_on(to), to, -(ss-1)->staticEval - ss->staticEval);
Gains.update(pos.piece_on(to), to, -(ss-1)->staticEval - ss->staticEval);
}
// Step 6. Razoring (is omitted in PV nodes)
// Step 6. Razoring (skipped when in check)
if ( !PvNode
&& depth < 4 * ONE_PLY
&& !inCheck
&& eval + razor_margin(depth) < beta
&& ttMove == MOVE_NONE
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
@@ -637,13 +648,12 @@ namespace {
return v;
}
// Step 7. Static null move pruning (is omitted in PV nodes)
// Step 7. Static null move pruning (skipped when in check)
// We're betting that the opponent doesn't have a move that will reduce
// the score by more than futility_margin(depth) if we do a null move.
if ( !PvNode
&& !ss->skipNullMove
&& depth < 4 * ONE_PLY
&& !inCheck
&& eval - futility_margin(depth, (ss-1)->futilityMoveCount) >= beta
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
&& abs(eval) < VALUE_KNOWN_WIN
@@ -654,7 +664,6 @@ namespace {
if ( !PvNode
&& !ss->skipNullMove
&& depth > ONE_PLY
&& !inCheck
&& eval >= beta
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
&& pos.non_pawn_material(pos.side_to_move()))
@@ -671,7 +680,7 @@ namespace {
pos.do_null_move(st);
(ss+1)->skipNullMove = true;
nullValue = depth-R < ONE_PLY ? -qsearch<NonPV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: - search<NonPV>(pos, ss+1, -beta, -alpha, depth-R);
: - search<NonPV>(pos, ss+1, -beta, -alpha, depth-R, !cutNode);
(ss+1)->skipNullMove = false;
pos.undo_null_move();
@@ -686,7 +695,7 @@ namespace {
// Do verification search at high depths
ss->skipNullMove = true;
Value v = search<NonPV>(pos, ss, alpha, beta, depth-R);
Value v = search<NonPV>(pos, ss, alpha, beta, depth-R, false);
ss->skipNullMove = false;
if (v >= beta)
@@ -706,19 +715,17 @@ namespace {
&& (ss-1)->reduction
&& threatMove != MOVE_NONE
&& allows(pos, (ss-1)->currentMove, threatMove))
return beta - 1;
return alpha;
}
}
// Step 9. ProbCut (is omitted in PV nodes)
// Step 9. ProbCut (skipped when in check)
// If we have a very good capture (i.e. SEE > seeValues[captured_piece_type])
// and a reduced search returns a value much above beta, we can (almost) safely
// prune the previous move.
if ( !PvNode
&& depth >= 5 * ONE_PLY
&& !inCheck
&& !ss->skipNullMove
&& excludedMove == MOVE_NONE
&& abs(beta) < VALUE_MATE_IN_MAX_PLY)
{
Value rbeta = beta + 200;
@@ -728,7 +735,7 @@ namespace {
assert((ss-1)->currentMove != MOVE_NONE);
assert((ss-1)->currentMove != MOVE_NULL);
MovePicker mp(pos, ttMove, Hist, pos.captured_piece_type());
MovePicker mp(pos, ttMove, History, pos.captured_piece_type());
CheckInfo ci(pos);
while ((move = mp.next_move<false>()) != MOVE_NONE)
@@ -736,39 +743,47 @@ namespace {
{
ss->currentMove = move;
pos.do_move(move, st, ci, pos.move_gives_check(move, ci));
value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, rdepth);
value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode);
pos.undo_move(move);
if (value >= rbeta)
return value;
}
}
// Step 10. Internal iterative deepening
// Step 10. Internal iterative deepening (skipped when in check)
if ( depth >= (PvNode ? 5 * ONE_PLY : 8 * ONE_PLY)
&& ttMove == MOVE_NONE
&& (PvNode || (!inCheck && ss->staticEval + Value(256) >= beta)))
&& (PvNode || ss->staticEval + Value(256) >= beta))
{
Depth d = depth - 2 * ONE_PLY - (PvNode ? DEPTH_ZERO : depth / 4);
ss->skipNullMove = true;
search<PvNode ? PV : NonPV>(pos, ss, alpha, beta, d);
search<PvNode ? PV : NonPV>(pos, ss, alpha, beta, d, true);
ss->skipNullMove = false;
tte = TT.probe(posKey);
ttMove = tte ? tte->move() : MOVE_NONE;
}
split_point_start: // At split points actual search starts from here
moves_loop: // When in check and at SpNode search starts from here
MovePicker mp(pos, ttMove, depth, Hist, ss, PvNode ? -VALUE_INFINITE : beta);
Square prevMoveSq = to_sq((ss-1)->currentMove);
Move countermoves[] = { Countermoves[pos.piece_on(prevMoveSq)][prevMoveSq].first,
Countermoves[pos.piece_on(prevMoveSq)][prevMoveSq].second };
MovePicker mp(pos, ttMove, depth, History, countermoves, ss);
CheckInfo ci(pos);
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
improving = ss->staticEval >= (ss-2)->staticEval
|| ss->staticEval == VALUE_NONE
||(ss-2)->staticEval == VALUE_NONE;
singularExtensionNode = !RootNode
&& !SpNode
&& depth >= (PvNode ? 6 * ONE_PLY : 8 * ONE_PLY)
&& ttMove != MOVE_NONE
&& !excludedMove // Recursive singular search is not allowed
&& (tte->type() & BOUND_LOWER)
&& (tte->bound() & BOUND_LOWER)
&& tte->depth() >= depth - 3 * ONE_PLY;
// Step 11. Loop through moves
@@ -802,7 +817,7 @@ split_point_start: // At split points actual search starts from here
{
Signals.firstRootMove = (moveCount == 1);
if (thisThread == Threads.main_thread() && Time::now() - SearchTime > 3000)
if (thisThread == Threads.main() && Time::now() - SearchTime > 3000)
sync_cout << "info depth " << depth / ONE_PLY
<< " currmove " << move_to_uci(move, pos.is_chess960())
<< " currmovenumber " << moveCount + PVIdx << sync_endl;
@@ -813,12 +828,7 @@ split_point_start: // At split points actual search starts from here
givesCheck = pos.move_gives_check(move, ci);
dangerous = givesCheck
|| pos.is_passed_pawn_push(move)
|| type_of(move) == CASTLE
|| ( captureOrPromotion // Entering a pawn endgame?
&& type_of(pos.piece_on(to_sq(move))) != PAWN
&& type_of(move) == NORMAL
&& ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
- PieceValue[MG][pos.piece_on(to_sq(move))] == VALUE_ZERO));
|| type_of(move) == CASTLE;
// Step 12. Extend checks and, in PV nodes, also dangerous moves
if (PvNode && dangerous)
@@ -843,7 +853,7 @@ split_point_start: // At split points actual search starts from here
Value rBeta = ttValue - int(depth);
ss->excludedMove = move;
ss->skipNullMove = true;
value = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2);
value = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2, cutNode);
ss->skipNullMove = false;
ss->excludedMove = MOVE_NONE;
@@ -864,7 +874,7 @@ split_point_start: // At split points actual search starts from here
{
// Move count based pruning
if ( depth < 16 * ONE_PLY
&& moveCount >= FutilityMoveCounts[depth]
&& moveCount >= FutilityMoveCounts[improving][depth]
&& (!threatMove || !refutes(pos, move, threatMove)))
{
if (SpNode)
@@ -876,9 +886,9 @@ split_point_start: // At split points actual search starts from here
// Value based pruning
// We illogically ignore reduction condition depth >= 3*ONE_PLY for predicted depth,
// but fixing this made program slightly weaker.
Depth predictedDepth = newDepth - reduction<PvNode>(depth, moveCount);
Depth predictedDepth = newDepth - reduction<PvNode>(improving, depth, moveCount);
futilityValue = ss->staticEval + ss->evalMargin + futility_margin(predictedDepth, moveCount)
+ Gain[pos.piece_moved(move)][to_sq(move)];
+ Gains[pos.piece_moved(move)][to_sq(move)];
if (futilityValue < beta)
{
@@ -919,8 +929,8 @@ split_point_start: // At split points actual search starts from here
pvMove = PvNode && moveCount == 1;
ss->currentMove = move;
if (!SpNode && !captureOrPromotion && playedMoveCount < 64)
movesSearched[playedMoveCount++] = move;
if (!SpNode && !captureOrPromotion && quietCount < 64)
quietsSearched[quietCount++] = move;
// Step 14. Make the move
pos.do_move(move, st, ci, givesCheck);
@@ -935,12 +945,19 @@ split_point_start: // At split points actual search starts from here
&& move != ss->killers[0]
&& move != ss->killers[1])
{
ss->reduction = reduction<PvNode>(depth, moveCount);
ss->reduction = reduction<PvNode>(improving, depth, moveCount);
if (!PvNode && cutNode)
ss->reduction += ONE_PLY;
if (move == countermoves[0] || move == countermoves[1])
ss->reduction = std::max(DEPTH_ZERO, ss->reduction-ONE_PLY);
Depth d = std::max(newDepth - ss->reduction, ONE_PLY);
if (SpNode)
alpha = splitPoint->alpha;
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d);
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true);
doFullDepthSearch = (value > alpha && ss->reduction != DEPTH_ZERO);
ss->reduction = DEPTH_ZERO;
@@ -957,7 +974,7 @@ split_point_start: // At split points actual search starts from here
value = newDepth < ONE_PLY ?
givesCheck ? -qsearch<NonPV, true>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
: -qsearch<NonPV, false>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
: - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth);
: - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode);
}
// Only for PV nodes do a full PV search on the first move or after a fail
@@ -967,7 +984,7 @@ split_point_start: // At split points actual search starts from here
value = newDepth < ONE_PLY ?
givesCheck ? -qsearch<PV, true>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: -qsearch<PV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: - search<PV>(pos, ss+1, -beta, -alpha, newDepth);
: - search<PV>(pos, ss+1, -beta, -alpha, newDepth, false);
// Step 17. Undo move
pos.undo_move(move);
@@ -1042,7 +1059,7 @@ split_point_start: // At split points actual search starts from here
assert(bestValue < beta);
thisThread->split<FakeSplit>(pos, ss, alpha, beta, &bestValue, &bestMove,
depth, threatMove, moveCount, &mp, NT);
depth, threatMove, moveCount, &mp, NT, cutNode);
if (bestValue >= beta)
break;
}
@@ -1064,41 +1081,37 @@ split_point_start: // At split points actual search starts from here
// If we have pruned all the moves without searching return a fail-low score
if (bestValue == -VALUE_INFINITE)
{
assert(!playedMoveCount);
bestValue = alpha;
}
if (bestValue >= beta) // Failed high
{
TT.store(posKey, value_to_tt(bestValue, ss->ply), BOUND_LOWER, depth,
bestMove, ss->staticEval, ss->evalMargin);
TT.store(posKey, value_to_tt(bestValue, ss->ply),
bestValue >= beta ? BOUND_LOWER :
PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
depth, bestMove, ss->staticEval, ss->evalMargin);
if (!pos.is_capture_or_promotion(bestMove) && !inCheck)
// Quiet best move: update killers, history and countermoves
if ( bestValue >= beta
&& !pos.is_capture_or_promotion(bestMove)
&& !inCheck)
{
if (bestMove != ss->killers[0])
if (ss->killers[0] != bestMove)
{
ss->killers[1] = ss->killers[0];
ss->killers[0] = bestMove;
}
// Increase history value of the cut-off move
// Increase history value of the cut-off move and decrease all the other
// played non-capture moves.
Value bonus = Value(int(depth) * int(depth));
Hist.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus);
// Decrease history of all the other played non-capture moves
for (int i = 0; i < playedMoveCount - 1; i++)
History.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus);
for (int i = 0; i < quietCount - 1; i++)
{
Move m = movesSearched[i];
Hist.update(pos.piece_moved(m), to_sq(m), -bonus);
Move m = quietsSearched[i];
History.update(pos.piece_moved(m), to_sq(m), -bonus);
}
if (is_ok((ss-1)->currentMove))
Countermoves.update(pos.piece_on(prevMoveSq), prevMoveSq, bestMove);
}
}
else // Failed low or PV search
TT.store(posKey, value_to_tt(bestValue, ss->ply),
PvNode && bestMove != MOVE_NONE ? BOUND_EXACT : BOUND_UPPER,
depth, bestMove, ss->staticEval, ss->evalMargin);
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
@@ -1126,7 +1139,7 @@ split_point_start: // At split points actual search starts from here
Key posKey;
Move ttMove, move, bestMove;
Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha;
bool givesCheck, enoughMaterial, evasionPrunable;
bool givesCheck, evasionPrunable;
Depth ttDepth;
// To flag BOUND_EXACT a node with eval above alpha and no available moves
@@ -1146,8 +1159,7 @@ split_point_start: // At split points actual search starts from here
ttDepth = InCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS
: DEPTH_QS_NO_CHECKS;
// Transposition table lookup. At PV nodes, we don't use the TT for
// pruning, but only for move ordering.
// Transposition table lookup
posKey = pos.key();
tte = TT.probe(posKey);
ttMove = tte ? tte->move() : MOVE_NONE;
@@ -1156,9 +1168,9 @@ split_point_start: // At split points actual search starts from here
if ( tte
&& tte->depth() >= ttDepth
&& ttValue != VALUE_NONE // Only in case of TT access race
&& ( PvNode ? tte->type() == BOUND_EXACT
: ttValue >= beta ? (tte->type() & BOUND_LOWER)
: (tte->type() & BOUND_UPPER)))
&& ( PvNode ? tte->bound() == BOUND_EXACT
: ttValue >= beta ? (tte->bound() & BOUND_LOWER)
: (tte->bound() & BOUND_UPPER)))
{
ss->currentMove = ttMove; // Can be MOVE_NONE
return ttValue;
@@ -1169,7 +1181,6 @@ split_point_start: // At split points actual search starts from here
{
ss->staticEval = ss->evalMargin = VALUE_NONE;
bestValue = futilityBase = -VALUE_INFINITE;
enoughMaterial = false;
}
else
{
@@ -1197,14 +1208,13 @@ split_point_start: // At split points actual search starts from here
alpha = bestValue;
futilityBase = ss->staticEval + ss->evalMargin + Value(128);
enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMg;
}
// Initialize a MovePicker object for the current position, and prepare
// to search the moves. Because the depth is <= 0 here, only captures,
// queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will
// be generated.
MovePicker mp(pos, ttMove, depth, Hist, to_sq((ss-1)->currentMove));
MovePicker mp(pos, ttMove, depth, History, to_sq((ss-1)->currentMove));
CheckInfo ci(pos);
// Loop through the moves until no moves remain or a beta cutoff occurs
@@ -1219,7 +1229,6 @@ split_point_start: // At split points actual search starts from here
&& !InCheck
&& !givesCheck
&& move != ttMove
&& enoughMaterial
&& type_of(move) != PROMOTION
&& !pos.is_passed_pawn_push(move))
{
@@ -1236,7 +1245,6 @@ split_point_start: // At split points actual search starts from here
// Prune moves with negative or equal SEE and also moves with positive
// SEE where capturing piece loses a tempo and SEE < beta - futilityBase.
if ( futilityBase < beta
&& depth < DEPTH_ZERO
&& pos.see(move, beta - futilityBase) <= 0)
{
bestValue = std::max(bestValue, futilityBase);
@@ -1245,8 +1253,7 @@ split_point_start: // At split points actual search starts from here
}
// Detect non-capture evasions that are candidate to be pruned
evasionPrunable = !PvNode
&& InCheck
evasionPrunable = InCheck
&& bestValue > VALUE_MATED_IN_MAX_PLY
&& !pos.is_capture(move)
&& !pos.can_castle(pos.side_to_move());
@@ -1449,18 +1456,18 @@ split_point_start: // At split points actual search starts from here
{
// Update occupancy as if the piece and the threat are moving
Bitboard occ = pos.pieces() ^ m1from ^ m1to ^ m2from;
Piece piece = pos.piece_on(m1from);
Piece pc = pos.piece_on(m1from);
// The moved piece attacks the square 'tto' ?
if (pos.attacks_from(piece, m1to, occ) & m2to)
if (pos.attacks_from(pc, m1to, occ) & m2to)
return true;
// Scan for possible X-ray attackers behind the moved piece
Bitboard xray = (attacks_bb< ROOK>(m2to, occ) & pos.pieces(color_of(piece), QUEEN, ROOK))
| (attacks_bb<BISHOP>(m2to, occ) & pos.pieces(color_of(piece), QUEEN, BISHOP));
Bitboard xray = (attacks_bb< ROOK>(m2to, occ) & pos.pieces(color_of(pc), QUEEN, ROOK))
| (attacks_bb<BISHOP>(m2to, occ) & pos.pieces(color_of(pc), QUEEN, BISHOP));
// Verify attackers are triggered by our move and not already existing
if (xray && (xray ^ (xray & pos.attacks_from<QUEEN>(m2to))))
if (unlikely(xray) && (xray & ~pos.attacks_from<QUEEN>(m2to)))
return true;
}
@@ -1521,7 +1528,7 @@ split_point_start: // At split points actual search starts from here
string uci_pv(const Position& pos, int depth, Value alpha, Value beta) {
std::stringstream s;
Time::point elaspsed = Time::now() - SearchTime + 1;
Time::point elapsed = Time::now() - SearchTime + 1;
size_t uciPVSize = std::min((size_t)Options["MultiPV"], RootMoves.size());
int selDepth = 0;
@@ -1546,8 +1553,8 @@ split_point_start: // At split points actual search starts from here
<< " seldepth " << selDepth
<< " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v))
<< " nodes " << pos.nodes_searched()
<< " nps " << pos.nodes_searched() * 1000 / elaspsed
<< " time " << elaspsed
<< " nps " << pos.nodes_searched() * 1000 / elapsed
<< " time " << elapsed
<< " multipv " << i + 1
<< " pv";
@@ -1568,8 +1575,8 @@ split_point_start: // At split points actual search starts from here
void RootMove::extract_pv_from_tt(Position& pos) {
StateInfo state[MAX_PLY_PLUS_2], *st = state;
TTEntry* tte;
StateInfo state[MAX_PLY_PLUS_6], *st = state;
const TTEntry* tte;
int ply = 0;
Move m = pv[0];
@@ -1601,8 +1608,8 @@ void RootMove::extract_pv_from_tt(Position& pos) {
void RootMove::insert_pv_in_tt(Position& pos) {
StateInfo state[MAX_PLY_PLUS_2], *st = state;
TTEntry* tte;
StateInfo state[MAX_PLY_PLUS_6], *st = state;
const TTEntry* tte;
int ply = 0;
do {
@@ -1671,15 +1678,16 @@ void Thread::idle_loop() {
Threads.mutex.lock();
assert(searching);
assert(activeSplitPoint);
SplitPoint* sp = activeSplitPoint;
Threads.mutex.unlock();
Stack ss[MAX_PLY_PLUS_2];
Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2)
Position pos(*sp->pos, this);
memcpy(ss, sp->ss - 1, 4 * sizeof(Stack));
(ss+1)->splitPoint = sp;
std::memcpy(ss-2, sp->ss-2, 5 * sizeof(Stack));
ss->splitPoint = sp;
sp->mutex.lock();
@@ -1689,13 +1697,13 @@ void Thread::idle_loop() {
switch (sp->nodeType) {
case Root:
search<SplitPointRoot>(pos, ss+1, sp->alpha, sp->beta, sp->depth);
search<SplitPointRoot>(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode);
break;
case PV:
search<SplitPointPV>(pos, ss+1, sp->alpha, sp->beta, sp->depth);
search<SplitPointPV>(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode);
break;
case NonPV:
search<SplitPointNonPV>(pos, ss+1, sp->alpha, sp->beta, sp->depth);
search<SplitPointNonPV>(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode);
break;
default:
assert(false);

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(SEARCH_H_INCLUDED)
#ifndef SEARCH_H_INCLUDED
#define SEARCH_H_INCLUDED
#include <cstring>
@@ -79,7 +79,7 @@ struct RootMove {
struct LimitsType {
LimitsType() { memset(this, 0, sizeof(LimitsType)); }
LimitsType() { std::memset(this, 0, sizeof(LimitsType)); }
bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); }
int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder;
@@ -109,4 +109,4 @@ extern void think();
} // namespace Search
#endif // !defined(SEARCH_H_INCLUDED)
#endif // #ifndef SEARCH_H_INCLUDED

View File

@@ -19,7 +19,6 @@
#include <algorithm> // For std::count
#include <cassert>
#include <iostream>
#include "movegen.h"
#include "search.h"
@@ -30,48 +29,66 @@ using namespace Search;
ThreadPool Threads; // Global object
namespace { extern "C" {
namespace {
// start_routine() is the C function which is called when a new thread
// is launched. It is a wrapper to the virtual function idle_loop().
long start_routine(Thread* th) { th->idle_loop(); return 0; }
extern "C" { long start_routine(ThreadBase* th) { th->idle_loop(); return 0; } }
} }
// Helpers to launch a thread after creation and joining before delete. Must be
// outside Thread c'tor and d'tor because object shall be fully initialized
// when start_routine (and hence virtual idle_loop) is called and when joining.
template<typename T> T* new_thread() {
T* th = new T();
thread_create(th->handle, start_routine, th); // Will go to sleep
return th;
}
void delete_thread(ThreadBase* th) {
th->exit = true; // Search must be already finished
th->notify_one();
thread_join(th->handle); // Wait for thread termination
delete th;
}
}
// ThreadBase::notify_one() wakes up the thread when there is some work to do
void ThreadBase::notify_one() {
mutex.lock();
sleepCondition.notify_one();
mutex.unlock();
}
// ThreadBase::wait_for() set the thread to sleep until condition 'b' turns true
void ThreadBase::wait_for(volatile const bool& b) {
mutex.lock();
while (!b) sleepCondition.wait(mutex);
mutex.unlock();
}
// Thread c'tor just inits data but does not launch any thread of execution that
// instead will be started only upon c'tor returns.
Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC
searching = exit = false;
searching = false;
maxPly = splitPointsSize = 0;
activeSplitPoint = NULL;
activePosition = NULL;
idx = Threads.size();
}
// Starts a newly-created thread of execution that will call
// the the virtual function idle_loop(), going immediately to sleep.
Thread* Thread::start() {
if (!thread_create(handle, start_routine, this))
{
std::cerr << "Failed to create thread number " << idx << std::endl;
::exit(EXIT_FAILURE);
}
return this;
}
Thread::~Thread() {
}
// Waits for thread termination before to return
Thread* Thread::stop() {
exit = true; // Search must be already finished
notify_one();
thread_join(handle); // Wait for thread termination
return this;
}
// TimerThread::idle_loop() is where the timer thread waits msec milliseconds
// and then calls check_time(). If msec is 0 thread sleeps until is woken up.
@@ -127,26 +144,6 @@ void MainThread::idle_loop() {
}
// Thread::notify_one() wakes up the thread when there is some search to do
void Thread::notify_one() {
mutex.lock();
sleepCondition.notify_one();
mutex.unlock();
}
// Thread::wait_for() set the thread to sleep until condition 'b' turns true
void Thread::wait_for(volatile const bool& b) {
mutex.lock();
while (!b) sleepCondition.wait(mutex);
mutex.unlock();
}
// Thread::cutoff_occurred() checks whether a beta cutoff has occurred in the
// current active split point, or in some ancestor of the split point.
@@ -167,7 +164,7 @@ bool Thread::cutoff_occurred() const {
// which are busy searching the split point at the top of slaves split point
// stack (the "helpful master concept" in YBWC terminology).
bool Thread::is_available_to(Thread* master) const {
bool Thread::is_available_to(const Thread* master) const {
if (searching)
return false;
@@ -190,8 +187,8 @@ bool Thread::is_available_to(Thread* master) const {
void ThreadPool::init() {
sleepWhileIdle = true;
timer = new TimerThread(); timer->start();
push_back((new MainThread())->start());
timer = new_thread<TimerThread>();
push_back(new_thread<MainThread>());
read_uci_options();
}
@@ -200,10 +197,10 @@ void ThreadPool::init() {
void ThreadPool::exit() {
delete timer->stop(); // As first because check_time() accesses threads data
delete_thread(timer); // As first because check_time() accesses threads data
for (iterator it = begin(); it != end(); ++it)
delete (*it)->stop();
delete_thread(*it);
}
@@ -220,12 +217,19 @@ void ThreadPool::read_uci_options() {
assert(requested > 0);
// Value 0 has a special meaning: We determine the optimal minimum split depth
// automatically. Anyhow the minimumSplitDepth should never be under 4 plies.
if (!minimumSplitDepth)
minimumSplitDepth = (requested < 8 ? 4 : 7) * ONE_PLY;
else
minimumSplitDepth = std::max(4 * ONE_PLY, minimumSplitDepth);
while (size() < requested)
push_back((new Thread())->start());
push_back(new_thread<Thread>());
while (size() > requested)
{
delete back()->stop();
delete_thread(back());
pop_back();
}
}
@@ -234,7 +238,7 @@ void ThreadPool::read_uci_options() {
// slave_available() tries to find an idle thread which is available as a slave
// for the thread 'master'.
Thread* ThreadPool::available_slave(Thread* master) const {
Thread* ThreadPool::available_slave(const Thread* master) const {
for (const_iterator it = begin(); it != end(); ++it)
if ((*it)->is_available_to(master))
@@ -254,9 +258,9 @@ Thread* ThreadPool::available_slave(Thread* master) const {
// search() then split() returns.
template <bool Fake>
void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bestValue,
void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Value* bestValue,
Move* bestMove, Depth depth, Move threatMove, int moveCount,
MovePicker* movePicker, int nodeType) {
MovePicker* movePicker, int nodeType, bool cutNode) {
assert(pos.pos_is_ok());
assert(*bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE);
@@ -278,6 +282,7 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes
sp.alpha = alpha;
sp.beta = beta;
sp.nodeType = nodeType;
sp.cutNode = cutNode;
sp.movePicker = movePicker;
sp.moveCount = moveCount;
sp.pos = &pos;
@@ -343,15 +348,15 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes
}
// Explicit template instantiations
template void Thread::split<false>(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);
template void Thread::split<false>(Position&, const Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int, bool);
template void Thread::split< true>(Position&, const Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int, bool);
// wait_for_think_finished() waits for main thread to go to sleep then returns
void ThreadPool::wait_for_think_finished() {
MainThread* t = main_thread();
MainThread* t = main();
t->mutex.lock();
while (t->thinking) sleepCondition.wait(t->mutex);
t->mutex.unlock();
@@ -370,16 +375,20 @@ void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
Signals.stopOnPonderhit = Signals.firstRootMove = false;
Signals.stop = Signals.failedLowAtRoot = false;
RootMoves.clear();
RootPos = pos;
Limits = limits;
if (states.get()) // If we don't set a new position, preserve current state
{
SetupStates = states; // Ownership transfer here
RootMoves.clear();
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
if ( searchMoves.empty()
|| std::count(searchMoves.begin(), searchMoves.end(), ml.move()))
RootMoves.push_back(RootMove(ml.move()));
main_thread()->thinking = true;
main_thread()->notify_one(); // Starts main thread
assert(!states.get());
}
for (MoveList<LEGAL> it(pos); *it; ++it)
if ( searchMoves.empty()
|| std::count(searchMoves.begin(), searchMoves.end(), *it))
RootMoves.push_back(RootMove(*it));
main()->thinking = true;
main()->notify_one(); // Starts main thread
}

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(THREAD_H_INCLUDED)
#ifndef THREAD_H_INCLUDED
#define THREAD_H_INCLUDED
#include <vector>
@@ -68,6 +68,7 @@ struct SplitPoint {
Value beta;
int nodeType;
Move threatMove;
bool cutNode;
// Const pointers to shared data
MovePicker* movePicker;
@@ -85,29 +86,39 @@ struct SplitPoint {
};
/// ThreadBase struct is the base of the hierarchy from where we derive all the
/// specialized thread classes.
struct ThreadBase {
ThreadBase() : exit(false) {}
virtual ~ThreadBase() {}
virtual void idle_loop() = 0;
void notify_one();
void wait_for(volatile const bool& b);
Mutex mutex;
ConditionVariable sleepCondition;
NativeHandle handle;
volatile bool exit;
};
/// Thread struct keeps together all the thread related stuff like locks, state
/// and especially split points. We also use per-thread pawn and material hash
/// tables so that once we get a pointer to an entry its life time is unlimited
/// and we don't have to care about someone changing the entry under our feet.
struct Thread {
struct Thread : public ThreadBase {
Thread();
virtual ~Thread();
Thread* start();
Thread* stop();
virtual void idle_loop();
void notify_one();
bool cutoff_occurred() const;
bool is_available_to(Thread* master) const;
void wait_for(volatile const bool& b);
bool is_available_to(const Thread* master) const;
template <bool Fake>
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);
void split(Position& pos, const Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove,
Depth depth, Move threatMove, int moveCount, MovePicker* movePicker, int nodeType, bool cutNode);
SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD];
Material::Table materialTable;
@@ -116,17 +127,13 @@ struct Thread {
Position* activePosition;
size_t idx;
int maxPly;
Mutex mutex;
ConditionVariable sleepCondition;
NativeHandle handle;
SplitPoint* volatile activeSplitPoint;
volatile int splitPointsSize;
volatile bool searching;
volatile bool exit;
};
/// MainThread and TimerThread are sublassed from Thread to characterize the two
/// MainThread and TimerThread are derived classes used to characterize the two
/// special threads: the main one and the recurring timer.
struct MainThread : public Thread {
@@ -135,7 +142,7 @@ struct MainThread : public Thread {
volatile bool thinking;
};
struct TimerThread : public Thread {
struct TimerThread : public ThreadBase {
TimerThread() : msec(0) {}
virtual void idle_loop();
int msec;
@@ -151,9 +158,9 @@ struct ThreadPool : public std::vector<Thread*> {
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.
MainThread* main_thread() { return static_cast<MainThread*>((*this)[0]); }
MainThread* main() { return static_cast<MainThread*>((*this)[0]); }
void read_uci_options();
Thread* available_slave(Thread* master) const;
Thread* available_slave(const Thread* master) const;
void wait_for_think_finished();
void start_thinking(const Position&, const Search::LimitsType&,
const std::vector<Move>&, Search::StateStackPtr&);
@@ -168,4 +175,4 @@ struct ThreadPool : public std::vector<Thread*> {
extern ThreadPool Threads;
#endif // !defined(THREAD_H_INCLUDED)
#endif // #ifndef THREAD_H_INCLUDED

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(TIMEMAN_H_INCLUDED)
#ifndef TIMEMAN_H_INCLUDED
#define TIMEMAN_H_INCLUDED
/// The TimeManager class computes the optimal time to think depending on the
@@ -36,4 +36,4 @@ private:
int unstablePVExtraTime;
};
#endif // !defined(TIMEMAN_H_INCLUDED)
#endif // #ifndef TIMEMAN_H_INCLUDED

View File

@@ -39,8 +39,10 @@ void TranspositionTable::set_size(size_t mbSize) {
if (hashMask == size - ClusterSize)
return;
hashMask = size - ClusterSize;
free(mem);
mem = malloc(size * sizeof(TTEntry) + (CACHE_LINE_SIZE - 1));
mem = calloc(size * sizeof(TTEntry) + CACHE_LINE_SIZE - 1, 1);
if (!mem)
{
std::cerr << "Failed to allocate " << mbSize
@@ -48,9 +50,7 @@ void TranspositionTable::set_size(size_t mbSize) {
exit(EXIT_FAILURE);
}
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
table = (TTEntry*)((uintptr_t(mem) + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1));
}
@@ -60,7 +60,24 @@ void TranspositionTable::set_size(size_t mbSize) {
void TranspositionTable::clear() {
memset(table, 0, (hashMask + ClusterSize) * sizeof(TTEntry));
std::memset(table, 0, (hashMask + ClusterSize) * sizeof(TTEntry));
}
/// TranspositionTable::probe() looks up the current position in the
/// transposition table. Returns a pointer to the TTEntry or NULL if
/// position is not found.
const TTEntry* TranspositionTable::probe(const Key key) const {
const TTEntry* tte = first_entry(key);
uint32_t key32 = key >> 32;
for (unsigned i = 0; i < ClusterSize; i++, tte++)
if (tte->key() == key32)
return tte;
return NULL;
}
@@ -72,7 +89,7 @@ void TranspositionTable::clear() {
/// 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.
void TranspositionTable::store(const Key key, Value v, Bound t, Depth d, Move m, Value statV, Value kingD) {
void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV, Value evalM) {
int c1, c2, c3;
TTEntry *tte, *replace;
@@ -84,38 +101,21 @@ void TranspositionTable::store(const Key key, Value v, Bound t, Depth d, Move m,
{
if (!tte->key() || tte->key() == key32) // Empty or overwrite old
{
// Preserve any existing ttMove
if (m == MOVE_NONE)
m = tte->move();
if (!m)
m = tte->move(); // Preserve any existing ttMove
tte->save(key32, v, t, d, m, generation, statV, kingD);
return;
replace = tte;
break;
}
// Implement replace strategy
c1 = (replace->generation() == generation ? 2 : 0);
c2 = (tte->generation() == generation || tte->type() == BOUND_EXACT ? -2 : 0);
c2 = (tte->generation() == generation || tte->bound() == BOUND_EXACT ? -2 : 0);
c3 = (tte->depth() < replace->depth() ? 1 : 0);
if (c1 + c2 + c3 > 0)
replace = tte;
}
replace->save(key32, v, t, d, m, generation, statV, kingD);
}
/// TranspositionTable::probe() looks up the current position in the
/// transposition table. Returns a pointer to the TTEntry or NULL if
/// position is not found.
TTEntry* TranspositionTable::probe(const Key key) const {
TTEntry* tte = first_entry(key);
uint32_t key32 = key >> 32;
for (unsigned i = 0; i < ClusterSize; i++, tte++)
if (tte->key() == key32)
return tte;
return NULL;
replace->save(key32, v, b, d, m, generation, statV, evalM);
}

View File

@@ -17,51 +17,43 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(TT_H_INCLUDED)
#ifndef TT_H_INCLUDED
#define TT_H_INCLUDED
#include "misc.h"
#include "types.h"
/// The TTEntry is the class of transposition table entries
/// The TTEntry is the 128 bit transposition table entry, defined as below:
///
/// A TTEntry needs 128 bits to be stored
///
/// bit 0-31: key
/// bit 32-63: data
/// bit 64-79: value
/// bit 80-95: depth
/// bit 96-111: static value
/// bit 112-127: margin of static value
///
/// the 32 bits of the data field are so defined
///
/// bit 0-15: move
/// bit 16-20: not used
/// bit 21-22: value type
/// bit 23-31: generation
/// key: 32 bit
/// move: 16 bit
/// bound type: 8 bit
/// generation: 8 bit
/// value: 16 bit
/// depth: 16 bit
/// static value: 16 bit
/// static margin: 16 bit
class TTEntry {
struct TTEntry {
public:
void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev, Value em) {
key32 = (uint32_t)k;
move16 = (uint16_t)m;
bound = (uint8_t)b;
bound8 = (uint8_t)b;
generation8 = (uint8_t)g;
value16 = (int16_t)v;
depth16 = (int16_t)d;
evalValue = (int16_t)ev;
evalMargin = (int16_t)em;
}
void set_generation(int g) { generation8 = (uint8_t)g; }
void set_generation(uint8_t g) { generation8 = g; }
uint32_t key() const { return key32; }
Depth depth() const { return (Depth)depth16; }
Move move() const { return (Move)move16; }
Value value() const { return (Value)value16; }
Bound type() const { return (Bound)bound; }
Bound bound() const { return (Bound)bound8; }
int generation() const { return (int)generation8; }
Value eval_value() const { return (Value)evalValue; }
Value eval_margin() const { return (Value)evalMargin; }
@@ -69,7 +61,7 @@ public:
private:
uint32_t key32;
uint16_t move16;
uint8_t bound, generation8;
uint8_t bound8, generation8;
int16_t value16, depth16, evalValue, evalMargin;
};
@@ -88,7 +80,7 @@ public:
~TranspositionTable() { free(mem); }
void new_search() { generation++; }
TTEntry* probe(const Key key) const;
const 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);
@@ -99,7 +91,7 @@ private:
uint32_t hashMask;
TTEntry* table;
void* mem;
uint8_t generation; // Size must be not bigger then TTEntry::generation8
uint8_t generation; // Size must be not bigger than TTEntry::generation8
};
extern TranspositionTable TT;
@@ -123,4 +115,4 @@ inline void TranspositionTable::refresh(const TTEntry* tte) const {
const_cast<TTEntry*>(tte)->set_generation(generation);
}
#endif // !defined(TT_H_INCLUDED)
#endif // #ifndef TT_H_INCLUDED

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(TYPES_H_INCLUDED)
#ifndef TYPES_H_INCLUDED
#define TYPES_H_INCLUDED
/// For Linux and OSX configuration is done automatically using Makefile. To get
@@ -42,6 +42,8 @@
#include "platform.h"
#define unlikely(x) (x) // For code annotation purposes
#if defined(_WIN64) && !defined(IS_64BIT)
# include <intrin.h> // MSVC popcnt and bsfq instrinsics
# define IS_64BIT
@@ -63,7 +65,7 @@
# define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(CACHE_LINE_SIZE)))
#endif
#if defined(_MSC_VER)
#ifdef _MSC_VER
# define FORCE_INLINE __forceinline
#elif defined(__GNUC__)
# define FORCE_INLINE inline __attribute__((always_inline))
@@ -71,13 +73,13 @@
# define FORCE_INLINE inline
#endif
#if defined(USE_POPCNT)
#ifdef USE_POPCNT
const bool HasPopCnt = true;
#else
const bool HasPopCnt = false;
#endif
#if defined(IS_64BIT)
#ifdef IS_64BIT
const bool Is64Bit = true;
#else
const bool Is64Bit = false;
@@ -88,26 +90,7 @@ typedef uint64_t Bitboard;
const int MAX_MOVES = 192;
const int MAX_PLY = 100;
const int MAX_PLY_PLUS_2 = MAX_PLY + 2;
const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = FileABB << 1;
const Bitboard FileCBB = FileABB << 2;
const Bitboard FileDBB = FileABB << 3;
const Bitboard FileEBB = FileABB << 4;
const Bitboard FileFBB = FileABB << 5;
const Bitboard FileGBB = FileABB << 6;
const Bitboard FileHBB = FileABB << 7;
const Bitboard Rank1BB = 0xFF;
const Bitboard Rank2BB = Rank1BB << (8 * 1);
const Bitboard Rank3BB = Rank1BB << (8 * 2);
const Bitboard Rank4BB = Rank1BB << (8 * 3);
const Bitboard Rank5BB = Rank1BB << (8 * 4);
const Bitboard Rank6BB = Rank1BB << (8 * 5);
const Bitboard Rank7BB = Rank1BB << (8 * 6);
const Bitboard Rank8BB = Rank1BB << (8 * 7);
const int MAX_PLY_PLUS_6 = MAX_PLY + 6;
/// A move needs 16 bits to be stored
///
@@ -121,24 +104,24 @@ const Bitboard Rank8BB = Rank1BB << (8 * 7);
/// while MOVE_NONE and MOVE_NULL have the same origin and destination square.
enum Move {
MOVE_NONE = 0,
MOVE_NONE,
MOVE_NULL = 65
};
enum MoveType {
NORMAL = 0,
NORMAL,
PROMOTION = 1 << 14,
ENPASSANT = 2 << 14,
CASTLE = 3 << 14
};
enum CastleRight { // Defined as in PolyGlot book hash key
CASTLES_NONE = 0,
WHITE_OO = 1,
WHITE_OOO = 2,
BLACK_OO = 4,
BLACK_OOO = 8,
ALL_CASTLES = 15,
CASTLES_NONE,
WHITE_OO,
WHITE_OOO = WHITE_OO << 1,
BLACK_OO = WHITE_OO << 2,
BLACK_OOO = WHITE_OO << 3,
ALL_CASTLES = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO,
CASTLE_RIGHT_NB = 16
};
@@ -149,7 +132,7 @@ enum CastlingSide {
};
enum Phase {
PHASE_ENDGAME = 0,
PHASE_ENDGAME,
PHASE_MIDGAME = 128,
MG = 0, EG = 1, PHASE_NB = 2
};
@@ -162,9 +145,9 @@ enum ScaleFactor {
};
enum Bound {
BOUND_NONE = 0,
BOUND_UPPER = 1,
BOUND_LOWER = 2,
BOUND_NONE,
BOUND_UPPER,
BOUND_LOWER,
BOUND_EXACT = BOUND_UPPER | BOUND_LOWER
};
@@ -190,15 +173,15 @@ enum Value {
};
enum PieceType {
NO_PIECE_TYPE = 0, ALL_PIECES = 0,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6,
NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING,
ALL_PIECES = 0,
PIECE_TYPE_NB = 8
};
enum Piece {
NO_PIECE = 0,
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,
NO_PIECE,
W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
PIECE_NB = 16
};
@@ -213,7 +196,7 @@ enum Depth {
DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
DEPTH_QS_RECAPTURES = -7 * ONE_PLY,
DEPTH_NONE = -127 * ONE_PLY
};
@@ -245,11 +228,11 @@ enum Square {
};
enum File {
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB = 8
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB
};
enum Rank {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB = 8
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB
};
@@ -258,7 +241,7 @@ enum Rank {
/// for midgame value. Compiler is free to choose the enum type as long as can
/// keep its data, so ensure Score to be an integer type.
enum Score {
SCORE_ZERO = 0,
SCORE_ZERO,
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
};
@@ -268,7 +251,7 @@ inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
/// Extracting the signed lower and upper 16 bits it not so trivial because
/// according to the standard a simple cast to short is implementation defined
/// and so is a right shift of a signed integer.
inline Value mg_value(Score s) { return Value(((s + 32768) & ~0xffff) / 0x10000); }
inline Value mg_value(Score s) { return Value(((s + 0x8000) & ~0xffff) / 0x10000); }
/// On Intel 64 bit we have a small speed regression with the standard conforming
/// version, so use a faster code in this case that, although not 100% standard
@@ -325,38 +308,17 @@ inline Score operator/(Score s, int i) {
return make_score(mg_value(s) / i, eg_value(s) / i);
}
/// Weight score v by score w trying to prevent overflow
inline Score apply_weight(Score v, Score w) {
return make_score((int(mg_value(v)) * mg_value(w)) / 0x100,
(int(eg_value(v)) * eg_value(w)) / 0x100);
}
#undef ENABLE_OPERATORS_ON
#undef ENABLE_SAFE_OPERATORS_ON
namespace Zobrist {
extern Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
extern Key enpassant[FILE_NB];
extern Key castle[CASTLE_RIGHT_NB];
extern Key side;
extern Key exclusion;
void init();
}
CACHE_LINE_ALIGNMENT
extern Score pieceSquareTable[PIECE_NB][SQUARE_NB];
extern Value PieceValue[PHASE_NB][PIECE_NB];
extern int SquareDistance[SQUARE_NB][SQUARE_NB];
struct MoveStack {
struct ExtMove {
Move move;
int score;
};
inline bool operator<(const MoveStack& f, const MoveStack& s) {
inline bool operator<(const ExtMove& f, const ExtMove& s) {
return f.score < s.score;
}
@@ -430,18 +392,6 @@ inline bool opposite_colors(Square s1, Square s2) {
return ((s >> 3) ^ s) & 1;
}
inline int file_distance(Square s1, Square s2) {
return abs(file_of(s1) - file_of(s2));
}
inline int rank_distance(Square s1, Square s2) {
return abs(rank_of(s1) - rank_of(s2));
}
inline int square_distance(Square s1, Square s2) {
return SquareDistance[s1][s2];
}
inline char file_to_char(File f, bool tolower = true) {
return char(f - FILE_A + (tolower ? 'a' : 'A'));
}
@@ -490,4 +440,4 @@ inline const std::string square_to_string(Square s) {
return ch;
}
#endif // !defined(TYPES_H_INCLUDED)
#endif // #ifndef TYPES_H_INCLUDED

View File

@@ -42,8 +42,8 @@ namespace {
// position just before to start searching). Needed by repetition draw detection.
Search::StateStackPtr SetupStates;
void set_option(istringstream& up);
void set_position(Position& pos, istringstream& up);
void setoption(istringstream& up);
void position(Position& pos, istringstream& up);
void go(const Position& pos, istringstream& up);
}
@@ -55,7 +55,7 @@ namespace {
void UCI::loop(const string& args) {
Position pos(StartFEN, false, Threads.main_thread()); // The root position
Position pos(StartFEN, false, Threads.main()); // The root position
string token, cmd = args;
do {
@@ -76,7 +76,7 @@ void UCI::loop(const string& args) {
if (token != "ponderhit" || Search::Signals.stopOnPonderhit)
{
Search::Signals.stop = true;
Threads.main_thread()->notify_one(); // Could be sleeping
Threads.main()->notify_one(); // Could be sleeping
}
else
Search::Limits.ponder = false;
@@ -102,15 +102,19 @@ void UCI::loop(const string& args) {
<< "\n" << Options
<< "\nuciok" << sync_endl;
else if (token == "eval")
{
Search::RootColor = pos.side_to_move(); // Ensure it is set
sync_cout << Eval::trace(pos) << sync_endl;
}
else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ }
else if (token == "go") go(pos, is);
else if (token == "position") set_position(pos, is);
else if (token == "setoption") set_option(is);
else if (token == "position") position(pos, is);
else if (token == "setoption") setoption(is);
else if (token == "flip") pos.flip();
else if (token == "bench") benchmark(pos, is);
else if (token == "d") sync_cout << pos.pretty() << sync_endl;
else if (token == "isready") sync_cout << "readyok" << sync_endl;
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
else
sync_cout << "Unknown command: " << cmd << sync_endl;
@@ -122,12 +126,12 @@ void UCI::loop(const string& args) {
namespace {
// set_position() is called when engine receives the "position" UCI command.
// position() is called when engine receives the "position" UCI command.
// The function sets up the position described in the given fen string ("fen")
// or the starting position ("startpos") and then makes the moves given in the
// following move list ("moves").
void set_position(Position& pos, istringstream& is) {
void position(Position& pos, istringstream& is) {
Move m;
string token, fen;
@@ -145,7 +149,7 @@ namespace {
else
return;
pos.set(fen, Options["UCI_Chess960"], Threads.main_thread());
pos.set(fen, Options["UCI_Chess960"], Threads.main());
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>());
// Parse move list (if any)
@@ -157,10 +161,10 @@ namespace {
}
// set_option() is called when engine receives the "setoption" UCI command. The
// setoption() is called when engine receives the "setoption" UCI command. The
// function updates the UCI option ("name") to the given value ("value").
void set_option(istringstream& is) {
void setoption(istringstream& is) {
string token, name, value;

View File

@@ -51,31 +51,28 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const
/// init() initializes the UCI options to their hard coded default values
/// and initializes the default value of "Threads" and "Min Split Depth"
/// parameters according to the number of CPU cores detected.
void init(OptionsMap& o) {
int cpus = std::min(cpu_count(), MAX_THREADS);
int msd = cpus < 8 ? 4 : 7;
o["Use Debug Log"] = Option(false, on_logger);
o["Use Search Log"] = Option(false);
o["Write Debug Log"] = Option(false, on_logger);
o["Write Search Log"] = Option(false);
o["Search Log Filename"] = Option("SearchLog.txt");
o["Book File"] = Option("book.bin");
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 (Midgame)"] = 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["Pawn Structure (Midgame)"] = Option(100, 0, 200, on_eval);
o["Pawn Structure (Endgame)"] = Option(100, 0, 200, on_eval);
o["Passed Pawns (Midgame)"] = Option(100, 0, 200, on_eval);
o["Passed Pawns (Endgame)"] = Option(100, 0, 200, on_eval);
o["Space"] = Option(100, 0, 200, on_eval);
o["Aggressiveness"] = Option(100, 0, 200, on_eval);
o["Cowardice"] = Option(100, 0, 200, on_eval);
o["Min Split Depth"] = Option(msd, 4, 12, on_threads);
o["Min Split Depth"] = Option(0, 0, 12, on_threads);
o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads);
o["Threads"] = Option(cpus, 1, MAX_THREADS, on_threads);
o["Use Sleeping Threads"] = Option(true);
o["Threads"] = Option(1, 1, MAX_THREADS, on_threads);
o["Idle Threads Sleep"] = Option(true);
o["Hash"] = Option(32, 1, 8192, on_hash_size);
o["Clear Hash"] = Option(on_clear_hash);
o["Ponder"] = Option(true);

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(UCIOPTION_H_INCLUDED)
#ifndef UCIOPTION_H_INCLUDED
#define UCIOPTION_H_INCLUDED
#include <map>
@@ -66,4 +66,4 @@ void loop(const std::string&);
extern UCI::OptionsMap Options;
#endif // !defined(UCIOPTION_H_INCLUDED)
#endif // #ifndef UCIOPTION_H_INCLUDED