DroidFish: Updated stockfish engine to version 3.

This commit is contained in:
Peter Osterlund
2013-05-03 17:03:42 +00:00
parent 3f55b61865
commit 34b9fd139f
42 changed files with 2092 additions and 2383 deletions

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -66,7 +66,7 @@ void benchmark(const Position& current, istream& is) {
vector<string> fens; vector<string> fens;
// Assign default values to missing arguments // Assign default values to missing arguments
string ttSize = (is >> token) ? token : "128"; string ttSize = (is >> token) ? token : "32";
string threads = (is >> token) ? token : "1"; string threads = (is >> token) ? token : "1";
string limit = (is >> token) ? token : "12"; string limit = (is >> token) ? token : "12";
string fenFile = (is >> token) ? token : "default"; string fenFile = (is >> token) ? token : "default";
@@ -82,6 +82,9 @@ void benchmark(const Position& current, istream& is) {
else if (limitType == "nodes") else if (limitType == "nodes")
limits.nodes = atoi(limit.c_str()); limits.nodes = atoi(limit.c_str());
else if (limitType == "mate")
limits.mate = atoi(limit.c_str());
else else
limits.depth = atoi(limit.c_str()); limits.depth = atoi(limit.c_str());
@@ -89,7 +92,7 @@ void benchmark(const Position& current, istream& is) {
fens.assign(Defaults, Defaults + 16); fens.assign(Defaults, Defaults + 16);
else if (fenFile == "current") else if (fenFile == "current")
fens.push_back(current.to_fen()); fens.push_back(current.fen());
else else
{ {
@@ -99,7 +102,7 @@ void benchmark(const Position& current, istream& is) {
if (!file.is_open()) if (!file.is_open())
{ {
cerr << "Unable to open file " << fenFile << endl; cerr << "Unable to open file " << fenFile << endl;
exit(EXIT_FAILURE); return;
} }
while (getline(file, fen)) while (getline(file, fen))
@@ -127,9 +130,9 @@ void benchmark(const Position& current, istream& is) {
} }
else else
{ {
Threads.start_searching(pos, limits, vector<Move>(), st); Threads.start_thinking(pos, limits, vector<Move>(), st);
Threads.wait_for_search_finished(); Threads.wait_for_think_finished();
nodes += Search::RootPosition.nodes_searched(); nodes += Search::RootPos.nodes_searched();
} }
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,12 +18,32 @@
*/ */
#include <cassert> #include <cassert>
#include <vector>
#include "bitboard.h" #include "bitboard.h"
#include "types.h" #include "types.h"
namespace { namespace {
// The possible pawns squares are 24, the first 4 files and ranks from 2 to 7
const unsigned IndexMax = 2*24*64*64; // stm * psq * wksq * bksq = 196608
// Each uint32_t stores results of 32 positions, one per bit
uint32_t KPKBitbase[IndexMax / 32];
// A KPK bitbase index is an integer in [0, IndexMax] range
//
// Information is mapped in a way that minimizes number of iterations:
//
// bit 0- 5: white king square (from SQ_A1 to SQ_H8)
// bit 6-11: black king square (from SQ_A1 to SQ_H8)
// bit 12: side to move (WHITE or BLACK)
// bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn 6 - rank (from 6 - RANK_7 to 6 - RANK_2)
unsigned index(Color us, Square bksq, Square wksq, Square psq) {
return wksq + (bksq << 6) + (us << 12) + (file_of(psq) << 13) + ((6 - rank_of(psq)) << 15);
}
enum Result { enum Result {
INVALID = 0, INVALID = 0,
UNKNOWN = 1, UNKNOWN = 1,
@@ -35,196 +55,119 @@ namespace {
struct KPKPosition { struct KPKPosition {
Result classify_leaf(int idx); operator Result() const { return res; }
Result classify(int idx, Result db[]); Result classify_leaf(unsigned idx);
Result classify(const std::vector<KPKPosition>& db)
{ return us == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); }
private: private:
template<Color Us> Result classify(const Result db[]) const; template<Color Us> Result classify(const std::vector<KPKPosition>& db);
template<Color Us> Bitboard k_attacks() const { Color us;
return Us == WHITE ? StepAttacksBB[W_KING][wksq] : StepAttacksBB[B_KING][bksq]; Square bksq, wksq, psq;
} Result res;
Bitboard p_attacks() const { return StepAttacksBB[W_PAWN][psq]; }
void decode_index(int idx);
Square wksq, bksq, psq;
Color stm;
}; };
// The possible pawns squares are 24, the first 4 files and ranks from 2 to 7 } // namespace
const int IndexMax = 2 * 24 * 64 * 64; // stm * wp_sq * wk_sq * bk_sq = 196608
// Each uint32_t stores results of 32 positions, one per bit
uint32_t KPKBitbase[IndexMax / 32];
int index(Square wksq, Square bksq, Square psq, Color stm);
}
uint32_t Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm) { bool Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color us) {
int idx = index(wksq, bksq, wpsq, stm); assert(file_of(wpsq) <= FILE_D);
return KPKBitbase[idx / 32] & (1 << (idx & 31));
unsigned idx = index(us, bksq, wksq, wpsq);
return KPKBitbase[idx / 32] & (1 << (idx & 0x1F));
} }
void Bitbases::init_kpk() { void Bitbases::init_kpk() {
Result db[IndexMax]; unsigned idx, repeat = 1;
KPKPosition pos; std::vector<KPKPosition> db(IndexMax);
int idx, bit, repeat = 1;
// Initialize table with known win / draw positions // Initialize db with known win / draw positions
for (idx = 0; idx < IndexMax; idx++) for (idx = 0; idx < IndexMax; idx++)
db[idx] = pos.classify_leaf(idx); db[idx].classify_leaf(idx);
// Iterate until all positions are classified (30 cycles needed) // Iterate through the positions until no more of the unknown positions can be
// changed to either wins or draws (15 cycles needed).
while (repeat) while (repeat)
for (repeat = idx = 0; idx < IndexMax; idx++) for (repeat = idx = 0; idx < IndexMax; idx++)
if (db[idx] == UNKNOWN && (db[idx] = pos.classify(idx, db)) != UNKNOWN) if (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN)
repeat = 1; repeat = 1;
// Map 32 position results into one KPKBitbase[] entry // Map 32 results into one KPKBitbase[] entry
for (idx = 0; idx < IndexMax / 32; idx++) for (idx = 0; idx < IndexMax; idx++)
for (bit = 0; bit < 32; bit++) if (db[idx] == WIN)
if (db[32 * idx + bit] == WIN) KPKBitbase[idx / 32] |= 1 << (idx & 0x1F);
KPKBitbase[idx] |= 1 << bit;
} }
namespace { namespace {
// A KPK bitbase index is an integer in [0, IndexMax] range Result KPKPosition::classify_leaf(unsigned idx) {
//
// Information is mapped in this way
//
// bit 0: side to move (WHITE or BLACK)
// bit 1- 6: black king square (from SQ_A1 to SQ_H8)
// bit 7-12: white king square (from SQ_A1 to SQ_H8)
// bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn rank - 1 (from RANK_2 - 1 to RANK_7 - 1)
int index(Square w, Square b, Square p, Color c) { wksq = Square((idx >> 0) & 0x3F);
bksq = Square((idx >> 6) & 0x3F);
assert(file_of(p) <= FILE_D); us = Color((idx >> 12) & 0x01);
psq = File((idx >> 13) & 3) | Rank(6 - (idx >> 15));
return c + (b << 1) + (w << 7) + (file_of(p) << 13) + ((rank_of(p) - 1) << 15);
}
void KPKPosition::decode_index(int idx) {
stm = Color(idx & 1);
bksq = Square((idx >> 1) & 63);
wksq = Square((idx >> 7) & 63);
psq = File((idx >> 13) & 3) | Rank((idx >> 15) + 1);
}
Result KPKPosition::classify_leaf(int idx) {
decode_index(idx);
// Check if two pieces are on the same square or if a king can be captured // Check if two pieces are on the same square or if a king can be captured
if ( wksq == psq || wksq == bksq || bksq == psq if ( wksq == psq || wksq == bksq || bksq == psq
|| (k_attacks<WHITE>() & bksq) || (StepAttacksBB[KING][wksq] & bksq)
|| (stm == WHITE && (p_attacks() & bksq))) || (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq)))
return INVALID; return res = INVALID;
// The position is an immediate win if it is white to move and the white if (us == WHITE)
// pawn can be promoted without getting captured. {
// Immediate win if pawn can be promoted without getting captured
if ( rank_of(psq) == RANK_7 if ( rank_of(psq) == RANK_7
&& stm == WHITE
&& wksq != psq + DELTA_N && wksq != psq + DELTA_N
&& ( square_distance(bksq, psq + DELTA_N) > 1 && ( square_distance(bksq, psq + DELTA_N) > 1
||(k_attacks<WHITE>() & (psq + DELTA_N)))) ||(StepAttacksBB[KING][wksq] & (psq + DELTA_N))))
return WIN; return res = WIN;
}
// Immediate draw if is stalemate or king captures undefended pawn
else if ( !(StepAttacksBB[KING][bksq] & ~(StepAttacksBB[KING][wksq] | StepAttacksBB[PAWN][psq]))
|| (StepAttacksBB[KING][bksq] & psq & ~StepAttacksBB[KING][wksq]))
return res = DRAW;
// Check for known draw positions return res = UNKNOWN;
//
// Case 1: Stalemate
if ( stm == BLACK
&& !(k_attacks<BLACK>() & ~(k_attacks<WHITE>() | p_attacks())))
return DRAW;
// Case 2: King can capture undefended pawn
if ( stm == BLACK
&& (k_attacks<BLACK>() & psq & ~k_attacks<WHITE>()))
return DRAW;
// Case 3: Black king in front of white pawn
if ( bksq == psq + DELTA_N
&& rank_of(psq) < RANK_7)
return DRAW;
// Case 4: White king in front of pawn and black has opposition
if ( stm == WHITE
&& wksq == psq + DELTA_N
&& bksq == wksq + DELTA_N + DELTA_N
&& rank_of(psq) < RANK_5)
return DRAW;
// Case 5: Stalemate with rook pawn
if ( bksq == SQ_A8
&& file_of(psq) == FILE_A)
return DRAW;
// Case 6: White king trapped on the rook file
if ( file_of(wksq) == FILE_A
&& file_of(psq) == FILE_A
&& rank_of(wksq) > rank_of(psq)
&& bksq == wksq + 2)
return DRAW;
return UNKNOWN;
} }
template<Color Us> template<Color Us>
Result KPKPosition::classify(const Result db[]) const { Result KPKPosition::classify(const std::vector<KPKPosition>& db) {
// White to Move: If one move leads to a position classified as RESULT_WIN, // White to Move: If one move leads to a position classified as WIN, the result
// the result of the current position is RESULT_WIN. If all moves lead to // of the current position is WIN. If all moves lead to positions classified
// positions classified as RESULT_DRAW, the current position is classified // as DRAW, the current position is classified DRAW otherwise the current
// RESULT_DRAW otherwise the current position is classified as RESULT_UNKNOWN. // position is classified as UNKNOWN.
// //
// Black to Move: If one move leads to a position classified as RESULT_DRAW, // Black to Move: If one move leads to a position classified as DRAW, the result
// the result of the current position is RESULT_DRAW. If all moves lead to // of the current position is DRAW. If all moves lead to positions classified
// positions classified as RESULT_WIN, the position is classified RESULT_WIN. // as WIN, the position is classified WIN otherwise the current position is
// Otherwise, the current position is classified as RESULT_UNKNOWN. // classified UNKNOWN.
Result r = INVALID; Result r = INVALID;
Bitboard b = k_attacks<Us>(); Bitboard b = StepAttacksBB[KING][Us == WHITE ? wksq : bksq];
while (b) while (b)
{ r |= Us == WHITE ? db[index(~Us, bksq, pop_lsb(&b), psq)]
r |= Us == WHITE ? db[index(pop_lsb(&b), bksq, psq, BLACK)] : db[index(~Us, pop_lsb(&b), wksq, psq)];
: db[index(wksq, pop_lsb(&b), psq, WHITE)];
if (Us == WHITE && (r & WIN))
return WIN;
if (Us == BLACK && (r & DRAW))
return DRAW;
}
if (Us == WHITE && rank_of(psq) < RANK_7) if (Us == WHITE && rank_of(psq) < RANK_7)
{ {
Square s = psq + DELTA_N; Square s = psq + DELTA_N;
r |= db[index(wksq, bksq, s, BLACK)]; // Single push r |= db[index(BLACK, bksq, wksq, s)]; // Single push
if (rank_of(s) == RANK_3 && s != wksq && s != bksq) if (rank_of(s) == RANK_3 && s != wksq && s != bksq)
r |= db[index(wksq, bksq, s + DELTA_N, BLACK)]; // Double push r |= db[index(BLACK, bksq, wksq, s + DELTA_N)]; // Double push
if (r & WIN)
return WIN;
} }
return r & UNKNOWN ? UNKNOWN : Us == WHITE ? DRAW : WIN; if (Us == WHITE)
return res = r & WIN ? WIN : r & UNKNOWN ? UNKNOWN : DRAW;
else
return res = r & DRAW ? DRAW : r & UNKNOWN ? UNKNOWN : WIN;
} }
Result KPKPosition::classify(int idx, Result db[]) { } // namespace
decode_index(idx);
return stm == WHITE ? classify<WHITE>(db) : classify<BLACK>(db);
}
}

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -28,45 +28,44 @@
CACHE_LINE_ALIGNMENT CACHE_LINE_ALIGNMENT
Bitboard RMasks[64]; Bitboard RMasks[SQUARE_NB];
Bitboard RMagics[64]; Bitboard RMagics[SQUARE_NB];
Bitboard* RAttacks[64]; Bitboard* RAttacks[SQUARE_NB];
unsigned RShifts[64]; unsigned RShifts[SQUARE_NB];
Bitboard BMasks[64]; Bitboard BMasks[SQUARE_NB];
Bitboard BMagics[64]; Bitboard BMagics[SQUARE_NB];
Bitboard* BAttacks[64]; Bitboard* BAttacks[SQUARE_NB];
unsigned BShifts[64]; unsigned BShifts[SQUARE_NB];
Bitboard SquareBB[64]; Bitboard SquareBB[SQUARE_NB];
Bitboard FileBB[8]; Bitboard FileBB[FILE_NB];
Bitboard RankBB[8]; Bitboard RankBB[RANK_NB];
Bitboard AdjacentFilesBB[8]; Bitboard AdjacentFilesBB[FILE_NB];
Bitboard ThisAndAdjacentFilesBB[8]; Bitboard ThisAndAdjacentFilesBB[FILE_NB];
Bitboard InFrontBB[2][8]; Bitboard InFrontBB[COLOR_NB][RANK_NB];
Bitboard StepAttacksBB[16][64]; Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
Bitboard BetweenBB[64][64]; Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard DistanceRingsBB[64][8]; Bitboard DistanceRingsBB[SQUARE_NB][8];
Bitboard ForwardBB[2][64]; Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
Bitboard PassedPawnMask[2][64]; Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
Bitboard AttackSpanMask[2][64]; Bitboard AttackSpanMask[COLOR_NB][SQUARE_NB];
Bitboard PseudoAttacks[6][64]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
int SquareDistance[64][64]; int SquareDistance[SQUARE_NB][SQUARE_NB];
namespace { namespace {
// De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan // De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan
const uint64_t DeBruijn_64 = 0x218A392CD3D5DBFULL; const uint64_t DeBruijn_64 = 0x3F79D71B4CB0A89ULL;
const uint32_t DeBruijn_32 = 0x783A9B23; const uint32_t DeBruijn_32 = 0x783A9B23;
CACHE_LINE_ALIGNMENT CACHE_LINE_ALIGNMENT
int MS1BTable[256]; int MS1BTable[256];
Square BSFTable[64]; Square BSFTable[SQUARE_NB];
Bitboard RTable[0x19000]; // Storage space for rook attacks Bitboard RTable[0x19000]; // Storage space for rook attacks
Bitboard BTable[0x1480]; // Storage space for bishop attacks Bitboard BTable[0x1480]; // Storage space for bishop attacks
uint8_t BitCount8Bit[256];
typedef unsigned (Fn)(Square, Bitboard); typedef unsigned (Fn)(Square, Bitboard);
@@ -75,12 +74,10 @@ namespace {
FORCE_INLINE unsigned bsf_index(Bitboard b) { FORCE_INLINE unsigned bsf_index(Bitboard b) {
if (Is64Bit) // Matt Taylor's folding for 32 bit systems, extended to 64 bits by Kim Walisch
return ((b & -b) * DeBruijn_64) >> 58;
// Use Matt Taylor's folding trick for 32 bit systems
b ^= (b - 1); b ^= (b - 1);
return ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26; return Is64Bit ? (b * DeBruijn_64) >> 58
: ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26;
} }
} }
@@ -161,9 +158,6 @@ void Bitboards::init() {
for (int i = 0; i < 64; i++) for (int i = 0; i < 64; i++)
BSFTable[bsf_index(1ULL << i)] = Square(i); BSFTable[bsf_index(1ULL << i)] = Square(i);
for (Bitboard b = 0; b < 256; b++)
BitCount8Bit[b] = (uint8_t)popcount<Max15>(b);
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; s++)
SquareBB[s] = 1ULL << s; SquareBB[s] = 1ULL << s;
@@ -326,7 +320,7 @@ namespace {
// until we find the one that passes the verification test. // until we find the one that passes the verification test.
do { do {
do magics[s] = pick_random(rk, booster); do magics[s] = pick_random(rk, booster);
while (BitCount8Bit[(magics[s] * masks[s]) >> 56] < 6); while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6);
memset(attacks[s], 0, size * sizeof(Bitboard)); memset(attacks[s], 0, size * sizeof(Bitboard));

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -33,36 +33,37 @@ void print(Bitboard b);
namespace Bitbases { namespace Bitbases {
void init_kpk(); void init_kpk();
uint32_t probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm); bool probe_kpk(Square wksq, Square wpsq, Square bksq, Color us);
} }
CACHE_LINE_ALIGNMENT CACHE_LINE_ALIGNMENT
extern Bitboard RMasks[64]; extern Bitboard RMasks[SQUARE_NB];
extern Bitboard RMagics[64]; extern Bitboard RMagics[SQUARE_NB];
extern Bitboard* RAttacks[64]; extern Bitboard* RAttacks[SQUARE_NB];
extern unsigned RShifts[64]; extern unsigned RShifts[SQUARE_NB];
extern Bitboard BMasks[64]; extern Bitboard BMasks[SQUARE_NB];
extern Bitboard BMagics[64]; extern Bitboard BMagics[SQUARE_NB];
extern Bitboard* BAttacks[64]; extern Bitboard* BAttacks[SQUARE_NB];
extern unsigned BShifts[64]; extern unsigned BShifts[SQUARE_NB];
extern Bitboard SquareBB[64]; extern Bitboard SquareBB[SQUARE_NB];
extern Bitboard FileBB[8]; extern Bitboard FileBB[FILE_NB];
extern Bitboard RankBB[8]; extern Bitboard RankBB[RANK_NB];
extern Bitboard AdjacentFilesBB[8]; extern Bitboard AdjacentFilesBB[FILE_NB];
extern Bitboard ThisAndAdjacentFilesBB[8]; extern Bitboard ThisAndAdjacentFilesBB[FILE_NB];
extern Bitboard InFrontBB[2][8]; extern Bitboard InFrontBB[COLOR_NB][RANK_NB];
extern Bitboard StepAttacksBB[16][64]; extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
extern Bitboard BetweenBB[64][64]; extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
extern Bitboard DistanceRingsBB[64][8]; extern Bitboard DistanceRingsBB[SQUARE_NB][8];
extern Bitboard ForwardBB[2][64]; extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
extern Bitboard PassedPawnMask[2][64]; extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
extern Bitboard AttackSpanMask[2][64]; extern Bitboard AttackSpanMask[COLOR_NB][SQUARE_NB];
extern Bitboard PseudoAttacks[6][64]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
const Bitboard BlackSquares = 0xAA55AA55AA55AA55ULL;
/// Overloads of bitwise operators between a Bitboard and a Square for testing /// Overloads of bitwise operators between a Bitboard and a Square for testing
/// whether a given bit is set in a bitboard, and for setting and clearing bits. /// whether a given bit is set in a bitboard, and for setting and clearing bits.
@@ -199,8 +200,7 @@ inline bool squares_aligned(Square s1, Square s2, Square s3) {
/// the same color of the given square. /// the same color of the given square.
inline Bitboard same_color_squares(Square s) { inline Bitboard same_color_squares(Square s) {
return Bitboard(0xAA55AA55AA55AA55ULL) & s ? 0xAA55AA55AA55AA55ULL return BlackSquares & s ? BlackSquares : ~BlackSquares;
: ~0xAA55AA55AA55AA55ULL;
} }
@@ -247,6 +247,21 @@ FORCE_INLINE Square msb(Bitboard b) {
return (Square) index; return (Square) index;
} }
# elif defined(__arm__)
FORCE_INLINE int lsb32(uint32_t v) {
__asm__("rbit %0, %1" : "=r"(v) : "r"(v));
return __builtin_clz(v);
}
FORCE_INLINE Square msb(Bitboard b) {
return (Square) (63 - __builtin_clzll(b));
}
FORCE_INLINE Square lsb(Bitboard b) {
return (Square) (uint32_t(b) ? lsb32(uint32_t(b)) : 32 + lsb32(uint32_t(b >> 32)));
}
# else # else
FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen
@@ -265,7 +280,7 @@ FORCE_INLINE Square msb(Bitboard b) {
FORCE_INLINE Square pop_lsb(Bitboard* b) { FORCE_INLINE Square pop_lsb(Bitboard* b) {
const Square s = lsb(*b); const Square s = lsb(*b);
*b &= ~(1ULL << s); *b &= *b - 1;
return s; return s;
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -33,8 +33,8 @@ enum BitCountType {
}; };
/// Determine at compile time the best popcount<> specialization according if /// Determine at compile time the best popcount<> specialization according if
/// platform is 32 or 64 bits, to the maximum number of nonzero bits to count or /// platform is 32 or 64 bits, to the maximum number of nonzero bits to count
/// use hardware popcnt instruction when available. /// and if hardware popcnt instruction is available.
const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32; const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32;
const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15; const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15;
@@ -44,19 +44,17 @@ template<BitCountType> inline int popcount(Bitboard);
template<> template<>
inline int popcount<CNT_64>(Bitboard b) { inline int popcount<CNT_64>(Bitboard b) {
b -= ((b>>1) & 0x5555555555555555ULL); b -= (b >> 1) & 0x5555555555555555ULL;
b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b = ((b >> 4) + b) & 0x0F0F0F0F0F0F0F0FULL; b = ((b >> 4) + b) & 0x0F0F0F0F0F0F0F0FULL;
b *= 0x0101010101010101ULL; return (b * 0x0101010101010101ULL) >> 56;
return int(b >> 56);
} }
template<> template<>
inline int popcount<CNT_64_MAX15>(Bitboard b) { inline int popcount<CNT_64_MAX15>(Bitboard b) {
b -= (b >> 1) & 0x5555555555555555ULL; b -= (b >> 1) & 0x5555555555555555ULL;
b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b *= 0x1111111111111111ULL; return (b * 0x1111111111111111ULL) >> 60;
return int(b >> 60);
} }
template<> template<>
@@ -66,10 +64,8 @@ inline int popcount<CNT_32>(Bitboard b) {
w -= (w >> 1) & 0x55555555; w -= (w >> 1) & 0x55555555;
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
w = ((w >> 2) & 0x33333333) + (w & 0x33333333); w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
v = ((v >> 4) + v) & 0x0F0F0F0F; // 0-8 in 8 bits v = ((v >> 4) + v + (w >> 4) + w) & 0x0F0F0F0F;
v += (((w >> 4) + w) & 0x0F0F0F0F); // 0-16 in 8 bits return (v * 0x01010101) >> 24;
v *= 0x01010101; // mul is fast on amd procs
return int(v >> 24);
} }
template<> template<>
@@ -79,9 +75,7 @@ inline int popcount<CNT_32_MAX15>(Bitboard b) {
w -= (w >> 1) & 0x55555555; w -= (w >> 1) & 0x55555555;
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
w = ((w >> 2) & 0x33333333) + (w & 0x33333333); w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
v += w; // 0-8 in 4 bits return ((v + w) * 0x11111111) >> 28;
v *= 0x11111111;
return int(v >> 28);
} }
template<> template<>
@@ -102,9 +96,8 @@ inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#else #else
unsigned long ret; __asm__("popcnt %1, %0" : "=r" (b) : "r" (b));
__asm__("popcnt %1, %0" : "=r" (ret) : "r" (b)); return b;
return ret;
#endif #endif
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -38,7 +38,7 @@ namespace {
// A Polyglot book is a series of "entries" of 16 bytes. All integers are // A Polyglot book is a series of "entries" of 16 bytes. All integers are
// stored in big-endian format, with highest byte first (regardless of size). // stored in big-endian format, with highest byte first (regardless of size).
// The entries are ordered according to the key in ascending order. // The entries are ordered according to the key in ascending order.
struct BookEntry { struct Entry {
uint64_t key; uint64_t key;
uint16_t move; uint16_t move;
uint16_t count; uint16_t count;
@@ -46,7 +46,15 @@ namespace {
}; };
// Random numbers from PolyGlot, used to compute book hash keys // Random numbers from PolyGlot, used to compute book hash keys
const Key PolyGlotRandoms[781] = { const union {
Key PolyGlotRandoms[781];
struct {
Key psq[12][64]; // [piece][square]
Key castle[4]; // [castle right]
Key enpassant[8]; // [file]
Key turn;
} Zobrist;
} PG = {{
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL, 0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL, 0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL, 0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL,
@@ -308,51 +316,40 @@ namespace {
0x003A93D8B2806962ULL, 0x1C99DED33CB890A1ULL, 0xCF3145DE0ADD4289ULL, 0x003A93D8B2806962ULL, 0x1C99DED33CB890A1ULL, 0xCF3145DE0ADD4289ULL,
0xD0E4427A5514FB72ULL, 0x77C621CC9FB3A483ULL, 0x67A34DAC4356550BULL, 0xD0E4427A5514FB72ULL, 0x77C621CC9FB3A483ULL, 0x67A34DAC4356550BULL,
0xF8D626AAAF278509ULL 0xF8D626AAAF278509ULL
}; }};
// Offsets to the PolyGlotRandoms[] array of zobrist keys // polyglot_key() returns the PolyGlot hash key of the given position
const Key* ZobPiece = PolyGlotRandoms; Key polyglot_key(const Position& pos) {
const Key* ZobCastle = ZobPiece + 12 * 64; // Pieces * squares
const Key* ZobEnPassant = ZobCastle + 4; // Castle flags
const Key* ZobTurn = ZobEnPassant + 8; // Number of files
// book_key() returns the PolyGlot hash key of the given position Key key = 0;
uint64_t book_key(const Position& pos) {
uint64_t key = 0;
Bitboard b = pos.pieces(); Bitboard b = pos.pieces();
while (b) while (b)
{ {
// In PolyGlotRandoms[] pieces are stored in the following sequence:
// BP = 0, WP = 1, BN = 2, WN = 3, ... BK = 10, WK = 11
Square s = pop_lsb(&b); Square s = pop_lsb(&b);
Piece p = pos.piece_on(s); Piece p = pos.piece_on(s);
int pieceOfs = 2 * (type_of(p) - 1) + (color_of(p) == WHITE);
key ^= ZobPiece[64 * pieceOfs + s]; // PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11
key ^= PG.Zobrist.psq[2 * (type_of(p) - 1) + (color_of(p) == WHITE)][s];
} }
b = pos.can_castle(ALL_CASTLES); b = pos.can_castle(ALL_CASTLES);
while (b) while (b)
key ^= ZobCastle[pop_lsb(&b)]; key ^= PG.Zobrist.castle[pop_lsb(&b)];
if (pos.ep_square() != SQ_NONE) if (pos.ep_square() != SQ_NONE)
key ^= ZobEnPassant[file_of(pos.ep_square())]; key ^= PG.Zobrist.enpassant[file_of(pos.ep_square())];
if (pos.side_to_move() == WHITE) if (pos.side_to_move() == WHITE)
key ^= ZobTurn[0]; key ^= PG.Zobrist.turn;
return key; return key;
} }
} // namespace } // namespace
PolyglotBook::PolyglotBook() { PolyglotBook::PolyglotBook() : rkiss(Time::now() % 10000) {}
for (int i = Time::now() % 10000; i > 0; i--)
RKiss.rand<unsigned>(); // Make random number generation less deterministic
}
PolyglotBook::~PolyglotBook() { if (is_open()) close(); } PolyglotBook::~PolyglotBook() { if (is_open()) close(); }
@@ -370,7 +367,7 @@ template<typename T> PolyglotBook& PolyglotBook::operator>>(T& n) {
return *this; return *this;
} }
template<> PolyglotBook& PolyglotBook::operator>>(BookEntry& e) { template<> PolyglotBook& PolyglotBook::operator>>(Entry& e) {
return *this >> e.key >> e.move >> e.count >> e.learn; return *this >> e.key >> e.move >> e.count >> e.learn;
} }
@@ -400,13 +397,13 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
if (fileName != fName && !open(fName.c_str())) if (fileName != fName && !open(fName.c_str()))
return MOVE_NONE; return MOVE_NONE;
BookEntry e; Entry e;
uint16_t best = 0; uint16_t best = 0;
unsigned sum = 0; unsigned sum = 0;
Move move = MOVE_NONE; Move move = MOVE_NONE;
uint64_t key = book_key(pos); Key key = polyglot_key(pos);
seekg(find_first(key) * sizeof(BookEntry), ios_base::beg); seekg(find_first(key) * sizeof(Entry), ios_base::beg);
while (*this >> e, e.key == key && good()) while (*this >> e, e.key == key && good())
{ {
@@ -416,7 +413,7 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
// Choose book move according to its score. If a move has a very // Choose book move according to its score. If a move has a very
// high score it has higher probability to be choosen than a move // high score it has higher probability to be choosen than a move
// with lower score. Note that first entry is always chosen. // with lower score. Note that first entry is always chosen.
if ( (sum && RKiss.rand<unsigned>() % sum < e.count) if ( (sum && rkiss.rand<unsigned>() % sum < e.count)
|| (pickBest && e.count == best)) || (pickBest && e.count == best))
move = Move(e.move); move = Move(e.move);
} }
@@ -440,7 +437,7 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
// Add 'special move' flags and verify it is legal // Add 'special move' flags and verify it is legal
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml) for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
if (move == (ml.move() & 0x3FFF)) if (move == (ml.move() ^ type_of(ml.move())))
return ml.move(); return ml.move();
return MOVE_NONE; return MOVE_NONE;
@@ -451,12 +448,12 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
/// the book file for the given key. Returns the index of the leftmost book /// the book file for the given key. Returns the index of the leftmost book
/// entry with the same key as the input. /// entry with the same key as the input.
size_t PolyglotBook::find_first(uint64_t key) { size_t PolyglotBook::find_first(Key key) {
seekg(0, ios::end); // Move pointer to end, so tellg() gets file's size seekg(0, ios::end); // Move pointer to end, so tellg() gets file's size
size_t low = 0, mid, high = (size_t)tellg() / sizeof(BookEntry) - 1; size_t low = 0, mid, high = (size_t)tellg() / sizeof(Entry) - 1;
BookEntry e; Entry e;
assert(low <= high); assert(low <= high);
@@ -466,7 +463,7 @@ size_t PolyglotBook::find_first(uint64_t key) {
assert(mid >= low && mid < high); assert(mid >= low && mid < high);
seekg(mid * sizeof(BookEntry), ios_base::beg); seekg(mid * sizeof(Entry), ios_base::beg);
*this >> e; *this >> e;
if (key <= e.key) if (key <= e.key)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -36,9 +36,9 @@ private:
template<typename T> PolyglotBook& operator>>(T& n); template<typename T> PolyglotBook& operator>>(T& n);
bool open(const char* fName); bool open(const char* fName);
size_t find_first(uint64_t key); size_t find_first(Key key);
RKISS RKiss; RKISS rkiss;
std::string fileName; std::string fileName;
}; };

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -31,7 +31,7 @@ namespace {
// Table used to drive the defending king towards the edge of the board // Table used to drive the defending king towards the edge of the board
// in KX vs K and KQ vs KR endgames. // in KX vs K and KQ vs KR endgames.
const int MateTable[64] = { const int MateTable[SQUARE_NB] = {
100, 90, 80, 70, 70, 80, 90, 100, 100, 90, 80, 70, 70, 80, 90, 100,
90, 70, 60, 50, 50, 60, 70, 90, 90, 70, 60, 50, 50, 60, 70, 90,
80, 60, 40, 30, 30, 40, 60, 80, 80, 60, 40, 30, 30, 40, 60, 80,
@@ -44,7 +44,7 @@ namespace {
// Table used to drive the defending king towards a corner square of the // Table used to drive the defending king towards a corner square of the
// right color in KBN vs K endgames. // right color in KBN vs K endgames.
const int KBNKMateTable[64] = { const int KBNKMateTable[SQUARE_NB] = {
200, 190, 180, 170, 160, 150, 140, 130, 200, 190, 180, 170, 160, 150, 140, 130,
190, 180, 170, 160, 150, 140, 130, 140, 190, 180, 170, 160, 150, 140, 130, 140,
180, 170, 155, 140, 140, 125, 140, 150, 180, 170, 155, 140, 140, 125, 140, 150,
@@ -95,10 +95,12 @@ Endgames::Endgames() {
add<KRKP>("KRKP"); add<KRKP>("KRKP");
add<KRKB>("KRKB"); add<KRKB>("KRKB");
add<KRKN>("KRKN"); add<KRKN>("KRKN");
add<KQKP>("KQKP");
add<KQKR>("KQKR"); add<KQKR>("KQKR");
add<KBBKN>("KBBKN"); add<KBBKN>("KBBKN");
add<KNPK>("KNPK"); add<KNPK>("KNPK");
add<KNPKB>("KNPKB");
add<KRPKR>("KRPKR"); add<KRPKR>("KRPKR");
add<KBPKB>("KBPKB"); add<KBPKB>("KBPKB");
add<KBPKN>("KBPKN"); add<KBPKN>("KBPKN");
@@ -132,7 +134,7 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
// Stalemate detection with lone king // Stalemate detection with lone king
if ( pos.side_to_move() == weakerSide if ( pos.side_to_move() == weakerSide
&& !pos.in_check() && !pos.checkers()
&& !MoveList<LEGAL>(pos).size()) { && !MoveList<LEGAL>(pos).size()) {
return VALUE_DRAW; return VALUE_DRAW;
} }
@@ -169,12 +171,12 @@ Value Endgame<KBNK>::operator()(const Position& pos) const {
Square winnerKSq = pos.king_square(strongerSide); Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide); Square loserKSq = pos.king_square(weakerSide);
Square bishopSquare = pos.piece_list(strongerSide, BISHOP)[0]; Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0];
// kbnk_mate_table() tries to drive toward corners A1 or H8, // kbnk_mate_table() tries to drive toward corners A1 or H8,
// if we have a bishop that cannot reach the above squares we // if we have a bishop that cannot reach the above squares we
// mirror the kings so to drive enemy toward corners A8 or H1. // mirror the kings so to drive enemy toward corners A8 or H1.
if (opposite_colors(bishopSquare, SQ_A1)) if (opposite_colors(bishopSq, SQ_A1))
{ {
winnerKSq = mirror(winnerKSq); winnerKSq = mirror(winnerKSq);
loserKSq = mirror(loserKSq); loserKSq = mirror(loserKSq);
@@ -198,21 +200,21 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, PAWN) == 0);
Square wksq, bksq, wpsq; Square wksq, bksq, wpsq;
Color stm; Color us;
if (strongerSide == WHITE) if (strongerSide == WHITE)
{ {
wksq = pos.king_square(WHITE); wksq = pos.king_square(WHITE);
bksq = pos.king_square(BLACK); bksq = pos.king_square(BLACK);
wpsq = pos.piece_list(WHITE, PAWN)[0]; wpsq = pos.piece_list(WHITE, PAWN)[0];
stm = pos.side_to_move(); us = pos.side_to_move();
} }
else else
{ {
wksq = ~pos.king_square(BLACK); wksq = ~pos.king_square(BLACK);
bksq = ~pos.king_square(WHITE); bksq = ~pos.king_square(WHITE);
wpsq = ~pos.piece_list(BLACK, PAWN)[0]; wpsq = ~pos.piece_list(BLACK, PAWN)[0];
stm = ~pos.side_to_move(); us = ~pos.side_to_move();
} }
if (file_of(wpsq) >= FILE_E) if (file_of(wpsq) >= FILE_E)
@@ -222,7 +224,7 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
wpsq = mirror(wpsq); wpsq = mirror(wpsq);
} }
if (!Bitbases::probe_kpk(wksq, wpsq, bksq, stm)) if (!Bitbases::probe_kpk(wksq, wpsq, bksq, us))
return VALUE_DRAW; return VALUE_DRAW;
Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(wpsq)); Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(wpsq));
@@ -326,6 +328,37 @@ Value Endgame<KRKN>::operator()(const Position& pos) const {
} }
/// KQ vs KP. In general, a win for the stronger side, however, there are a few
/// important exceptions. Pawn on 7th rank, A,C,F or H file, with king next can
/// be a draw, so we scale down to distance between kings only.
template<>
Value Endgame<KQKP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide) == 0);
assert(pos.piece_count(weakerSide, PAWN) == 1);
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
Square pawnSq = pos.piece_list(weakerSide, PAWN)[0];
Value result = QueenValueEg
- PawnValueEg
+ DistanceBonus[square_distance(winnerKSq, loserKSq)];
if ( square_distance(loserKSq, pawnSq) == 1
&& relative_rank(weakerSide, pawnSq) == RANK_7)
{
File f = file_of(pawnSq);
if (f == FILE_A || f == FILE_C || f == FILE_F || f == FILE_H)
result = Value(DistanceBonus[square_distance(winnerKSq, loserKSq)]);
}
return strongerSide == pos.side_to_move() ? result : -result;
}
/// KQ vs KR. This is almost identical to KX vs K: We give the attacking /// KQ vs KR. This is almost identical to KX vs K: We give the attacking
/// king a bonus for having the kings close together, and for forcing the /// king a bonus for having the kings close together, and for forcing the
/// defending king towards the edge. If we also take care to avoid null move /// defending king towards the edge. If we also take care to avoid null move
@@ -439,6 +472,29 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
} }
} }
// All pawns on same B or G file? Then potential draw
if ( (pawnFile == FILE_B || pawnFile == FILE_G)
&& !(pos.pieces(PAWN) & ~file_bb(pawnFile))
&& pos.non_pawn_material(weakerSide) == 0
&& pos.piece_count(weakerSide, PAWN) >= 1)
{
// Get weaker pawn closest to opponent's queening square
Bitboard wkPawns = pos.pieces(weakerSide, PAWN);
Square weakerPawnSq = strongerSide == WHITE ? msb(wkPawns) : lsb(wkPawns);
Square strongerKingSq = pos.king_square(strongerSide);
Square weakerKingSq = pos.king_square(weakerSide);
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0];
// Draw if weaker pawn is on rank 7, bishop can't attack the pawn, and
// weaker king can stop opposing opponent's king from penetrating.
if ( relative_rank(strongerSide, weakerPawnSq) == RANK_7
&& opposite_colors(bishopSq, weakerPawnSq)
&& square_distance(weakerPawnSq, weakerKingSq) <= square_distance(weakerPawnSq, strongerKingSq))
return SCALE_FACTOR_DRAW;
}
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
@@ -849,6 +905,24 @@ ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
} }
/// K, knight and a pawn vs K and bishop. If knight can block bishop from taking
/// pawn, it's a win. Otherwise, drawn.
template<>
ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0];
Square bishopSq = pos.piece_list(weakerSide, BISHOP)[0];
Square weakerKingSq = pos.king_square(weakerSide);
// King needs to get close to promoting pawn to prevent knight from blocking.
// Rules for this are very tricky, so just approximate.
if (forward_bb(strongerSide, pawnSq) & pos.attacks_from<BISHOP>(bishopSq))
return ScaleFactor(square_distance(weakerKingSq, pawnSq));
return SCALE_FACTOR_NONE;
}
/// K and a pawn vs K and a pawn. This is done by removing the weakest side's /// K and a pawn vs K and a pawn. This is done by removing the weakest side's
/// pawn and probing the KP vs K bitbase: If the weakest side has a draw without /// pawn and probing the KP vs K bitbase: If the weakest side has a draw without
/// the pawn, she probably has at least a draw with the pawn as well. The exception /// the pawn, she probably has at least a draw with the pawn as well. The exception
@@ -865,14 +939,14 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
Square wksq = pos.king_square(strongerSide); Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide); Square bksq = pos.king_square(weakerSide);
Square wpsq = pos.piece_list(strongerSide, PAWN)[0]; Square wpsq = pos.piece_list(strongerSide, PAWN)[0];
Color stm = pos.side_to_move(); Color us = pos.side_to_move();
if (strongerSide == BLACK) if (strongerSide == BLACK)
{ {
wksq = ~wksq; wksq = ~wksq;
bksq = ~bksq; bksq = ~bksq;
wpsq = ~wpsq; wpsq = ~wpsq;
stm = ~stm; us = ~us;
} }
if (file_of(wpsq) >= FILE_E) if (file_of(wpsq) >= FILE_E)
@@ -890,5 +964,5 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw, // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
// it's probably at least a draw even with the pawn. // it's probably at least a draw even with the pawn.
return Bitbases::probe_kpk(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; return Bitbases::probe_kpk(wksq, wpsq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -39,6 +39,7 @@ enum EndgameType {
KRKP, // KR vs KP KRKP, // KR vs KP
KRKB, // KR vs KB KRKB, // KR vs KB
KRKN, // KR vs KN KRKN, // KR vs KN
KQKP, // KQ vs KP
KQKR, // KQ vs KR KQKR, // KQ vs KR
KBBKN, // KBB vs KN KBBKN, // KBB vs KN
KNNK, // KNN vs K KNNK, // KNN vs K
@@ -57,6 +58,7 @@ enum EndgameType {
KBPPKB, // KBPP vs KB KBPPKB, // KBPP vs KB
KBPKN, // KBP vs KN KBPKN, // KBP vs KN
KNPK, // KNP vs K KNPK, // KNP vs K
KNPKB, // KNP vs KB
KPKP // KP vs KP KPKP // KP vs KP
}; };

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -36,13 +36,13 @@ namespace {
struct EvalInfo { struct EvalInfo {
// Pointers to material and pawn hash table entries // Pointers to material and pawn hash table entries
MaterialEntry* mi; Material::Entry* mi;
PawnEntry* pi; Pawns::Entry* pi;
// attackedBy[color][piece type] is a bitboard representing all squares // attackedBy[color][piece type] is a bitboard representing all squares
// attacked by a given color and piece type, attackedBy[color][0] contains // attacked by a given color and piece type, attackedBy[color][ALL_PIECES]
// all squares attacked by the given color. // contains all squares attacked by the given color.
Bitboard attackedBy[2][8]; Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB];
// kingRing[color] is the zone around the king which is considered // kingRing[color] is the zone around the king which is considered
// by the king safety evaluation. This consists of the squares directly // by the king safety evaluation. This consists of the squares directly
@@ -50,25 +50,25 @@ namespace {
// squares two ranks in front of the king. For instance, if black's king // squares two ranks in front of the king. For instance, if black's king
// is on g8, kingRing[BLACK] is a bitboard containing the squares f8, h8, // is on g8, kingRing[BLACK] is a bitboard containing the squares f8, h8,
// f7, g7, h7, f6, g6 and h6. // f7, g7, h7, f6, g6 and h6.
Bitboard kingRing[2]; Bitboard kingRing[COLOR_NB];
// kingAttackersCount[color] is the number of pieces of the given color // kingAttackersCount[color] is the number of pieces of the given color
// which attack a square in the kingRing of the enemy king. // which attack a square in the kingRing of the enemy king.
int kingAttackersCount[2]; int kingAttackersCount[COLOR_NB];
// kingAttackersWeight[color] is the sum of the "weight" of the pieces of the // kingAttackersWeight[color] is the sum of the "weight" of the pieces of the
// given color which attack a square in the kingRing of the enemy king. The // given color which attack a square in the kingRing of the enemy king. The
// weights of the individual piece types are given by the variables // weights of the individual piece types are given by the variables
// QueenAttackWeight, RookAttackWeight, BishopAttackWeight and // QueenAttackWeight, RookAttackWeight, BishopAttackWeight and
// KnightAttackWeight in evaluate.cpp // KnightAttackWeight in evaluate.cpp
int kingAttackersWeight[2]; int kingAttackersWeight[COLOR_NB];
// kingAdjacentZoneAttacksCount[color] is the number of attacks to squares // kingAdjacentZoneAttacksCount[color] is the number of attacks to squares
// directly adjacent to the king of the given color. Pieces which attack // directly adjacent to the king of the given color. Pieces which attack
// more than one square are counted multiple times. For instance, if black's // more than one square are counted multiple times. For instance, if black's
// king is on g8 and there's a white knight on g5, this knight adds // king is on g8 and there's a white knight on g5, this knight adds
// 2 to kingAdjacentZoneAttacksCount[BLACK]. // 2 to kingAdjacentZoneAttacksCount[BLACK].
int kingAdjacentZoneAttacksCount[2]; int kingAdjacentZoneAttacksCount[COLOR_NB];
}; };
// Evaluation grain size, must be a power of 2 // Evaluation grain size, must be a power of 2
@@ -88,7 +88,7 @@ namespace {
// //
// Values modified by Joona Kiiski // Values modified by Joona Kiiski
const Score WeightsInternal[] = { const Score WeightsInternal[] = {
S(252, 344), S(216, 266), S(46, 0), S(247, 0), S(259, 0) S(289, 344), S(221, 273), S(46, 0), S(271, 0), S(307, 0)
}; };
// MobilityBonus[PieceType][attacked] contains mobility bonuses for middle and // MobilityBonus[PieceType][attacked] contains mobility bonuses for middle and
@@ -114,7 +114,7 @@ namespace {
// OutpostBonus[PieceType][Square] contains outpost bonuses of knights and // OutpostBonus[PieceType][Square] contains outpost bonuses of knights and
// bishops, indexed by piece type and square (from white's point of view). // bishops, indexed by piece type and square (from white's point of view).
const Value OutpostBonus[][64] = { const Value OutpostBonus[][SQUARE_NB] = {
{ {
// A B C D E F G H // A B C D E F G H
V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), // Knights V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), // Knights
@@ -134,7 +134,7 @@ namespace {
// ThreatBonus[attacking][attacked] contains threat bonuses according to // ThreatBonus[attacking][attacked] contains threat bonuses according to
// which piece type attacks which one. // which piece type attacks which one.
const Score ThreatBonus[][8] = { const Score ThreatBonus[][PIECE_TYPE_NB] = {
{}, {}, {}, {},
{ S(0, 0), S( 7, 39), S( 0, 0), S(24, 49), S(41,100), S(41,100) }, // KNIGHT { S(0, 0), S( 7, 39), S( 0, 0), S(24, 49), S(41,100), S(41,100) }, // KNIGHT
{ S(0, 0), S( 7, 39), S(24, 49), S( 0, 0), S(41,100), S(41,100) }, // BISHOP { S(0, 0), S( 7, 39), S(24, 49), S( 0, 0), S(41,100), S(41,100) }, // BISHOP
@@ -150,16 +150,18 @@ namespace {
#undef S #undef S
const Score BishopPinBonus = make_score(66, 11);
// Bonus for having the side to move (modified by Joona Kiiski) // Bonus for having the side to move (modified by Joona Kiiski)
const Score Tempo = make_score(24, 11); const Score Tempo = make_score(24, 11);
// Rooks and queens on the 7th rank // Rooks and queens on the 7th rank
const Score RookOn7thBonus = make_score(3, 20); const Score RookOn7thBonus = make_score(11, 20);
const Score QueenOn7thBonus = make_score(1, 8); const Score QueenOn7thBonus = make_score( 3, 8);
// Rooks and queens attacking pawns on the same rank // Rooks and queens attacking pawns on the same rank
const Score RookOnPawnBonus = make_score(3, 48); const Score RookOnPawnBonus = make_score(10, 28);
const Score QueenOnPawnBonus = make_score(1, 40); const Score QueenOnPawnBonus = make_score( 4, 20);
// Rooks on open files (modified by Joona Kiiski) // Rooks on open files (modified by Joona Kiiski)
const Score RookOpenFileBonus = make_score(43, 21); const Score RookOpenFileBonus = make_score(43, 21);
@@ -169,6 +171,9 @@ namespace {
// right to castle. // right to castle.
const Value TrappedRookPenalty = Value(180); const Value TrappedRookPenalty = Value(180);
// Penalty for bishop with pawns on the same coloured squares
const Score BishopPawnsPenalty = make_score(8, 12);
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by // Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
// a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only // a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only
// happen in Chess960 games. // happen in Chess960 games.
@@ -221,11 +226,11 @@ namespace {
// KingDangerTable[Color][attackUnits] contains the actual king danger // KingDangerTable[Color][attackUnits] contains the actual king danger
// weighted scores, indexed by color and by a calculated integer number. // weighted scores, indexed by color and by a calculated integer number.
Score KingDangerTable[2][128]; Score KingDangerTable[COLOR_NB][128];
// TracedTerms[Color][PieceType || TracedType] contains a breakdown of the // TracedTerms[Color][PieceType || TracedType] contains a breakdown of the
// evaluation terms, used when tracing. // evaluation terms, used when tracing.
Score TracedScores[2][16]; Score TracedScores[COLOR_NB][16];
std::stringstream TraceStream; std::stringstream TraceStream;
enum TracedType { enum TracedType {
@@ -267,8 +272,6 @@ namespace {
namespace Eval { namespace Eval {
Color RootColor;
/// evaluate() is the main evaluation function. It always computes two /// evaluate() is the main evaluation function. It always computes two
/// values, an endgame score and a middle game score, and interpolates /// values, an endgame score and a middle game score, and interpolates
/// between them based on the remaining material. /// between them based on the remaining material.
@@ -289,14 +292,6 @@ namespace Eval {
Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]); Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]);
Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]); Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]);
// King safety is asymmetrical. Our king danger level is weighted by
// "Cowardice" UCI parameter, instead the opponent one by "Aggressiveness".
// If running in analysis mode, make sure we use symmetrical king safety. We
// do this by replacing both Weights[kingDangerUs] and Weights[kingDangerThem]
// by their average.
if (Options["UCI_AnalyseMode"])
Weights[KingDangerUs] = Weights[KingDangerThem] = (Weights[KingDangerUs] + Weights[KingDangerThem]) / 2;
const int MaxSlope = 30; const int MaxSlope = 30;
const int Peak = 1280; const int Peak = 1280;
@@ -319,7 +314,7 @@ namespace Eval {
Value margin; Value margin;
std::string totals; std::string totals;
RootColor = pos.side_to_move(); Search::RootColor = pos.side_to_move();
TraceStream.str(""); TraceStream.str("");
TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2); TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
@@ -363,11 +358,12 @@ namespace {
template<bool Trace> template<bool Trace>
Value do_evaluate(const Position& pos, Value& margin) { Value do_evaluate(const Position& pos, Value& margin) {
assert(!pos.in_check()); assert(!pos.checkers());
EvalInfo ei; EvalInfo ei;
Value margins[2]; Value margins[COLOR_NB];
Score score, mobilityWhite, mobilityBlack; Score score, mobilityWhite, mobilityBlack;
Thread* th = pos.this_thread();
// margins[] store the uncertainty estimation of position's evaluation // margins[] store the uncertainty estimation of position's evaluation
// that typically is used by the search for pruning decisions. // that typically is used by the search for pruning decisions.
@@ -379,7 +375,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo); score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo);
// Probe the material hash table // Probe the material hash table
ei.mi = pos.this_thread()->materialTable.probe(pos); ei.mi = Material::probe(pos, th->materialTable, th->endgames);
score += ei.mi->material_value(); score += ei.mi->material_value();
// If we have a specialized evaluation function for the current material // If we have a specialized evaluation function for the current material
@@ -391,7 +387,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
} }
// Probe the pawn hash table // Probe the pawn hash table
ei.pi = pos.this_thread()->pawnTable.probe(pos); ei.pi = Pawns::probe(pos, th->pawnsTable);
score += ei.pi->pawns_value(); score += ei.pi->pawns_value();
// Initialize attack and king safety bitboards // Initialize attack and king safety bitboards
@@ -496,7 +492,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
// Init king safety tables only if we are going to use them // Init king safety tables only if we are going to use them
if ( pos.piece_count(Us, QUEEN) if ( pos.piece_count(Us, QUEEN)
&& pos.non_pawn_material(Us) >= QueenValueMg + RookValueMg) && pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg)
{ {
ei.kingRing[Them] = (b | (Us == WHITE ? b >> 8 : b << 8)); ei.kingRing[Them] = (b | (Us == WHITE ? b >> 8 : b << 8));
b &= ei.attackedBy[Us][PAWN]; b &= ei.attackedBy[Us][PAWN];
@@ -577,23 +573,22 @@ Value do_evaluate(const Position& pos, Value& margin) {
mobility += MobilityBonus[Piece][mob]; mobility += MobilityBonus[Piece][mob];
// Add a bonus if a slider is pinning an enemy piece
if ( (Piece == BISHOP || Piece == ROOK || Piece == QUEEN)
&& (PseudoAttacks[Piece][pos.king_square(Them)] & s))
{
b = BetweenBB[s][pos.king_square(Them)] & pos.pieces();
assert(b);
if (!more_than_one(b) && (b & pos.pieces(Them)))
score += ThreatBonus[Piece][type_of(pos.piece_on(lsb(b)))];
}
// Decrease score if we are attacked by an enemy pawn. Remaining part // Decrease score if we are attacked by an enemy pawn. Remaining part
// of threat evaluation must be done later when we have full attack info. // of threat evaluation must be done later when we have full attack info.
if (ei.attackedBy[Them][PAWN] & s) if (ei.attackedBy[Them][PAWN] & s)
score -= ThreatenedByPawnPenalty[Piece]; score -= ThreatenedByPawnPenalty[Piece];
// Otherwise give a bonus if we are a bishop and can pin a piece or
// can give a discovered check through an x-ray attack.
else if ( Piece == BISHOP
&& (PseudoAttacks[Piece][pos.king_square(Them)] & s)
&& !more_than_one(BetweenBB[s][pos.king_square(Them)] & pos.pieces()))
score += BishopPinBonus;
// Penalty for bishop with same coloured pawns
if (Piece == BISHOP)
score -= BishopPawnsPenalty * ei.pi->pawns_on_same_color_squares(Us, s);
// Bishop and knight outposts squares // Bishop and knight outposts squares
if ( (Piece == BISHOP || Piece == KNIGHT) if ( (Piece == BISHOP || Piece == KNIGHT)
&& !(pos.pieces(Them, PAWN) & attack_span_mask(Us, s))) && !(pos.pieces(Them, PAWN) & attack_span_mask(Us, s)))
@@ -696,16 +691,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
// Undefended minors get penalized even if not under attack // Undefended minors get penalized even if not under attack
undefendedMinors = pos.pieces(Them) undefendedMinors = pos.pieces(Them)
& (pos.pieces(BISHOP) | pos.pieces(KNIGHT)) & (pos.pieces(BISHOP) | pos.pieces(KNIGHT))
& ~ei.attackedBy[Them][0]; & ~ei.attackedBy[Them][ALL_PIECES];
if (undefendedMinors) if (undefendedMinors)
score += more_than_one(undefendedMinors) ? UndefendedMinorPenalty * 2 score += UndefendedMinorPenalty;
: UndefendedMinorPenalty;
// Enemy pieces not defended by a pawn and under our attack // Enemy pieces not defended by a pawn and under our attack
weakEnemies = pos.pieces(Them) weakEnemies = pos.pieces(Them)
& ~ei.attackedBy[Them][PAWN] & ~ei.attackedBy[Them][PAWN]
& ei.attackedBy[Us][0]; & ei.attackedBy[Us][ALL_PIECES];
if (!weakEnemies) if (!weakEnemies)
return score; return score;
@@ -744,7 +738,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
score += evaluate_pieces<QUEEN, Us, Trace>(pos, ei, mobility, mobilityArea); score += evaluate_pieces<QUEEN, Us, Trace>(pos, ei, mobility, mobilityArea);
// Sum up all attacked squares // Sum up all attacked squares
ei.attackedBy[Us][0] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
| ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING]; | ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING];
return score; return score;
@@ -772,7 +766,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
{ {
// Find the attacked squares around the king which has no defenders // Find the attacked squares around the king which has no defenders
// apart from the king itself // apart from the king itself
undefended = ei.attackedBy[Them][0] & ei.attackedBy[Us][KING]; undefended = ei.attackedBy[Them][ALL_PIECES] & ei.attackedBy[Us][KING];
undefended &= ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] undefended &= ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
| ei.attackedBy[Us][QUEEN]); | ei.attackedBy[Us][QUEEN]);
@@ -820,7 +814,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
} }
// Analyse enemy's safe distance checks for sliders and knights // Analyse enemy's safe distance checks for sliders and knights
safe = ~(pos.pieces(Them) | ei.attackedBy[Us][0]); safe = ~(pos.pieces(Them) | ei.attackedBy[Us][ALL_PIECES]);
b1 = pos.attacks_from<ROOK>(ksq) & safe; b1 = pos.attacks_from<ROOK>(ksq) & safe;
b2 = pos.attacks_from<BISHOP>(ksq) & safe; b2 = pos.attacks_from<BISHOP>(ksq) & safe;
@@ -853,8 +847,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
// value that will be used for pruning because this value can sometimes // value that will be used for pruning because this value can sometimes
// be very big, and so capturing a single attacking piece can therefore // be very big, and so capturing a single attacking piece can therefore
// result in a score change far bigger than the value of the captured piece. // result in a score change far bigger than the value of the captured piece.
score -= KingDangerTable[Us == Eval::RootColor][attackUnits]; score -= KingDangerTable[Us == Search::RootColor][attackUnits];
margins[Us] += mg_value(KingDangerTable[Us == Eval::RootColor][attackUnits]); margins[Us] += mg_value(KingDangerTable[Us == Search::RootColor][attackUnits]);
} }
if (Trace) if (Trace)
@@ -907,7 +901,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
if (pos.is_empty(blockSq)) if (pos.is_empty(blockSq))
{ {
squaresToQueen = forward_bb(Us, s); squaresToQueen = forward_bb(Us, s);
defendedSquares = squaresToQueen & ei.attackedBy[Us][0]; defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES];
// If there is an enemy rook or queen attacking the pawn from behind, // If there is an enemy rook or queen attacking the pawn from behind,
// add all X-ray attacks by the rook or queen. Otherwise consider only // add all X-ray attacks by the rook or queen. Otherwise consider only
@@ -916,7 +910,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
&& (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN) & pos.attacks_from<ROOK>(s))) && (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN) & pos.attacks_from<ROOK>(s)))
unsafeSquares = squaresToQueen; unsafeSquares = squaresToQueen;
else else
unsafeSquares = squaresToQueen & (ei.attackedBy[Them][0] | pos.pieces(Them)); unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
// If there aren't enemy attacks or pieces along the path to queen give // If there aren't enemy attacks or pieces along the path to queen give
// huge bonus. Even bigger if we protect the pawn's path. // huge bonus. Even bigger if we protect the pawn's path.
@@ -993,14 +987,14 @@ Value do_evaluate(const Position& pos, Value& margin) {
// Compute plies to queening and check direct advancement // Compute plies to queening and check direct advancement
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2); movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2);
oppMovesToGo = square_distance(pos.king_square(~c), queeningSquare) - int(c != pos.side_to_move()); oppMovesToGo = square_distance(pos.king_square(~c), queeningSquare) - int(c != pos.side_to_move());
pathDefended = ((ei.attackedBy[c][0] & queeningPath) == queeningPath); pathDefended = ((ei.attackedBy[c][ALL_PIECES] & queeningPath) == queeningPath);
if (movesToGo >= oppMovesToGo && !pathDefended) if (movesToGo >= oppMovesToGo && !pathDefended)
continue; continue;
// Opponent king cannot block because path is defended and position // Opponent king cannot block because path is defended and position
// is not in check. So only friendly pieces can be blockers. // is not in check. So only friendly pieces can be blockers.
assert(!pos.in_check()); assert(!pos.checkers());
assert((queeningPath & pos.pieces()) == (queeningPath & pos.pieces(c))); assert((queeningPath & pos.pieces()) == (queeningPath & pos.pieces(c)));
// Add moves needed to free the path from friendly pieces and retest condition // Add moves needed to free the path from friendly pieces and retest condition
@@ -1140,14 +1134,18 @@ Value do_evaluate(const Position& pos, Value& margin) {
Bitboard safe = SpaceMask[Us] Bitboard safe = SpaceMask[Us]
& ~pos.pieces(Us, PAWN) & ~pos.pieces(Us, PAWN)
& ~ei.attackedBy[Them][PAWN] & ~ei.attackedBy[Them][PAWN]
& (ei.attackedBy[Us][0] | ~ei.attackedBy[Them][0]); & (ei.attackedBy[Us][ALL_PIECES] | ~ei.attackedBy[Them][ALL_PIECES]);
// Find all squares which are at most three squares behind some friendly pawn // Find all squares which are at most three squares behind some friendly pawn
Bitboard behind = pos.pieces(Us, PAWN); Bitboard behind = pos.pieces(Us, PAWN);
behind |= (Us == WHITE ? behind >> 8 : behind << 8); behind |= (Us == WHITE ? behind >> 8 : behind << 8);
behind |= (Us == WHITE ? behind >> 16 : behind << 16); behind |= (Us == WHITE ? behind >> 16 : behind << 16);
return popcount<Max15>(safe) + popcount<Max15>(behind & safe); // Since SpaceMask[Us] is fully on our half of the board
assert(unsigned(safe >> (Us == WHITE ? 32 : 0)) == 0);
// Count safe + (behind & safe) with a single popcount
return popcount<Full>((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe));
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -26,8 +26,6 @@ class Position;
namespace Eval { namespace Eval {
extern Color RootColor;
extern void init(); extern void init();
extern Value evaluate(const Position& pos, Value& margin); extern Value evaluate(const Position& pos, Value& margin);
extern std::string trace(const Position& pos); extern std::string trace(const Position& pos);

View File

@@ -1,72 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(HISTORY_H_INCLUDED)
#define HISTORY_H_INCLUDED
#include <algorithm>
#include <cstring>
#include "types.h"
/// The History class stores statistics about how often different moves
/// have been successful or unsuccessful during the current search. These
/// statistics are used for reduction and move ordering decisions. History
/// entries are stored according only to moving piece and destination square,
/// in particular two moves with different origin but same destination and
/// same piece will be considered identical.
class History {
public:
void clear();
Value value(Piece p, Square to) const;
void add(Piece p, Square to, Value bonus);
Value gain(Piece p, Square to) const;
void update_gain(Piece p, Square to, Value g);
static const Value MaxValue = Value(2000);
private:
Value history[16][64]; // [piece][to_square]
Value maxGains[16][64]; // [piece][to_square]
};
inline void History::clear() {
memset(history, 0, 16 * 64 * sizeof(Value));
memset(maxGains, 0, 16 * 64 * sizeof(Value));
}
inline Value History::value(Piece p, Square to) const {
return history[p][to];
}
inline void History::add(Piece p, Square to, Value bonus) {
if (abs(history[p][to] + bonus) < MaxValue) history[p][to] += bonus;
}
inline Value History::gain(Piece p, Square to) const {
return maxGains[p][to];
}
inline void History::update_gain(Piece p, Square to, Value g) {
maxGains[p][to] = std::max(g, maxGains[p][to] - 1);
}
#endif // !defined(HISTORY_H_INCLUDED)

View File

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

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm> #include <algorithm> // For std::min
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
@@ -38,15 +38,29 @@ namespace {
const Value RedundantQueenPenalty = Value(320); const Value RedundantQueenPenalty = Value(320);
const Value RedundantRookPenalty = Value(554); const Value RedundantRookPenalty = Value(554);
// pair pawn knight bishop rook queen
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 }; const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
const int QuadraticCoefficientsSameColor[][8] = { const int QuadraticCoefficientsSameColor[][PIECE_TYPE_NB] = {
{ 7, 7, 7, 7, 7, 7 }, { 39, 2, 7, 7, 7, 7 }, { 35, 271, -4, 7, 7, 7 }, // pair pawn knight bishop rook queen
{ 7, 25, 4, 7, 7, 7 }, { -27, -2, 46, 100, 56, 7 }, { 58, 29, 83, 148, -3, -25 } }; { 7 }, // Bishop pair
{ 39, 2 }, // Pawn
{ 35, 271, -4 }, // Knight
{ 7, 105, 4, 7 }, // Bishop
{ -27, -2, 46, 100, 56 }, // Rook
{ 58, 29, 83, 148, -3, -25 } // Queen
};
const int QuadraticCoefficientsOppositeColor[][8] = { const int QuadraticCoefficientsOppositeColor[][PIECE_TYPE_NB] = {
{ 41, 41, 41, 41, 41, 41 }, { 37, 41, 41, 41, 41, 41 }, { 10, 62, 41, 41, 41, 41 }, // THEIR PIECES
{ 57, 64, 39, 41, 41, 41 }, { 50, 40, 23, -22, 41, 41 }, { 106, 101, 3, 151, 171, 41 } }; // pair pawn knight bishop rook queen
{ 41 }, // Bishop pair
{ 37, 41 }, // Pawn
{ 10, 62, 41 }, // Knight OUR PIECES
{ 57, 64, 39, 41 }, // Bishop
{ 50, 40, 23, -22, 41 }, // Rook
{ 106, 101, 3, 151, 171, 41 } // Queen
};
// Endgame evaluation and scaling functions accessed direcly and not through // Endgame evaluation and scaling functions accessed direcly and not through
// the function maps because correspond to more then one material hash key. // the function maps because correspond to more then one material hash key.
@@ -81,18 +95,54 @@ namespace {
&& pos.piece_count(Them, PAWN) >= 1; && pos.piece_count(Them, PAWN) >= 1;
} }
/// imbalance() calculates imbalance comparing piece count of each
/// piece type for both colors.
template<Color Us>
int imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
int pt1, pt2, pc, v;
int value = 0;
// Redundancy of major pieces, formula based on Kaufman's paper
// "The Evaluation of Material Imbalances in Chess"
if (pieceCount[Us][ROOK] > 0)
value -= RedundantRookPenalty * (pieceCount[Us][ROOK] - 1)
+ RedundantQueenPenalty * pieceCount[Us][QUEEN];
// Second-degree polynomial material imbalance by Tord Romstad
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
{
pc = pieceCount[Us][pt1];
if (!pc)
continue;
v = LinearCoefficients[pt1];
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];
value += pc * v;
}
return value;
}
} // namespace } // namespace
namespace Material {
/// MaterialTable::probe() takes a position object as input, looks up a MaterialEntry /// Material::probe() takes a position object as input, looks up a MaterialEntry
/// object, and returns a pointer to it. If the material configuration is not /// object, and returns a pointer to it. If the material configuration is not
/// already present in the table, it is computed and stored there, so we don't /// already present in the table, it is computed and stored there, so we don't
/// have to recompute everything when the same material configuration occurs again. /// have to recompute everything when the same material configuration occurs again.
MaterialEntry* MaterialTable::probe(const Position& pos) { Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
Key key = pos.material_key(); Key key = pos.material_key();
MaterialEntry* e = entries[key]; Entry* e = entries[key];
// If e->key matches the position's material hash key, it means that we // If e->key matches the position's material hash key, it means that we
// have analysed this material configuration before, and we can simply // have analysed this material configuration before, and we can simply
@@ -100,10 +150,10 @@ MaterialEntry* MaterialTable::probe(const Position& pos) {
if (e->key == key) if (e->key == key)
return e; return e;
memset(e, 0, sizeof(MaterialEntry)); memset(e, 0, sizeof(Entry));
e->key = key; e->key = key;
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL; e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
e->gamePhase = MaterialTable::game_phase(pos); e->gamePhase = game_phase(pos);
// Let's look if we have a specialized evaluation function for this // Let's look if we have a specialized evaluation function for this
// particular material configuration. First we look for a fixed // particular material configuration. First we look for a fixed
@@ -215,7 +265,7 @@ MaterialEntry* MaterialTable::probe(const Position& pos) {
// Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
// for the bishop pair "extended piece", this allow us to be more flexible // for the bishop pair "extended piece", this allow us to be more flexible
// in defining bishop pair bonuses. // in defining bishop pair bonuses.
const int pieceCount[2][8] = { const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = {
{ pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT), { pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT),
pos.piece_count(WHITE, BISHOP) , pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) }, pos.piece_count(WHITE, BISHOP) , pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) },
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT), { pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT),
@@ -226,47 +276,11 @@ MaterialEntry* MaterialTable::probe(const Position& pos) {
} }
/// MaterialTable::imbalance() calculates imbalance comparing piece count of each /// Material::game_phase() calculates the phase given the current
/// piece type for both colors.
template<Color Us>
int MaterialTable::imbalance(const int pieceCount[][8]) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
int pt1, pt2, pc, v;
int value = 0;
// Redundancy of major pieces, formula based on Kaufman's paper
// "The Evaluation of Material Imbalances in Chess"
if (pieceCount[Us][ROOK] > 0)
value -= RedundantRookPenalty * (pieceCount[Us][ROOK] - 1)
+ RedundantQueenPenalty * pieceCount[Us][QUEEN];
// Second-degree polynomial material imbalance by Tord Romstad
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
{
pc = pieceCount[Us][pt1];
if (!pc)
continue;
v = LinearCoefficients[pt1];
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];
value += pc * v;
}
return value;
}
/// MaterialTable::game_phase() calculates the phase given the current
/// position. Because the phase is strictly a function of the material, it /// position. Because the phase is strictly a function of the material, it
/// is stored in MaterialEntry. /// is stored in MaterialEntry.
Phase MaterialTable::game_phase(const Position& pos) { Phase game_phase(const Position& pos) {
Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK); Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK);
@@ -274,3 +288,5 @@ Phase MaterialTable::game_phase(const Position& pos) {
: npm <= EndgameLimit ? PHASE_ENDGAME : npm <= EndgameLimit ? PHASE_ENDGAME
: Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit)); : Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
} }
} // namespace Material

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -25,96 +25,53 @@
#include "position.h" #include "position.h"
#include "types.h" #include "types.h"
const int MaterialTableSize = 8192; namespace Material {
/// Game phase /// Material::Entry contains various information about a material configuration.
enum Phase { /// It contains a material balance evaluation, a function pointer to a special
PHASE_ENDGAME = 0, /// endgame evaluation function (which in most cases is NULL, meaning that the
PHASE_MIDGAME = 128 /// standard evaluation function will be used), and "scale factors".
};
/// MaterialEntry is a class which contains various information about a
/// material configuration. It contains a material balance evaluation,
/// a function pointer to a special endgame evaluation function (which in
/// most cases is NULL, meaning that the standard evaluation function will
/// be used), and "scale factors" for black and white.
/// ///
/// The scale factors are used to scale the evaluation score up or down. /// The scale factors are used to scale the evaluation score up or down.
/// For instance, in KRB vs KR endgames, the score is scaled down by a factor /// For instance, in KRB vs KR endgames, the score is scaled down by a factor
/// of 4, which will result in scores of absolute value less than one pawn. /// of 4, which will result in scores of absolute value less than one pawn.
class MaterialEntry { struct Entry {
friend struct MaterialTable; Score material_value() const { return make_score(value, value); }
int space_weight() const { return spaceWeight; }
public: Phase game_phase() const { return gamePhase; }
Score material_value() const; bool specialized_eval_exists() const { return evaluationFunction != NULL; }
Value evaluate(const Position& p) const { return (*evaluationFunction)(p); }
ScaleFactor scale_factor(const Position& pos, Color c) const; ScaleFactor scale_factor(const Position& pos, Color c) const;
int space_weight() const;
Phase game_phase() const;
bool specialized_eval_exists() const;
Value evaluate(const Position& pos) const;
private:
Key key; Key key;
int16_t value; int16_t value;
uint8_t factor[2]; uint8_t factor[COLOR_NB];
EndgameBase<Value>* evaluationFunction; EndgameBase<Value>* evaluationFunction;
EndgameBase<ScaleFactor>* scalingFunction[2]; EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB];
int spaceWeight; int spaceWeight;
Phase gamePhase; Phase gamePhase;
}; };
typedef HashTable<Entry, 8192> Table;
/// The MaterialTable class represents a material hash table. The most important Entry* probe(const Position& pos, Table& entries, Endgames& endgames);
/// method is probe(), which returns a pointer to a MaterialEntry object. Phase game_phase(const Position& pos);
struct MaterialTable { /// Material::scale_factor takes a position and a color as input, and
MaterialEntry* probe(const Position& pos);
static Phase game_phase(const Position& pos);
template<Color Us> static int imbalance(const int pieceCount[][8]);
HashTable<MaterialEntry, MaterialTableSize> entries;
Endgames endgames;
};
/// MaterialEntry::scale_factor takes a position and a color as input, and
/// returns a scale factor for the given color. We have to provide the /// returns a scale factor for the given color. We have to provide the
/// position in addition to the color, because the scale factor need not /// position in addition to the color, because the scale factor need not
/// to be a constant: It can also be a function which should be applied to /// to be a constant: It can also be a function which should be applied to
/// the position. For instance, in KBP vs K endgames, a scaling function /// the position. For instance, in KBP vs K endgames, a scaling function
/// which checks for draws with rook pawns and wrong-colored bishops. /// which checks for draws with rook pawns and wrong-colored bishops.
inline ScaleFactor MaterialEntry::scale_factor(const Position& pos, Color c) const { inline ScaleFactor Entry::scale_factor(const Position& pos, Color c) const {
if (!scalingFunction[c]) return !scalingFunction[c] || (*scalingFunction[c])(pos) == SCALE_FACTOR_NONE
return ScaleFactor(factor[c]); ? ScaleFactor(factor[c]) : (*scalingFunction[c])(pos);
ScaleFactor sf = (*scalingFunction[c])(pos);
return sf == SCALE_FACTOR_NONE ? ScaleFactor(factor[c]) : sf;
} }
inline Value MaterialEntry::evaluate(const Position& pos) const {
return (*evaluationFunction)(pos);
}
inline Score MaterialEntry::material_value() const {
return make_score(value, value);
}
inline int MaterialEntry::space_weight() const {
return spaceWeight;
}
inline Phase MaterialEntry::game_phase() const {
return gamePhase;
}
inline bool MaterialEntry::specialized_eval_exists() const {
return evaluationFunction != NULL;
} }
#endif // !defined(MATERIAL_H_INCLUDED) #endif // !defined(MATERIAL_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -31,16 +31,16 @@
using namespace std; using namespace std;
/// Version number. If Version is left empty, then Tag plus current /// Version number. If Version is left empty, then Tag plus current
/// date (in the format YYMMDD) is used as a version number. /// date, in the format DD-MM-YY, are used as a version number.
static const string Version = "2.3.1"; static const string Version = "3";
static const string Tag = ""; static const string Tag = "";
/// engine_info() returns the full name of the current Stockfish version. /// engine_info() returns the full name of the current Stockfish version. This
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when /// will be either "Stockfish <Tag> DD-MM-YY" (where DD-MM-YY is the date when
/// the program was compiled) or "Stockfish <version number>", depending /// the program was compiled) or "Stockfish <Version>", depending on whether
/// on whether Version is empty. /// Version is empty.
const string engine_info(bool to_uci) { const string engine_info(bool to_uci) {
@@ -57,8 +57,8 @@ const string engine_info(bool to_uci) {
{ {
date >> month >> day >> year; date >> month >> day >> year;
s << Tag << setfill('0') << " " << year.substr(2) s << Tag << string(Tag.empty() ? "" : " ") << setfill('0') << setw(2) << day
<< setw(2) << (1 + months.find(month) / 4) << setw(2) << day; << "-" << setw(2) << (1 + months.find(month) / 4) << "-" << year.substr(2);
} }
s << cpu64 << popcnt << (to_uci ? "\nid author ": " by ") s << cpu64 << popcnt << (to_uci ? "\nid author ": " by ")
@@ -227,18 +227,19 @@ void prefetch(char*) {}
#else #else
# include <xmmintrin.h>
void prefetch(char* addr) { void prefetch(char* addr) {
# if defined(__INTEL_COMPILER) || defined(__ICL) # if defined(__INTEL_COMPILER)
// This hack prevents prefetches to be optimized away by // This hack prevents prefetches to be optimized away by
// Intel compiler. Both MSVC and gcc seems not affected. // Intel compiler. Both MSVC and gcc seems not affected.
__asm__ (""); __asm__ ("");
# endif # endif
_mm_prefetch(addr, _MM_HINT_T2); # if defined(__INTEL_COMPILER) || defined(_MSC_VER)
_mm_prefetch(addr+64, _MM_HINT_T2); // 64 bytes ahead _mm_prefetch(addr, _MM_HINT_T0);
# else
__builtin_prefetch(addr);
# endif
} }
#endif #endif

View File

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

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -44,7 +44,7 @@ namespace {
Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1); Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
Bitboard enemies = pos.pieces(~us); Bitboard enemies = pos.pieces(~us);
assert(!pos.in_check()); assert(!pos.checkers());
const int K = Chess960 ? kto > kfrom ? -1 : 1 const int K = Chess960 ? kto > kfrom ? -1 : 1
: Side == KING_SIDE ? -1 : 1; : Side == KING_SIDE ? -1 : 1;
@@ -249,41 +249,38 @@ namespace {
} }
FORCE_INLINE MoveStack* generate_king_moves(const Position& pos, MoveStack* mlist,
Color us, Bitboard target) {
Square from = pos.king_square(us);
Bitboard b = pos.attacks_from<KING>(from) & target;
SERIALIZE(b);
return mlist;
}
template<GenType Type> FORCE_INLINE template<GenType Type> FORCE_INLINE
MoveStack* generate_all_moves(const Position& pos, MoveStack* mlist, Color us, MoveStack* generate_all(const Position& pos, MoveStack* mlist, Color us,
Bitboard target, const CheckInfo* ci = NULL) { Bitboard target, const CheckInfo* ci = NULL) {
const bool Checks = Type == QUIET_CHECKS;
mlist = (us == WHITE ? generate_pawn_moves<WHITE, Type>(pos, mlist, target, ci) mlist = (us == WHITE ? generate_pawn_moves<WHITE, Type>(pos, mlist, target, ci)
: generate_pawn_moves<BLACK, Type>(pos, mlist, target, ci)); : generate_pawn_moves<BLACK, Type>(pos, mlist, target, ci));
mlist = generate_moves<KNIGHT, Type == QUIET_CHECKS>(pos, mlist, us, target, ci); mlist = generate_moves<KNIGHT, Checks>(pos, mlist, us, target, ci);
mlist = generate_moves<BISHOP, Type == QUIET_CHECKS>(pos, mlist, us, target, ci); mlist = generate_moves<BISHOP, Checks>(pos, mlist, us, target, ci);
mlist = generate_moves<ROOK, Type == QUIET_CHECKS>(pos, mlist, us, target, ci); mlist = generate_moves<ROOK, Checks>(pos, mlist, us, target, ci);
mlist = generate_moves<QUEEN, Type == QUIET_CHECKS>(pos, mlist, us, target, ci); mlist = generate_moves<QUEEN, Checks>(pos, mlist, us, target, ci);
if (Type != QUIET_CHECKS && Type != EVASIONS) if (Type != QUIET_CHECKS && Type != EVASIONS)
mlist = generate_king_moves(pos, mlist, us, target); {
Square from = pos.king_square(us);
Bitboard b = pos.attacks_from<KING>(from) & target;
SERIALIZE(b);
}
if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(us)) if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(us))
{ {
if (pos.is_chess960()) if (pos.is_chess960())
{ {
mlist = generate_castle<KING_SIDE, Type == QUIET_CHECKS, true>(pos, mlist, us); mlist = generate_castle<KING_SIDE, Checks, true>(pos, mlist, us);
mlist = generate_castle<QUEEN_SIDE, Type == QUIET_CHECKS, true>(pos, mlist, us); mlist = generate_castle<QUEEN_SIDE, Checks, true>(pos, mlist, us);
} }
else else
{ {
mlist = generate_castle<KING_SIDE, Type == QUIET_CHECKS, false>(pos, mlist, us); mlist = generate_castle<KING_SIDE, Checks, false>(pos, mlist, us);
mlist = generate_castle<QUEEN_SIDE, Type == QUIET_CHECKS, false>(pos, mlist, us); mlist = generate_castle<QUEEN_SIDE, Checks, false>(pos, mlist, us);
} }
} }
@@ -307,21 +304,15 @@ template<GenType Type>
MoveStack* generate(const Position& pos, MoveStack* mlist) { MoveStack* generate(const Position& pos, MoveStack* mlist) {
assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS); assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS);
assert(!pos.in_check()); assert(!pos.checkers());
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Bitboard target;
if (Type == CAPTURES) Bitboard target = Type == CAPTURES ? pos.pieces(~us)
target = pos.pieces(~us); : Type == QUIETS ? ~pos.pieces()
: Type == NON_EVASIONS ? ~pos.pieces(us) : 0;
else if (Type == QUIETS) return generate_all<Type>(pos, mlist, us, target);
target = ~pos.pieces();
else if (Type == NON_EVASIONS)
target = ~pos.pieces(us);
return generate_all_moves<Type>(pos, mlist, us, target);
} }
// Explicit template instantiations // Explicit template instantiations
@@ -335,9 +326,8 @@ template MoveStack* generate<NON_EVASIONS>(const Position&, MoveStack*);
template<> template<>
MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) { MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
assert(!pos.in_check()); assert(!pos.checkers());
Color us = pos.side_to_move();
CheckInfo ci(pos); CheckInfo ci(pos);
Bitboard dc = ci.dcCandidates; Bitboard dc = ci.dcCandidates;
@@ -357,7 +347,7 @@ MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
SERIALIZE(b); SERIALIZE(b);
} }
return generate_all_moves<QUIET_CHECKS>(pos, mlist, us, ~pos.pieces(), &ci); return generate_all<QUIET_CHECKS>(pos, mlist, pos.side_to_move(), ~pos.pieces(), &ci);
} }
@@ -366,7 +356,7 @@ MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
template<> template<>
MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) { MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) {
assert(pos.in_check()); assert(pos.checkers());
Square from, checksq; Square from, checksq;
int checkersCnt = 0; int checkersCnt = 0;
@@ -419,7 +409,7 @@ MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) {
// Generate blocking evasions or captures of the checking piece // Generate blocking evasions or captures of the checking piece
Bitboard target = between_bb(checksq, ksq) | pos.checkers(); Bitboard target = between_bb(checksq, ksq) | pos.checkers();
return generate_all_moves<EVASIONS>(pos, mlist, us, target); return generate_all<EVASIONS>(pos, mlist, us, target);
} }
@@ -432,7 +422,7 @@ MoveStack* generate<LEGAL>(const Position& pos, MoveStack* mlist) {
Bitboard pinned = pos.pinned_pieces(); Bitboard pinned = pos.pinned_pieces();
Square ksq = pos.king_square(pos.side_to_move()); Square ksq = pos.king_square(pos.side_to_move());
end = pos.in_check() ? generate<EVASIONS>(pos, mlist) end = pos.checkers() ? generate<EVASIONS>(pos, mlist)
: generate<NON_EVASIONS>(pos, mlist); : generate<NON_EVASIONS>(pos, mlist);
while (cur != end) while (cur != end)
if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT) if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -46,6 +46,10 @@ struct MoveList {
bool end() const { return cur == last; } bool end() const { return cur == last; }
Move move() const { return cur->move; } Move move() const { return cur->move; }
size_t size() const { return last - mlist; } size_t size() const { return last - mlist; }
bool contains(Move m) const {
for (const MoveStack* it(mlist); it != last; ++it) if (it->move == m) return true;
return false;
}
private: private:
MoveStack mlist[MAX_MOVES]; MoveStack mlist[MAX_MOVES];

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,10 +18,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include "movegen.h"
#include "movepick.h" #include "movepick.h"
#include "thread.h" #include "thread.h"
@@ -37,6 +35,20 @@ namespace {
STOP STOP
}; };
// Our insertion sort, guaranteed to be stable, as is needed
void insertion_sort(MoveStack* begin, MoveStack* end)
{
MoveStack tmp, *p, *q;
for (p = begin + 1; p < end; ++p)
{
tmp = *p;
for (q = p; q != begin && *(q-1) < tmp; --q)
*q = *(q-1);
*q = tmp;
}
}
// Unary predicate used by std::partition to split positive scores from remaining // Unary predicate used by std::partition to split positive scores from remaining
// ones so to sort separately the two sets, and with the second sort delayed. // ones so to sort separately the two sets, and with the second sort delayed.
inline bool has_positive_score(const MoveStack& ms) { return ms.score > 0; } inline bool has_positive_score(const MoveStack& ms) { return ms.score > 0; }
@@ -59,7 +71,7 @@ namespace {
/// move ordering is at the current node. /// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
Search::Stack* s, Value beta) : pos(p), H(h), depth(d) { Search::Stack* s, Value beta) : pos(p), Hist(h), depth(d) {
assert(d > DEPTH_ZERO); assert(d > DEPTH_ZERO);
@@ -68,7 +80,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
endBadCaptures = moves + MAX_MOVES - 1; endBadCaptures = moves + MAX_MOVES - 1;
ss = s; ss = s;
if (p.in_check()) if (p.checkers())
phase = EVASION; phase = EVASION;
else else
@@ -79,12 +91,12 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
killers[1].move = ss->killers[1]; killers[1].move = ss->killers[1];
// Consider sligtly negative captures as good if at low depth and far from beta // Consider sligtly negative captures as good if at low depth and far from beta
if (ss && ss->eval < beta - PawnValueMg && d < 3 * ONE_PLY) if (ss && ss->staticEval < beta - PawnValueMg && d < 3 * ONE_PLY)
captureThreshold = -PawnValueMg; captureThreshold = -PawnValueMg;
// Consider negative captures as good if still enough to reach beta // Consider negative captures as good if still enough to reach beta
else if (ss && ss->eval > beta) else if (ss && ss->staticEval > beta)
captureThreshold = beta - ss->eval; captureThreshold = beta - ss->staticEval;
} }
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
@@ -92,11 +104,11 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
} }
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
Square sq) : pos(p), H(h), cur(moves), end(moves) { Square sq) : pos(p), Hist(h), cur(moves), end(moves) {
assert(d <= DEPTH_ZERO); assert(d <= DEPTH_ZERO);
if (p.in_check()) if (p.checkers())
phase = EVASION; phase = EVASION;
else if (d > DEPTH_QS_NO_CHECKS) else if (d > DEPTH_QS_NO_CHECKS)
@@ -124,14 +136,14 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
} }
MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType pt) MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType pt)
: pos(p), H(h), cur(moves), end(moves) { : pos(p), Hist(h), cur(moves), end(moves) {
assert(!pos.in_check()); assert(!pos.checkers());
phase = PROBCUT; phase = PROBCUT;
// In ProbCut we generate only captures better than parent's captured piece // In ProbCut we generate only captures better than parent's captured piece
captureThreshold = PieceValue[Mg][pt]; captureThreshold = PieceValue[MG][pt];
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold)) if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold))
@@ -141,12 +153,10 @@ MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType
} }
/// MovePicker::score_captures(), MovePicker::score_noncaptures() and /// score() assign a numerical move ordering score to each move in a move list.
/// MovePicker::score_evasions() assign a numerical move ordering score /// The moves with highest scores will be picked first.
/// to each move in a move list. The moves with highest scores will be template<>
/// picked first by next_move(). void MovePicker::score<CAPTURES>() {
void MovePicker::score_captures() {
// Winning and equal captures in the main search are ordered by MVV/LVA. // Winning and equal captures in the main search are ordered by MVV/LVA.
// Suprisingly, this appears to perform slightly better than SEE based // Suprisingly, this appears to perform slightly better than SEE based
// move ordering. The reason is probably that in a position with a winning // move ordering. The reason is probably that in a position with a winning
@@ -165,51 +175,54 @@ void MovePicker::score_captures() {
for (MoveStack* it = moves; it != end; ++it) for (MoveStack* it = moves; it != end; ++it)
{ {
m = it->move; m = it->move;
it->score = PieceValue[Mg][pos.piece_on(to_sq(m))] it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
- type_of(pos.piece_moved(m)); - type_of(pos.piece_moved(m));
if (type_of(m) == PROMOTION) if (type_of(m) == PROMOTION)
it->score += PieceValue[Mg][promotion_type(m)]; it->score += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN];
else if (type_of(m) == ENPASSANT)
it->score += PieceValue[MG][PAWN];
} }
} }
void MovePicker::score_noncaptures() { template<>
void MovePicker::score<QUIETS>() {
Move m; Move m;
for (MoveStack* it = moves; it != end; ++it) for (MoveStack* it = moves; it != end; ++it)
{ {
m = it->move; m = it->move;
it->score = H.value(pos.piece_moved(m), to_sq(m)); it->score = Hist[pos.piece_moved(m)][to_sq(m)];
} }
} }
void MovePicker::score_evasions() { template<>
void MovePicker::score<EVASIONS>() {
// Try good captures ordered by MVV/LVA, then non-captures if destination square // Try good captures ordered by MVV/LVA, then non-captures if destination square
// is not under attack, ordered by history value, then bad-captures and quiet // is not under attack, ordered by history value, then bad-captures and quiet
// moves with a negative SEE. This last group is ordered by the SEE score. // moves with a negative SEE. This last group is ordered by the SEE score.
Move m; Move m;
int seeScore; int seeScore;
if (end < moves + 2)
return;
for (MoveStack* it = moves; it != end; ++it) for (MoveStack* it = moves; it != end; ++it)
{ {
m = it->move; m = it->move;
if ((seeScore = pos.see_sign(m)) < 0) if ((seeScore = pos.see_sign(m)) < 0)
it->score = seeScore - History::MaxValue; // Be sure we are at the bottom it->score = seeScore - History::Max; // At the bottom
else if (pos.is_capture(m)) else if (pos.is_capture(m))
it->score = PieceValue[Mg][pos.piece_on(to_sq(m))] it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
- type_of(pos.piece_moved(m)) + History::MaxValue; - type_of(pos.piece_moved(m)) + History::Max;
else else
it->score = H.value(pos.piece_moved(m), to_sq(m)); it->score = Hist[pos.piece_moved(m)][to_sq(m)];
} }
} }
/// MovePicker::generate_next() generates, scores and sorts the next bunch of moves, /// generate_next() generates, scores and sorts the next bunch of moves, when
/// when there are no more moves to try for the current phase. /// there are no more moves to try for the current phase.
void MovePicker::generate_next() { void MovePicker::generate_next() {
@@ -219,7 +232,7 @@ void MovePicker::generate_next() {
case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6: case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6:
end = generate<CAPTURES>(pos, moves); end = generate<CAPTURES>(pos, moves);
score_captures(); score<CAPTURES>();
return; return;
case KILLERS_S1: case KILLERS_S1:
@@ -229,16 +242,16 @@ void MovePicker::generate_next() {
case QUIETS_1_S1: case QUIETS_1_S1:
endQuiets = end = generate<QUIETS>(pos, moves); endQuiets = end = generate<QUIETS>(pos, moves);
score_noncaptures(); score<QUIETS>();
end = std::partition(cur, end, has_positive_score); end = std::partition(cur, end, has_positive_score);
sort<MoveStack>(cur, end); insertion_sort(cur, end);
return; return;
case QUIETS_2_S1: case QUIETS_2_S1:
cur = end; cur = end;
end = endQuiets; end = endQuiets;
if (depth >= 3 * ONE_PLY) if (depth >= 3 * ONE_PLY)
sort<MoveStack>(cur, end); insertion_sort(cur, end);
return; return;
case BAD_CAPTURES_S1: case BAD_CAPTURES_S1:
@@ -249,7 +262,8 @@ void MovePicker::generate_next() {
case EVASIONS_S2: case EVASIONS_S2:
end = generate<EVASIONS>(pos, moves); end = generate<EVASIONS>(pos, moves);
score_evasions(); if (end > moves + 1)
score<EVASIONS>();
return; return;
case QUIET_CHECKS_S3: case QUIET_CHECKS_S3:
@@ -268,11 +282,10 @@ void MovePicker::generate_next() {
} }
/// MovePicker::next_move() is the most important method of the MovePicker class. /// next_move() is the most important method of the MovePicker class. It returns
/// It returns a new pseudo legal move every time it is called, until there /// a new pseudo legal move every time is called, until there are no more moves
/// are no more moves left. It picks the move with the biggest score from a list /// left. It picks the move with the biggest score from a list of generated moves
/// of generated moves taking care not to return the tt move if has already been /// taking care not returning the ttMove if has already been searched previously.
/// searched previously.
template<> template<>
Move MovePicker::next_move<false>() { Move MovePicker::next_move<false>() {
@@ -359,6 +372,6 @@ Move MovePicker::next_move<false>() {
/// Version of next_move() to use at split point nodes where the move is grabbed /// Version of next_move() to use at split point nodes where the move is grabbed
/// from the split point's shared MovePicker object. This function is not thread /// from the split point's shared MovePicker object. This function is not thread
/// safe so should be lock protected by the caller. /// safe so must be lock protected by the caller.
template<> template<>
Move MovePicker::next_move<true>() { return ss->sp->mp->next_move<false>(); } Move MovePicker::next_move<true>() { return ss->splitPoint->movePicker->next_move<false>(); }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,12 +20,48 @@
#if !defined MOVEPICK_H_INCLUDED #if !defined MOVEPICK_H_INCLUDED
#define MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED
#include "history.h" #include <algorithm> // For std::max
#include <cstring> // For memset
#include "movegen.h"
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "types.h" #include "types.h"
/// The Stats struct stores moves statistics. According to the template parameter
/// the class can store both History and Gains type statistics. History records
/// how often different moves have been successful or unsuccessful during the
/// current search and is used for reduction and move ordering decisions. Gains
/// records the move's best evaluation gain from one ply to the next and is used
/// for pruning decisions. Entries are stored according only to moving piece and
/// destination square, in particular two moves with different origin but same
/// destination and same piece will be considered identical.
template<bool Gain>
struct Stats {
static const Value Max = Value(2000);
const Value* operator[](Piece p) const { return &table[p][0]; }
void clear() { memset(table, 0, sizeof(table)); }
void update(Piece p, Square to, Value v) {
if (Gain)
table[p][to] = std::max(v, table[p][to] - 1);
else if (abs(table[p][to] + v) < Max)
table[p][to] += v;
}
private:
Value table[PIECE_NB][SQUARE_NB];
};
typedef Stats<false> History;
typedef Stats<true> Gains;
/// MovePicker class is used to pick one pseudo legal move at a time from the /// MovePicker class is used to pick one pseudo legal move at a time from the
/// current position. The most important method is next_move(), which returns a /// current position. The most important method is next_move(), which returns a
/// new pseudo legal move each time it is called, until there are no moves left, /// new pseudo legal move each time it is called, until there are no moves left,
@@ -44,13 +80,11 @@ public:
template<bool SpNode> Move next_move(); template<bool SpNode> Move next_move();
private: private:
void score_captures(); template<GenType> void score();
void score_noncaptures();
void score_evasions();
void generate_next(); void generate_next();
const Position& pos; const Position& pos;
const History& H; const History& Hist;
Search::Stack* ss; Search::Stack* ss;
Depth depth; Depth depth;
Move ttMove; Move ttMove;

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,7 +20,7 @@
#include <cassert> #include <cassert>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <string> #include <stack>
#include "movegen.h" #include "movegen.h"
#include "notation.h" #include "notation.h"
@@ -28,7 +28,7 @@
using namespace std; using namespace std;
static const char* PieceToChar = " PNBRQK pnbrqk"; static const char* PieceToChar[COLOR_NB] = { " PNBRQK", " pnbrqk" };
/// score_to_uci() converts a value to a string suitable for use with the UCI /// score_to_uci() converts a value to a string suitable for use with the UCI
@@ -75,7 +75,7 @@ const string move_to_uci(Move m, bool chess960) {
string move = square_to_string(from) + square_to_string(to); string move = square_to_string(from) + square_to_string(to);
if (type_of(m) == PROMOTION) if (type_of(m) == PROMOTION)
move += PieceToChar[make_piece(BLACK, promotion_type(m))]; // Lower case move += PieceToChar[BLACK][promotion_type(m)]; // Lower case
return move; return move;
} }
@@ -108,10 +108,9 @@ const string move_to_san(Position& pos, Move m) {
if (m == MOVE_NULL) if (m == MOVE_NULL)
return "(null)"; return "(null)";
assert(pos.move_is_legal(m)); assert(MoveList<LEGAL>(pos).contains(m));
Bitboard attackers; Bitboard others, b;
bool ambiguousMove, ambiguousFile, ambiguousRank;
string san; string san;
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Square from = from_sq(m); Square from = from_sq(m);
@@ -125,33 +124,25 @@ const string move_to_san(Position& pos, Move m) {
{ {
if (pt != PAWN) if (pt != PAWN)
{ {
san = PieceToChar[pt]; // Upper case san = PieceToChar[WHITE][pt]; // Upper case
// Disambiguation if we have more then one piece with destination 'to' // Disambiguation if we have more then one piece of type 'pt' that can
// note that for pawns is not needed because starting file is explicit. // reach 'to' with a legal move.
ambiguousMove = ambiguousFile = ambiguousRank = false; others = b = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from;
attackers = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from; while (b)
while (attackers)
{ {
Square sq = pop_lsb(&attackers); Move move = make_move(pop_lsb(&b), to);
if (!pos.pl_move_is_legal(move, pos.pinned_pieces()))
// Pinned pieces are not included in the possible sub-set others ^= from_sq(move);
if (!pos.pl_move_is_legal(make_move(sq, to), pos.pinned_pieces()))
continue;
ambiguousFile |= file_of(sq) == file_of(from);
ambiguousRank |= rank_of(sq) == rank_of(from);
ambiguousMove = true;
} }
if (ambiguousMove) if (others)
{ {
if (!ambiguousFile) if (!(others & file_bb(from)))
san += file_to_char(file_of(from)); san += file_to_char(file_of(from));
else if (!ambiguousRank) else if (!(others & rank_bb(from)))
san += rank_to_char(rank_of(from)); san += rank_to_char(rank_of(from));
else else
@@ -167,7 +158,7 @@ const string move_to_san(Position& pos, Move m) {
san += square_to_string(to); san += square_to_string(to);
if (type_of(m) == PROMOTION) if (type_of(m) == PROMOTION)
san += string("=") + PieceToChar[promotion_type(m)]; san += string("=") + PieceToChar[WHITE][promotion_type(m)];
} }
if (pos.move_gives_check(m, CheckInfo(pos))) if (pos.move_gives_check(m, CheckInfo(pos)))
@@ -226,7 +217,7 @@ string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]
const int64_t K = 1000; const int64_t K = 1000;
const int64_t M = 1000000; const int64_t M = 1000000;
StateInfo state[MAX_PLY_PLUS_2], *st = state; std::stack<StateInfo> st;
Move* m = pv; Move* m = pv;
string san, padding; string san, padding;
size_t length; size_t length;
@@ -261,7 +252,8 @@ string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]
s << san << ' '; s << san << ' ';
length += san.length() + 1; length += san.length() + 1;
pos.do_move(*m++, *st++); st.push(StateInfo());
pos.do_move(*m++, st.top());
} }
while (m != pv) while (m != pv)

View File

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

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -30,34 +30,34 @@ namespace {
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
// Doubled pawn penalty by opposed flag and file // Doubled pawn penalty by opposed flag and file
const Score DoubledPawnPenalty[2][8] = { const Score DoubledPawnPenalty[2][FILE_NB] = {
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48), { S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }, S(23, 48), S(23, 48), S(20, 48), S(13, 43) },
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48), { S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }}; S(23, 48), S(23, 48), S(20, 48), S(13, 43) }};
// Isolated pawn penalty by opposed flag and file // Isolated pawn penalty by opposed flag and file
const Score IsolatedPawnPenalty[2][8] = { const Score IsolatedPawnPenalty[2][FILE_NB] = {
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52), { S(37, 45), S(54, 52), S(60, 52), S(60, 52),
S(60, 52), S(60, 52), S(54, 52), S(37, 45) }, S(60, 52), S(60, 52), S(54, 52), S(37, 45) },
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35), { S(25, 30), S(36, 35), S(40, 35), S(40, 35),
S(40, 35), S(40, 35), S(36, 35), S(25, 30) }}; S(40, 35), S(40, 35), S(36, 35), S(25, 30) }};
// Backward pawn penalty by opposed flag and file // Backward pawn penalty by opposed flag and file
const Score BackwardPawnPenalty[2][8] = { const Score BackwardPawnPenalty[2][FILE_NB] = {
{ S(30, 42), S(43, 46), S(49, 46), S(49, 46), { S(30, 42), S(43, 46), S(49, 46), S(49, 46),
S(49, 46), S(49, 46), S(43, 46), S(30, 42) }, S(49, 46), S(49, 46), S(43, 46), S(30, 42) },
{ S(20, 28), S(29, 31), S(33, 31), S(33, 31), { S(20, 28), S(29, 31), S(33, 31), S(33, 31),
S(33, 31), S(33, 31), S(29, 31), S(20, 28) }}; S(33, 31), S(33, 31), S(29, 31), S(20, 28) }};
// Pawn chain membership bonus by file // Pawn chain membership bonus by file
const Score ChainBonus[8] = { const Score ChainBonus[FILE_NB] = {
S(11,-1), S(13,-1), S(13,-1), S(14,-1), S(11,-1), S(13,-1), S(13,-1), S(14,-1),
S(14,-1), S(13,-1), S(13,-1), S(11,-1) S(14,-1), S(13,-1), S(13,-1), S(11,-1)
}; };
// Candidate passed pawn bonus by rank // Candidate passed pawn bonus by rank
const Score CandidateBonus[8] = { const Score CandidateBonus[RANK_NB] = {
S( 0, 0), S( 6, 13), S(6,13), S(14,29), S( 0, 0), S( 6, 13), S(6,13), S(14,29),
S(34,68), S(83,166), S(0, 0), S( 0, 0) S(34,68), S(83,166), S(0, 0), S( 0, 0)
}; };
@@ -65,12 +65,12 @@ namespace {
const Score PawnStructureWeight = S(233, 201); const Score PawnStructureWeight = S(233, 201);
// Weakness of our pawn shelter in front of the king indexed by [king pawn][rank] // Weakness of our pawn shelter in front of the king indexed by [king pawn][rank]
const Value ShelterWeakness[2][8] = const Value ShelterWeakness[2][RANK_NB] =
{ { V(141), V(0), V(38), V(102), V(128), V(141), V(141) }, { { V(141), V(0), V(38), V(102), V(128), V(141), V(141) },
{ V( 61), V(0), V(16), V( 44), V( 56), V( 61), V( 61) } }; { V( 61), V(0), V(16), V( 44), V( 56), V( 61), V( 61) } };
// Danger of enemy pawns moving toward our king indexed by [pawn blocked][rank] // Danger of enemy pawns moving toward our king indexed by [pawn blocked][rank]
const Value StormDanger[2][8] = const Value StormDanger[2][RANK_NB] =
{ { V(26), V(0), V(128), V(51), V(26) }, { { V(26), V(0), V(128), V(51), V(26) },
{ V(13), V(0), V( 64), V(25), V(13) } }; { V(13), V(0), V( 64), V(25), V(13) } };
@@ -80,49 +80,10 @@ namespace {
#undef S #undef S
#undef V #undef V
}
/// PawnTable::probe() takes a position object as input, computes a PawnEntry
/// object, and returns a pointer to it. The result is also stored in a hash
/// table, so we don't have to recompute everything when the same pawn structure
/// occurs again.
PawnEntry* PawnTable::probe(const Position& pos) {
Key key = pos.pawn_key();
PawnEntry* 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);
return e;
}
/// PawnTable::evaluate_pawns() evaluates each pawn of the given color
template<Color Us> template<Color Us>
Score PawnTable::evaluate_pawns(const Position& pos, Bitboard ourPawns, Score evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnEntry* e) { Bitboard theirPawns, Pawns::Entry* e) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -215,15 +176,57 @@ Score PawnTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
value += CandidateBonus[relative_rank(Us, s)]; value += CandidateBonus[relative_rank(Us, s)];
} }
e->pawnsOnSquares[Us][BLACK] = popcount<Max15>(ourPawns & BlackSquares);
e->pawnsOnSquares[Us][WHITE] = pos.piece_count(Us, PAWN) - e->pawnsOnSquares[Us][BLACK];
e->pawnsOnSquares[Them][BLACK] = popcount<Max15>(theirPawns & BlackSquares);
e->pawnsOnSquares[Them][WHITE] = pos.piece_count(Them, PAWN) - e->pawnsOnSquares[Them][BLACK];
return value; return value;
} }
}
namespace Pawns {
/// probe() takes a position object as input, computes a Entry object, and returns
/// a pointer to it. The result is also stored in a hash table, so we don't have
/// to recompute everything when the same pawn structure occurs again.
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);
return e;
}
/// PawnEntry::shelter_storm() calculates shelter and storm penalties for the file /// Entry::shelter_storm() calculates shelter and storm penalties for the file
/// the king is on, as well as the two adjacent files. /// the king is on, as well as the two adjacent files.
template<Color Us> template<Color Us>
Value PawnEntry::shelter_storm(const Position& pos, Square ksq) { Value Entry::shelter_storm(const Position& pos, Square ksq) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -234,7 +237,7 @@ Value PawnEntry::shelter_storm(const Position& pos, Square ksq) {
Rank rkUs, rkThem; Rank rkUs, rkThem;
File kf = file_of(ksq); File kf = file_of(ksq);
kf = (kf == FILE_A) ? kf++ : (kf == FILE_H) ? kf-- : kf; kf = (kf == FILE_A) ? FILE_B : (kf == FILE_H) ? FILE_G : kf;
for (int f = kf - 1; f <= kf + 1; f++) for (int f = kf - 1; f <= kf + 1; f++)
{ {
@@ -253,11 +256,11 @@ Value PawnEntry::shelter_storm(const Position& pos, Square ksq) {
} }
/// PawnEntry::update_safety() calculates and caches a bonus for king safety. It is /// Entry::update_safety() calculates and caches a bonus for king safety. It is
/// called only when king square changes, about 20% of total king_safety() calls. /// called only when king square changes, about 20% of total king_safety() calls.
template<Color Us> template<Color Us>
Score PawnEntry::update_safety(const Position& pos, Square ksq) { Score Entry::update_safety(const Position& pos, Square ksq) {
kingSquares[Us] = ksq; kingSquares[Us] = ksq;
castleRights[Us] = pos.can_castle(Us); castleRights[Us] = pos.can_castle(Us);
@@ -283,5 +286,7 @@ Score PawnEntry::update_safety(const Position& pos, Square ksq) {
} }
// Explicit template instantiation // Explicit template instantiation
template Score PawnEntry::update_safety<WHITE>(const Position& pos, Square ksq); template Score Entry::update_safety<WHITE>(const Position& pos, Square ksq);
template Score PawnEntry::update_safety<BLACK>(const Position& pos, Square ksq); template Score Entry::update_safety<BLACK>(const Position& pos, Square ksq);
} // namespace Pawns

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -24,31 +24,31 @@
#include "position.h" #include "position.h"
#include "types.h" #include "types.h"
const int PawnTableSize = 16384; namespace Pawns {
/// PawnEntry is a class which contains various information about a pawn /// Pawns::Entry contains various information about a pawn structure. Currently,
/// structure. Currently, it only includes a middle game and an end game /// it only includes a middle game and end game pawn structure evaluation, and a
/// pawn structure evaluation, and a bitboard of passed pawns. We may want /// bitboard of passed pawns. We may want to add further information in the future.
/// to add further information in the future. A lookup to the pawn hash /// A lookup to the pawn hash table (performed by calling the probe function)
/// table (performed by calling the probe method in a PawnTable object) /// returns a pointer to an Entry object.
/// returns a pointer to a PawnEntry object.
class PawnEntry { struct Entry {
friend struct PawnTable; Score pawns_value() const { return value; }
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
public: Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
Score pawns_value() const; int file_is_half_open(Color c, File f) const { return halfOpenFiles[c] & (1 << int(f)); }
Bitboard pawn_attacks(Color c) const; int has_open_file_to_left(Color c, File f) const { return halfOpenFiles[c] & ((1 << int(f)) - 1); }
Bitboard passed_pawns(Color c) const; int has_open_file_to_right(Color c, File f) const { return halfOpenFiles[c] & ~((1 << int(f+1)) - 1); }
int file_is_half_open(Color c, File f) const; int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(BlackSquares & s)]; }
int has_open_file_to_left(Color c, File f) const;
int has_open_file_to_right(Color c, File f) const;
template<Color Us> template<Color Us>
Score king_safety(const Position& pos, Square ksq); Score king_safety(const Position& pos, Square ksq) {
return kingSquares[Us] == ksq && castleRights[Us] == pos.can_castle(Us)
? kingSafety[Us] : update_safety<Us>(pos, ksq);
}
private:
template<Color Us> template<Color Us>
Score update_safety(const Position& pos, Square ksq); Score update_safety(const Position& pos, Square ksq);
@@ -56,60 +56,21 @@ private:
Value shelter_storm(const Position& pos, Square ksq); Value shelter_storm(const Position& pos, Square ksq);
Key key; Key key;
Bitboard passedPawns[2]; Bitboard passedPawns[COLOR_NB];
Bitboard pawnAttacks[2]; Bitboard pawnAttacks[COLOR_NB];
Square kingSquares[2]; Square kingSquares[COLOR_NB];
int minKPdistance[2]; int minKPdistance[COLOR_NB];
int castleRights[2]; int castleRights[COLOR_NB];
Score value; Score value;
int halfOpenFiles[2]; int halfOpenFiles[COLOR_NB];
Score kingSafety[2]; Score kingSafety[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB];
}; };
typedef HashTable<Entry, 16384> Table;
/// The PawnTable class represents a pawn hash table. The most important Entry* probe(const Position& pos, Table& entries);
/// method is probe, which returns a pointer to a PawnEntry object.
struct PawnTable {
PawnEntry* probe(const Position& pos);
template<Color Us>
static Score evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnEntry* e);
HashTable<PawnEntry, PawnTableSize> entries;
};
inline Score PawnEntry::pawns_value() const {
return value;
}
inline Bitboard PawnEntry::pawn_attacks(Color c) const {
return pawnAttacks[c];
}
inline Bitboard PawnEntry::passed_pawns(Color c) const {
return passedPawns[c];
}
inline int PawnEntry::file_is_half_open(Color c, File f) const {
return halfOpenFiles[c] & (1 << int(f));
}
inline int PawnEntry::has_open_file_to_left(Color c, File f) const {
return halfOpenFiles[c] & ((1 << int(f)) - 1);
}
inline int PawnEntry::has_open_file_to_right(Color c, File f) const {
return halfOpenFiles[c] & ~((1 << int(f+1)) - 1);
}
template<Color Us>
inline Score PawnEntry::king_safety(const Position& pos, Square ksq) {
return kingSquares[Us] == ksq && castleRights[Us] == pos.can_castle(Us)
? kingSafety[Us] : update_safety<Us>(pos, ksq);
} }
#endif // !defined(PAWNS_H_INCLUDED) #endif // !defined(PAWNS_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -40,6 +40,7 @@ typedef unsigned __int64 uint64_t;
#else #else
# include <inttypes.h> # include <inttypes.h>
# include <unistd.h> // Used by sysconf(_SC_NPROCESSORS_ONLN)
#endif #endif
#if !defined(_WIN32) && !defined(_WIN64) // Linux - Unix #if !defined(_WIN32) && !defined(_WIN64) // Linux - Unix
@@ -92,6 +93,9 @@ typedef CRITICAL_SECTION Lock;
typedef HANDLE WaitCondition; typedef HANDLE WaitCondition;
typedef HANDLE NativeHandle; typedef HANDLE NativeHandle;
// On Windows 95 and 98 parameter lpThreadId my not be null
inline DWORD* dwWin9xKludge() { static DWORD dw; return &dw; }
# define lock_init(x) InitializeCriticalSection(&(x)) # define lock_init(x) InitializeCriticalSection(&(x))
# define lock_grab(x) EnterCriticalSection(&(x)) # define lock_grab(x) EnterCriticalSection(&(x))
# define lock_release(x) LeaveCriticalSection(&(x)) # define lock_release(x) LeaveCriticalSection(&(x))
@@ -101,7 +105,7 @@ typedef HANDLE NativeHandle;
# define cond_signal(x) SetEvent(x) # define cond_signal(x) SetEvent(x)
# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(x, INFINITE); lock_grab(y); } # define cond_wait(x,y) { lock_release(y); WaitForSingleObject(x, INFINITE); lock_grab(y); }
# define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(x,z); lock_grab(y); } # define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(x,z); lock_grab(y); }
# define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,NULL), x != NULL) # define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,dwWin9xKludge()), x != NULL)
# define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); } # define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); }
#endif #endif

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <iomanip>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <algorithm> #include <algorithm>
@@ -40,16 +41,16 @@ static const string PieceToChar(" PNBRQK pnbrqk");
CACHE_LINE_ALIGNMENT CACHE_LINE_ALIGNMENT
Score pieceSquareTable[16][64]; // [piece][square] Score pieceSquareTable[PIECE_NB][SQUARE_NB];
Value PieceValue[2][18] = { // [Mg / Eg][piece / pieceType] Value PieceValue[PHASE_NB][PIECE_NB] = {
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } }; { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
namespace Zobrist { namespace Zobrist {
Key psq[2][8][64]; // [color][pieceType][square / piece count] Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
Key enpassant[8]; // [file] Key enpassant[FILE_NB];
Key castle[16]; // [castleRight] Key castle[CASTLE_RIGHT_NB];
Key side; Key side;
Key exclusion; Key exclusion;
@@ -86,10 +87,10 @@ void init() {
for (PieceType pt = PAWN; pt <= KING; pt++) for (PieceType pt = PAWN; pt <= KING; pt++)
{ {
PieceValue[Mg][make_piece(BLACK, pt)] = PieceValue[Mg][pt]; PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt];
PieceValue[Eg][make_piece(BLACK, pt)] = PieceValue[Eg][pt]; PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt];
Score v = make_score(PieceValue[Mg][pt], PieceValue[Eg][pt]); Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]);
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; s++)
{ {
@@ -172,11 +173,11 @@ Position& Position::operator=(const Position& pos) {
} }
/// Position::from_fen() initializes the position object with the given FEN /// Position::set() initializes the position object with the given FEN string.
/// string. This function is not very robust - make sure that input FENs are /// This function is not very robust - make sure that input FENs are correct,
/// correct (this is assumed to be the responsibility of the GUI). /// this is assumed to be the responsibility of the GUI.
void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) { void Position::set(const string& fenStr, bool isChess960, Thread* th) {
/* /*
A FEN string defines a particular position using only the ASCII character set. A FEN string defines a particular position using only the ASCII character set.
@@ -214,13 +215,13 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
char col, row, token; char col, row, token;
size_t p; size_t p;
Square sq = SQ_A8; Square sq = SQ_A8;
std::istringstream fen(fenStr); std::istringstream ss(fenStr);
clear(); clear();
fen >> std::noskipws; ss >> std::noskipws;
// 1. Piece placement // 1. Piece placement
while ((fen >> token) && !isspace(token)) while ((ss >> token) && !isspace(token))
{ {
if (isdigit(token)) if (isdigit(token))
sq += Square(token - '0'); // Advance the given number of files sq += Square(token - '0'); // Advance the given number of files
@@ -236,16 +237,16 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
} }
// 2. Active color // 2. Active color
fen >> token; ss >> token;
sideToMove = (token == 'w' ? WHITE : BLACK); sideToMove = (token == 'w' ? WHITE : BLACK);
fen >> token; ss >> token;
// 3. Castling availability. Compatible with 3 standards: Normal FEN standard, // 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
// Shredder-FEN that uses the letters of the columns on which the rooks began // Shredder-FEN that uses the letters of the columns on which the rooks began
// the game instead of KQkq and also X-FEN standard that, in case of Chess960, // the game instead of KQkq and also X-FEN standard that, in case of Chess960,
// if an inner rook is associated with the castling right, the castling tag is // if an inner rook is associated with the castling right, the castling tag is
// replaced by the file letter of the involved rook, as for the Shredder-FEN. // replaced by the file letter of the involved rook, as for the Shredder-FEN.
while ((fen >> token) && !isspace(token)) while ((ss >> token) && !isspace(token))
{ {
Square rsq; Square rsq;
Color c = islower(token) ? BLACK : WHITE; Color c = islower(token) ? BLACK : WHITE;
@@ -268,8 +269,8 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
} }
// 4. En passant square. Ignore if no pawn capture is possible // 4. En passant square. Ignore if no pawn capture is possible
if ( ((fen >> col) && (col >= 'a' && col <= 'h')) if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
&& ((fen >> row) && (row == '3' || row == '6'))) && ((ss >> row) && (row == '3' || row == '6')))
{ {
st->epSquare = File(col - 'a') | Rank(row - '1'); st->epSquare = File(col - 'a') | Rank(row - '1');
@@ -278,11 +279,11 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
} }
// 5-6. Halfmove clock and fullmove number // 5-6. Halfmove clock and fullmove number
fen >> std::skipws >> st->rule50 >> startPosPly; ss >> std::skipws >> st->rule50 >> gamePly;
// Convert from fullmove starting from 1 to ply starting from 0, // Convert from fullmove starting from 1 to ply starting from 0,
// handle also common incorrect FEN with fullmove = 0. // handle also common incorrect FEN with fullmove = 0.
startPosPly = std::max(2 * (startPosPly - 1), 0) + int(sideToMove == BLACK); gamePly = std::max(2 * (gamePly - 1), 0) + int(sideToMove == BLACK);
st->key = compute_key(); st->key = compute_key();
st->pawnKey = compute_pawn_key(); st->pawnKey = compute_pawn_key();
@@ -325,71 +326,64 @@ void Position::set_castle_right(Color c, Square rfrom) {
} }
/// Position::to_fen() returns a FEN representation of the position. In case /// Position::fen() returns a FEN representation of the position. In case
/// of Chess960 the Shredder-FEN notation is used. Mainly a debugging function. /// of Chess960 the Shredder-FEN notation is used. Mainly a debugging function.
const string Position::to_fen() const { const string Position::fen() const {
std::ostringstream fen; std::ostringstream ss;
Square sq;
int emptyCnt;
for (Rank rank = RANK_8; rank >= RANK_1; rank--) for (Rank rank = RANK_8; rank >= RANK_1; rank--)
{ {
emptyCnt = 0;
for (File file = FILE_A; file <= FILE_H; file++) for (File file = FILE_A; file <= FILE_H; file++)
{ {
sq = file | rank; Square sq = file | rank;
if (is_empty(sq)) if (is_empty(sq))
emptyCnt++;
else
{ {
if (emptyCnt > 0) int emptyCnt = 1;
{
fen << emptyCnt;
emptyCnt = 0;
}
fen << PieceToChar[piece_on(sq)];
}
}
if (emptyCnt > 0) for ( ; file < FILE_H && is_empty(sq++); file++)
fen << emptyCnt; emptyCnt++;
ss << emptyCnt;
}
else
ss << PieceToChar[piece_on(sq)];
}
if (rank > RANK_1) if (rank > RANK_1)
fen << '/'; ss << '/';
} }
fen << (sideToMove == WHITE ? " w " : " b "); ss << (sideToMove == WHITE ? " w " : " b ");
if (can_castle(WHITE_OO)) if (can_castle(WHITE_OO))
fen << (chess960 ? char(toupper(file_to_char(file_of(castle_rook_square(WHITE, KING_SIDE))))) : 'K'); ss << (chess960 ? file_to_char(file_of(castle_rook_square(WHITE, KING_SIDE)), false) : 'K');
if (can_castle(WHITE_OOO)) if (can_castle(WHITE_OOO))
fen << (chess960 ? char(toupper(file_to_char(file_of(castle_rook_square(WHITE, QUEEN_SIDE))))) : 'Q'); ss << (chess960 ? file_to_char(file_of(castle_rook_square(WHITE, QUEEN_SIDE)), false) : 'Q');
if (can_castle(BLACK_OO)) if (can_castle(BLACK_OO))
fen << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, KING_SIDE))) : 'k'); ss << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, KING_SIDE)), true) : 'k');
if (can_castle(BLACK_OOO)) if (can_castle(BLACK_OOO))
fen << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, QUEEN_SIDE))) : 'q'); ss << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, QUEEN_SIDE)), true) : 'q');
if (st->castleRights == CASTLES_NONE) if (st->castleRights == CASTLES_NONE)
fen << '-'; ss << '-';
fen << (ep_square() == SQ_NONE ? " - " : " " + square_to_string(ep_square()) + " ") ss << (ep_square() == SQ_NONE ? " - " : " " + square_to_string(ep_square()) + " ")
<< st->rule50 << " " << 1 + (startPosPly - int(sideToMove == BLACK)) / 2; << st->rule50 << " " << 1 + (gamePly - int(sideToMove == BLACK)) / 2;
return fen.str(); return ss.str();
} }
/// Position::print() prints an ASCII representation of the position to /// Position::pretty() returns an ASCII representation of the position to be
/// the standard output. If a move is given then also the san is printed. /// printed to the standard output together with the move's san notation.
void Position::print(Move move) const { const string Position::pretty(Move move) const {
const string dottedLine = "\n+---+---+---+---+---+---+---+---+"; const string dottedLine = "\n+---+---+---+---+---+---+---+---+";
const string twoRows = dottedLine + "\n| | . | | . | | . | | . |" const string twoRows = dottedLine + "\n| | . | | . | | . | | . |"
@@ -397,19 +391,27 @@ void Position::print(Move move) const {
string brd = twoRows + twoRows + twoRows + twoRows + dottedLine; string brd = twoRows + twoRows + twoRows + twoRows + dottedLine;
sync_cout; std::ostringstream ss;
if (move) if (move)
{ ss << "\nMove: " << (sideToMove == BLACK ? ".." : "")
Position p(*this); << move_to_san(*const_cast<Position*>(this), move);
cout << "\nMove is: " << (sideToMove == BLACK ? ".." : "") << move_to_san(p, move);
}
for (Square sq = SQ_A1; sq <= SQ_H8; sq++) for (Square sq = SQ_A1; sq <= SQ_H8; sq++)
if (piece_on(sq) != NO_PIECE) if (piece_on(sq) != NO_PIECE)
brd[513 - 68*rank_of(sq) + 4*file_of(sq)] = PieceToChar[piece_on(sq)]; brd[513 - 68*rank_of(sq) + 4*file_of(sq)] = PieceToChar[piece_on(sq)];
cout << brd << "\nFen is: " << to_fen() << "\nKey is: " << st->key << sync_endl; ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase
<< std::setfill('0') << std::setw(16) << st->key << "\nCheckers: ";
for (Bitboard b = checkers(); b; )
ss << square_to_string(pop_lsb(&b)) << " ";
ss << "\nLegal moves: ";
for (MoveList<LEGAL> ml(*this); !ml.end(); ++ml)
ss << move_to_san(*const_cast<Position*>(this), ml.move()) << " ";
return ss.str();
} }
@@ -475,37 +477,6 @@ Bitboard Position::attacks_from(Piece p, Square s, Bitboard occ) {
} }
/// Position::move_attacks_square() tests whether a move from the current
/// position attacks a given square.
bool Position::move_attacks_square(Move m, Square s) const {
assert(is_ok(m));
assert(is_ok(s));
Bitboard occ, xray;
Square from = from_sq(m);
Square to = to_sq(m);
Piece piece = piece_moved(m);
assert(!is_empty(from));
// Update occupancy as if the piece is moving
occ = pieces() ^ from ^ to;
// The piece moved in 'to' attacks the square 's' ?
if (attacks_from(piece, to, occ) & s)
return true;
// Scan for possible X-ray attackers behind the moved piece
xray = (attacks_bb< ROOK>(s, occ) & pieces(color_of(piece), QUEEN, ROOK))
| (attacks_bb<BISHOP>(s, occ) & pieces(color_of(piece), QUEEN, BISHOP));
// Verify attackers are triggered by our move and not already existing
return xray && (xray ^ (xray & attacks_from<QUEEN>(s)));
}
/// Position::pl_move_is_legal() tests whether a pseudo-legal move is legal /// Position::pl_move_is_legal() tests whether a pseudo-legal move is legal
bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
@@ -553,20 +524,6 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
} }
/// Position::move_is_legal() takes a random move and tests whether the move
/// is legal. This version is not very fast and should be used only in non
/// time-critical paths.
bool Position::move_is_legal(const Move m) const {
for (MoveList<LEGAL> ml(*this); !ml.end(); ++ml)
if (ml.move() == m)
return true;
return false;
}
/// Position::is_pseudo_legal() takes a random move and tests whether the move /// Position::is_pseudo_legal() takes a random move and tests whether the move
/// is pseudo legal. It is used to validate moves from TT that can be corrupted /// is pseudo legal. It is used to validate moves from TT that can be corrupted
/// due to SMP concurrent access or hash position key aliasing. /// due to SMP concurrent access or hash position key aliasing.
@@ -574,14 +531,13 @@ bool Position::move_is_legal(const Move m) const {
bool Position::is_pseudo_legal(const Move m) const { bool Position::is_pseudo_legal(const Move m) const {
Color us = sideToMove; Color us = sideToMove;
Color them = ~sideToMove;
Square from = from_sq(m); Square from = from_sq(m);
Square to = to_sq(m); Square to = to_sq(m);
Piece pc = piece_moved(m); Piece pc = piece_moved(m);
// Use a slower but simpler function for uncommon cases // Use a slower but simpler function for uncommon cases
if (type_of(m) != NORMAL) if (type_of(m) != NORMAL)
return move_is_legal(m); return MoveList<LEGAL>(*this).contains(m);
// Is not a promotion, so promotion piece must be empty // Is not a promotion, so promotion piece must be empty
if (promotion_type(m) - 2 != NO_PIECE_TYPE) if (promotion_type(m) - 2 != NO_PIECE_TYPE)
@@ -593,7 +549,7 @@ bool Position::is_pseudo_legal(const Move m) const {
return false; return false;
// The destination square cannot be occupied by a friendly piece // The destination square cannot be occupied by a friendly piece
if (color_of(piece_on(to)) == us) if (piece_on(to) != NO_PIECE && color_of(piece_on(to)) == us)
return false; return false;
// Handle the special case of a pawn move // Handle the special case of a pawn move
@@ -619,7 +575,7 @@ bool Position::is_pseudo_legal(const Move m) const {
case DELTA_SE: case DELTA_SE:
// Capture. The destination square must be occupied by an enemy // Capture. The destination square must be occupied by an enemy
// piece (en passant captures was handled earlier). // piece (en passant captures was handled earlier).
if (color_of(piece_on(to)) != them) if (piece_on(to) == NO_PIECE || color_of(piece_on(to)) != ~us)
return false; return false;
// From and to files must be one file apart, avoids a7h5 // From and to files must be one file apart, avoids a7h5
@@ -664,18 +620,16 @@ bool Position::is_pseudo_legal(const Move m) const {
// Evasions generator already takes care to avoid some kind of illegal moves // Evasions generator already takes care to avoid some kind of illegal moves
// and pl_move_is_legal() relies on this. So we have to take care that the // and pl_move_is_legal() relies on this. So we have to take care that the
// same kind of moves are filtered out here. // same kind of moves are filtered out here.
if (in_check()) if (checkers())
{ {
if (type_of(pc) != KING) if (type_of(pc) != KING)
{ {
Bitboard b = checkers(); // Double check? In this case a king move is required
Square checksq = pop_lsb(&b); if (more_than_one(checkers()))
if (b) // double check ? In this case a king move is required
return false; return false;
// Our move must be a blocking evasion or a capture of the checking piece // Our move must be a blocking evasion or a capture of the checking piece
if (!((between_bb(checksq, king_square(us)) | checkers()) & to)) if (!((between_bb(lsb(checkers()), king_square(us)) | checkers()) & to))
return false; return false;
} }
// In case of king moves under check we have to remove king so to catch // In case of king moves under check we have to remove king so to catch
@@ -720,15 +674,16 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
Color us = sideToMove; Color us = sideToMove;
Square ksq = king_square(~us); Square ksq = king_square(~us);
// Promotion with check ? switch (type_of(m))
if (type_of(m) == PROMOTION) {
case PROMOTION:
return attacks_from(Piece(promotion_type(m)), to, pieces() ^ from) & ksq; return attacks_from(Piece(promotion_type(m)), to, pieces() ^ from) & ksq;
// En passant capture with check ? We have already handled the case // En passant capture with check ? We have already handled the case
// of direct checks and ordinary discovered check, the only case we // of direct checks and ordinary discovered check, the only case we
// need to handle is the unusual case of a discovered check through // need to handle is the unusual case of a discovered check through
// the captured pawn. // the captured pawn.
if (type_of(m) == ENPASSANT) case ENPASSANT:
{ {
Square capsq = file_of(to) | rank_of(from); Square capsq = file_of(to) | rank_of(from);
Bitboard b = (pieces() ^ from ^ capsq) | to; Bitboard b = (pieces() ^ from ^ capsq) | to;
@@ -736,9 +691,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
return (attacks_bb< ROOK>(ksq, b) & pieces(us, QUEEN, ROOK)) return (attacks_bb< ROOK>(ksq, b) & pieces(us, QUEEN, ROOK))
| (attacks_bb<BISHOP>(ksq, b) & pieces(us, QUEEN, BISHOP)); | (attacks_bb<BISHOP>(ksq, b) & pieces(us, QUEEN, BISHOP));
} }
case CASTLE:
// Castling with check ?
if (type_of(m) == CASTLE)
{ {
Square kfrom = from; Square kfrom = from;
Square rfrom = to; // 'King captures the rook' notation Square rfrom = to; // 'King captures the rook' notation
@@ -748,9 +701,11 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
return attacks_bb<ROOK>(rto, b) & ksq; return attacks_bb<ROOK>(rto, b) & ksq;
} }
default:
assert(false);
return false; return false;
} }
}
/// Position::do_move() makes a move, and saves all information necessary /// Position::do_move() makes a move, and saves all information necessary
@@ -772,9 +727,9 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
Key k = st->key; Key k = st->key;
// Copy some fields of old state to our new StateInfo object except the ones // Copy some fields of old state to our new StateInfo object except the ones
// which are recalculated from scratch anyway, then switch our state pointer // which are going to be recalculated from scratch anyway, then switch our state
// to point to the new, ready to be updated, state. // pointer to point to the new, ready to be updated, state.
memcpy(&newSt, st, sizeof(ReducedStateInfo)); memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t));
newSt.previous = st; newSt.previous = st;
st = &newSt; st = &newSt;
@@ -782,18 +737,12 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Update side to move // Update side to move
k ^= Zobrist::side; k ^= Zobrist::side;
// Increment the 50 moves rule draw counter. Resetting it to zero in the // Increment ply counters.In particular rule50 will be later reset it to zero
// case of a capture or a pawn move is taken care of later. // in case of a capture or a pawn move.
gamePly++;
st->rule50++; st->rule50++;
st->pliesFromNull++; st->pliesFromNull++;
if (type_of(m) == CASTLE)
{
st->key = k;
do_castle_move<true>(m);
return;
}
Color us = sideToMove; Color us = sideToMove;
Color them = ~us; Color them = ~us;
Square from = from_sq(m); Square from = from_sq(m);
@@ -803,9 +752,25 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to)); PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to));
assert(color_of(piece) == us); assert(color_of(piece) == us);
assert(color_of(piece_on(to)) != us); assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE);
assert(capture != KING); assert(capture != KING);
if (type_of(m) == CASTLE)
{
assert(piece == make_piece(us, KING));
bool kingSide = to > from;
Square rfrom = to; // Castle is encoded as "king captures friendly rook"
Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
capture = NO_PIECE_TYPE;
do_castle(from, to, rfrom, rto);
st->psqScore += psq_delta(make_piece(us, ROOK), rfrom, rto);
k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
}
if (capture) if (capture)
{ {
Square capsq = to; Square capsq = to;
@@ -830,7 +795,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
st->pawnKey ^= Zobrist::psq[them][PAWN][capsq]; st->pawnKey ^= Zobrist::psq[them][PAWN][capsq];
} }
else else
st->npMaterial[them] -= PieceValue[Mg][capture]; st->npMaterial[them] -= PieceValue[MG][capture];
// Remove the captured piece // Remove the captured piece
byTypeBB[ALL_PIECES] ^= capsq; byTypeBB[ALL_PIECES] ^= capsq;
@@ -840,7 +805,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Update piece list, move the last piece at index[capsq] position and // Update piece list, move the last piece at index[capsq] position and
// shrink the list. // shrink the list.
// //
// WARNING: This is a not revresible operation. When we will reinsert the // WARNING: This is a not reversible operation. When we will reinsert the
// captured piece in undo_move() we will put it at the end of the list and // captured piece in undo_move() we will put it at the end of the list and
// not in its original place, it means index[] and pieceList[] are not // not in its original place, it means index[] and pieceList[] are not
// guaranteed to be invariant to a do_move() + undo_move() sequence. // guaranteed to be invariant to a do_move() + undo_move() sequence.
@@ -849,9 +814,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
pieceList[them][capture][index[lastSquare]] = lastSquare; pieceList[them][capture][index[lastSquare]] = lastSquare;
pieceList[them][capture][pieceCount[them][capture]] = SQ_NONE; pieceList[them][capture][pieceCount[them][capture]] = SQ_NONE;
// Update hash keys // Update material hash key and prefetch access to materialTable
k ^= Zobrist::psq[them][capture][capsq]; k ^= Zobrist::psq[them][capture][capsq];
st->materialKey ^= Zobrist::psq[them][capture][pieceCount[them][capture]]; st->materialKey ^= Zobrist::psq[them][capture][pieceCount[them][capture]];
prefetch((char*)thisThread->materialTable[st->materialKey]);
// Update incremental scores // Update incremental scores
st->psqScore -= pieceSquareTable[make_piece(them, capture)][capsq]; st->psqScore -= pieceSquareTable[make_piece(them, capture)][capsq];
@@ -878,22 +844,25 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
st->castleRights &= ~cr; st->castleRights &= ~cr;
} }
// Prefetch TT access as soon as we know key is updated // Prefetch TT access as soon as we know the new hash key
prefetch((char*)TT.first_entry(k)); prefetch((char*)TT.first_entry(k));
// Move the piece // Move the piece. The tricky Chess960 castle is handled earlier
if (type_of(m) != CASTLE)
{
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to]; Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb; byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb; byTypeBB[pt] ^= from_to_bb;
byColorBB[us] ^= from_to_bb; byColorBB[us] ^= from_to_bb;
board[to] = board[from];
board[from] = NO_PIECE; board[from] = NO_PIECE;
board[to] = piece;
// Update piece lists, index[from] is not updated and becomes stale. This // Update piece lists, index[from] is not updated and becomes stale. This
// works as long as index[] is accessed just by known occupied squares. // works as long as index[] is accessed just by known occupied squares.
index[to] = index[from]; index[to] = index[from];
pieceList[us][pt][index[to]] = to; pieceList[us][pt][index[to]] = to;
}
// If the moving piece is a pawn do some special extra work // If the moving piece is a pawn do some special extra work
if (pt == PAWN) if (pt == PAWN)
@@ -938,20 +907,17 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
- pieceSquareTable[make_piece(us, PAWN)][to]; - pieceSquareTable[make_piece(us, PAWN)][to];
// Update material // Update material
st->npMaterial[us] += PieceValue[Mg][promotion]; st->npMaterial[us] += PieceValue[MG][promotion];
} }
// Update pawn hash key // Update pawn hash key and prefetch access to pawnsTable
st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to]; st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to];
prefetch((char*)thisThread->pawnsTable[st->pawnKey]);
// Reset rule 50 draw counter // Reset rule 50 draw counter
st->rule50 = 0; st->rule50 = 0;
} }
// Prefetch pawn and material hash tables
prefetch((char*)thisThread->pawnTable.entries[st->pawnKey]);
prefetch((char*)thisThread->materialTable.entries[st->materialKey]);
// Update incremental scores // Update incremental scores
st->psqScore += psq_delta(piece, from, to); st->psqScore += psq_delta(piece, from, to);
@@ -1001,22 +967,14 @@ void Position::undo_move(Move m) {
sideToMove = ~sideToMove; sideToMove = ~sideToMove;
if (type_of(m) == CASTLE)
{
do_castle_move<false>(m);
return;
}
Color us = sideToMove; Color us = sideToMove;
Color them = ~us; Color them = ~us;
Square from = from_sq(m); Square from = from_sq(m);
Square to = to_sq(m); Square to = to_sq(m);
Piece piece = piece_on(to); PieceType pt = type_of(piece_on(to));
PieceType pt = type_of(piece);
PieceType capture = st->capturedType; PieceType capture = st->capturedType;
assert(is_empty(from)); assert(is_empty(from) || type_of(m) == CASTLE);
assert(color_of(piece) == us);
assert(capture != KING); assert(capture != KING);
if (type_of(m) == PROMOTION) if (type_of(m) == PROMOTION)
@@ -1044,19 +1002,32 @@ void Position::undo_move(Move m) {
pt = PAWN; pt = PAWN;
} }
if (type_of(m) == CASTLE)
{
bool kingSide = to > from;
Square rfrom = to; // Castle is encoded as "king captures friendly rook"
Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
capture = NO_PIECE_TYPE;
pt = KING;
do_castle(to, from, rto, rfrom);
}
else
{
// Put the piece back at the source square // Put the piece back at the source square
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to]; Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb; byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb; byTypeBB[pt] ^= from_to_bb;
byColorBB[us] ^= from_to_bb; byColorBB[us] ^= from_to_bb;
board[from] = board[to];
board[to] = NO_PIECE; board[to] = NO_PIECE;
board[from] = make_piece(us, pt);
// Update piece lists, index[to] is not updated and becomes stale. This // Update piece lists, index[to] is not updated and becomes stale. This
// works as long as index[] is accessed just by known occupied squares. // works as long as index[] is accessed just by known occupied squares.
index[from] = index[to]; index[from] = index[to];
pieceList[us][pt][index[from]] = from; pieceList[us][pt][index[from]] = from;
}
if (capture) if (capture)
{ {
@@ -1086,49 +1057,18 @@ void Position::undo_move(Move m) {
// Finally point our state pointer back to the previous state // Finally point our state pointer back to the previous state
st = st->previous; st = st->previous;
gamePly--;
assert(pos_is_ok()); assert(pos_is_ok());
} }
/// Position::do_castle_move() is a private method used to do/undo a castling /// Position::do_castle() is a helper used to do/undo a castling move. This
/// move. Note that castling moves are encoded as "king captures friendly rook" /// is a bit tricky, especially in Chess960.
/// moves, for instance white short castling in a non-Chess960 game is encoded
/// as e1h1.
template<bool Do>
void Position::do_castle_move(Move m) {
assert(is_ok(m)); void Position::do_castle(Square kfrom, Square kto, Square rfrom, Square rto) {
assert(type_of(m) == CASTLE);
Square kto, kfrom, rfrom, rto, kAfter, rAfter;
Color us = sideToMove; Color us = sideToMove;
Square kBefore = from_sq(m);
Square rBefore = to_sq(m);
// Find after-castle squares for king and rook
if (rBefore > kBefore) // O-O
{
kAfter = relative_square(us, SQ_G1);
rAfter = relative_square(us, SQ_F1);
}
else // O-O-O
{
kAfter = relative_square(us, SQ_C1);
rAfter = relative_square(us, SQ_D1);
}
kfrom = Do ? kBefore : kAfter;
rfrom = Do ? rBefore : rAfter;
kto = Do ? kAfter : kBefore;
rto = Do ? rAfter : rBefore;
assert(piece_on(kfrom) == make_piece(us, KING));
assert(piece_on(rfrom) == make_piece(us, ROOK));
// Move the pieces, with some care; in chess960 could be kto == rfrom
Bitboard k_from_to_bb = SquareBB[kfrom] ^ SquareBB[kto]; Bitboard k_from_to_bb = SquareBB[kfrom] ^ SquareBB[kto];
Bitboard r_from_to_bb = SquareBB[rfrom] ^ SquareBB[rto]; Bitboard r_from_to_bb = SquareBB[rfrom] ^ SquareBB[rto];
byTypeBB[KING] ^= k_from_to_bb; byTypeBB[KING] ^= k_from_to_bb;
@@ -1136,105 +1076,63 @@ void Position::do_castle_move(Move m) {
byTypeBB[ALL_PIECES] ^= k_from_to_bb ^ r_from_to_bb; byTypeBB[ALL_PIECES] ^= k_from_to_bb ^ r_from_to_bb;
byColorBB[us] ^= k_from_to_bb ^ r_from_to_bb; byColorBB[us] ^= k_from_to_bb ^ r_from_to_bb;
// Update board // Could be from == to, so first set NO_PIECE then KING and ROOK
Piece king = make_piece(us, KING);
Piece rook = make_piece(us, ROOK);
board[kfrom] = board[rfrom] = NO_PIECE; board[kfrom] = board[rfrom] = NO_PIECE;
board[kto] = king; board[kto] = make_piece(us, KING);
board[rto] = rook; board[rto] = make_piece(us, ROOK);
// Update piece lists // Could be kfrom == rto, so use a 'tmp' variable
pieceList[us][KING][index[kfrom]] = kto; int tmp = index[kfrom];
pieceList[us][ROOK][index[rfrom]] = rto; index[rto] = index[rfrom];
int tmp = index[rfrom]; // In Chess960 could be kto == rfrom index[kto] = tmp;
index[kto] = index[kfrom]; pieceList[us][KING][index[kto]] = kto;
index[rto] = tmp; pieceList[us][ROOK][index[rto]] = rto;
}
if (Do)
{
// Reset capture field
st->capturedType = NO_PIECE_TYPE;
// Update incremental scores /// Position::do(undo)_null_move() is used to do(undo) a "null move": It flips
st->psqScore += psq_delta(king, kfrom, kto); /// the side to move without executing any move on the board.
st->psqScore += psq_delta(rook, rfrom, rto);
// Update hash key void Position::do_null_move(StateInfo& newSt) {
st->key ^= Zobrist::psq[us][KING][kfrom] ^ Zobrist::psq[us][KING][kto];
st->key ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; assert(!checkers());
memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here
newSt.previous = st;
st = &newSt;
// Clear en passant square
if (st->epSquare != SQ_NONE) if (st->epSquare != SQ_NONE)
{ {
st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
st->epSquare = SQ_NONE; st->epSquare = SQ_NONE;
} }
// Update castling rights
st->key ^= Zobrist::castle[st->castleRights & castleRightsMask[kfrom]];
st->castleRights &= ~castleRightsMask[kfrom];
// Update checkers BB
st->checkersBB = attackers_to(king_square(~us)) & pieces(us);
sideToMove = ~sideToMove;
}
else
// Undo: point our state pointer back to the previous state
st = st->previous;
assert(pos_is_ok());
}
/// Position::do_null_move() is used to do/undo a "null move": It flips the side
/// to move and updates the hash key without executing any move on the board.
template<bool Do>
void Position::do_null_move(StateInfo& backupSt) {
assert(!in_check());
// Back up the information necessary to undo the null move to the supplied
// StateInfo object. Note that differently from normal case here backupSt
// is actually used as a backup storage not as the new state. This reduces
// the number of fields to be copied.
StateInfo* src = Do ? st : &backupSt;
StateInfo* dst = Do ? &backupSt : st;
dst->key = src->key;
dst->epSquare = src->epSquare;
dst->psqScore = src->psqScore;
dst->rule50 = src->rule50;
dst->pliesFromNull = src->pliesFromNull;
sideToMove = ~sideToMove;
if (Do)
{
if (st->epSquare != SQ_NONE)
st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
st->key ^= Zobrist::side; st->key ^= Zobrist::side;
prefetch((char*)TT.first_entry(st->key)); prefetch((char*)TT.first_entry(st->key));
st->epSquare = SQ_NONE;
st->rule50++; st->rule50++;
st->pliesFromNull = 0; st->pliesFromNull = 0;
}
sideToMove = ~sideToMove;
assert(pos_is_ok()); assert(pos_is_ok());
} }
// Explicit template instantiations void Position::undo_null_move() {
template void Position::do_null_move<false>(StateInfo& backupSt);
template void Position::do_null_move<true>(StateInfo& backupSt); assert(!checkers());
st = st->previous;
sideToMove = ~sideToMove;
}
/// Position::see() is a static exchange evaluator: It tries to estimate the /// Position::see() is a static exchange evaluator: It tries to estimate the
/// material gain or loss resulting from a move. There are three versions of /// material gain or loss resulting from a move. Parameter 'asymmThreshold' takes
/// this function: One which takes a destination square as input, one takes a /// tempi into account. If the side who initiated the capturing sequence does the
/// move, and one which takes a 'from' and a 'to' square. The function does /// last capture, he loses a tempo and if the result is below 'asymmThreshold'
/// not yet understand promotions captures. /// the capturing sequence is considered bad.
int Position::see_sign(Move m) const { int Position::see_sign(Move m) const {
@@ -1243,13 +1141,13 @@ int Position::see_sign(Move m) const {
// Early return if SEE cannot be negative because captured piece value // Early return if SEE cannot be negative because captured piece value
// is not less then capturing one. Note that king moves always return // is not less then capturing one. Note that king moves always return
// here because king midgame value is set to 0. // here because king midgame value is set to 0.
if (PieceValue[Mg][piece_on(to_sq(m))] >= PieceValue[Mg][piece_moved(m)]) if (PieceValue[MG][piece_on(to_sq(m))] >= PieceValue[MG][piece_moved(m)])
return 1; return 1;
return see(m); return see(m);
} }
int Position::see(Move m) const { int Position::see(Move m, int asymmThreshold) const {
Square from, to; Square from, to;
Bitboard occupied, attackers, stmAttackers; Bitboard occupied, attackers, stmAttackers;
@@ -1290,7 +1188,7 @@ int Position::see(Move m) const {
stm = ~color_of(piece_on(from)); stm = ~color_of(piece_on(from));
stmAttackers = attackers & pieces(stm); stmAttackers = attackers & pieces(stm);
if (!stmAttackers) if (!stmAttackers)
return PieceValue[Mg][captured]; return PieceValue[MG][captured];
// The destination square is defended, which makes things rather more // The destination square is defended, which makes things rather more
// difficult to compute. We proceed by building up a "swap list" containing // difficult to compute. We proceed by building up a "swap list" containing
@@ -1298,14 +1196,14 @@ int Position::see(Move m) const {
// destination square, where the sides alternately capture, and always // destination square, where the sides alternately capture, and always
// capture with the least valuable piece. After each capture, we look for // capture with the least valuable piece. After each capture, we look for
// new X-ray attacks from behind the capturing piece. // new X-ray attacks from behind the capturing piece.
swapList[0] = PieceValue[Mg][captured]; swapList[0] = PieceValue[MG][captured];
captured = type_of(piece_on(from)); captured = type_of(piece_on(from));
do { do {
assert(slIndex < 32); assert(slIndex < 32);
// Add the new entry to the swap list // Add the new entry to the swap list
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[Mg][captured]; swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured];
slIndex++; slIndex++;
// Locate and remove from 'occupied' the next least valuable attacker // Locate and remove from 'occupied' the next least valuable attacker
@@ -1326,6 +1224,15 @@ int Position::see(Move m) const {
} while (stmAttackers); } while (stmAttackers);
// If we are doing asymmetric SEE evaluation and the same side does the first
// and the last capture, he loses a tempo and gain must be at least worth
// 'asymmThreshold', otherwise we replace the score with a very low value,
// before negamaxing.
if (asymmThreshold)
for (int i = 0; i < slIndex; i += 2)
if (swapList[i] < asymmThreshold)
swapList[i] = - QueenValueMg * 16;
// Having built the swap list, we negamax through it to find the best // Having built the swap list, we negamax through it to find the best
// achievable score from the point of view of the side to move. // achievable score from the point of view of the side to move.
while (--slIndex) while (--slIndex)
@@ -1347,9 +1254,6 @@ void Position::clear() {
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
for (int j = 0; j < 16; j++) for (int j = 0; j < 16; j++)
pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE; pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE;
for (Square sq = SQ_A1; sq <= SQ_H8; sq++)
board[sq] = NO_PIECE;
} }
@@ -1463,7 +1367,7 @@ Value Position::compute_non_pawn_material(Color c) const {
Value value = VALUE_ZERO; Value value = VALUE_ZERO;
for (PieceType pt = KNIGHT; pt <= QUEEN; pt++) for (PieceType pt = KNIGHT; pt <= QUEEN; pt++)
value += piece_count(c, pt) * PieceValue[Mg][pt]; value += piece_count(c, pt) * PieceValue[MG][pt];
return value; return value;
} }
@@ -1472,7 +1376,6 @@ Value Position::compute_non_pawn_material(Color c) const {
/// Position::is_draw() tests whether the position is drawn by material, /// Position::is_draw() tests whether the position is drawn by material,
/// repetition, or the 50 moves rule. It does not detect stalemates, this /// repetition, or the 50 moves rule. It does not detect stalemates, this
/// must be done by the search. /// must be done by the search.
template<bool SkipRepetition>
bool Position::is_draw() const { bool Position::is_draw() const {
// Draw by material? // Draw by material?
@@ -1481,12 +1384,10 @@ bool Position::is_draw() const {
return true; return true;
// Draw by the 50 moves rule? // Draw by the 50 moves rule?
if (st->rule50 > 99 && (!in_check() || MoveList<LEGAL>(*this).size())) if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
return true; return true;
// Draw by repetition? // Draw by repetition?
if (!SkipRepetition)
{
int i = 4, e = std::min(st->rule50, st->pliesFromNull); int i = 4, e = std::min(st->rule50, st->pliesFromNull);
if (i <= e) if (i <= e)
@@ -1503,15 +1404,10 @@ bool Position::is_draw() const {
} while (i <= e); } while (i <= e);
} }
}
return false; return false;
} }
// Explicit template instantiations
template bool Position::is_draw<false>() const;
template bool Position::is_draw<true>() const;
/// Position::flip() flips position with the white and black sides reversed. This /// Position::flip() flips position with the white and black sides reversed. This
/// is only useful for debugging especially for finding evaluation symmetry bugs. /// is only useful for debugging especially for finding evaluation symmetry bugs.
@@ -1526,7 +1422,7 @@ void Position::flip() {
thisThread = pos.this_thread(); thisThread = pos.this_thread();
nodes = pos.nodes_searched(); nodes = pos.nodes_searched();
chess960 = pos.is_chess960(); chess960 = pos.is_chess960();
startPosPly = pos.startpos_ply_counter(); gamePly = pos.game_ply();
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; s++)
if (!pos.is_empty(s)) if (!pos.is_empty(s))
@@ -1593,7 +1489,7 @@ bool Position::pos_is_ok(int* failedStep) const {
if ((*step)++, debugKingCount) if ((*step)++, debugKingCount)
{ {
int kingCount[2] = {}; int kingCount[COLOR_NB] = {};
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; s++)
if (type_of(piece_on(s)) == KING) if (type_of(piece_on(s)) == KING)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
#define POSITION_H_INCLUDED #define POSITION_H_INCLUDED
#include <cassert> #include <cassert>
#include <cstddef>
#include "bitboard.h" #include "bitboard.h"
#include "types.h" #include "types.h"
@@ -29,7 +30,7 @@
/// The checkInfo struct is initialized at c'tor time and keeps info used /// The checkInfo struct is initialized at c'tor time and keeps info used
/// to detect if a move gives check. /// to detect if a move gives check.
class Position; class Position;
class Thread; struct Thread;
struct CheckInfo { struct CheckInfo {
@@ -37,19 +38,19 @@ struct CheckInfo {
Bitboard dcCandidates; Bitboard dcCandidates;
Bitboard pinned; Bitboard pinned;
Bitboard checkSq[8]; Bitboard checkSq[PIECE_TYPE_NB];
Square ksq; Square ksq;
}; };
/// The StateInfo struct stores information we need to restore a Position /// The StateInfo struct stores information we need to restore a Position
/// object to its previous state when we retract a move. Whenever a move /// object to its previous state when we retract a move. Whenever a move
/// is made on the board (by calling Position::do_move), an StateInfo object /// is made on the board (by calling Position::do_move), a StateInfo object
/// must be passed as a parameter. /// must be passed as a parameter.
struct StateInfo { struct StateInfo {
Key pawnKey, materialKey; Key pawnKey, materialKey;
Value npMaterial[2]; Value npMaterial[COLOR_NB];
int castleRights, rule50, pliesFromNull; int castleRights, rule50, pliesFromNull;
Score psqScore; Score psqScore;
Square epSquare; Square epSquare;
@@ -60,13 +61,10 @@ struct StateInfo {
StateInfo* previous; StateInfo* previous;
}; };
struct ReducedStateInfo {
Key pawnKey, materialKey; /// When making a move the current StateInfo up to 'key' excluded is copied to
Value npMaterial[2]; /// the new one. Here we calculate the quad words (64bits) needed to be copied.
int castleRights, rule50, pliesFromNull; const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1;
Score psqScore;
Square epSquare;
};
/// The position data structure. A position consists of the following data: /// The position data structure. A position consists of the following data:
@@ -95,13 +93,13 @@ class Position {
public: public:
Position() {} Position() {}
Position(const Position& p, Thread* t) { *this = p; thisThread = t; } Position(const Position& p, Thread* t) { *this = p; thisThread = t; }
Position(const std::string& f, bool c960, Thread* t) { from_fen(f, c960, t); } Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); }
Position& operator=(const Position&); Position& operator=(const Position&);
// Text input/output // Text input/output
void from_fen(const std::string& fen, bool isChess960, Thread* th); void set(const std::string& fen, bool isChess960, Thread* th);
const std::string to_fen() const; const std::string fen() const;
void print(Move m = MOVE_NONE) const; const std::string pretty(Move m = MOVE_NONE) const;
// Position representation // Position representation
Bitboard pieces() const; Bitboard pieces() const;
@@ -124,7 +122,6 @@ public:
Square castle_rook_square(Color c, CastlingSide s) const; Square castle_rook_square(Color c, CastlingSide s) const;
// Checking // Checking
bool in_check() const;
Bitboard checkers() const; Bitboard checkers() const;
Bitboard discovered_check_candidates() const; Bitboard discovered_check_candidates() const;
Bitboard pinned_pieces() const; Bitboard pinned_pieces() const;
@@ -139,8 +136,6 @@ public:
// Properties of moves // Properties of moves
bool move_gives_check(Move m, const CheckInfo& ci) const; bool move_gives_check(Move m, const CheckInfo& ci) const;
bool move_attacks_square(Move m, Square s) const;
bool move_is_legal(const Move m) const;
bool pl_move_is_legal(Move m, Bitboard pinned) const; bool pl_move_is_legal(Move m, Bitboard pinned) const;
bool is_pseudo_legal(const Move m) const; bool is_pseudo_legal(const Move m) const;
bool is_capture(Move m) const; bool is_capture(Move m) const;
@@ -159,10 +154,11 @@ public:
void do_move(Move m, StateInfo& st); void do_move(Move m, StateInfo& st);
void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck); void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck);
void undo_move(Move m); void undo_move(Move m);
template<bool Do> void do_null_move(StateInfo& st); void do_null_move(StateInfo& st);
void undo_null_move();
// Static exchange evaluation // Static exchange evaluation
int see(Move m) const; int see(Move m, int asymmThreshold = 0) const;
int see_sign(Move m) const; int see_sign(Move m) const;
// Accessing hash keys // Accessing hash keys
@@ -178,12 +174,12 @@ public:
// Other properties of the position // Other properties of the position
Color side_to_move() const; Color side_to_move() const;
int startpos_ply_counter() const; int game_ply() const;
bool is_chess960() const; bool is_chess960() const;
Thread* this_thread() const; Thread* this_thread() const;
int64_t nodes_searched() const; int64_t nodes_searched() const;
void set_nodes_searched(int64_t n); void set_nodes_searched(int64_t n);
template<bool SkipRepetition> bool is_draw() const; bool is_draw() const;
// Position consistency check, for debugging // Position consistency check, for debugging
bool pos_is_ok(int* failedStep = NULL) const; bool pos_is_ok(int* failedStep = NULL) const;
@@ -195,8 +191,8 @@ private:
void put_piece(Piece p, Square s); void put_piece(Piece p, Square s);
void set_castle_right(Color c, Square rfrom); void set_castle_right(Color c, Square rfrom);
// Helper template functions // Helper functions
template<bool Do> void do_castle_move(Move m); void do_castle(Square kfrom, Square kto, Square rfrom, Square rto);
template<bool FindPinned> Bitboard hidden_checkers() const; template<bool FindPinned> Bitboard hidden_checkers() const;
// Computing hash keys from scratch (for initialization and debugging) // Computing hash keys from scratch (for initialization and debugging)
@@ -209,20 +205,20 @@ private:
Value compute_non_pawn_material(Color c) const; Value compute_non_pawn_material(Color c) const;
// Board and pieces // Board and pieces
Piece board[64]; // [square] Piece board[SQUARE_NB];
Bitboard byTypeBB[8]; // [pieceType] Bitboard byTypeBB[PIECE_TYPE_NB];
Bitboard byColorBB[2]; // [color] Bitboard byColorBB[COLOR_NB];
int pieceCount[2][8]; // [color][pieceType] int pieceCount[COLOR_NB][PIECE_TYPE_NB];
Square pieceList[2][8][16]; // [color][pieceType][index] Square pieceList[COLOR_NB][PIECE_TYPE_NB][16];
int index[64]; // [square] int index[SQUARE_NB];
// Other info // Other info
int castleRightsMask[64]; // [square] int castleRightsMask[SQUARE_NB];
Square castleRookSquare[2][2]; // [color][side] Square castleRookSquare[COLOR_NB][CASTLING_SIDE_NB];
Bitboard castlePath[2][2]; // [color][side] Bitboard castlePath[COLOR_NB][CASTLING_SIDE_NB];
StateInfo startState; StateInfo startState;
int64_t nodes; int64_t nodes;
int startPosPly; int gamePly;
Color sideToMove; Color sideToMove;
Thread* thisThread; Thread* thisThread;
StateInfo* st; StateInfo* st;
@@ -334,10 +330,6 @@ inline Bitboard Position::checkers() const {
return st->checkersBB; return st->checkersBB;
} }
inline bool Position::in_check() const {
return st->checkersBB != 0;
}
inline Bitboard Position::discovered_check_candidates() const { inline Bitboard Position::discovered_check_candidates() const {
return hidden_checkers<false>(); return hidden_checkers<false>();
} }
@@ -384,8 +376,8 @@ inline bool Position::is_passed_pawn_push(Move m) const {
&& pawn_is_passed(sideToMove, to_sq(m)); && pawn_is_passed(sideToMove, to_sq(m));
} }
inline int Position::startpos_ply_counter() const { inline int Position::game_ply() const {
return startPosPly + st->pliesFromNull; // HACK return gamePly;
} }
inline bool Position::opposite_bishops() const { inline bool Position::opposite_bishops() const {

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -29,7 +29,7 @@
/// a given square a (midgame, endgame) score pair is assigned. PSQT is defined /// a given square a (midgame, endgame) score pair is assigned. PSQT is defined
/// for white side, for black side the tables are symmetric. /// for white side, for black side the tables are symmetric.
static const Score PSQT[][64] = { static const Score PSQT[][SQUARE_NB] = {
{ }, { },
{ // Pawn { // Pawn
S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0),

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -61,17 +61,15 @@ class RKISS {
return s.d = e + s.a; return s.d = e + s.a;
} }
// Init seed and scramble a few rounds public:
void raninit() { RKISS(int seed = 73) {
s.a = 0xf1ea5eed; s.a = 0xf1ea5eed;
s.b = s.c = s.d = 0xd4e12c77; s.b = s.c = s.d = 0xd4e12c77;
for (int i = 0; i < 73; i++) for (int i = 0; i < seed; i++) // Scramble a few rounds
rand64(); rand64();
} }
public:
RKISS() { raninit(); }
template<typename T> T rand() { return T(rand64()); } template<typename T> T rand() { return T(rand64()); }
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -38,15 +38,16 @@ namespace Search {
/// has its own array of Stack objects, indexed by the current ply. /// has its own array of Stack objects, indexed by the current ply.
struct Stack { struct Stack {
SplitPoint* sp; SplitPoint* splitPoint;
int ply; int ply;
Move currentMove; Move currentMove;
Move excludedMove; Move excludedMove;
Move killers[2]; Move killers[2];
Depth reduction; Depth reduction;
Value eval; Value staticEval;
Value evalMargin; Value evalMargin;
int skipNullMove; int skipNullMove;
int futilityMoveCount;
}; };
@@ -56,12 +57,11 @@ struct Stack {
/// all non-pv moves. /// all non-pv moves.
struct RootMove { struct RootMove {
RootMove(){} // Needed by sort()
RootMove(Move m) : score(-VALUE_INFINITE), prevScore(-VALUE_INFINITE) { RootMove(Move m) : score(-VALUE_INFINITE), prevScore(-VALUE_INFINITE) {
pv.push_back(m); pv.push_back(MOVE_NONE); pv.push_back(m); pv.push_back(MOVE_NONE);
} }
bool operator<(const RootMove& m) const { return score < m.score; } bool operator<(const RootMove& m) const { return score > m.score; } // Ascending sort
bool operator==(const Move& m) const { return pv[0] == m; } bool operator==(const Move& m) const { return pv[0] == m; }
void extract_pv_from_tt(Position& pos); void extract_pv_from_tt(Position& pos);
@@ -80,9 +80,9 @@ struct RootMove {
struct LimitsType { struct LimitsType {
LimitsType() { memset(this, 0, sizeof(LimitsType)); } LimitsType() { memset(this, 0, sizeof(LimitsType)); }
bool use_time_management() const { return !(movetime | depth | nodes | infinite); } bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); }
int time[2], inc[2], movestogo, depth, nodes, movetime, infinite, ponder; int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder;
}; };
@@ -98,7 +98,8 @@ typedef std::auto_ptr<std::stack<StateInfo> > StateStackPtr;
extern volatile SignalsType Signals; extern volatile SignalsType Signals;
extern LimitsType Limits; extern LimitsType Limits;
extern std::vector<RootMove> RootMoves; extern std::vector<RootMove> RootMoves;
extern Position RootPosition; extern Position RootPos;
extern Color RootColor;
extern Time::point SearchTime; extern Time::point SearchTime;
extern StateStackPtr SetupStates; extern StateStackPtr SetupStates;

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm> // For std::count
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
@@ -32,26 +33,24 @@ ThreadPool Threads; // Global object
namespace { extern "C" { namespace { extern "C" {
// start_routine() is the C function which is called when a new thread // start_routine() is the C function which is called when a new thread
// is launched. It is a wrapper to member function pointed by start_fn. // is launched. It is a wrapper to the virtual function idle_loop().
long start_routine(Thread* th) { (th->*(th->start_fn))(); return 0; } long start_routine(Thread* th) { th->idle_loop(); return 0; }
} } } }
// Thread c'tor starts a newly-created thread of execution that will call // Thread c'tor starts a newly-created thread of execution that will call
// the idle loop function pointed by start_fn going immediately to sleep. // the the virtual function idle_loop(), going immediately to sleep.
Thread::Thread(Fn fn) { Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC
is_searching = do_exit = false; searching = exit = false;
maxPly = splitPointsCnt = 0; maxPly = splitPointsSize = 0;
curSplitPoint = NULL; activeSplitPoint = NULL;
start_fn = fn; activePosition = NULL;
idx = Threads.size(); idx = Threads.size();
do_sleep = (fn != &Thread::main_loop); // Avoid a race with start_searching()
if (!thread_create(handle, start_routine, this)) if (!thread_create(handle, start_routine, this))
{ {
std::cerr << "Failed to create thread number " << idx << std::endl; std::cerr << "Failed to create thread number " << idx << std::endl;
@@ -60,47 +59,49 @@ Thread::Thread(Fn fn) {
} }
// Thread d'tor waits for thread termination before to return. // Thread d'tor waits for thread termination before to return
Thread::~Thread() { Thread::~Thread() {
assert(do_sleep); exit = true; // Search must be already finished
notify_one();
do_exit = true; // Search must be already finished
wake_up();
thread_join(handle); // Wait for thread termination thread_join(handle); // Wait for thread termination
} }
// Thread::timer_loop() is where the timer thread waits maxPly milliseconds and // TimerThread::idle_loop() is where the timer thread waits msec milliseconds
// then calls check_time(). If maxPly is 0 thread sleeps until is woken up. // and then calls check_time(). If msec is 0 thread sleeps until is woken up.
extern void check_time(); extern void check_time();
void Thread::timer_loop() { void TimerThread::idle_loop() {
while (!do_exit) while (!exit)
{ {
mutex.lock(); mutex.lock();
sleepCondition.wait_for(mutex, maxPly ? maxPly : INT_MAX);
if (!exit)
sleepCondition.wait_for(mutex, msec ? msec : INT_MAX);
mutex.unlock(); mutex.unlock();
if (msec)
check_time(); check_time();
} }
} }
// Thread::main_loop() is where the main thread is parked waiting to be started // MainThread::idle_loop() is where the main thread is parked waiting to be started
// when there is a new search. Main thread will launch all the slave threads. // when there is a new search. Main thread will launch all the slave threads.
void Thread::main_loop() { void MainThread::idle_loop() {
while (true) while (true)
{ {
mutex.lock(); mutex.lock();
do_sleep = true; // Always return to sleep after a search thinking = false;
is_searching = false;
while (do_sleep && !do_exit) while (!thinking && !exit)
{ {
Threads.sleepCondition.notify_one(); // Wake up UI thread if needed Threads.sleepCondition.notify_one(); // Wake up UI thread if needed
sleepCondition.wait(mutex); sleepCondition.wait(mutex);
@@ -108,22 +109,23 @@ void Thread::main_loop() {
mutex.unlock(); mutex.unlock();
if (do_exit) if (exit)
return; return;
is_searching = true; searching = true;
Search::think(); Search::think();
assert(is_searching); assert(searching);
searching = false;
} }
} }
// Thread::wake_up() wakes up the thread, normally at the beginning of the search // Thread::notify_one() wakes up the thread when there is some search to do
// or, if "sleeping threads" is used at split time.
void Thread::wake_up() { void Thread::notify_one() {
mutex.lock(); mutex.lock();
sleepCondition.notify_one(); sleepCondition.notify_one();
@@ -131,19 +133,12 @@ void Thread::wake_up() {
} }
// Thread::wait_for_stop_or_ponderhit() is called when the maximum depth is // Thread::wait_for() set the thread to sleep until condition 'b' turns true
// reached while the program is pondering. The point is to work around a wrinkle
// in the UCI protocol: When pondering, the engine is not allowed to give a
// "bestmove" before the GUI sends it a "stop" or "ponderhit" command. We simply
// wait here until one of these commands (that raise StopRequest) is sent and
// then return, after which the bestmove and pondermove will be printed.
void Thread::wait_for_stop_or_ponderhit() { void Thread::wait_for(volatile const bool& b) {
Signals.stopOnPonderhit = true;
mutex.lock(); mutex.lock();
while (!Signals.stop) sleepCondition.wait(mutex);; while (!b) sleepCondition.wait(mutex);
mutex.unlock(); mutex.unlock();
} }
@@ -153,7 +148,7 @@ void Thread::wait_for_stop_or_ponderhit() {
bool Thread::cutoff_occurred() const { bool Thread::cutoff_occurred() const {
for (SplitPoint* sp = curSplitPoint; sp; sp = sp->parent) for (SplitPoint* sp = activeSplitPoint; sp; sp = sp->parentSplitPoint)
if (sp->cutoff) if (sp->cutoff)
return true; return true;
@@ -164,46 +159,47 @@ bool Thread::cutoff_occurred() const {
// Thread::is_available_to() checks whether the thread is available to help the // Thread::is_available_to() checks whether the thread is available to help the
// thread 'master' at a split point. An obvious requirement is that thread must // thread 'master' at a split point. An obvious requirement is that thread must
// be idle. With more than two threads, this is not sufficient: If the thread is // be idle. With more than two threads, this is not sufficient: If the thread is
// the master of some active split point, it is only available as a slave to the // the master of some split point, it is only available as a slave to the slaves
// slaves which are busy searching the split point at the top of slaves split // which are busy searching the split point at the top of slaves split point
// point stack (the "helpful master concept" in YBWC terminology). // stack (the "helpful master concept" in YBWC terminology).
bool Thread::is_available_to(Thread* master) const { bool Thread::is_available_to(Thread* master) const {
if (is_searching) if (searching)
return false; return false;
// Make a local copy to be sure doesn't become zero under our feet while // Make a local copy to be sure doesn't become zero under our feet while
// testing next condition and so leading to an out of bound access. // testing next condition and so leading to an out of bound access.
int spCnt = splitPointsCnt; int size = splitPointsSize;
// No active split points means that the thread is available as a slave for any // No split points means that the thread is available as a slave for any
// other thread otherwise apply the "helpful master" concept if possible. // other thread otherwise apply the "helpful master" concept if possible.
return !spCnt || (splitPoints[spCnt - 1].slavesMask & (1ULL << master->idx)); return !size || (splitPoints[size - 1].slavesMask & (1ULL << master->idx));
} }
// init() is called at startup. Initializes lock and condition variable and // init() is called at startup to create and launch requested threads, that will
// launches requested threads sending them immediately to sleep. We cannot use // go immediately to sleep due to 'sleepWhileIdle' set to true. We cannot use
// a c'tor becuase Threads is a static object and we need a fully initialized // a c'tor becuase Threads is a static object and we need a fully initialized
// engine at this point due to allocation of endgames in Thread c'tor. // engine at this point due to allocation of Endgames in Thread c'tor.
void ThreadPool::init() { void ThreadPool::init() {
timer = new Thread(&Thread::timer_loop); sleepWhileIdle = true;
threads.push_back(new Thread(&Thread::main_loop)); timer = new TimerThread();
push_back(new MainThread());
read_uci_options(); read_uci_options();
} }
// exit() cleanly terminates the threads before the program exits. // exit() cleanly terminates the threads before the program exits
void ThreadPool::exit() { void ThreadPool::exit() {
for (size_t i = 0; i < threads.size(); i++) delete timer; // As first because check_time() accesses threads data
delete threads[i];
delete timer; for (iterator it = begin(); it != end(); ++it)
delete *it;
} }
@@ -216,221 +212,170 @@ void ThreadPool::read_uci_options() {
maxThreadsPerSplitPoint = Options["Max Threads per Split Point"]; maxThreadsPerSplitPoint = Options["Max Threads per Split Point"];
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY; minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
useSleepingThreads = Options["Use Sleeping Threads"];
size_t requested = Options["Threads"]; size_t requested = Options["Threads"];
assert(requested > 0); assert(requested > 0);
while (threads.size() < requested) while (size() < requested)
threads.push_back(new Thread(&Thread::idle_loop)); push_back(new Thread());
while (threads.size() > requested) while (size() > requested)
{ {
delete threads.back(); delete back();
threads.pop_back(); pop_back();
} }
} }
// wake_up() is called before a new search to start the threads that are waiting // slave_available() tries to find an idle thread which is available as a slave
// on the sleep condition and to reset maxPly. When useSleepingThreads is set // for the thread 'master'.
// threads will be woken up at split time.
void ThreadPool::wake_up() const { Thread* ThreadPool::available_slave(Thread* master) const {
for (size_t i = 0; i < threads.size(); i++) for (const_iterator it = begin(); it != end(); ++it)
{ if ((*it)->is_available_to(master))
threads[i]->maxPly = 0; return *it;
threads[i]->do_sleep = false;
if (!useSleepingThreads) return NULL;
threads[i]->wake_up();
}
}
// sleep() is called after the search finishes to ask all the threads but the
// main one to go waiting on a sleep condition.
void ThreadPool::sleep() const {
// Main thread will go to sleep by itself to avoid a race with start_searching()
for (size_t i = 1; i < threads.size(); i++)
threads[i]->do_sleep = true;
}
// available_slave_exists() tries to find an idle thread which is available as
// a slave for the thread 'master'.
bool ThreadPool::available_slave_exists(Thread* master) const {
for (size_t i = 0; i < threads.size(); i++)
if (threads[i]->is_available_to(master))
return true;
return false;
} }
// split() does the actual work of distributing the work at a node between // split() does the actual work of distributing the work at a node between
// several available threads. If it does not succeed in splitting the node // several available threads. If it does not succeed in splitting the node
// (because no idle threads are available, or because we have no unused split // (because no idle threads are available), the function immediately returns.
// point objects), the function immediately returns. If splitting is possible, a // If splitting is possible, a SplitPoint object is initialized with all the
// SplitPoint object is initialized with all the data that must be copied to the // data that must be copied to the helper threads and then helper threads are
// helper threads and then helper threads are told that they have been assigned // told that they have been assigned work. This will cause them to instantly
// work. This will cause them to instantly leave their idle loops and call // leave their idle loops and call search(). When all threads have returned from
// search(). When all threads have returned from search() then split() returns. // search() then split() returns.
template <bool Fake> template <bool Fake>
Value ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta, void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bestValue,
Value bestValue, Move* bestMove, Depth depth, Move* bestMove, Depth depth, Move threatMove, int moveCount,
Move threatMove, int moveCount, MovePicker* mp, int nodeType) { MovePicker* movePicker, int nodeType) {
assert(pos.pos_is_ok()); assert(pos.pos_is_ok());
assert(bestValue > -VALUE_INFINITE); assert(*bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE);
assert(bestValue <= alpha); assert(*bestValue > -VALUE_INFINITE);
assert(alpha < beta); assert(depth >= Threads.minimumSplitDepth);
assert(beta <= VALUE_INFINITE); assert(searching);
assert(depth > DEPTH_ZERO); assert(splitPointsSize < MAX_SPLITPOINTS_PER_THREAD);
Thread* master = pos.this_thread();
if (master->splitPointsCnt >= MAX_SPLITPOINTS_PER_THREAD)
return bestValue;
// Pick the next available split point from the split point stack // Pick the next available split point from the split point stack
SplitPoint& sp = master->splitPoints[master->splitPointsCnt]; SplitPoint& sp = splitPoints[splitPointsSize];
sp.parent = master->curSplitPoint; sp.masterThread = this;
sp.master = master; sp.parentSplitPoint = activeSplitPoint;
sp.cutoff = false; sp.slavesMask = 1ULL << idx;
sp.slavesMask = 1ULL << master->idx;
sp.depth = depth; sp.depth = depth;
sp.bestValue = *bestValue;
sp.bestMove = *bestMove; sp.bestMove = *bestMove;
sp.threatMove = threatMove; sp.threatMove = threatMove;
sp.alpha = alpha; sp.alpha = alpha;
sp.beta = beta; sp.beta = beta;
sp.nodeType = nodeType; sp.nodeType = nodeType;
sp.bestValue = bestValue; sp.movePicker = movePicker;
sp.mp = mp;
sp.moveCount = moveCount; sp.moveCount = moveCount;
sp.pos = &pos; sp.pos = &pos;
sp.nodes = 0; sp.nodes = 0;
sp.cutoff = false;
sp.ss = ss; sp.ss = ss;
assert(master->is_searching);
master->curSplitPoint = &sp;
int slavesCnt = 0;
// Try to allocate available threads and ask them to start searching setting // Try to allocate available threads and ask them to start searching setting
// is_searching flag. This must be done under lock protection to avoid concurrent // 'searching' flag. This must be done under lock protection to avoid concurrent
// allocation of the same slave by another master. // allocation of the same slave by another master.
Threads.mutex.lock();
sp.mutex.lock(); sp.mutex.lock();
mutex.lock();
for (size_t i = 0; i < threads.size() && !Fake; ++i) splitPointsSize++;
if (threads[i]->is_available_to(master)) activeSplitPoint = &sp;
activePosition = NULL;
size_t slavesCnt = 1; // This thread is always included
Thread* slave;
while ( (slave = Threads.available_slave(this)) != NULL
&& ++slavesCnt <= Threads.maxThreadsPerSplitPoint && !Fake)
{ {
sp.slavesMask |= 1ULL << i; sp.slavesMask |= 1ULL << slave->idx;
threads[i]->curSplitPoint = &sp; slave->activeSplitPoint = &sp;
threads[i]->is_searching = true; // Slave leaves idle_loop() slave->searching = true; // Slave leaves idle_loop()
slave->notify_one(); // Could be sleeping
if (useSleepingThreads)
threads[i]->wake_up();
if (++slavesCnt + 1 >= maxThreadsPerSplitPoint) // Master is always included
break;
} }
master->splitPointsCnt++;
mutex.unlock();
sp.mutex.unlock();
// Everything is set up. The master thread enters the idle loop, from which // Everything is set up. The master thread enters the idle loop, from which
// it will instantly launch a search, because its is_searching flag is set. // it will instantly launch a search, because its 'searching' flag is set.
// The thread will return from the idle loop when all slaves have finished // The thread will return from the idle loop when all slaves have finished
// their work at this split point. // their work at this split point.
if (slavesCnt || Fake) if (slavesCnt > 1 || Fake)
{ {
master->idle_loop(); sp.mutex.unlock();
Threads.mutex.unlock();
Thread::idle_loop(); // Force a call to base class idle_loop()
// In helpful master concept a master can help only a sub-tree of its split // In helpful master concept a master can help only a sub-tree of its split
// point, and because here is all finished is not possible master is booked. // point, and because here is all finished is not possible master is booked.
assert(!master->is_searching); assert(!searching);
} assert(!activePosition);
// We have returned from the idle loop, which means that all threads are // We have returned from the idle loop, which means that all threads are
// finished. Note that setting is_searching and decreasing splitPointsCnt is // finished. Note that setting 'searching' and decreasing splitPointsSize is
// done under lock protection to avoid a race with Thread::is_available_to(). // done under lock protection to avoid a race with Thread::is_available_to().
sp.mutex.lock(); // To protect sp.nodes Threads.mutex.lock();
mutex.lock(); sp.mutex.lock();
}
master->is_searching = true; searching = true;
master->splitPointsCnt--; splitPointsSize--;
master->curSplitPoint = sp.parent; activeSplitPoint = sp.parentSplitPoint;
activePosition = &pos;
pos.set_nodes_searched(pos.nodes_searched() + sp.nodes); pos.set_nodes_searched(pos.nodes_searched() + sp.nodes);
*bestMove = sp.bestMove; *bestMove = sp.bestMove;
*bestValue = sp.bestValue;
mutex.unlock();
sp.mutex.unlock(); sp.mutex.unlock();
Threads.mutex.unlock();
return sp.bestValue;
} }
// Explicit template instantiations // Explicit template instantiations
template Value ThreadPool::split<false>(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int); template void Thread::split<false>(Position&, Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int);
template Value ThreadPool::split<true>(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int); template void Thread::split< true>(Position&, Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int);
// set_timer() is used to set the timer to trigger after msec milliseconds. // wait_for_think_finished() waits for main thread to go to sleep then returns
// If msec is 0 then timer is stopped.
void ThreadPool::set_timer(int msec) { void ThreadPool::wait_for_think_finished() {
timer->mutex.lock(); MainThread* t = main_thread();
timer->maxPly = msec;
timer->sleepCondition.notify_one(); // Wake up and restart the timer
timer->mutex.unlock();
}
// wait_for_search_finished() waits for main thread to go to sleep, this means
// search is finished. Then returns.
void ThreadPool::wait_for_search_finished() {
Thread* t = main_thread();
t->mutex.lock(); t->mutex.lock();
t->sleepCondition.notify_one(); // In case is waiting for stop or ponderhit while (t->thinking) sleepCondition.wait(t->mutex);
while (!t->do_sleep) sleepCondition.wait(t->mutex);
t->mutex.unlock(); t->mutex.unlock();
} }
// start_searching() wakes up the main thread sleeping in main_loop() so to start // start_thinking() wakes up the main thread sleeping in MainThread::idle_loop()
// a new search, then returns immediately. // so to start a new search, then returns immediately.
void ThreadPool::start_searching(const Position& pos, const LimitsType& limits, void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
const std::vector<Move>& searchMoves, StateStackPtr& states) { const std::vector<Move>& searchMoves, StateStackPtr& states) {
wait_for_search_finished(); wait_for_think_finished();
SearchTime = Time::now(); // As early as possible SearchTime = Time::now(); // As early as possible
Signals.stopOnPonderhit = Signals.firstRootMove = false; Signals.stopOnPonderhit = Signals.firstRootMove = false;
Signals.stop = Signals.failedLowAtRoot = false; Signals.stop = Signals.failedLowAtRoot = false;
RootPosition = pos; RootPos = pos;
Limits = limits; Limits = limits;
SetupStates = states; // Ownership transfer here SetupStates = states; // Ownership transfer here
RootMoves.clear(); RootMoves.clear();
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml) for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
if (searchMoves.empty() || std::count(searchMoves.begin(), searchMoves.end(), ml.move())) if ( searchMoves.empty()
|| std::count(searchMoves.begin(), searchMoves.end(), ml.move()))
RootMoves.push_back(RootMove(ml.move())); RootMoves.push_back(RootMove(ml.move()));
main_thread()->do_sleep = false; main_thread()->thinking = true;
main_thread()->wake_up(); main_thread()->notify_one(); // Starts main thread
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -28,7 +28,7 @@
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
const int MAX_THREADS = 32; const int MAX_THREADS = 64; // Because SplitPoint::slavesMask is a uint64_t
const int MAX_SPLITPOINTS_PER_THREAD = 8; const int MAX_SPLITPOINTS_PER_THREAD = 8;
struct Mutex { struct Mutex {
@@ -56,22 +56,22 @@ private:
WaitCondition c; WaitCondition c;
}; };
class Thread; struct Thread;
struct SplitPoint { struct SplitPoint {
// Const data after split point has been setup // Const data after split point has been setup
const Position* pos; const Position* pos;
const Search::Stack* ss; const Search::Stack* ss;
Thread* masterThread;
Depth depth; Depth depth;
Value beta; Value beta;
int nodeType; int nodeType;
Thread* master;
Move threatMove; Move threatMove;
// Const pointers to shared data // Const pointers to shared data
MovePicker* mp; MovePicker* movePicker;
SplitPoint* parent; SplitPoint* parentSplitPoint;
// Shared data // Shared data
Mutex mutex; Mutex mutex;
@@ -90,77 +90,76 @@ struct SplitPoint {
/// tables so that once we get a pointer to an entry its life time is unlimited /// tables so that once we get a pointer to an entry its life time is unlimited
/// and we don't have to care about someone changing the entry under our feet. /// and we don't have to care about someone changing the entry under our feet.
class Thread { struct Thread {
typedef void (Thread::* Fn) (); // Pointer to member function Thread();
virtual ~Thread();
public: virtual void idle_loop();
Thread(Fn fn); void notify_one();
~Thread();
void wake_up();
bool cutoff_occurred() const; bool cutoff_occurred() const;
bool is_available_to(Thread* master) const; bool is_available_to(Thread* master) const;
void idle_loop(); void wait_for(volatile const bool& b);
void main_loop();
void timer_loop(); template <bool Fake>
void wait_for_stop_or_ponderhit(); void split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove,
Depth depth, Move threatMove, int moveCount, MovePicker* movePicker, int nodeType);
SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD]; SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD];
MaterialTable materialTable; Material::Table materialTable;
PawnTable pawnTable; Endgames endgames;
Pawns::Table pawnsTable;
Position* activePosition;
size_t idx; size_t idx;
int maxPly; int maxPly;
Mutex mutex; Mutex mutex;
ConditionVariable sleepCondition; ConditionVariable sleepCondition;
NativeHandle handle; NativeHandle handle;
Fn start_fn; SplitPoint* volatile activeSplitPoint;
SplitPoint* volatile curSplitPoint; volatile int splitPointsSize;
volatile int splitPointsCnt; volatile bool searching;
volatile bool is_searching; volatile bool exit;
volatile bool do_sleep;
volatile bool do_exit;
}; };
/// ThreadPool class handles all the threads related stuff like init, starting, /// MainThread and TimerThread are sublassed from Thread to characterize the two
/// special threads: the main one and the recurring timer.
struct MainThread : public Thread {
MainThread() : thinking(true) {} // Avoid a race with start_thinking()
virtual void idle_loop();
volatile bool thinking;
};
struct TimerThread : public Thread {
TimerThread() : msec(0) {}
virtual void idle_loop();
int msec;
};
/// ThreadPool struct handles all the threads related stuff like init, starting,
/// parking and, the most important, launching a slave thread at a split point. /// parking and, the most important, launching a slave thread at a split point.
/// All the access to shared thread data is done through this class. /// All the access to shared thread data is done through this class.
class ThreadPool { struct ThreadPool : public std::vector<Thread*> {
public:
void init(); // No c'tor and d'tor, threads rely on globals that should void init(); // No c'tor and d'tor, threads rely on globals that should
void exit(); // be initialized and valid during the whole thread lifetime. void exit(); // be initialized and valid during the whole thread lifetime.
Thread& operator[](size_t id) { return *threads[id]; } MainThread* main_thread() { return static_cast<MainThread*>((*this)[0]); }
bool use_sleeping_threads() const { return useSleepingThreads; }
int min_split_depth() const { return minimumSplitDepth; }
size_t size() const { return threads.size(); }
Thread* main_thread() { return threads[0]; }
void wake_up() const;
void sleep() const;
void read_uci_options(); void read_uci_options();
bool available_slave_exists(Thread* master) const; Thread* available_slave(Thread* master) const;
void set_timer(int msec); void wait_for_think_finished();
void wait_for_search_finished(); void start_thinking(const Position&, const Search::LimitsType&,
void start_searching(const Position&, const Search::LimitsType&,
const std::vector<Move>&, Search::StateStackPtr&); const std::vector<Move>&, Search::StateStackPtr&);
template <bool Fake> bool sleepWhileIdle;
Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue, Move* bestMove, Depth minimumSplitDepth;
Depth depth, Move threatMove, int moveCount, MovePicker* mp, int nodeType); size_t maxThreadsPerSplitPoint;
private:
friend class Thread;
std::vector<Thread*> threads;
Thread* timer;
Mutex mutex; Mutex mutex;
ConditionVariable sleepCondition; ConditionVariable sleepCondition;
Depth minimumSplitDepth; TimerThread* timer;
int maxThreadsPerSplitPoint;
bool useSleepingThreads;
}; };
extern ThreadPool Threads; extern ThreadPool Threads;

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -29,7 +29,7 @@ namespace {
/// Constants /// Constants
const int MoveHorizon = 50; // Plan time management at most this many moves ahead const int MoveHorizon = 50; // Plan time management at most this many moves ahead
const float MaxRatio = 3.0f; // When in trouble, we can step over reserved time with this ratio const float MaxRatio = 7.0f; // When in trouble, we can step over reserved time with this ratio
const float StealRatio = 0.33f; // However we must not steal time from remaining moves over this ratio const float StealRatio = 0.33f; // However we must not steal time from remaining moves over this ratio

View File

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

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -25,42 +25,32 @@
TranspositionTable TT; // Our global transposition table TranspositionTable TT; // Our global transposition table
TranspositionTable::TranspositionTable() {
size = generation = 0;
entries = NULL;
}
TranspositionTable::~TranspositionTable() {
delete [] entries;
}
/// TranspositionTable::set_size() sets the size of the transposition table, /// TranspositionTable::set_size() sets the size of the transposition table,
/// measured in megabytes. Transposition table consists of a power of 2 number of /// measured in megabytes. Transposition table consists of a power of 2 number
/// TTCluster and each cluster consists of ClusterSize number of TTEntries. Each /// of clusters and each cluster consists of ClusterSize number of TTEntry.
/// non-empty entry contains information of exactly one position.
void TranspositionTable::set_size(size_t mbSize) { void TranspositionTable::set_size(size_t mbSize) {
size_t newSize = 1ULL << msb((mbSize << 20) / sizeof(TTCluster)); assert(msb((mbSize << 20) / sizeof(TTEntry)) < 32);
if (newSize == size) uint32_t size = ClusterSize << msb((mbSize << 20) / sizeof(TTEntry[ClusterSize]));
if (hashMask == size - ClusterSize)
return; return;
size = newSize; free(mem);
delete [] entries; mem = malloc(size * sizeof(TTEntry) + (CACHE_LINE_SIZE - 1));
entries = new (std::nothrow) TTCluster[size]; if (!mem)
if (!entries)
{ {
std::cerr << "Failed to allocate " << mbSize std::cerr << "Failed to allocate " << mbSize
<< "MB for transposition table." << std::endl; << "MB for transposition table." << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
clear(); // Operator new is not guaranteed to initialize memory to zero table = (TTEntry*)((size_t(mem) + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1));
hashMask = size - ClusterSize;
clear(); // Newly allocated block of memory is not initialized
} }
@@ -70,7 +60,7 @@ void TranspositionTable::set_size(size_t mbSize) {
void TranspositionTable::clear() { void TranspositionTable::clear() {
memset(entries, 0, size * sizeof(TTCluster)); memset(table, 0, (hashMask + ClusterSize) * sizeof(TTEntry));
} }
@@ -82,23 +72,23 @@ void TranspositionTable::clear() {
/// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from /// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from
/// a previous search, or if the depth of t1 is bigger than the depth of t2. /// a previous search, or if the depth of t1 is bigger than the depth of t2.
void TranspositionTable::store(const Key posKey, Value v, Bound t, Depth d, Move m, Value statV, Value kingD) { void TranspositionTable::store(const Key key, Value v, Bound t, Depth d, Move m, Value statV, Value kingD) {
int c1, c2, c3; int c1, c2, c3;
TTEntry *tte, *replace; TTEntry *tte, *replace;
uint32_t posKey32 = posKey >> 32; // Use the high 32 bits as key inside the cluster uint32_t key32 = key >> 32; // Use the high 32 bits as key inside the cluster
tte = replace = first_entry(posKey); tte = replace = first_entry(key);
for (int i = 0; i < ClusterSize; i++, tte++) for (unsigned i = 0; i < ClusterSize; i++, tte++)
{ {
if (!tte->key() || tte->key() == posKey32) // Empty or overwrite old if (!tte->key() || tte->key() == key32) // Empty or overwrite old
{ {
// Preserve any existing ttMove // Preserve any existing ttMove
if (m == MOVE_NONE) if (m == MOVE_NONE)
m = tte->move(); m = tte->move();
tte->save(posKey32, v, t, d, m, generation, statV, kingD); tte->save(key32, v, t, d, m, generation, statV, kingD);
return; return;
} }
@@ -110,7 +100,7 @@ void TranspositionTable::store(const Key posKey, Value v, Bound t, Depth d, Move
if (c1 + c2 + c3 > 0) if (c1 + c2 + c3 > 0)
replace = tte; replace = tte;
} }
replace->save(posKey32, v, t, d, m, generation, statV, kingD); replace->save(key32, v, t, d, m, generation, statV, kingD);
} }
@@ -118,24 +108,14 @@ void TranspositionTable::store(const Key posKey, Value v, Bound t, Depth d, Move
/// transposition table. Returns a pointer to the TTEntry or NULL if /// transposition table. Returns a pointer to the TTEntry or NULL if
/// position is not found. /// position is not found.
TTEntry* TranspositionTable::probe(const Key posKey) const { TTEntry* TranspositionTable::probe(const Key key) const {
uint32_t posKey32 = posKey >> 32; TTEntry* tte = first_entry(key);
TTEntry* tte = first_entry(posKey); uint32_t key32 = key >> 32;
for (int i = 0; i < ClusterSize; i++, tte++) for (unsigned i = 0; i < ClusterSize; i++, tte++)
if (tte->key() == posKey32) if (tte->key() == key32)
return tte; return tte;
return NULL; return NULL;
} }
/// TranspositionTable::new_search() is called at the beginning of every new
/// search. It increments the "generation" variable, which is used to
/// distinguish transposition table entries from previous searches from
/// entries from the current search.
void TranspositionTable::new_search() {
generation++;
}

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -44,7 +44,7 @@
class TTEntry { class TTEntry {
public: public:
void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value statV, Value statM) { void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev, Value em) {
key32 = (uint32_t)k; key32 = (uint32_t)k;
move16 = (uint16_t)m; move16 = (uint16_t)m;
@@ -52,8 +52,8 @@ public:
generation8 = (uint8_t)g; generation8 = (uint8_t)g;
value16 = (int16_t)v; value16 = (int16_t)v;
depth16 = (int16_t)d; depth16 = (int16_t)d;
staticValue = (int16_t)statV; evalValue = (int16_t)ev;
staticMargin = (int16_t)statM; evalMargin = (int16_t)em;
} }
void set_generation(int g) { generation8 = (uint8_t)g; } void set_generation(int g) { generation8 = (uint8_t)g; }
@@ -63,52 +63,42 @@ public:
Value value() const { return (Value)value16; } Value value() const { return (Value)value16; }
Bound type() const { return (Bound)bound; } Bound type() const { return (Bound)bound; }
int generation() const { return (int)generation8; } int generation() const { return (int)generation8; }
Value static_value() const { return (Value)staticValue; } Value eval_value() const { return (Value)evalValue; }
Value static_value_margin() const { return (Value)staticMargin; } Value eval_margin() const { return (Value)evalMargin; }
private: private:
uint32_t key32; uint32_t key32;
uint16_t move16; uint16_t move16;
uint8_t bound, generation8; uint8_t bound, generation8;
int16_t value16, depth16, staticValue, staticMargin; int16_t value16, depth16, evalValue, evalMargin;
}; };
/// This is the number of TTEntry slots for each cluster /// A TranspositionTable consists of a power of 2 number of clusters and each
const int ClusterSize = 4; /// cluster consists of ClusterSize number of TTEntry. Each non-empty entry
/// contains information of exactly one position. Size of a cluster shall not be
/// bigger than a cache line size. In case it is less, it should be padded to
/// TTCluster consists of ClusterSize number of TTEntries. Size of TTCluster /// guarantee always aligned accesses.
/// must not be bigger than a cache line size. In case it is less, it should
/// be padded to guarantee always aligned accesses.
struct TTCluster {
TTEntry data[ClusterSize];
};
/// The transposition table class. This is basically just a huge array containing
/// TTCluster objects, and a few methods for writing and reading entries.
class TranspositionTable { class TranspositionTable {
TranspositionTable(const TranspositionTable&); static const unsigned ClusterSize = 4; // A cluster is 64 Bytes
TranspositionTable& operator=(const TranspositionTable&);
public: public:
TranspositionTable(); ~TranspositionTable() { free(mem); }
~TranspositionTable(); void new_search() { generation++; }
TTEntry* probe(const Key key) const;
TTEntry* first_entry(const Key key) const;
void refresh(const TTEntry* tte) const;
void set_size(size_t mbSize); void set_size(size_t mbSize);
void clear(); void clear();
void store(const Key posKey, Value v, Bound type, Depth d, Move m, Value statV, Value kingD); void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV, Value kingD);
TTEntry* probe(const Key posKey) const;
void new_search();
TTEntry* first_entry(const Key posKey) const;
void refresh(const TTEntry* tte) const;
private: private:
size_t size; uint32_t hashMask;
TTCluster* entries; TTEntry* table;
void* mem;
uint8_t generation; // Size must be not bigger then TTEntry::generation8 uint8_t generation; // Size must be not bigger then TTEntry::generation8
}; };
@@ -119,9 +109,9 @@ extern TranspositionTable TT;
/// a cluster given a position. The lowest order bits of the key are used to /// a cluster given a position. The lowest order bits of the key are used to
/// get the index of the cluster. /// get the index of the cluster.
inline TTEntry* TranspositionTable::first_entry(const Key posKey) const { inline TTEntry* TranspositionTable::first_entry(const Key key) const {
return entries[((uint32_t)posKey) & (size - 1)].data; return table + ((uint32_t)key & hashMask);
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -35,13 +35,14 @@
/// | only in 64-bit mode. For compiling requires hardware with /// | only in 64-bit mode. For compiling requires hardware with
/// | popcnt support. /// | popcnt support.
#include <cassert>
#include <cctype> #include <cctype>
#include <climits> #include <climits>
#include <cstdlib> #include <cstdlib>
#include "platform.h" #include "platform.h"
#if defined(_WIN64) #if defined(_WIN64) && !defined(IS_64BIT)
# include <intrin.h> // MSVC popcnt and bsfq instrinsics # include <intrin.h> // MSVC popcnt and bsfq instrinsics
# define IS_64BIT # define IS_64BIT
# define USE_BSFQ # define USE_BSFQ
@@ -51,10 +52,15 @@
# include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic # include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic
#endif #endif
# if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER))
# include <xmmintrin.h> // Intel and Microsoft header for _mm_prefetch()
# endif
#define CACHE_LINE_SIZE 64
#if defined(_MSC_VER) || defined(__INTEL_COMPILER) #if defined(_MSC_VER) || defined(__INTEL_COMPILER)
# define CACHE_LINE_ALIGNMENT __declspec(align(64)) # define CACHE_LINE_ALIGNMENT __declspec(align(CACHE_LINE_SIZE))
#else #else
# define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64))) # define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(CACHE_LINE_SIZE)))
#endif #endif
#if defined(_MSC_VER) #if defined(_MSC_VER)
@@ -132,12 +138,20 @@ enum CastleRight { // Defined as in PolyGlot book hash key
WHITE_OOO = 2, WHITE_OOO = 2,
BLACK_OO = 4, BLACK_OO = 4,
BLACK_OOO = 8, BLACK_OOO = 8,
ALL_CASTLES = 15 ALL_CASTLES = 15,
CASTLE_RIGHT_NB = 16
}; };
enum CastlingSide { enum CastlingSide {
KING_SIDE, KING_SIDE,
QUEEN_SIDE QUEEN_SIDE,
CASTLING_SIDE_NB = 2
};
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128,
MG = 0, EG = 1, PHASE_NB = 2
}; };
enum ScaleFactor { enum ScaleFactor {
@@ -168,8 +182,6 @@ enum Value {
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX, VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN, VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN,
Mg = 0, Eg = 1,
PawnValueMg = 198, PawnValueEg = 258, PawnValueMg = 198, PawnValueEg = 258,
KnightValueMg = 817, KnightValueEg = 846, KnightValueMg = 817, KnightValueEg = 846,
BishopValueMg = 836, BishopValueEg = 857, BishopValueMg = 836, BishopValueEg = 857,
@@ -179,17 +191,19 @@ enum Value {
enum PieceType { enum PieceType {
NO_PIECE_TYPE = 0, ALL_PIECES = 0, NO_PIECE_TYPE = 0, ALL_PIECES = 0,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6 PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6,
PIECE_TYPE_NB = 8
}; };
enum Piece { enum Piece {
NO_PIECE = 16, // color_of(NO_PIECE) == NO_COLOR NO_PIECE = 0,
W_PAWN = 1, W_KNIGHT = 2, W_BISHOP = 3, W_ROOK = 4, W_QUEEN = 5, W_KING = 6, W_PAWN = 1, W_KNIGHT = 2, W_BISHOP = 3, W_ROOK = 4, W_QUEEN = 5, W_KING = 6,
B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14 B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14,
PIECE_NB = 16
}; };
enum Color { enum Color {
WHITE, BLACK, NO_COLOR WHITE, BLACK, NO_COLOR, COLOR_NB = 2
}; };
enum Depth { enum Depth {
@@ -215,6 +229,8 @@ enum Square {
SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8, SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8,
SQ_NONE, SQ_NONE,
SQUARE_NB = 64,
DELTA_N = 8, DELTA_N = 8,
DELTA_E = 1, DELTA_E = 1,
DELTA_S = -8, DELTA_S = -8,
@@ -229,11 +245,11 @@ enum Square {
}; };
enum File { enum File {
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB = 8
}; };
enum Rank { enum Rank {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8 RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB = 8
}; };
@@ -320,9 +336,9 @@ inline Score apply_weight(Score v, Score w) {
namespace Zobrist { namespace Zobrist {
extern Key psq[2][8][64]; // [color][pieceType][square / piece count] extern Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
extern Key enpassant[8]; // [file] extern Key enpassant[FILE_NB];
extern Key castle[16]; // [castleRight] extern Key castle[CASTLE_RIGHT_NB];
extern Key side; extern Key side;
extern Key exclusion; extern Key exclusion;
@@ -331,9 +347,9 @@ namespace Zobrist {
CACHE_LINE_ALIGNMENT CACHE_LINE_ALIGNMENT
extern Score pieceSquareTable[16][64]; // [piece][square] extern Score pieceSquareTable[PIECE_NB][SQUARE_NB];
extern Value PieceValue[2][18]; // [Mg / Eg][piece / pieceType] extern Value PieceValue[PHASE_NB][PIECE_NB];
extern int SquareDistance[64][64]; // [square][square] extern int SquareDistance[SQUARE_NB][SQUARE_NB];
struct MoveStack { struct MoveStack {
Move move; Move move;
@@ -377,6 +393,7 @@ inline PieceType type_of(Piece p) {
} }
inline Color color_of(Piece p) { inline Color color_of(Piece p) {
assert(p != NO_PIECE);
return Color(p >> 3); return Color(p >> 3);
} }
@@ -425,12 +442,12 @@ inline int square_distance(Square s1, Square s2) {
return SquareDistance[s1][s2]; return SquareDistance[s1][s2];
} }
inline char file_to_char(File f) { inline char file_to_char(File f, bool tolower = true) {
return char(f - FILE_A + int('a')); return char(f - FILE_A + (tolower ? 'a' : 'A'));
} }
inline char rank_to_char(Rank r) { inline char rank_to_char(Rank r) {
return char(r - RANK_1 + int('1')); return char(r - RANK_1 + '1');
} }
inline Square pawn_push(Color c) { inline Square pawn_push(Color c) {
@@ -473,21 +490,4 @@ inline const std::string square_to_string(Square s) {
return ch; return ch;
} }
/// Our insertion sort implementation, works with pointers and iterators and is
/// guaranteed to be stable, as is needed.
template<typename T, typename K>
void sort(K first, K last)
{
T tmp;
K p, q;
for (p = first + 1; p < last; p++)
{
tmp = *p;
for (q = p; q != first && *(q-1) < tmp; --q)
*q = *(q-1);
*q = tmp;
}
}
#endif // !defined(TYPES_H_INCLUDED) #endif // !defined(TYPES_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <iomanip>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <string> #include <string>
@@ -43,7 +44,7 @@ namespace {
void set_option(istringstream& up); void set_option(istringstream& up);
void set_position(Position& pos, istringstream& up); void set_position(Position& pos, istringstream& up);
void go(Position& pos, istringstream& up); void go(const Position& pos, istringstream& up);
} }
@@ -55,78 +56,32 @@ namespace {
void UCI::loop(const string& args) { void UCI::loop(const string& args) {
Position pos(StartFEN, false, Threads.main_thread()); // The root position Position pos(StartFEN, false, Threads.main_thread()); // The root position
string cmd, token; string token, cmd = args;
while (token != "quit") do {
{ if (args.empty() && !getline(cin, cmd)) // Block here waiting for input
if (!args.empty())
cmd = args;
else if (!getline(cin, cmd)) // Block here waiting for input
cmd = "quit"; cmd = "quit";
istringstream is(cmd); istringstream is(cmd);
is >> skipws >> token; is >> skipws >> token;
if (token == "quit" || token == "stop") if (token == "quit" || token == "stop" || token == "ponderhit")
{
// GUI sends 'ponderhit' to tell us to ponder on the same move the
// opponent has played. In case Signals.stopOnPonderhit is set we are
// waiting for 'ponderhit' to stop the search (for instance because we
// already ran out of time), otherwise we should continue searching but
// switching from pondering to normal search.
if (token != "ponderhit" || Search::Signals.stopOnPonderhit)
{ {
Search::Signals.stop = true; Search::Signals.stop = true;
Threads.wait_for_search_finished(); // Cannot quit while threads are running Threads.main_thread()->notify_one(); // Could be sleeping
} }
else
else if (token == "ponderhit")
{
// The opponent has played the expected move. GUI sends "ponderhit" if
// we were told to ponder on the same move the opponent has played. We
// should continue searching but switching from pondering to normal search.
Search::Limits.ponder = false; Search::Limits.ponder = false;
if (Search::Signals.stopOnPonderhit)
{
Search::Signals.stop = true;
Threads.main_thread()->wake_up(); // Could be sleeping
} }
} else if (token == "perft" && (is >> token)) // Read perft depth
else if (token == "go")
go(pos, is);
else if (token == "ucinewgame")
{ /* Avoid returning "Unknown command" */ }
else if (token == "isready")
sync_cout << "readyok" << sync_endl;
else if (token == "position")
set_position(pos, is);
else if (token == "setoption")
set_option(is);
else if (token == "d")
pos.print();
else if (token == "flip")
pos.flip();
else if (token == "eval")
sync_cout << Eval::trace(pos) << sync_endl;
else if (token == "bench")
benchmark(pos, is);
else if (token == "key")
sync_cout << "key: " << hex << pos.key()
<< "\nmaterial key: " << pos.material_key()
<< "\npawn key: " << pos.pawn_key() << sync_endl;
else if (token == "uci")
sync_cout << "id name " << engine_info(true)
<< "\n" << Options
<< "\nuciok" << sync_endl;
else if (token == "perft" && (is >> token)) // Read depth
{ {
stringstream ss; stringstream ss;
@@ -135,16 +90,33 @@ void UCI::loop(const string& args) {
benchmark(pos, ss); benchmark(pos, ss);
} }
else if (token == "key")
sync_cout << hex << uppercase << setfill('0')
<< "position key: " << setw(16) << pos.key()
<< "\nmaterial key: " << setw(16) << pos.material_key()
<< "\npawn key: " << setw(16) << pos.pawn_key()
<< dec << sync_endl;
else if (token == "uci")
sync_cout << "id name " << engine_info(true)
<< "\n" << Options
<< "\nuciok" << sync_endl;
else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ }
else if (token == "go") go(pos, is);
else if (token == "position") set_position(pos, is);
else if (token == "setoption") set_option(is);
else if (token == "flip") pos.flip();
else if (token == "bench") benchmark(pos, is);
else if (token == "d") sync_cout << pos.pretty() << sync_endl;
else if (token == "isready") sync_cout << "readyok" << sync_endl;
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
else else
sync_cout << "Unknown command: " << cmd << sync_endl; sync_cout << "Unknown command: " << cmd << sync_endl;
if (!args.empty()) // Command line arguments have one-shot behaviour } while (token != "quit" && args.empty()); // Args have one-shot behaviour
{
Threads.wait_for_search_finished(); Threads.wait_for_think_finished(); // Cannot quit while search is running
break;
}
}
} }
@@ -173,7 +145,7 @@ namespace {
else else
return; return;
pos.from_fen(fen, Options["UCI_Chess960"], Threads.main_thread()); pos.set(fen, Options["UCI_Chess960"], Threads.main_thread());
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>()); SetupStates = Search::StateStackPtr(new std::stack<StateInfo>());
// Parse move list (if any) // Parse move list (if any)
@@ -210,10 +182,10 @@ namespace {
// go() is called when engine receives the "go" UCI command. The function sets // go() is called when engine receives the "go" UCI command. The function sets
// the thinking time and other parameters from the input string, and then starts // the thinking time and other parameters from the input string, and starts
// the search. // the search.
void go(Position& pos, istringstream& is) { void go(const Position& pos, istringstream& is) {
Search::LimitsType limits; Search::LimitsType limits;
vector<Move> searchMoves; vector<Move> searchMoves;
@@ -221,31 +193,23 @@ namespace {
while (is >> token) while (is >> token)
{ {
if (token == "wtime") if (token == "searchmoves")
is >> limits.time[WHITE];
else if (token == "btime")
is >> limits.time[BLACK];
else if (token == "winc")
is >> limits.inc[WHITE];
else if (token == "binc")
is >> limits.inc[BLACK];
else if (token == "movestogo")
is >> limits.movestogo;
else if (token == "depth")
is >> limits.depth;
else if (token == "nodes")
is >> limits.nodes;
else if (token == "movetime")
is >> limits.movetime;
else if (token == "infinite")
limits.infinite = true;
else if (token == "ponder")
limits.ponder = true;
else if (token == "searchmoves")
while (is >> token) while (is >> token)
searchMoves.push_back(move_from_uci(pos, token)); searchMoves.push_back(move_from_uci(pos, token));
else if (token == "wtime") is >> limits.time[WHITE];
else if (token == "btime") is >> limits.time[BLACK];
else if (token == "winc") is >> limits.inc[WHITE];
else if (token == "binc") is >> limits.inc[BLACK];
else if (token == "movestogo") is >> limits.movestogo;
else if (token == "depth") is >> limits.depth;
else if (token == "nodes") is >> limits.nodes;
else if (token == "movetime") is >> limits.movetime;
else if (token == "mate") is >> limits.mate;
else if (token == "infinite") limits.infinite = true;
else if (token == "ponder") limits.ponder = true;
} }
Threads.start_searching(pos, limits, searchMoves, SetupStates); Threads.start_thinking(pos, limits, searchMoves, SetupStates);
} }
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -64,6 +64,7 @@ void init(OptionsMap& o) {
o["Search Log Filename"] = Option("SearchLog.txt"); o["Search Log Filename"] = Option("SearchLog.txt");
o["Book File"] = Option("book.bin"); o["Book File"] = Option("book.bin");
o["Best Book Move"] = Option(false); o["Best Book Move"] = Option(false);
o["Contempt Factor"] = Option(0, -50, 50);
o["Mobility (Middle Game)"] = Option(100, 0, 200, on_eval); o["Mobility (Middle Game)"] = Option(100, 0, 200, on_eval);
o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval); o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval);
o["Passed Pawns (Middle Game)"] = Option(100, 0, 200, on_eval); o["Passed Pawns (Middle Game)"] = Option(100, 0, 200, on_eval);
@@ -71,11 +72,11 @@ void init(OptionsMap& o) {
o["Space"] = Option(100, 0, 200, on_eval); o["Space"] = Option(100, 0, 200, on_eval);
o["Aggressiveness"] = Option(100, 0, 200, on_eval); o["Aggressiveness"] = Option(100, 0, 200, on_eval);
o["Cowardice"] = Option(100, 0, 200, on_eval); o["Cowardice"] = Option(100, 0, 200, on_eval);
o["Min Split Depth"] = Option(msd, 4, 7, on_threads); o["Min Split Depth"] = Option(msd, 4, 12, on_threads);
o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads); o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads);
o["Threads"] = Option(cpus, 1, MAX_THREADS, on_threads); o["Threads"] = Option(cpus, 1, MAX_THREADS, on_threads);
o["Use Sleeping Threads"] = Option(true, on_threads); o["Use Sleeping Threads"] = Option(true);
o["Hash"] = Option(32, 4, 8192, on_hash_size); o["Hash"] = Option(32, 1, 8192, on_hash_size);
o["Clear Hash"] = Option(on_clear_hash); o["Clear Hash"] = Option(on_clear_hash);
o["Ponder"] = Option(true); o["Ponder"] = Option(true);
o["OwnBook"] = Option(false); o["OwnBook"] = Option(false);

View File

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