DroidFish: Updated stockfish to the latest development version and included syzygy tablebases probing code.

This commit is contained in:
Peter Osterlund
2014-10-12 12:38:00 +00:00
parent e621fb77bd
commit 8c99b770dd
35 changed files with 3197 additions and 1312 deletions

View File

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

View File

@@ -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>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <istream> #include <istream>
@@ -81,7 +82,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 : "32"; string ttSize = (is >> token) ? token : "16";
string threads = (is >> token) ? token : "1"; string threads = (is >> token) ? token : "1";
string limit = (is >> token) ? token : "13"; string limit = (is >> token) ? token : "13";
string fenFile = (is >> token) ? token : "default"; string fenFile = (is >> token) ? token : "default";
@@ -137,22 +138,9 @@ void benchmark(const Position& current, istream& is) {
cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl; cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl;
if (limitType == "divide") if (limitType == "perft")
for (MoveList<LEGAL> it(pos); *it; ++it) nodes += Search::perft<true>(pos, limits.depth * ONE_PLY);
{
StateInfo si;
pos.do_move(*it, si);
uint64_t cnt = limits.depth > 1 ? Search::perft(pos, (limits.depth - 1) * ONE_PLY) : 1;
pos.undo_move(*it);
cerr << move_to_uci(*it, pos.is_chess960()) << ": " << cnt << endl;
nodes += cnt;
}
else if (limitType == "perft")
{
uint64_t cnt = Search::perft(pos, limits.depth * ONE_PLY);
cerr << "\nPerft " << limits.depth << " leaf nodes: " << cnt << endl;
nodes += cnt;
}
else else
{ {
Threads.start_thinking(pos, limits, st); Threads.start_thinking(pos, limits, st);
@@ -161,7 +149,7 @@ void benchmark(const Position& current, istream& is) {
} }
} }
elapsed = Time::now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero' elapsed = std::max(Time::now() - elapsed, Time::point(1)); // Avoid a 'divide by zero'
dbg_print(); // Just before to exit dbg_print(); // Just before to exit

View File

@@ -107,11 +107,11 @@ namespace {
KPKPosition::KPKPosition(unsigned idx) { KPKPosition::KPKPosition(unsigned idx) {
wksq = Square((idx >> 0) & 0x3F); wksq = Square((idx >> 0) & 0x3F);
bksq = Square((idx >> 6) & 0x3F); bksq = Square((idx >> 6) & 0x3F);
us = Color ((idx >> 12) & 0x01); us = Color ((idx >> 12) & 0x01);
psq = make_square(File((idx >> 13) & 0x03), Rank(RANK_7 - (idx >> 15))); psq = make_square(File((idx >> 13) & 0x3), RANK_7 - Rank((idx >> 15) & 0x7));
result = UNKNOWN; result = UNKNOWN;
// Check if two pieces are on the same square or if a king can be captured // Check if two pieces are on the same square or if a king can be captured
if ( square_distance(wksq, bksq) <= 1 if ( square_distance(wksq, bksq) <= 1

View File

@@ -149,7 +149,10 @@ const std::string Bitboards::pretty(Bitboard b) {
void Bitboards::init() { void Bitboards::init() {
for (Square s = SQ_A1; s <= SQ_H8; ++s) for (Square s = SQ_A1; s <= SQ_H8; ++s)
BSFTable[bsf_index(SquareBB[s] = 1ULL << s)] = s; {
SquareBB[s] = 1ULL << s;
BSFTable[bsf_index(SquareBB[s])] = s;
}
for (Bitboard b = 1; b < 256; ++b) for (Bitboard b = 1; b < 256; ++b)
MS1BTable[b] = more_than_one(b) ? MS1BTable[b - 1] : lsb(b); MS1BTable[b] = more_than_one(b) ? MS1BTable[b - 1] : lsb(b);
@@ -251,9 +254,8 @@ namespace {
void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[], void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[],
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) { Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) {
int MagicBoosters[][8] = { { 969, 1976, 2850, 542, 2069, 2852, 1708, 164 }, int MagicBoosters[][RANK_NB] = { { 969, 1976, 2850, 542, 2069, 2852, 1708, 164 },
{ 3101, 552, 3555, 926, 834, 26, 2131, 1117 } }; { 3101, 552, 3555, 926, 834, 26, 2131, 1117 } };
RKISS rk; RKISS rk;
Bitboard occupancy[4096], reference[4096], edges, b; Bitboard occupancy[4096], reference[4096], edges, b;
int i, size, booster; int i, size, booster;
@@ -301,7 +303,8 @@ namespace {
// Find a magic for square 's' picking up an (almost) random number // Find a magic for square 's' picking up an (almost) random number
// until we find the one that passes the verification test. // until we find the one that passes the verification test.
do { do {
do magics[s] = rk.magic_rand<Bitboard>(booster); do
magics[s] = rk.magic_rand<Bitboard>(booster);
while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6); while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6);
std::memset(attacks[s], 0, size * sizeof(Bitboard)); std::memset(attacks[s], 0, size * sizeof(Bitboard));

View File

@@ -21,6 +21,8 @@
#ifndef BITBOARD_H_INCLUDED #ifndef BITBOARD_H_INCLUDED
#define BITBOARD_H_INCLUDED #define BITBOARD_H_INCLUDED
#include <string>
#include "types.h" #include "types.h"
namespace Bitboards { namespace Bitboards {

View File

@@ -96,8 +96,7 @@ inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#else #else
__asm__("popcnt %1, %0" : "=r" (b) : "r" (b)); return __builtin_popcountll(b);
return b;
#endif #endif
} }

View File

@@ -1,478 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 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/>.
*/
/*
The code in this file is based on the opening book code in PolyGlot
by Fabien Letouzey. PolyGlot is available under the GNU General
Public License, and can be downloaded from http://wbec-ridderkerk.nl
*/
#include <algorithm>
#include <cassert>
#include "book.h"
#include "misc.h"
#include "movegen.h"
using namespace std;
namespace {
// A Polyglot book is a series of "entries" of 16 bytes. All integers are
// stored in big-endian format, with the highest byte first (regardless of
// size). The entries are ordered according to the key in ascending order.
struct Entry {
uint64_t key;
uint16_t move;
uint16_t count;
uint32_t learn;
};
// Random numbers from PolyGlot, used to compute book hash keys
const union {
Key PolyGlotRandoms[781];
struct {
Key psq[12][64]; // [piece][square]
Key castling[4]; // [castling flag]
Key enpassant[8]; // [file]
Key turn;
} Zobrist;
} PG = {{
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL,
0x1A083822CEAFE02DULL, 0x9605D5F0E25EC3B0ULL, 0xD021FF5CD13A2ED5ULL,
0x40BDF15D4A672E32ULL, 0x011355146FD56395ULL, 0x5DB4832046F3D9E5ULL,
0x239F8B2D7FF719CCULL, 0x05D1A1AE85B49AA1ULL, 0x679F848F6E8FC971ULL,
0x7449BBFF801FED0BULL, 0x7D11CDB1C3B7ADF0ULL, 0x82C7709E781EB7CCULL,
0xF3218F1C9510786CULL, 0x331478F3AF51BBE6ULL, 0x4BB38DE5E7219443ULL,
0xAA649C6EBCFD50FCULL, 0x8DBD98A352AFD40BULL, 0x87D2074B81D79217ULL,
0x19F3C751D3E92AE1ULL, 0xB4AB30F062B19ABFULL, 0x7B0500AC42047AC4ULL,
0xC9452CA81A09D85DULL, 0x24AA6C514DA27500ULL, 0x4C9F34427501B447ULL,
0x14A68FD73C910841ULL, 0xA71B9B83461CBD93ULL, 0x03488B95B0F1850FULL,
0x637B2B34FF93C040ULL, 0x09D1BC9A3DD90A94ULL, 0x3575668334A1DD3BULL,
0x735E2B97A4C45A23ULL, 0x18727070F1BD400BULL, 0x1FCBACD259BF02E7ULL,
0xD310A7C2CE9B6555ULL, 0xBF983FE0FE5D8244ULL, 0x9F74D14F7454A824ULL,
0x51EBDC4AB9BA3035ULL, 0x5C82C505DB9AB0FAULL, 0xFCF7FE8A3430B241ULL,
0x3253A729B9BA3DDEULL, 0x8C74C368081B3075ULL, 0xB9BC6C87167C33E7ULL,
0x7EF48F2B83024E20ULL, 0x11D505D4C351BD7FULL, 0x6568FCA92C76A243ULL,
0x4DE0B0F40F32A7B8ULL, 0x96D693460CC37E5DULL, 0x42E240CB63689F2FULL,
0x6D2BDCDAE2919661ULL, 0x42880B0236E4D951ULL, 0x5F0F4A5898171BB6ULL,
0x39F890F579F92F88ULL, 0x93C5B5F47356388BULL, 0x63DC359D8D231B78ULL,
0xEC16CA8AEA98AD76ULL, 0x5355F900C2A82DC7ULL, 0x07FB9F855A997142ULL,
0x5093417AA8A7ED5EULL, 0x7BCBC38DA25A7F3CULL, 0x19FC8A768CF4B6D4ULL,
0x637A7780DECFC0D9ULL, 0x8249A47AEE0E41F7ULL, 0x79AD695501E7D1E8ULL,
0x14ACBAF4777D5776ULL, 0xF145B6BECCDEA195ULL, 0xDABF2AC8201752FCULL,
0x24C3C94DF9C8D3F6ULL, 0xBB6E2924F03912EAULL, 0x0CE26C0B95C980D9ULL,
0xA49CD132BFBF7CC4ULL, 0xE99D662AF4243939ULL, 0x27E6AD7891165C3FULL,
0x8535F040B9744FF1ULL, 0x54B3F4FA5F40D873ULL, 0x72B12C32127FED2BULL,
0xEE954D3C7B411F47ULL, 0x9A85AC909A24EAA1ULL, 0x70AC4CD9F04F21F5ULL,
0xF9B89D3E99A075C2ULL, 0x87B3E2B2B5C907B1ULL, 0xA366E5B8C54F48B8ULL,
0xAE4A9346CC3F7CF2ULL, 0x1920C04D47267BBDULL, 0x87BF02C6B49E2AE9ULL,
0x092237AC237F3859ULL, 0xFF07F64EF8ED14D0ULL, 0x8DE8DCA9F03CC54EULL,
0x9C1633264DB49C89ULL, 0xB3F22C3D0B0B38EDULL, 0x390E5FB44D01144BULL,
0x5BFEA5B4712768E9ULL, 0x1E1032911FA78984ULL, 0x9A74ACB964E78CB3ULL,
0x4F80F7A035DAFB04ULL, 0x6304D09A0B3738C4ULL, 0x2171E64683023A08ULL,
0x5B9B63EB9CEFF80CULL, 0x506AACF489889342ULL, 0x1881AFC9A3A701D6ULL,
0x6503080440750644ULL, 0xDFD395339CDBF4A7ULL, 0xEF927DBCF00C20F2ULL,
0x7B32F7D1E03680ECULL, 0xB9FD7620E7316243ULL, 0x05A7E8A57DB91B77ULL,
0xB5889C6E15630A75ULL, 0x4A750A09CE9573F7ULL, 0xCF464CEC899A2F8AULL,
0xF538639CE705B824ULL, 0x3C79A0FF5580EF7FULL, 0xEDE6C87F8477609DULL,
0x799E81F05BC93F31ULL, 0x86536B8CF3428A8CULL, 0x97D7374C60087B73ULL,
0xA246637CFF328532ULL, 0x043FCAE60CC0EBA0ULL, 0x920E449535DD359EULL,
0x70EB093B15B290CCULL, 0x73A1921916591CBDULL, 0x56436C9FE1A1AA8DULL,
0xEFAC4B70633B8F81ULL, 0xBB215798D45DF7AFULL, 0x45F20042F24F1768ULL,
0x930F80F4E8EB7462ULL, 0xFF6712FFCFD75EA1ULL, 0xAE623FD67468AA70ULL,
0xDD2C5BC84BC8D8FCULL, 0x7EED120D54CF2DD9ULL, 0x22FE545401165F1CULL,
0xC91800E98FB99929ULL, 0x808BD68E6AC10365ULL, 0xDEC468145B7605F6ULL,
0x1BEDE3A3AEF53302ULL, 0x43539603D6C55602ULL, 0xAA969B5C691CCB7AULL,
0xA87832D392EFEE56ULL, 0x65942C7B3C7E11AEULL, 0xDED2D633CAD004F6ULL,
0x21F08570F420E565ULL, 0xB415938D7DA94E3CULL, 0x91B859E59ECB6350ULL,
0x10CFF333E0ED804AULL, 0x28AED140BE0BB7DDULL, 0xC5CC1D89724FA456ULL,
0x5648F680F11A2741ULL, 0x2D255069F0B7DAB3ULL, 0x9BC5A38EF729ABD4ULL,
0xEF2F054308F6A2BCULL, 0xAF2042F5CC5C2858ULL, 0x480412BAB7F5BE2AULL,
0xAEF3AF4A563DFE43ULL, 0x19AFE59AE451497FULL, 0x52593803DFF1E840ULL,
0xF4F076E65F2CE6F0ULL, 0x11379625747D5AF3ULL, 0xBCE5D2248682C115ULL,
0x9DA4243DE836994FULL, 0x066F70B33FE09017ULL, 0x4DC4DE189B671A1CULL,
0x51039AB7712457C3ULL, 0xC07A3F80C31FB4B4ULL, 0xB46EE9C5E64A6E7CULL,
0xB3819A42ABE61C87ULL, 0x21A007933A522A20ULL, 0x2DF16F761598AA4FULL,
0x763C4A1371B368FDULL, 0xF793C46702E086A0ULL, 0xD7288E012AEB8D31ULL,
0xDE336A2A4BC1C44BULL, 0x0BF692B38D079F23ULL, 0x2C604A7A177326B3ULL,
0x4850E73E03EB6064ULL, 0xCFC447F1E53C8E1BULL, 0xB05CA3F564268D99ULL,
0x9AE182C8BC9474E8ULL, 0xA4FC4BD4FC5558CAULL, 0xE755178D58FC4E76ULL,
0x69B97DB1A4C03DFEULL, 0xF9B5B7C4ACC67C96ULL, 0xFC6A82D64B8655FBULL,
0x9C684CB6C4D24417ULL, 0x8EC97D2917456ED0ULL, 0x6703DF9D2924E97EULL,
0xC547F57E42A7444EULL, 0x78E37644E7CAD29EULL, 0xFE9A44E9362F05FAULL,
0x08BD35CC38336615ULL, 0x9315E5EB3A129ACEULL, 0x94061B871E04DF75ULL,
0xDF1D9F9D784BA010ULL, 0x3BBA57B68871B59DULL, 0xD2B7ADEEDED1F73FULL,
0xF7A255D83BC373F8ULL, 0xD7F4F2448C0CEB81ULL, 0xD95BE88CD210FFA7ULL,
0x336F52F8FF4728E7ULL, 0xA74049DAC312AC71ULL, 0xA2F61BB6E437FDB5ULL,
0x4F2A5CB07F6A35B3ULL, 0x87D380BDA5BF7859ULL, 0x16B9F7E06C453A21ULL,
0x7BA2484C8A0FD54EULL, 0xF3A678CAD9A2E38CULL, 0x39B0BF7DDE437BA2ULL,
0xFCAF55C1BF8A4424ULL, 0x18FCF680573FA594ULL, 0x4C0563B89F495AC3ULL,
0x40E087931A00930DULL, 0x8CFFA9412EB642C1ULL, 0x68CA39053261169FULL,
0x7A1EE967D27579E2ULL, 0x9D1D60E5076F5B6FULL, 0x3810E399B6F65BA2ULL,
0x32095B6D4AB5F9B1ULL, 0x35CAB62109DD038AULL, 0xA90B24499FCFAFB1ULL,
0x77A225A07CC2C6BDULL, 0x513E5E634C70E331ULL, 0x4361C0CA3F692F12ULL,
0xD941ACA44B20A45BULL, 0x528F7C8602C5807BULL, 0x52AB92BEB9613989ULL,
0x9D1DFA2EFC557F73ULL, 0x722FF175F572C348ULL, 0x1D1260A51107FE97ULL,
0x7A249A57EC0C9BA2ULL, 0x04208FE9E8F7F2D6ULL, 0x5A110C6058B920A0ULL,
0x0CD9A497658A5698ULL, 0x56FD23C8F9715A4CULL, 0x284C847B9D887AAEULL,
0x04FEABFBBDB619CBULL, 0x742E1E651C60BA83ULL, 0x9A9632E65904AD3CULL,
0x881B82A13B51B9E2ULL, 0x506E6744CD974924ULL, 0xB0183DB56FFC6A79ULL,
0x0ED9B915C66ED37EULL, 0x5E11E86D5873D484ULL, 0xF678647E3519AC6EULL,
0x1B85D488D0F20CC5ULL, 0xDAB9FE6525D89021ULL, 0x0D151D86ADB73615ULL,
0xA865A54EDCC0F019ULL, 0x93C42566AEF98FFBULL, 0x99E7AFEABE000731ULL,
0x48CBFF086DDF285AULL, 0x7F9B6AF1EBF78BAFULL, 0x58627E1A149BBA21ULL,
0x2CD16E2ABD791E33ULL, 0xD363EFF5F0977996ULL, 0x0CE2A38C344A6EEDULL,
0x1A804AADB9CFA741ULL, 0x907F30421D78C5DEULL, 0x501F65EDB3034D07ULL,
0x37624AE5A48FA6E9ULL, 0x957BAF61700CFF4EULL, 0x3A6C27934E31188AULL,
0xD49503536ABCA345ULL, 0x088E049589C432E0ULL, 0xF943AEE7FEBF21B8ULL,
0x6C3B8E3E336139D3ULL, 0x364F6FFA464EE52EULL, 0xD60F6DCEDC314222ULL,
0x56963B0DCA418FC0ULL, 0x16F50EDF91E513AFULL, 0xEF1955914B609F93ULL,
0x565601C0364E3228ULL, 0xECB53939887E8175ULL, 0xBAC7A9A18531294BULL,
0xB344C470397BBA52ULL, 0x65D34954DAF3CEBDULL, 0xB4B81B3FA97511E2ULL,
0xB422061193D6F6A7ULL, 0x071582401C38434DULL, 0x7A13F18BBEDC4FF5ULL,
0xBC4097B116C524D2ULL, 0x59B97885E2F2EA28ULL, 0x99170A5DC3115544ULL,
0x6F423357E7C6A9F9ULL, 0x325928EE6E6F8794ULL, 0xD0E4366228B03343ULL,
0x565C31F7DE89EA27ULL, 0x30F5611484119414ULL, 0xD873DB391292ED4FULL,
0x7BD94E1D8E17DEBCULL, 0xC7D9F16864A76E94ULL, 0x947AE053EE56E63CULL,
0xC8C93882F9475F5FULL, 0x3A9BF55BA91F81CAULL, 0xD9A11FBB3D9808E4ULL,
0x0FD22063EDC29FCAULL, 0xB3F256D8ACA0B0B9ULL, 0xB03031A8B4516E84ULL,
0x35DD37D5871448AFULL, 0xE9F6082B05542E4EULL, 0xEBFAFA33D7254B59ULL,
0x9255ABB50D532280ULL, 0xB9AB4CE57F2D34F3ULL, 0x693501D628297551ULL,
0xC62C58F97DD949BFULL, 0xCD454F8F19C5126AULL, 0xBBE83F4ECC2BDECBULL,
0xDC842B7E2819E230ULL, 0xBA89142E007503B8ULL, 0xA3BC941D0A5061CBULL,
0xE9F6760E32CD8021ULL, 0x09C7E552BC76492FULL, 0x852F54934DA55CC9ULL,
0x8107FCCF064FCF56ULL, 0x098954D51FFF6580ULL, 0x23B70EDB1955C4BFULL,
0xC330DE426430F69DULL, 0x4715ED43E8A45C0AULL, 0xA8D7E4DAB780A08DULL,
0x0572B974F03CE0BBULL, 0xB57D2E985E1419C7ULL, 0xE8D9ECBE2CF3D73FULL,
0x2FE4B17170E59750ULL, 0x11317BA87905E790ULL, 0x7FBF21EC8A1F45ECULL,
0x1725CABFCB045B00ULL, 0x964E915CD5E2B207ULL, 0x3E2B8BCBF016D66DULL,
0xBE7444E39328A0ACULL, 0xF85B2B4FBCDE44B7ULL, 0x49353FEA39BA63B1ULL,
0x1DD01AAFCD53486AULL, 0x1FCA8A92FD719F85ULL, 0xFC7C95D827357AFAULL,
0x18A6A990C8B35EBDULL, 0xCCCB7005C6B9C28DULL, 0x3BDBB92C43B17F26ULL,
0xAA70B5B4F89695A2ULL, 0xE94C39A54A98307FULL, 0xB7A0B174CFF6F36EULL,
0xD4DBA84729AF48ADULL, 0x2E18BC1AD9704A68ULL, 0x2DE0966DAF2F8B1CULL,
0xB9C11D5B1E43A07EULL, 0x64972D68DEE33360ULL, 0x94628D38D0C20584ULL,
0xDBC0D2B6AB90A559ULL, 0xD2733C4335C6A72FULL, 0x7E75D99D94A70F4DULL,
0x6CED1983376FA72BULL, 0x97FCAACBF030BC24ULL, 0x7B77497B32503B12ULL,
0x8547EDDFB81CCB94ULL, 0x79999CDFF70902CBULL, 0xCFFE1939438E9B24ULL,
0x829626E3892D95D7ULL, 0x92FAE24291F2B3F1ULL, 0x63E22C147B9C3403ULL,
0xC678B6D860284A1CULL, 0x5873888850659AE7ULL, 0x0981DCD296A8736DULL,
0x9F65789A6509A440ULL, 0x9FF38FED72E9052FULL, 0xE479EE5B9930578CULL,
0xE7F28ECD2D49EECDULL, 0x56C074A581EA17FEULL, 0x5544F7D774B14AEFULL,
0x7B3F0195FC6F290FULL, 0x12153635B2C0CF57ULL, 0x7F5126DBBA5E0CA7ULL,
0x7A76956C3EAFB413ULL, 0x3D5774A11D31AB39ULL, 0x8A1B083821F40CB4ULL,
0x7B4A38E32537DF62ULL, 0x950113646D1D6E03ULL, 0x4DA8979A0041E8A9ULL,
0x3BC36E078F7515D7ULL, 0x5D0A12F27AD310D1ULL, 0x7F9D1A2E1EBE1327ULL,
0xDA3A361B1C5157B1ULL, 0xDCDD7D20903D0C25ULL, 0x36833336D068F707ULL,
0xCE68341F79893389ULL, 0xAB9090168DD05F34ULL, 0x43954B3252DC25E5ULL,
0xB438C2B67F98E5E9ULL, 0x10DCD78E3851A492ULL, 0xDBC27AB5447822BFULL,
0x9B3CDB65F82CA382ULL, 0xB67B7896167B4C84ULL, 0xBFCED1B0048EAC50ULL,
0xA9119B60369FFEBDULL, 0x1FFF7AC80904BF45ULL, 0xAC12FB171817EEE7ULL,
0xAF08DA9177DDA93DULL, 0x1B0CAB936E65C744ULL, 0xB559EB1D04E5E932ULL,
0xC37B45B3F8D6F2BAULL, 0xC3A9DC228CAAC9E9ULL, 0xF3B8B6675A6507FFULL,
0x9FC477DE4ED681DAULL, 0x67378D8ECCEF96CBULL, 0x6DD856D94D259236ULL,
0xA319CE15B0B4DB31ULL, 0x073973751F12DD5EULL, 0x8A8E849EB32781A5ULL,
0xE1925C71285279F5ULL, 0x74C04BF1790C0EFEULL, 0x4DDA48153C94938AULL,
0x9D266D6A1CC0542CULL, 0x7440FB816508C4FEULL, 0x13328503DF48229FULL,
0xD6BF7BAEE43CAC40ULL, 0x4838D65F6EF6748FULL, 0x1E152328F3318DEAULL,
0x8F8419A348F296BFULL, 0x72C8834A5957B511ULL, 0xD7A023A73260B45CULL,
0x94EBC8ABCFB56DAEULL, 0x9FC10D0F989993E0ULL, 0xDE68A2355B93CAE6ULL,
0xA44CFE79AE538BBEULL, 0x9D1D84FCCE371425ULL, 0x51D2B1AB2DDFB636ULL,
0x2FD7E4B9E72CD38CULL, 0x65CA5B96B7552210ULL, 0xDD69A0D8AB3B546DULL,
0x604D51B25FBF70E2ULL, 0x73AA8A564FB7AC9EULL, 0x1A8C1E992B941148ULL,
0xAAC40A2703D9BEA0ULL, 0x764DBEAE7FA4F3A6ULL, 0x1E99B96E70A9BE8BULL,
0x2C5E9DEB57EF4743ULL, 0x3A938FEE32D29981ULL, 0x26E6DB8FFDF5ADFEULL,
0x469356C504EC9F9DULL, 0xC8763C5B08D1908CULL, 0x3F6C6AF859D80055ULL,
0x7F7CC39420A3A545ULL, 0x9BFB227EBDF4C5CEULL, 0x89039D79D6FC5C5CULL,
0x8FE88B57305E2AB6ULL, 0xA09E8C8C35AB96DEULL, 0xFA7E393983325753ULL,
0xD6B6D0ECC617C699ULL, 0xDFEA21EA9E7557E3ULL, 0xB67C1FA481680AF8ULL,
0xCA1E3785A9E724E5ULL, 0x1CFC8BED0D681639ULL, 0xD18D8549D140CAEAULL,
0x4ED0FE7E9DC91335ULL, 0xE4DBF0634473F5D2ULL, 0x1761F93A44D5AEFEULL,
0x53898E4C3910DA55ULL, 0x734DE8181F6EC39AULL, 0x2680B122BAA28D97ULL,
0x298AF231C85BAFABULL, 0x7983EED3740847D5ULL, 0x66C1A2A1A60CD889ULL,
0x9E17E49642A3E4C1ULL, 0xEDB454E7BADC0805ULL, 0x50B704CAB602C329ULL,
0x4CC317FB9CDDD023ULL, 0x66B4835D9EAFEA22ULL, 0x219B97E26FFC81BDULL,
0x261E4E4C0A333A9DULL, 0x1FE2CCA76517DB90ULL, 0xD7504DFA8816EDBBULL,
0xB9571FA04DC089C8ULL, 0x1DDC0325259B27DEULL, 0xCF3F4688801EB9AAULL,
0xF4F5D05C10CAB243ULL, 0x38B6525C21A42B0EULL, 0x36F60E2BA4FA6800ULL,
0xEB3593803173E0CEULL, 0x9C4CD6257C5A3603ULL, 0xAF0C317D32ADAA8AULL,
0x258E5A80C7204C4BULL, 0x8B889D624D44885DULL, 0xF4D14597E660F855ULL,
0xD4347F66EC8941C3ULL, 0xE699ED85B0DFB40DULL, 0x2472F6207C2D0484ULL,
0xC2A1E7B5B459AEB5ULL, 0xAB4F6451CC1D45ECULL, 0x63767572AE3D6174ULL,
0xA59E0BD101731A28ULL, 0x116D0016CB948F09ULL, 0x2CF9C8CA052F6E9FULL,
0x0B090A7560A968E3ULL, 0xABEEDDB2DDE06FF1ULL, 0x58EFC10B06A2068DULL,
0xC6E57A78FBD986E0ULL, 0x2EAB8CA63CE802D7ULL, 0x14A195640116F336ULL,
0x7C0828DD624EC390ULL, 0xD74BBE77E6116AC7ULL, 0x804456AF10F5FB53ULL,
0xEBE9EA2ADF4321C7ULL, 0x03219A39EE587A30ULL, 0x49787FEF17AF9924ULL,
0xA1E9300CD8520548ULL, 0x5B45E522E4B1B4EFULL, 0xB49C3B3995091A36ULL,
0xD4490AD526F14431ULL, 0x12A8F216AF9418C2ULL, 0x001F837CC7350524ULL,
0x1877B51E57A764D5ULL, 0xA2853B80F17F58EEULL, 0x993E1DE72D36D310ULL,
0xB3598080CE64A656ULL, 0x252F59CF0D9F04BBULL, 0xD23C8E176D113600ULL,
0x1BDA0492E7E4586EULL, 0x21E0BD5026C619BFULL, 0x3B097ADAF088F94EULL,
0x8D14DEDB30BE846EULL, 0xF95CFFA23AF5F6F4ULL, 0x3871700761B3F743ULL,
0xCA672B91E9E4FA16ULL, 0x64C8E531BFF53B55ULL, 0x241260ED4AD1E87DULL,
0x106C09B972D2E822ULL, 0x7FBA195410E5CA30ULL, 0x7884D9BC6CB569D8ULL,
0x0647DFEDCD894A29ULL, 0x63573FF03E224774ULL, 0x4FC8E9560F91B123ULL,
0x1DB956E450275779ULL, 0xB8D91274B9E9D4FBULL, 0xA2EBEE47E2FBFCE1ULL,
0xD9F1F30CCD97FB09ULL, 0xEFED53D75FD64E6BULL, 0x2E6D02C36017F67FULL,
0xA9AA4D20DB084E9BULL, 0xB64BE8D8B25396C1ULL, 0x70CB6AF7C2D5BCF0ULL,
0x98F076A4F7A2322EULL, 0xBF84470805E69B5FULL, 0x94C3251F06F90CF3ULL,
0x3E003E616A6591E9ULL, 0xB925A6CD0421AFF3ULL, 0x61BDD1307C66E300ULL,
0xBF8D5108E27E0D48ULL, 0x240AB57A8B888B20ULL, 0xFC87614BAF287E07ULL,
0xEF02CDD06FFDB432ULL, 0xA1082C0466DF6C0AULL, 0x8215E577001332C8ULL,
0xD39BB9C3A48DB6CFULL, 0x2738259634305C14ULL, 0x61CF4F94C97DF93DULL,
0x1B6BACA2AE4E125BULL, 0x758F450C88572E0BULL, 0x959F587D507A8359ULL,
0xB063E962E045F54DULL, 0x60E8ED72C0DFF5D1ULL, 0x7B64978555326F9FULL,
0xFD080D236DA814BAULL, 0x8C90FD9B083F4558ULL, 0x106F72FE81E2C590ULL,
0x7976033A39F7D952ULL, 0xA4EC0132764CA04BULL, 0x733EA705FAE4FA77ULL,
0xB4D8F77BC3E56167ULL, 0x9E21F4F903B33FD9ULL, 0x9D765E419FB69F6DULL,
0xD30C088BA61EA5EFULL, 0x5D94337FBFAF7F5BULL, 0x1A4E4822EB4D7A59ULL,
0x6FFE73E81B637FB3ULL, 0xDDF957BC36D8B9CAULL, 0x64D0E29EEA8838B3ULL,
0x08DD9BDFD96B9F63ULL, 0x087E79E5A57D1D13ULL, 0xE328E230E3E2B3FBULL,
0x1C2559E30F0946BEULL, 0x720BF5F26F4D2EAAULL, 0xB0774D261CC609DBULL,
0x443F64EC5A371195ULL, 0x4112CF68649A260EULL, 0xD813F2FAB7F5C5CAULL,
0x660D3257380841EEULL, 0x59AC2C7873F910A3ULL, 0xE846963877671A17ULL,
0x93B633ABFA3469F8ULL, 0xC0C0F5A60EF4CDCFULL, 0xCAF21ECD4377B28CULL,
0x57277707199B8175ULL, 0x506C11B9D90E8B1DULL, 0xD83CC2687A19255FULL,
0x4A29C6465A314CD1ULL, 0xED2DF21216235097ULL, 0xB5635C95FF7296E2ULL,
0x22AF003AB672E811ULL, 0x52E762596BF68235ULL, 0x9AEBA33AC6ECC6B0ULL,
0x944F6DE09134DFB6ULL, 0x6C47BEC883A7DE39ULL, 0x6AD047C430A12104ULL,
0xA5B1CFDBA0AB4067ULL, 0x7C45D833AFF07862ULL, 0x5092EF950A16DA0BULL,
0x9338E69C052B8E7BULL, 0x455A4B4CFE30E3F5ULL, 0x6B02E63195AD0CF8ULL,
0x6B17B224BAD6BF27ULL, 0xD1E0CCD25BB9C169ULL, 0xDE0C89A556B9AE70ULL,
0x50065E535A213CF6ULL, 0x9C1169FA2777B874ULL, 0x78EDEFD694AF1EEDULL,
0x6DC93D9526A50E68ULL, 0xEE97F453F06791EDULL, 0x32AB0EDB696703D3ULL,
0x3A6853C7E70757A7ULL, 0x31865CED6120F37DULL, 0x67FEF95D92607890ULL,
0x1F2B1D1F15F6DC9CULL, 0xB69E38A8965C6B65ULL, 0xAA9119FF184CCCF4ULL,
0xF43C732873F24C13ULL, 0xFB4A3D794A9A80D2ULL, 0x3550C2321FD6109CULL,
0x371F77E76BB8417EULL, 0x6BFA9AAE5EC05779ULL, 0xCD04F3FF001A4778ULL,
0xE3273522064480CAULL, 0x9F91508BFFCFC14AULL, 0x049A7F41061A9E60ULL,
0xFCB6BE43A9F2FE9BULL, 0x08DE8A1C7797DA9BULL, 0x8F9887E6078735A1ULL,
0xB5B4071DBFC73A66ULL, 0x230E343DFBA08D33ULL, 0x43ED7F5A0FAE657DULL,
0x3A88A0FBBCB05C63ULL, 0x21874B8B4D2DBC4FULL, 0x1BDEA12E35F6A8C9ULL,
0x53C065C6C8E63528ULL, 0xE34A1D250E7A8D6BULL, 0xD6B04D3B7651DD7EULL,
0x5E90277E7CB39E2DULL, 0x2C046F22062DC67DULL, 0xB10BB459132D0A26ULL,
0x3FA9DDFB67E2F199ULL, 0x0E09B88E1914F7AFULL, 0x10E8B35AF3EEAB37ULL,
0x9EEDECA8E272B933ULL, 0xD4C718BC4AE8AE5FULL, 0x81536D601170FC20ULL,
0x91B534F885818A06ULL, 0xEC8177F83F900978ULL, 0x190E714FADA5156EULL,
0xB592BF39B0364963ULL, 0x89C350C893AE7DC1ULL, 0xAC042E70F8B383F2ULL,
0xB49B52E587A1EE60ULL, 0xFB152FE3FF26DA89ULL, 0x3E666E6F69AE2C15ULL,
0x3B544EBE544C19F9ULL, 0xE805A1E290CF2456ULL, 0x24B33C9D7ED25117ULL,
0xE74733427B72F0C1ULL, 0x0A804D18B7097475ULL, 0x57E3306D881EDB4FULL,
0x4AE7D6A36EB5DBCBULL, 0x2D8D5432157064C8ULL, 0xD1E649DE1E7F268BULL,
0x8A328A1CEDFE552CULL, 0x07A3AEC79624C7DAULL, 0x84547DDC3E203C94ULL,
0x990A98FD5071D263ULL, 0x1A4FF12616EEFC89ULL, 0xF6F7FD1431714200ULL,
0x30C05B1BA332F41CULL, 0x8D2636B81555A786ULL, 0x46C9FEB55D120902ULL,
0xCCEC0A73B49C9921ULL, 0x4E9D2827355FC492ULL, 0x19EBB029435DCB0FULL,
0x4659D2B743848A2CULL, 0x963EF2C96B33BE31ULL, 0x74F85198B05A2E7DULL,
0x5A0F544DD2B1FB18ULL, 0x03727073C2E134B1ULL, 0xC7F6AA2DE59AEA61ULL,
0x352787BAA0D7C22FULL, 0x9853EAB63B5E0B35ULL, 0xABBDCDD7ED5C0860ULL,
0xCF05DAF5AC8D77B0ULL, 0x49CAD48CEBF4A71EULL, 0x7A4C10EC2158C4A6ULL,
0xD9E92AA246BF719EULL, 0x13AE978D09FE5557ULL, 0x730499AF921549FFULL,
0x4E4B705B92903BA4ULL, 0xFF577222C14F0A3AULL, 0x55B6344CF97AAFAEULL,
0xB862225B055B6960ULL, 0xCAC09AFBDDD2CDB4ULL, 0xDAF8E9829FE96B5FULL,
0xB5FDFC5D3132C498ULL, 0x310CB380DB6F7503ULL, 0xE87FBB46217A360EULL,
0x2102AE466EBB1148ULL, 0xF8549E1A3AA5E00DULL, 0x07A69AFDCC42261AULL,
0xC4C118BFE78FEAAEULL, 0xF9F4892ED96BD438ULL, 0x1AF3DBE25D8F45DAULL,
0xF5B4B0B0D2DEEEB4ULL, 0x962ACEEFA82E1C84ULL, 0x046E3ECAAF453CE9ULL,
0xF05D129681949A4CULL, 0x964781CE734B3C84ULL, 0x9C2ED44081CE5FBDULL,
0x522E23F3925E319EULL, 0x177E00F9FC32F791ULL, 0x2BC60A63A6F3B3F2ULL,
0x222BBFAE61725606ULL, 0x486289DDCC3D6780ULL, 0x7DC7785B8EFDFC80ULL,
0x8AF38731C02BA980ULL, 0x1FAB64EA29A2DDF7ULL, 0xE4D9429322CD065AULL,
0x9DA058C67844F20CULL, 0x24C0E332B70019B0ULL, 0x233003B5A6CFE6ADULL,
0xD586BD01C5C217F6ULL, 0x5E5637885F29BC2BULL, 0x7EBA726D8C94094BULL,
0x0A56A5F0BFE39272ULL, 0xD79476A84EE20D06ULL, 0x9E4C1269BAA4BF37ULL,
0x17EFEE45B0DEE640ULL, 0x1D95B0A5FCF90BC6ULL, 0x93CBE0B699C2585DULL,
0x65FA4F227A2B6D79ULL, 0xD5F9E858292504D5ULL, 0xC2B5A03F71471A6FULL,
0x59300222B4561E00ULL, 0xCE2F8642CA0712DCULL, 0x7CA9723FBB2E8988ULL,
0x2785338347F2BA08ULL, 0xC61BB3A141E50E8CULL, 0x150F361DAB9DEC26ULL,
0x9F6A419D382595F4ULL, 0x64A53DC924FE7AC9ULL, 0x142DE49FFF7A7C3DULL,
0x0C335248857FA9E7ULL, 0x0A9C32D5EAE45305ULL, 0xE6C42178C4BBB92EULL,
0x71F1CE2490D20B07ULL, 0xF1BCC3D275AFE51AULL, 0xE728E8C83C334074ULL,
0x96FBF83A12884624ULL, 0x81A1549FD6573DA5ULL, 0x5FA7867CAF35E149ULL,
0x56986E2EF3ED091BULL, 0x917F1DD5F8886C61ULL, 0xD20D8C88C8FFE65FULL,
0x31D71DCE64B2C310ULL, 0xF165B587DF898190ULL, 0xA57E6339DD2CF3A0ULL,
0x1EF6E6DBB1961EC9ULL, 0x70CC73D90BC26E24ULL, 0xE21A6B35DF0C3AD7ULL,
0x003A93D8B2806962ULL, 0x1C99DED33CB890A1ULL, 0xCF3145DE0ADD4289ULL,
0xD0E4427A5514FB72ULL, 0x77C621CC9FB3A483ULL, 0x67A34DAC4356550BULL,
0xF8D626AAAF278509ULL
}};
// polyglot_key() returns the PolyGlot hash key of the given position
Key polyglot_key(const Position& pos) {
Key key = 0;
Bitboard b = pos.pieces();
while (b)
{
Square s = pop_lsb(&b);
Piece pc = pos.piece_on(s);
// PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11
key ^= PG.Zobrist.psq[2 * (type_of(pc) - 1) + (color_of(pc) == WHITE)][s];
}
b = pos.can_castle(ANY_CASTLING);
while (b)
key ^= PG.Zobrist.castling[pop_lsb(&b)];
if (pos.ep_square() != SQ_NONE)
key ^= PG.Zobrist.enpassant[file_of(pos.ep_square())];
if (pos.side_to_move() == WHITE)
key ^= PG.Zobrist.turn;
return key;
}
} // namespace
PolyglotBook::PolyglotBook() : rkiss(Time::now() % 10000) {}
PolyglotBook::~PolyglotBook() { if (is_open()) close(); }
/// operator>>() reads sizeof(T) chars from the file's binary byte stream and
/// converts them into a number of type T. A Polyglot book stores numbers in
/// big-endian format.
template<typename T> PolyglotBook& PolyglotBook::operator>>(T& n) {
n = 0;
for (size_t i = 0; i < sizeof(T); ++i)
n = T((n << 8) + ifstream::get());
return *this;
}
template<> PolyglotBook& PolyglotBook::operator>>(Entry& e) {
return *this >> e.key >> e.move >> e.count >> e.learn;
}
/// open() tries to open a book file with the given name after closing any
/// existing one.
bool PolyglotBook::open(const char* fName) {
if (is_open()) // Cannot close an already closed file
close();
ifstream::open(fName, ifstream::in | ifstream::binary);
fileName = is_open() ? fName : "";
ifstream::clear(); // Reset any error flag to allow a retry ifstream::open()
return !fileName.empty();
}
/// probe() tries to find a book move for the given position. If no move is
/// found, it returns MOVE_NONE. If pickBest is true, then it always returns
/// the highest-rated move, otherwise it randomly chooses one based on the
/// move score.
Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest) {
if (fileName != fName && !open(fName.c_str()))
return MOVE_NONE;
Entry e;
uint16_t best = 0;
unsigned sum = 0;
Move move = MOVE_NONE;
Key key = polyglot_key(pos);
seekg(find_first(key) * sizeof(Entry), ios_base::beg);
while (*this >> e, e.key == key && good())
{
best = max(best, e.count);
sum += e.count;
// Choose book move according to its score. If a move has a very high
// score it has a higher probability of being choosen than a move with
// a lower score. Note that first entry is always chosen.
if ( (!pickBest && sum && rkiss.rand<unsigned>() % sum < e.count)
|| (pickBest && e.count == best))
move = Move(e.move);
}
if (!move)
return MOVE_NONE;
// A PolyGlot book move is encoded as follows:
//
// bit 0- 5: destination square (from 0 to 63)
// bit 6-11: origin square (from 0 to 63)
// bit 12-14: promotion piece (from KNIGHT == 1 to QUEEN == 4)
//
// Castling moves follow the "king captures rook" representation. If a book
// move is a promotion, we have to convert it to our representation and in
// all other cases, we can directly compare with a Move after having masked
// out the special Move flags (bit 14-15) that are not supported by PolyGlot.
int pt = (move >> 12) & 7;
if (pt)
move = make<PROMOTION>(from_sq(move), to_sq(move), PieceType(pt + 1));
// Add 'special move' flags and verify it is legal
for (MoveList<LEGAL> it(pos); *it; ++it)
if (move == (*it ^ type_of(*it)))
return *it;
return MOVE_NONE;
}
/// find_first() takes a book key as input, and does a binary search through
/// the book file for the given key. Returns the index of the leftmost book
/// entry with the same key as the input.
size_t PolyglotBook::find_first(Key key) {
seekg(0, ios::end); // Move pointer to end, so tellg() gets file's size
size_t low = 0, mid, high = (size_t)tellg() / sizeof(Entry) - 1;
Entry e;
assert(low <= high);
while (low < high && good())
{
mid = (low + high) / 2;
assert(mid >= low && mid < high);
seekg(mid * sizeof(Entry), ios_base::beg);
*this >> e;
if (key <= e.key)
high = mid;
else
low = mid + 1;
}
assert(low == high);
return low;
}

View File

@@ -1,45 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 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/>.
*/
#ifndef BOOK_H_INCLUDED
#define BOOK_H_INCLUDED
#include <fstream>
#include <string>
#include "position.h"
#include "rkiss.h"
class PolyglotBook : private std::ifstream {
public:
PolyglotBook();
~PolyglotBook();
Move probe(const Position& pos, const std::string& fName, bool pickBest);
private:
template<typename T> PolyglotBook& operator>>(T& n);
bool open(const char* fName);
size_t find_first(Key key);
RKISS rkiss;
std::string fileName;
};
#endif // #ifndef BOOK_H_INCLUDED

View File

@@ -76,7 +76,7 @@ namespace {
namespace Tracing { namespace Tracing {
enum Terms { // First 8 entries are for PieceType enum Terms { // First 8 entries are for PieceType
PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERMS_NB MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERMS_NB
}; };
Score terms[COLOR_NB][TERMS_NB]; Score terms[COLOR_NB][TERMS_NB];
@@ -89,23 +89,15 @@ namespace {
std::string do_trace(const Position& pos); std::string do_trace(const Position& pos);
} }
// Evaluation weights, initialized from UCI options // Evaluation weights, indexed by evaluation term
enum { Mobility, PawnStructure, PassedPawns, Space, KingDangerUs, KingDangerThem }; enum { Mobility, PawnStructure, PassedPawns, Space, KingSafety };
struct Weight { int mg, eg; } Weights[6]; const struct Weight { int mg, eg; } Weights[] = {
{289, 344}, {233, 201}, {221, 273}, {46, 0}, {318, 0}
};
typedef Value V; typedef Value V;
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
// Internal evaluation weights. These are applied on top of the evaluation
// weights read from UCI parameters. The purpose is to be able to change
// the evaluation weights while keeping the default values of the UCI
// parameters at 100, which looks prettier.
//
// Values modified by Joona Kiiski
const Score WeightsInternal[] = {
S(289, 344), S(233, 201), S(221, 273), S(46, 0), S(271, 0), S(307, 0)
};
// MobilityBonus[PieceType][attacked] contains bonuses for middle and end // MobilityBonus[PieceType][attacked] contains bonuses for middle and end
// game, indexed by piece type and number of attacked squares not occupied by // game, indexed by piece type and number of attacked squares not occupied by
// friendly pieces. // friendly pieces.
@@ -155,27 +147,27 @@ namespace {
// ThreatenedByPawn[PieceType] contains a penalty according to which piece // ThreatenedByPawn[PieceType] contains a penalty according to which piece
// type is attacked by an enemy pawn. // type is attacked by an enemy pawn.
const Score ThreatenedByPawn[] = { const Score ThreatenedByPawn[] = {
S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118) S(0, 0), S(0, 0), S(80, 119), S(80, 119), S(117, 199), S(127, 218)
}; };
// Hanging[side to move] contains a bonus for each enemy hanging piece // Assorted bonuses and penalties used by evaluation
const Score Hanging[2] = { S(23, 20) , S(35, 45) }; const Score KingOnOne = S(2 , 58);
const Score KingOnMany = S(6 ,125);
#undef S const Score RookOnPawn = S(10, 28);
const Score RookOpenFile = S(43, 21);
const Score Tempo = make_score(24, 11); const Score RookSemiOpenFile = S(19, 10);
const Score RookOnPawn = make_score(10, 28); const Score BishopPawns = S( 8, 12);
const Score RookOpenFile = make_score(43, 21); const Score MinorBehindPawn = S(16, 0);
const Score RookSemiopenFile = make_score(19, 10); const Score TrappedRook = S(92, 0);
const Score BishopPawns = make_score( 8, 12); const Score Unstoppable = S( 0, 20);
const Score MinorBehindPawn = make_score(16, 0); const Score Hanging = S(23, 20);
const Score TrappedRook = make_score(90, 0);
const Score Unstoppable = make_score( 0, 20);
// 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.
const Score TrappedBishopA1H1 = make_score(50, 50); const Score TrappedBishopA1H1 = S(50, 50);
#undef S
// SpaceMask[Color] contains the area of the board which is considered // SpaceMask[Color] contains the area of the board which is considered
// by the space evaluation. In the middlegame, each side is given a bonus // by the space evaluation. In the middlegame, each side is given a bonus
@@ -202,10 +194,11 @@ namespace {
const int BishopCheck = 2; const int BishopCheck = 2;
const int KnightCheck = 3; const int KnightCheck = 3;
// KingDanger[Color][attackUnits] contains the actual king danger weighted // KingDanger[attackUnits] contains the actual king danger weighted
// scores, indexed by color and by a calculated integer number. // scores, indexed by a calculated integer number.
Score KingDanger[COLOR_NB][128]; Score KingDanger[128];
const int ScalePawnSpan[2] = { 38, 56 };
// apply_weight() weighs score 'v' by weight 'w' trying to prevent overflow // apply_weight() weighs score 'v' by weight 'w' trying to prevent overflow
Score apply_weight(Score v, const Weight& w) { Score apply_weight(Score v, const Weight& w) {
@@ -213,17 +206,6 @@ namespace {
} }
// weight_option() computes the value of an evaluation weight, by combining
// two UCI-configurable weights (midgame and endgame) with an internal weight.
Weight weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) {
Weight w = { Options[mgOpt] * mg_value(internalWeight) / 100,
Options[egOpt] * eg_value(internalWeight) / 100 };
return w;
}
// init_eval_info() initializes king bitboards for given color adding // init_eval_info() initializes king bitboards for given color adding
// pawn attacks. To be done at the beginning of the evaluation. // pawn attacks. To be done at the beginning of the evaluation.
@@ -239,7 +221,7 @@ namespace {
ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us); ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us);
// 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.count<QUEEN>(Us) && pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg) if (pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg)
{ {
ei.kingRing[Them] = b | shift_bb<Down>(b); ei.kingRing[Them] = b | shift_bb<Down>(b);
b &= ei.attackedBy[Us][PAWN]; b &= ei.attackedBy[Us][PAWN];
@@ -251,10 +233,10 @@ namespace {
} }
// evaluate_outposts() evaluates bishop and knight outpost squares // evaluate_outpost() evaluates bishop and knight outpost squares
template<PieceType Pt, Color Us> template<PieceType Pt, Color Us>
Score evaluate_outposts(const Position& pos, EvalInfo& ei, Square s) { Score evaluate_outpost(const Position& pos, const EvalInfo& ei, Square s) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -274,7 +256,7 @@ namespace {
bonus += bonus / 2; bonus += bonus / 2;
} }
return make_score(bonus, bonus); return make_score(bonus * 2, bonus / 2);
} }
@@ -335,9 +317,9 @@ namespace {
if (Pt == BISHOP) if (Pt == BISHOP)
score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s); score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s);
// Bishop and knight outposts squares // Bishop and knight outpost square
if (!(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s))) if (!(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s)))
score += evaluate_outposts<Pt, Us>(pos, ei, s); score += evaluate_outpost<Pt, Us>(pos, ei, s);
// Bishop or knight behind a pawn // Bishop or knight behind a pawn
if ( relative_rank(Us, s) < RANK_5 if ( relative_rank(Us, s) < RANK_5
@@ -357,7 +339,7 @@ namespace {
// Give a bonus for a rook on a open or semi-open file // Give a bonus for a rook on a open or semi-open file
if (ei.pi->semiopen_file(Us, file_of(s))) if (ei.pi->semiopen_file(Us, file_of(s)))
score += ei.pi->semiopen_file(Them, file_of(s)) ? RookOpenFile : RookSemiopenFile; score += ei.pi->semiopen_file(Them, file_of(s)) ? RookOpenFile : RookSemiOpenFile;
if (mob > 3 || ei.pi->semiopen_file(Us, file_of(s))) if (mob > 3 || ei.pi->semiopen_file(Us, file_of(s)))
continue; continue;
@@ -369,7 +351,7 @@ namespace {
if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq))) if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq)))
&& (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1) && (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1)
&& !ei.pi->semiopen_side(Us, file_of(ksq), file_of(s) < file_of(ksq))) && !ei.pi->semiopen_side(Us, file_of(ksq), file_of(s) < file_of(ksq)))
score -= (TrappedRook - make_score(mob * 8, 0)) * (1 + !pos.can_castle(Us)); score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.can_castle(Us));
} }
// An important Chess960 pattern: A cornered bishop blocked by a friendly // An important Chess960 pattern: A cornered bishop blocked by a friendly
@@ -432,7 +414,8 @@ namespace {
attackUnits = std::min(20, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) attackUnits = std::min(20, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
+ 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount<Max15>(undefended)) + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount<Max15>(undefended))
+ 2 * (ei.pinnedPieces[Us] != 0) + 2 * (ei.pinnedPieces[Us] != 0)
- mg_value(score) / 32; - mg_value(score) / 32
- !pos.count<QUEEN>(Them) * 15;
// Analyse the enemy's safe queen contact checks. Firstly, find the // Analyse the enemy's safe queen contact checks. Firstly, find the
// undefended squares around the king that are attacked by the enemy's // undefended squares around the king that are attacked by the enemy's
@@ -445,9 +428,7 @@ namespace {
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]); | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]);
if (b) if (b)
attackUnits += QueenContactCheck attackUnits += QueenContactCheck * popcount<Max15>(b);
* popcount<Max15>(b)
* (Them == pos.side_to_move() ? 2 : 1);
} }
// Analyse the enemy's safe rook contact checks. Firstly, find the // Analyse the enemy's safe rook contact checks. Firstly, find the
@@ -465,9 +446,7 @@ namespace {
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]); | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]);
if (b) if (b)
attackUnits += RookContactCheck attackUnits += RookContactCheck * popcount<Max15>(b);
* popcount<Max15>(b)
* (Them == pos.side_to_move() ? 2 : 1);
} }
// Analyse the enemy's safe distance checks for sliders and knights // Analyse the enemy's safe distance checks for sliders and knights
@@ -501,7 +480,7 @@ namespace {
// Finally, extract the king danger score from the KingDanger[] // Finally, extract the king danger score from the KingDanger[]
// array and subtract the score from evaluation. // array and subtract the score from evaluation.
score -= KingDanger[Us == Search::RootColor][attackUnits]; score -= KingDanger[attackUnits];
} }
if (Trace) if (Trace)
@@ -519,8 +498,17 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b, weakEnemies; Bitboard b, weakEnemies, protectedEnemies;
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
enum { Minor, Major };
// Protected enemies
protectedEnemies = (pos.pieces(Them) ^ pos.pieces(Them,PAWN))
& ei.attackedBy[Them][PAWN]
& (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]);
if (protectedEnemies)
score += Threat[Minor][type_of(pos.piece_on(lsb(protectedEnemies)))];
// Enemies not defended by a pawn and under our attack // Enemies not defended by a pawn and under our attack
weakEnemies = pos.pieces(Them) weakEnemies = pos.pieces(Them)
@@ -530,18 +518,21 @@ namespace {
// Add a bonus according if the attacking pieces are minor or major // Add a bonus according if the attacking pieces are minor or major
if (weakEnemies) if (weakEnemies)
{ {
b = weakEnemies & (ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]); b = weakEnemies & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]);
if (b) if (b)
score += Threat[0][type_of(pos.piece_on(lsb(b)))]; score += Threat[Minor][type_of(pos.piece_on(lsb(b)))];
b = weakEnemies & (ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][QUEEN]); b = weakEnemies & (ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][QUEEN]);
if (b) if (b)
score += Threat[1][type_of(pos.piece_on(lsb(b)))]; score += Threat[Major][type_of(pos.piece_on(lsb(b)))];
b = weakEnemies & ~ei.attackedBy[Them][ALL_PIECES]; b = weakEnemies & ~ei.attackedBy[Them][ALL_PIECES];
if (b) if (b)
score += more_than_one(b) ? Hanging[Us != pos.side_to_move()] * popcount<Max15>(b) score += more_than_one(b) ? Hanging * popcount<Max15>(b) : Hanging;
: Hanging[Us == pos.side_to_move()];
b = weakEnemies & ei.attackedBy[Us][KING];
if (b)
score += more_than_one(b) ? KingOnMany : KingOnOne;
} }
if (Trace) if (Trace)
@@ -590,22 +581,18 @@ namespace {
// If the pawn is free to advance, then increase the bonus // If the pawn is free to advance, then increase the bonus
if (pos.empty(blockSq)) if (pos.empty(blockSq))
{ {
squaresToQueen = forward_bb(Us, s); // If there is a rook or queen attacking/defending the pawn from behind,
// consider all the squaresToQueen. Otherwise consider only the squares
// in the pawn's path attacked or occupied by the enemy.
defendedSquares = unsafeSquares = squaresToQueen = forward_bb(Us, s);
// If there is an enemy rook or queen attacking the pawn from behind, Bitboard bb = forward_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from<ROOK>(s);
// add all X-ray attacks by the rook or queen. Otherwise consider only
// the squares in the pawn's path attacked or occupied by the enemy.
if ( unlikely(forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN))
&& (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN) & pos.attacks_from<ROOK>(s)))
unsafeSquares = squaresToQueen;
else
unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
if ( unlikely(forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN)) if (!(pos.pieces(Us) & bb))
&& (forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN) & pos.attacks_from<ROOK>(s))) defendedSquares &= ei.attackedBy[Us][ALL_PIECES];
defendedSquares = squaresToQueen;
else if (!(pos.pieces(Them) & bb))
defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES]; unsafeSquares &= ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
// If there aren't any enemy attacks, assign a big bonus. Otherwise // If there aren't any enemy attacks, assign a big bonus. Otherwise
// assign a smaller bonus if the block square isn't attacked. // assign a smaller bonus if the block square isn't attacked.
@@ -621,6 +608,8 @@ namespace {
mbonus += k * rr, ebonus += k * rr; mbonus += k * rr, ebonus += k * rr;
} }
else if (pos.pieces(Us) & blockSq)
mbonus += rr * 3 + r * 2 + 3, ebonus += rr + r * 2;
} // rr != 0 } // rr != 0
if (pos.count<PAWN>(Us) < pos.count<PAWN>(Them)) if (pos.count<PAWN>(Us) < pos.count<PAWN>(Them))
@@ -637,18 +626,15 @@ namespace {
} }
// evaluate_unstoppable_pawns() scores the most advanced among the passed and // evaluate_unstoppable_pawns() scores the most advanced passed pawn. In case
// candidate pawns. In case opponent has no pieces but pawns, this is somewhat // both players have no pieces but pawns, this is somewhat related to the
// related to the possibility that pawns are unstoppable. // possibility that pawns are unstoppable.
Score evaluate_unstoppable_pawns(const Position& pos, Color us, const EvalInfo& ei) { Score evaluate_unstoppable_pawns(Color us, const EvalInfo& ei) {
Bitboard b = ei.pi->passed_pawns(us) | ei.pi->candidate_pawns(us); Bitboard b = ei.pi->passed_pawns(us);
if (!b || pos.non_pawn_material(~us)) return b ? Unstoppable * int(relative_rank(us, frontmost_sq(us, b))) : SCORE_ZERO;
return SCORE_ZERO;
return Unstoppable * int(relative_rank(us, frontmost_sq(us, b)));
} }
@@ -696,9 +682,9 @@ namespace {
Thread* thisThread = pos.this_thread(); Thread* thisThread = pos.this_thread();
// Initialize score by reading the incrementally updated scores included // Initialize score by reading the incrementally updated scores included
// in the position object (material + piece square tables) and adding a // in the position object (material + piece square tables).
// Tempo bonus. Score is computed from the point of view of white. // Score is computed from the point of view of white.
score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo); score = pos.psq_score();
// Probe the material hash table // Probe the material hash table
ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames); ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames);
@@ -741,10 +727,10 @@ namespace {
score += evaluate_passed_pawns<WHITE, Trace>(pos, ei) score += evaluate_passed_pawns<WHITE, Trace>(pos, ei)
- evaluate_passed_pawns<BLACK, Trace>(pos, ei); - evaluate_passed_pawns<BLACK, Trace>(pos, ei);
// If one side has only a king, score for potential unstoppable pawns // If both sides have only pawns, score for potential unstoppable pawns
if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK)) if (!pos.non_pawn_material(WHITE) && !pos.non_pawn_material(BLACK))
score += evaluate_unstoppable_pawns(pos, WHITE, ei) score += evaluate_unstoppable_pawns(WHITE, ei)
- evaluate_unstoppable_pawns(pos, BLACK, ei); - evaluate_unstoppable_pawns(BLACK, ei);
// Evaluate space for both sides, only in middlegame // Evaluate space for both sides, only in middlegame
if (ei.mi->space_weight()) if (ei.mi->space_weight())
@@ -754,29 +740,35 @@ namespace {
} }
// Scale winning side if position is more drawish than it appears // Scale winning side if position is more drawish than it appears
ScaleFactor sf = eg_value(score) > VALUE_DRAW ? ei.mi->scale_factor(pos, WHITE) Color strongSide = eg_value(score) > VALUE_DRAW ? WHITE : BLACK;
: ei.mi->scale_factor(pos, BLACK); ScaleFactor sf = ei.mi->scale_factor(pos, strongSide);
// If we don't already have an unusual scale factor, check for opposite // If we don't already have an unusual scale factor, check for certain
// colored bishop endgames, and use a lower scale for those. // types of endgames, and use a lower scale for those.
if ( ei.mi->game_phase() < PHASE_MIDGAME if ( ei.mi->game_phase() < PHASE_MIDGAME
&& pos.opposite_bishops()
&& (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN)) && (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN))
{ {
// Ignoring any pawns, do both sides only have a single bishop and no if (pos.opposite_bishops()) {
// other pieces? // Ignoring any pawns, do both sides only have a single bishop and no
if ( pos.non_pawn_material(WHITE) == BishopValueMg // other pieces?
&& pos.non_pawn_material(BLACK) == BishopValueMg) if ( pos.non_pawn_material(WHITE) == BishopValueMg
{ && pos.non_pawn_material(BLACK) == BishopValueMg)
// Check for KBP vs KB with only a single pawn that is almost {
// certainly a draw or at least two pawns. // Check for KBP vs KB with only a single pawn that is almost
bool one_pawn = (pos.count<PAWN>(WHITE) + pos.count<PAWN>(BLACK) == 1); // certainly a draw or at least two pawns.
sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32); bool one_pawn = (pos.count<PAWN>(WHITE) + pos.count<PAWN>(BLACK) == 1);
sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32);
}
else
// Endgame with opposite-colored bishops, but also other pieces. Still
// a bit drawish, but not as drawish as with only the two bishops.
sf = ScaleFactor(50 * sf / SCALE_FACTOR_NORMAL);
} else if ( abs(eg_value(score)) <= BishopValueEg
&& ei.pi->pawn_span(strongSide) <= 1
&& !pos.pawn_passed(~strongSide, pos.king_square(~strongSide))) {
// Endings where weaker side can be place his king in front of the opponent's pawns are drawish.
sf = ScaleFactor(ScalePawnSpan[ei.pi->pawn_span(strongSide)]);
} }
else
// Endgame with opposite-colored bishops, but also other pieces. Still
// a bit drawish, but not as drawish as with only the two bishops.
sf = ScaleFactor(50 * sf / SCALE_FACTOR_NORMAL);
} }
// Interpolate between a middlegame and a (scaled by 'sf') endgame score // Interpolate between a middlegame and a (scaled by 'sf') endgame score
@@ -788,7 +780,7 @@ namespace {
// In case of tracing add all single evaluation contributions for both white and black // In case of tracing add all single evaluation contributions for both white and black
if (Trace) if (Trace)
{ {
Tracing::add_term(Tracing::PST, pos.psq_score()); Tracing::add_term(Tracing::MATERIAL, pos.psq_score());
Tracing::add_term(Tracing::IMBALANCE, ei.mi->material_value()); Tracing::add_term(Tracing::IMBALANCE, ei.mi->material_value());
Tracing::add_term(PAWN, ei.pi->pawns_value()); Tracing::add_term(PAWN, ei.pi->pawns_value());
Tracing::add_term(Tracing::MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility]) Tracing::add_term(Tracing::MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility])
@@ -821,13 +813,13 @@ namespace {
Score bScore = terms[BLACK][idx]; Score bScore = terms[BLACK][idx];
switch (idx) { switch (idx) {
case PST: case IMBALANCE: case PAWN: case TOTAL: case MATERIAL: case IMBALANCE: case PAWN: case TOTAL:
ss << std::setw(20) << name << " | --- --- | --- --- | " ss << std::setw(15) << name << " | --- --- | --- --- | "
<< std::setw(5) << to_cp(mg_value(wScore - bScore)) << " " << std::setw(5) << to_cp(mg_value(wScore - bScore)) << " "
<< std::setw(5) << to_cp(eg_value(wScore - bScore)) << " \n"; << std::setw(5) << to_cp(eg_value(wScore - bScore)) << " \n";
break; break;
default: default:
ss << std::setw(20) << name << " | " << std::noshowpos ss << std::setw(15) << name << " | " << std::noshowpos
<< std::setw(5) << to_cp(mg_value(wScore)) << " " << std::setw(5) << to_cp(mg_value(wScore)) << " "
<< std::setw(5) << to_cp(eg_value(wScore)) << " | " << std::setw(5) << to_cp(eg_value(wScore)) << " | "
<< std::setw(5) << to_cp(mg_value(bScore)) << " " << std::setw(5) << to_cp(mg_value(bScore)) << " "
@@ -846,12 +838,12 @@ namespace {
std::stringstream ss; std::stringstream ss;
ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2) ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
<< " Eval term | White | Black | Total \n" << " Eval term | White | Black | Total \n"
<< " | MG EG | MG EG | MG EG \n" << " | MG EG | MG EG | MG EG \n"
<< "---------------------+-------------+-------------+-------------\n"; << "----------------+-------------+-------------+-------------\n";
format_row(ss, "Material, PST, Tempo", PST); format_row(ss, "Material", MATERIAL);
format_row(ss, "Material imbalance", IMBALANCE); format_row(ss, "Imbalance", IMBALANCE);
format_row(ss, "Pawns", PAWN); format_row(ss, "Pawns", PAWN);
format_row(ss, "Knights", KNIGHT); format_row(ss, "Knights", KNIGHT);
format_row(ss, "Bishops", BISHOP); format_row(ss, "Bishops", BISHOP);
@@ -863,7 +855,7 @@ namespace {
format_row(ss, "Passed pawns", PASSED); format_row(ss, "Passed pawns", PASSED);
format_row(ss, "Space", SPACE); format_row(ss, "Space", SPACE);
ss << "---------------------+-------------+-------------+-------------\n"; ss << "----------------+-------------+-------------+-------------\n";
format_row(ss, "Total", TOTAL); format_row(ss, "Total", TOTAL);
ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n"; ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n";
@@ -880,7 +872,7 @@ namespace Eval {
/// of the position always from the point of view of the side to move. /// of the position always from the point of view of the side to move.
Value evaluate(const Position& pos) { Value evaluate(const Position& pos) {
return do_evaluate<false>(pos); return do_evaluate<false>(pos) + Tempo;
} }
@@ -898,22 +890,13 @@ namespace Eval {
void init() { void init() {
Weights[Mobility] = weight_option("Mobility (Midgame)", "Mobility (Endgame)", WeightsInternal[Mobility]);
Weights[PawnStructure] = weight_option("Pawn Structure (Midgame)", "Pawn Structure (Endgame)", WeightsInternal[PawnStructure]);
Weights[PassedPawns] = weight_option("Passed Pawns (Midgame)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]);
Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]);
Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]);
Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]);
const double MaxSlope = 30; const double MaxSlope = 30;
const double Peak = 1280; const double Peak = 1280;
for (int t = 0, i = 1; i < 100; ++i) for (int t = 0, i = 1; i < 100; ++i)
{ {
t = int(std::min(Peak, std::min(0.4 * i * i, t + MaxSlope))); t = int(std::min(Peak, std::min(0.4 * i * i, t + MaxSlope)));
KingDanger[i] = apply_weight(make_score(t, 0), Weights[KingSafety]);
KingDanger[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]);
KingDanger[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]);
} }
} }

View File

@@ -26,6 +26,8 @@ class Position;
namespace Eval { namespace Eval {
const Value Tempo = Value(17); // Must be visible to search
extern void init(); extern void init();
extern Value evaluate(const Position& pos); extern Value evaluate(const Position& pos);
extern std::string trace(const Position& pos); extern std::string trace(const Position& pos);

View File

@@ -23,6 +23,7 @@
#include "evaluate.h" #include "evaluate.h"
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "tbprobe.h"
#include "thread.h" #include "thread.h"
#include "tt.h" #include "tt.h"
#include "ucioption.h" #include "ucioption.h"
@@ -36,10 +37,10 @@ int main(int argc, char* argv[]) {
Position::init(); Position::init();
Bitbases::init_kpk(); Bitbases::init_kpk();
Search::init(); Search::init();
Pawns::init();
Eval::init(); Eval::init();
Threads.init(); Threads.init();
TT.resize(Options["Hash"]); TT.resize(Options["Hash"]);
Tablebases::init(Options["SyzygyPath"]);
UCI::loop(argc, argv); UCI::loop(argc, argv);

View File

@@ -29,10 +29,10 @@ namespace {
// Polynomial material balance parameters // Polynomial material balance parameters
// pair pawn knight bishop rook queen // pair pawn knight bishop rook queen
const int LinearCoefficients[6] = { 1852, -162, -1122, -183, 249, -154 }; const int Linear[6] = { 1852, -162, -1122, -183, 249, -154 };
const int QuadraticCoefficientsSameSide[][PIECE_TYPE_NB] = { const int QuadraticSameSide[][PIECE_TYPE_NB] = {
// OUR PIECES // OUR PIECES
// pair pawn knight bishop rook queen // pair pawn knight bishop rook queen
{ 0 }, // Bishop pair { 0 }, // Bishop pair
@@ -43,7 +43,7 @@ namespace {
{-177, 25, 129, 142, -137, 0 } // Queen {-177, 25, 129, 142, -137, 0 } // Queen
}; };
const int QuadraticCoefficientsOppositeSide[][PIECE_TYPE_NB] = { const int QuadraticOppositeSide[][PIECE_TYPE_NB] = {
// THEIR PIECES // THEIR PIECES
// pair pawn knight bishop rook queen // pair pawn knight bishop rook queen
{ 0 }, // Bishop pair { 0 }, // Bishop pair
@@ -66,8 +66,7 @@ namespace {
// Helper templates used to detect a given material distribution // Helper templates used to detect a given material distribution
template<Color Us> bool is_KXK(const Position& pos) { template<Color Us> bool is_KXK(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
return !pos.count<PAWN>(Them) return !more_than_one(pos.pieces(Them))
&& pos.non_pawn_material(Them) == VALUE_ZERO
&& pos.non_pawn_material(Us) >= RookValueMg; && pos.non_pawn_material(Us) >= RookValueMg;
} }
@@ -94,26 +93,24 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
int pt1, pt2, pc, v; int bonus = 0;
int value = 0;
// Second-degree polynomial material imbalance by Tord Romstad // Second-degree polynomial material imbalance by Tord Romstad
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1) for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
{ {
pc = pieceCount[Us][pt1]; if (!pieceCount[Us][pt1])
if (!pc)
continue; continue;
v = LinearCoefficients[pt1]; int v = Linear[pt1];
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2) for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2)
v += QuadraticCoefficientsSameSide[pt1][pt2] * pieceCount[Us][pt2] v += QuadraticSameSide[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticCoefficientsOppositeSide[pt1][pt2] * pieceCount[Them][pt2]; + QuadraticOppositeSide[pt1][pt2] * pieceCount[Them][pt2];
value += pc * v; bonus += pieceCount[Us][pt1] * v;
} }
return value; return bonus;
} }
} // namespace } // namespace
@@ -139,7 +136,7 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
std::memset(e, 0, sizeof(Entry)); std::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 = game_phase(pos); e->gamePhase = pos.game_phase();
// Let's look if we have a specialized evaluation function for this particular // Let's look if we have a specialized evaluation function for this particular
// material configuration. Firstly we look for a fixed configuration one, then // material configuration. Firstly we look for a fixed configuration one, then
@@ -248,18 +245,4 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
return e; return e;
} }
/// Material::game_phase() calculates the phase given the current
/// position. Because the phase is strictly a function of the material, it
/// is stored in MaterialEntry.
Phase game_phase(const Position& pos) {
Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK);
return npm >= MidgameLimit ? PHASE_MIDGAME
: npm <= EndgameLimit ? PHASE_ENDGAME
: Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
}
} // namespace Material } // namespace Material

View File

@@ -68,7 +68,6 @@ struct Entry {
typedef HashTable<Entry, 8192> Table; typedef HashTable<Entry, 8192> Table;
Entry* probe(const Position& pos, Table& entries, Endgames& endgames); Entry* probe(const Position& pos, Table& entries, Endgames& endgames);
Phase game_phase(const Position& pos);
} // namespace Material } // namespace Material

View File

@@ -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 <fstream>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
@@ -28,7 +29,7 @@ using namespace std;
/// Version number. If Version is left empty, then compile date in the format /// Version number. If Version is left empty, then compile date in the format
/// DD-MM-YY and show in engine_info. /// DD-MM-YY and show in engine_info.
static const string Version = "5"; static const string Version = "121014";
/// engine_info() returns the full name of the current Stockfish version. This /// engine_info() returns the full name of the current Stockfish version. This

View File

@@ -20,7 +20,7 @@
#ifndef MISC_H_INCLUDED #ifndef MISC_H_INCLUDED
#define MISC_H_INCLUDED #define MISC_H_INCLUDED
#include <fstream> #include <ostream>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -37,12 +37,6 @@ extern void dbg_mean_of(int v);
extern void dbg_print(); extern void dbg_print();
struct Log : public std::ofstream {
Log(const std::string& f = "log.txt") : std::ofstream(f.c_str(), std::ios::out | std::ios::app) {}
~Log() { if (is_open()) close(); }
};
namespace Time { namespace Time {
typedef int64_t point; typedef int64_t point;
inline point now() { return system_time_to_msec(); } inline point now() { return system_time_to_msec(); }

View File

@@ -18,9 +18,7 @@
*/ */
#include <cassert> #include <cassert>
#include <iomanip>
#include <sstream> #include <sstream>
#include <stack>
#include "movegen.h" #include "movegen.h"
#include "notation.h" #include "notation.h"
@@ -42,7 +40,7 @@ string score_to_uci(Value v, Value alpha, Value beta) {
stringstream ss; stringstream ss;
if (abs(v) < VALUE_MATE_IN_MAX_PLY) if (abs(v) < VALUE_MATE - MAX_PLY)
ss << "cp " << v * 100 / PawnValueEg; ss << "cp " << v * 100 / PawnValueEg;
else else
ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
@@ -95,162 +93,3 @@ Move move_from_uci(const Position& pos, string& str) {
return MOVE_NONE; return MOVE_NONE;
} }
/// move_to_san() takes a position and a legal Move as input and returns its
/// short algebraic notation representation.
const string move_to_san(Position& pos, Move m) {
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "(null)";
assert(MoveList<LEGAL>(pos).contains(m));
Bitboard others, b;
string san;
Color us = pos.side_to_move();
Square from = from_sq(m);
Square to = to_sq(m);
Piece pc = pos.piece_on(from);
PieceType pt = type_of(pc);
if (type_of(m) == CASTLING)
san = to > from ? "O-O" : "O-O-O";
else
{
if (pt != PAWN)
{
san = PieceToChar[WHITE][pt]; // Upper case
// A disambiguation occurs if we have more then one piece of type 'pt'
// that can reach 'to' with a legal move.
others = b = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from;
while (b)
{
Square s = pop_lsb(&b);
if (!pos.legal(make_move(s, to), pos.pinned_pieces(us)))
others ^= s;
}
if (!others)
{ /* Disambiguation is not needed */ }
else if (!(others & file_bb(from)))
san += to_char(file_of(from));
else if (!(others & rank_bb(from)))
san += to_char(rank_of(from));
else
san += to_string(from);
}
else if (pos.capture(m))
san = to_char(file_of(from));
if (pos.capture(m))
san += 'x';
san += to_string(to);
if (type_of(m) == PROMOTION)
san += string("=") + PieceToChar[WHITE][promotion_type(m)];
}
if (pos.gives_check(m, CheckInfo(pos)))
{
StateInfo st;
pos.do_move(m, st);
san += MoveList<LEGAL>(pos).size() ? "+" : "#";
pos.undo_move(m);
}
return san;
}
/// pretty_pv() formats human-readable search information, typically to be
/// appended to the search log file. It uses the two helpers below to pretty
/// format the time and score respectively.
static string format(int64_t msecs) {
const int MSecMinute = 1000 * 60;
const int MSecHour = 1000 * 60 * 60;
int64_t hours = msecs / MSecHour;
int64_t minutes = (msecs % MSecHour) / MSecMinute;
int64_t seconds = ((msecs % MSecHour) % MSecMinute) / 1000;
stringstream ss;
if (hours)
ss << hours << ':';
ss << setfill('0') << setw(2) << minutes << ':' << setw(2) << seconds;
return ss.str();
}
static string format(Value v) {
stringstream ss;
if (v >= VALUE_MATE_IN_MAX_PLY)
ss << "#" << (VALUE_MATE - v + 1) / 2;
else if (v <= VALUE_MATED_IN_MAX_PLY)
ss << "-#" << (VALUE_MATE + v) / 2;
else
ss << setprecision(2) << fixed << showpos << double(v) / PawnValueEg;
return ss.str();
}
string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]) {
const uint64_t K = 1000;
const uint64_t M = 1000000;
std::stack<StateInfo> st;
Move* m = pv;
string san, str, padding;
stringstream ss;
ss << setw(2) << depth << setw(8) << format(value) << setw(8) << format(msecs);
if (pos.nodes_searched() < M)
ss << setw(8) << pos.nodes_searched() / 1 << " ";
else if (pos.nodes_searched() < K * M)
ss << setw(7) << pos.nodes_searched() / K << "K ";
else
ss << setw(7) << pos.nodes_searched() / M << "M ";
str = ss.str();
padding = string(str.length(), ' ');
while (*m != MOVE_NONE)
{
san = move_to_san(pos, *m) + ' ';
if ((str.length() + san.length()) % 80 <= san.length()) // Exceed 80 cols
str += "\n" + padding;
str += san;
st.push(StateInfo());
pos.do_move(*m++, st.top());
}
while (m != pv)
pos.undo_move(*--m);
return str;
}

View File

@@ -29,7 +29,18 @@ class Position;
std::string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE); std::string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE);
Move move_from_uci(const Position& pos, std::string& str); Move move_from_uci(const Position& pos, std::string& str);
const std::string move_to_uci(Move m, bool chess960); const std::string move_to_uci(Move m, bool chess960);
const std::string move_to_san(Position& pos, Move m);
std::string pretty_pv(Position& pos, int depth, Value score, int64_t msecs, Move pv[]); inline char to_char(File f, bool tolower = true) {
return char(f - FILE_A + (tolower ? 'a' : 'A'));
}
inline char to_char(Rank r) {
return char(r - RANK_1 + '1');
}
inline const std::string to_string(Square s) {
char ch[] = { to_char(file_of(s)), to_char(rank_of(s)), 0 };
return ch;
}
#endif // #ifndef NOTATION_H_INCLUDED #endif // #ifndef NOTATION_H_INCLUDED

View File

@@ -49,13 +49,13 @@ namespace {
{ S(20, 28), S(29, 31), S(33, 31), S(33, 31), { S(20, 28), S(29, 31), S(33, 31), S(33, 31),
S(33, 31), S(33, 31), S(29, 31), S(20, 28) } }; S(33, 31), S(33, 31), S(29, 31), S(20, 28) } };
// Connected pawn bonus by file and rank (initialized by formula) // Connected bonus by rank
Score Connected[FILE_NB][RANK_NB]; const int Connected[RANK_NB] = {0, 6, 15, 10, 57, 75, 135, 258};
// Candidate passed pawn bonus by rank // Levers bonus by rank
const Score CandidatePassed[RANK_NB] = { const Score Lever[RANK_NB] = {
S( 0, 0), S( 6, 13), S(6,13), S(14,29), S( 0, 0), S( 0, 0), S(0, 0), S(0, 0),
S(34,68), S(83,166), S(0, 0), S( 0, 0) }; S(20,20), S(40,40), S(0, 0), S(0, 0) };
// Bonus for file distance of the two outermost pawns // Bonus for file distance of the two outermost pawns
const Score PawnsFileSpan = S(0, 15); const Score PawnsFileSpan = S(0, 15);
@@ -69,7 +69,7 @@ namespace {
// Danger of enemy pawns moving toward our king indexed by // Danger of enemy pawns moving toward our king indexed by
// [no friendly pawn | pawn unblocked | pawn blocked][rank of enemy pawn] // [no friendly pawn | pawn unblocked | pawn blocked][rank of enemy pawn]
const Value StormDanger[3][RANK_NB] = { const Value StormDanger[][RANK_NB] = {
{ V( 0), V(64), V(128), V(51), V(26) }, { V( 0), V(64), V(128), V(51), V(26) },
{ V(26), V(32), V( 96), V(38), V(20) }, { V(26), V(32), V( 96), V(38), V(20) },
{ V( 0), V( 0), V(160), V(25), V(13) } }; { V( 0), V( 0), V(160), V(25), V(13) } };
@@ -91,15 +91,15 @@ namespace {
Bitboard b, p, doubled; Bitboard b, p, doubled;
Square s; Square s;
File f; bool passed, isolated, opposed, connected, backward, unsupported, lever;
bool passed, isolated, opposed, connected, backward, candidate, unsupported;
Score value = SCORE_ZERO; Score value = SCORE_ZERO;
const Square* pl = pos.list<PAWN>(Us); const Square* pl = pos.list<PAWN>(Us);
const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)];
Bitboard ourPawns = pos.pieces(Us, PAWN); Bitboard ourPawns = pos.pieces(Us , PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN);
e->passedPawns[Us] = e->candidatePawns[Us] = 0; e->passedPawns[Us] = 0;
e->kingSquares[Us] = SQ_NONE; e->kingSquares[Us] = SQ_NONE;
e->semiopenFiles[Us] = 0xFF; e->semiopenFiles[Us] = 0xFF;
e->pawnAttacks[Us] = shift_bb<Right>(ourPawns) | shift_bb<Left>(ourPawns); e->pawnAttacks[Us] = shift_bb<Right>(ourPawns) | shift_bb<Left>(ourPawns);
@@ -111,7 +111,8 @@ namespace {
{ {
assert(pos.piece_on(s) == make_piece(Us, PAWN)); assert(pos.piece_on(s) == make_piece(Us, PAWN));
f = file_of(s); Rank r = rank_of(s), rr = relative_rank(Us, s);
File f = file_of(s);
// This file cannot be semi-open // This file cannot be semi-open
e->semiopenFiles[Us] &= ~(1 << f); e->semiopenFiles[Us] &= ~(1 << f);
@@ -130,6 +131,7 @@ namespace {
doubled = ourPawns & forward_bb(Us, s); doubled = ourPawns & forward_bb(Us, s);
opposed = theirPawns & forward_bb(Us, s); opposed = theirPawns & forward_bb(Us, s);
passed = !(theirPawns & passed_pawn_mask(Us, s)); passed = !(theirPawns & passed_pawn_mask(Us, s));
lever = theirPawns & pawnAttacksBB[s];
// Test for backward pawn. // Test for backward pawn.
// If the pawn is passed, isolated, or connected it cannot be // If the pawn is passed, isolated, or connected it cannot be
@@ -155,14 +157,6 @@ namespace {
assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns)); assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns));
// A not-passed pawn is a candidate to become passed, if it is free to
// advance and if the number of friendly pawns beside or behind this
// pawn on adjacent files is higher than or equal to the number of
// enemy pawns in the forward direction on the adjacent files.
candidate = !(opposed | passed | backward | isolated)
&& (b = pawn_attack_span(Them, s + pawn_push(Us)) & ourPawns) != 0
&& popcount<Max15>(b) >= popcount<Max15>(pawn_attack_span(Us, s) & theirPawns);
// Passed pawns will be properly scored in evaluation because we need // Passed pawns will be properly scored in evaluation because we need
// full attack info to evaluate passed pawns. Only the frontmost passed // full attack info to evaluate passed pawns. Only the frontmost passed
// pawn on each file is considered a true passed pawn. // pawn on each file is considered a true passed pawn.
@@ -182,25 +176,23 @@ namespace {
if (backward) if (backward)
value -= Backward[opposed][f]; value -= Backward[opposed][f];
if (connected) if (connected) {
value += Connected[f][relative_rank(Us, s)]; int bonus = Connected[rr];
if (ourPawns & adjacent_files_bb(f) & rank_bb(r))
if (candidate) bonus += (Connected[rr+1] - Connected[rr]) / 2;
{ value += make_score(bonus / 2, bonus >> opposed);
value += CandidatePassed[relative_rank(Us, s)];
if (!doubled)
e->candidatePawns[Us] |= s;
} }
if (lever)
value += Lever[rr];
} }
b = e->semiopenFiles[Us] ^ 0xFF;
e->pawnSpan[Us] = b ? int(msb(b) - lsb(b)) : 0;
// In endgame it's better to have pawns on both wings. So give a bonus according // In endgame it's better to have pawns on both wings. So give a bonus according
// to file distance between left and right outermost pawns. // to file distance between left and right outermost pawns.
if (pos.count<PAWN>(Us) > 1) value += PawnsFileSpan * e->pawnSpan[Us];
{
b = e->semiopenFiles[Us] ^ 0xFF;
value += PawnsFileSpan * int(msb(b) - lsb(b));
}
return value; return value;
} }
@@ -209,24 +201,8 @@ namespace {
namespace Pawns { namespace Pawns {
/// init() initializes some tables by formula instead of hard-coding their values /// probe() takes a position 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
void init() {
const int bonusesByFile[8] = { 1, 3, 3, 4, 4, 3, 3, 1 };
int bonus;
for (Rank r = RANK_1; r < RANK_8; ++r)
for (File f = FILE_A; f <= FILE_H; ++f)
{
bonus = r * (r-1) * (r-2) + bonusesByFile[f] * (r/2 + 1);
Connected[f][r] = make_score(bonus, bonus);
}
}
/// 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. /// to recompute everything when the same pawn structure occurs again.
Entry* probe(const Position& pos, Table& entries) { Entry* probe(const Position& pos, Table& entries) {
@@ -250,30 +226,30 @@ template<Color Us>
Value Entry::shelter_storm(const Position& pos, Square ksq) { Value Entry::shelter_storm(const Position& pos, Square ksq) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
static const Bitboard MiddleEdges = (FileABB | FileHBB) & (Rank2BB | Rank3BB); const Bitboard Edges = (FileABB | FileHBB) & (Rank2BB | Rank3BB);
Value safety = MaxSafetyBonus;
Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq)); Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq));
Bitboard ourPawns = b & pos.pieces(Us); Bitboard ourPawns = b & pos.pieces(Us);
Bitboard theirPawns = b & pos.pieces(Them); Bitboard theirPawns = b & pos.pieces(Them);
Rank rkUs, rkThem; Value safety = MaxSafetyBonus;
File kf = std::max(FILE_B, std::min(FILE_G, file_of(ksq))); File kf = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));
for (File f = kf - File(1); f <= kf + File(1); ++f) for (File f = kf - File(1); f <= kf + File(1); ++f)
{ {
b = ourPawns & file_bb(f); b = ourPawns & file_bb(f);
rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1; Rank rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
b = theirPawns & file_bb(f); b = theirPawns & file_bb(f);
rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
if ( (MiddleEdges & make_square(f, rkThem)) if ( (Edges & make_square(f, rkThem))
&& file_of(ksq) == f && file_of(ksq) == f
&& relative_rank(Us, ksq) == rkThem - 1) && relative_rank(Us, ksq) == rkThem - 1)
safety += 200; safety += 200;
else else
safety -= ShelterWeakness[rkUs] safety -= ShelterWeakness[rkUs]
+ StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem]; + StormDanger[rkUs == RANK_1 ? 0 :
rkThem != rkUs + 1 ? 1 : 2][rkThem];
} }
return safety; return safety;

View File

@@ -35,7 +35,6 @@ struct Entry {
Score pawns_value() const { return value; } Score pawns_value() const { return value; }
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
Bitboard candidate_pawns(Color c) const { return candidatePawns[c]; }
int semiopen_file(Color c, File f) const { int semiopen_file(Color c, File f) const {
return semiopenFiles[c] & (1 << f); return semiopenFiles[c] & (1 << f);
@@ -45,6 +44,10 @@ struct Entry {
return semiopenFiles[c] & (leftSide ? (1 << f) - 1 : ~((1 << (f + 1)) - 1)); return semiopenFiles[c] & (leftSide ? (1 << f) - 1 : ~((1 << (f + 1)) - 1));
} }
int pawn_span(Color c) const {
return pawnSpan[c];
}
int pawns_on_same_color_squares(Color c, Square s) const { int pawns_on_same_color_squares(Color c, Square s) const {
return pawnsOnSquares[c][!!(DarkSquares & s)]; return pawnsOnSquares[c][!!(DarkSquares & s)];
} }
@@ -64,19 +67,18 @@ struct Entry {
Key key; Key key;
Score value; Score value;
Bitboard passedPawns[COLOR_NB]; Bitboard passedPawns[COLOR_NB];
Bitboard candidatePawns[COLOR_NB];
Bitboard pawnAttacks[COLOR_NB]; Bitboard pawnAttacks[COLOR_NB];
Square kingSquares[COLOR_NB]; Square kingSquares[COLOR_NB];
Score kingSafety[COLOR_NB]; Score kingSafety[COLOR_NB];
int minKPdistance[COLOR_NB]; int minKPdistance[COLOR_NB];
int castlingRights[COLOR_NB]; int castlingRights[COLOR_NB];
int semiopenFiles[COLOR_NB]; int semiopenFiles[COLOR_NB];
int pawnSpan[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares] int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
}; };
typedef HashTable<Entry, 16384> Table; typedef HashTable<Entry, 16384> Table;
void init();
Entry* probe(const Position& pos, Table& entries); Entry* probe(const Position& pos, Table& entries);
} // namespace Pawns } // namespace Pawns

View File

@@ -226,7 +226,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
incremented after Black's move. incremented after Black's move.
*/ */
char col, row, token; unsigned char col, row, token;
size_t idx; size_t idx;
Square sq = SQ_A8; Square sq = SQ_A8;
std::istringstream ss(fenStr); std::istringstream ss(fenStr);
@@ -430,17 +430,12 @@ const string Position::fen() const {
} }
/// Position::pretty() returns an ASCII representation of the position to be /// Position::pretty() returns an ASCII representation of the position
/// printed to the standard output together with the move's san notation.
const string Position::pretty(Move m) const { const string Position::pretty() const {
std::ostringstream ss; std::ostringstream ss;
if (m)
ss << "\nMove: " << (sideToMove == BLACK ? ".." : "")
<< move_to_san(*const_cast<Position*>(this), m);
ss << "\n +---+---+---+---+---+---+---+---+\n"; ss << "\n +---+---+---+---+---+---+---+---+\n";
for (Rank r = RANK_8; r >= RANK_1; --r) for (Rank r = RANK_8; r >= RANK_1; --r)
@@ -457,14 +452,23 @@ const string Position::pretty(Move m) const {
for (Bitboard b = checkers(); b; ) for (Bitboard b = checkers(); b; )
ss << to_string(pop_lsb(&b)) << " "; ss << to_string(pop_lsb(&b)) << " ";
ss << "\nLegal moves: ";
for (MoveList<LEGAL> it(*this); *it; ++it)
ss << move_to_san(*const_cast<Position*>(this), *it) << " ";
return ss.str(); return ss.str();
} }
/// Position::game_phase() calculates the game phase interpolating total non-pawn
/// material between endgame and midgame limits.
Phase Position::game_phase() const {
Value npm = st->npMaterial[WHITE] + st->npMaterial[BLACK];
npm = std::max(EndgameLimit, std::min(npm, MidgameLimit));
return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
}
/// Position::check_blockers() returns a bitboard of all the pieces with color /// Position::check_blockers() returns a bitboard of all the pieces with color
/// 'c' that are blocking check on the king with color 'kingColor'. A piece /// 'c' that are blocking check on the king with color 'kingColor'. A piece
/// blocks a check if removing that piece from the board would result in a /// blocks a check if removing that piece from the board would result in a
@@ -803,9 +807,6 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
st->castlingRights &= ~cr; st->castlingRights &= ~cr;
} }
// Prefetch TT access as soon as we know the new hash key
prefetch((char*)TT.first_entry(k));
// Move the piece. The tricky Chess960 castling is handled earlier // Move the piece. The tricky Chess960 castling is handled earlier
if (type_of(m) != CASTLING) if (type_of(m) != CASTLING)
move_piece(from, to, us, pt); move_piece(from, to, us, pt);
@@ -1012,6 +1013,26 @@ void Position::undo_null_move() {
} }
/// Position::key_after() computes the new hash key after the given moven. Needed
/// for speculative prefetch. It doesn't recognize special moves like castling,
/// en-passant and promotions.
Key Position::key_after(Move m) const {
Color us = sideToMove;
Square from = from_sq(m);
Square to = to_sq(m);
PieceType pt = type_of(piece_on(from));
PieceType captured = type_of(piece_on(to));
Key k = st->key ^ Zobrist::side;
if (captured)
k ^= Zobrist::psq[~us][captured][to];
return k ^ Zobrist::psq[us][pt][to] ^ Zobrist::psq[us][pt][from];
}
/// 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. /// material gain or loss resulting from a move.
@@ -1113,10 +1134,6 @@ Value Position::see(Move m) const {
bool Position::is_draw() const { bool Position::is_draw() const {
if ( !pieces(PAWN)
&& (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMg))
return true;
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size())) if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
return true; return true;

View File

@@ -24,9 +24,9 @@
#include <cstddef> #include <cstddef>
#include "bitboard.h" #include "bitboard.h"
#include "bitcount.h"
#include "types.h" #include "types.h"
/// 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;
@@ -83,7 +83,7 @@ public:
// Text input/output // Text input/output
void set(const std::string& fenStr, bool isChess960, Thread* th); void set(const std::string& fenStr, bool isChess960, Thread* th);
const std::string fen() const; const std::string fen() const;
const std::string pretty(Move m = MOVE_NONE) const; const std::string pretty() const;
// Position representation // Position representation
Bitboard pieces() const; Bitboard pieces() const;
@@ -98,6 +98,7 @@ public:
bool empty(Square s) const; bool empty(Square s) const;
template<PieceType Pt> int count(Color c) const; template<PieceType Pt> int count(Color c) const;
template<PieceType Pt> const Square* list(Color c) const; template<PieceType Pt> const Square* list(Color c) const;
int total_piece_count() const;
// Castling // Castling
int can_castle(Color c) const; int can_castle(Color c) const;
@@ -146,6 +147,7 @@ public:
// Accessing hash keys // Accessing hash keys
Key key() const; Key key() const;
Key key_after(Move m) const;
Key exclusion_key() const; Key exclusion_key() const;
Key pawn_key() const; Key pawn_key() const;
Key material_key() const; Key material_key() const;
@@ -156,12 +158,14 @@ public:
// Other properties of the position // Other properties of the position
Color side_to_move() const; Color side_to_move() const;
Phase game_phase() const;
int game_ply() const; int game_ply() const;
bool is_chess960() const; bool is_chess960() const;
Thread* this_thread() const; Thread* this_thread() const;
uint64_t nodes_searched() const; uint64_t nodes_searched() const;
void set_nodes_searched(uint64_t n); void set_nodes_searched(uint64_t n);
bool is_draw() const; bool is_draw() const;
int rule50_count() const;
// Position consistency check, for debugging // Position consistency check, for debugging
bool pos_is_ok(int* step = NULL) const; bool pos_is_ok(int* step = NULL) const;
@@ -348,6 +352,14 @@ inline int Position::game_ply() const {
return gamePly; return gamePly;
} }
inline int Position::rule50_count() const {
return st->rule50;
}
inline int Position::total_piece_count() const {
return HasPopCnt ? popcount<Full>(pieces()) : pieceCount[WHITE][ALL_PIECES];
}
inline bool Position::opposite_bishops() const { inline bool Position::opposite_bishops() const {
return pieceCount[WHITE][BISHOP] == 1 return pieceCount[WHITE][BISHOP] == 1
@@ -398,6 +410,8 @@ inline void Position::put_piece(Square s, Color c, PieceType pt) {
byColorBB[c] |= s; byColorBB[c] |= s;
index[s] = pieceCount[c][pt]++; index[s] = pieceCount[c][pt]++;
pieceList[c][pt][index[s]] = s; pieceList[c][pt][index[s]] = s;
if (!HasPopCnt)
pieceCount[WHITE][ALL_PIECES]++;
} }
inline void Position::move_piece(Square from, Square to, Color c, PieceType pt) { inline void Position::move_piece(Square from, Square to, Color c, PieceType pt) {
@@ -428,6 +442,8 @@ inline void Position::remove_piece(Square s, Color c, PieceType pt) {
index[lastSquare] = index[s]; index[lastSquare] = index[s];
pieceList[c][pt][index[lastSquare]] = lastSquare; pieceList[c][pt][index[lastSquare]] = lastSquare;
pieceList[c][pt][pieceCount[c][pt]] = SQ_NONE; pieceList[c][pt][pieceCount[c][pt]] = SQ_NONE;
if (!HasPopCnt)
pieceCount[WHITE][ALL_PIECES]--;
} }
#endif // #ifndef POSITION_H_INCLUDED #endif // #ifndef POSITION_H_INCLUDED

View File

@@ -19,18 +19,18 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cfloat>
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include "book.h"
#include "evaluate.h" #include "evaluate.h"
#include "movegen.h" #include "movegen.h"
#include "movepick.h" #include "movepick.h"
#include "notation.h" #include "notation.h"
#include "rkiss.h"
#include "search.h" #include "search.h"
#include "tbprobe.h"
#include "timeman.h" #include "timeman.h"
#include "thread.h" #include "thread.h"
#include "tt.h" #include "tt.h"
@@ -42,9 +42,14 @@ namespace Search {
LimitsType Limits; LimitsType Limits;
std::vector<RootMove> RootMoves; std::vector<RootMove> RootMoves;
Position RootPos; Position RootPos;
Color RootColor;
Time::point SearchTime; Time::point SearchTime;
StateStackPtr SetupStates; StateStackPtr SetupStates;
int TBCardinality;
uint64_t TBHits;
bool RootInTB;
bool TB50MoveRule;
Depth TBProbeDepth;
Value TBScore;
} }
using std::string; using std::string;
@@ -53,31 +58,27 @@ using namespace Search;
namespace { namespace {
// Set to true to force running with one thread. Used for debugging
const bool FakeSplit = false;
// Different node types, used as template parameter // Different node types, used as template parameter
enum NodeType { Root, PV, NonPV }; enum NodeType { Root, PV, NonPV };
// Dynamic razoring margin based on depth // Dynamic razoring margin based on depth
inline Value razor_margin(Depth d) { return Value(512 + 16 * d); } inline Value razor_margin(Depth d) { return Value(512 + 32 * d); }
// Futility lookup tables (initialized at startup) and their access functions // Futility lookup tables (initialized at startup) and their access functions
int FutilityMoveCounts[2][32]; // [improving][depth] int FutilityMoveCounts[2][32]; // [improving][depth]
inline Value futility_margin(Depth d) { inline Value futility_margin(Depth d) {
return Value(100 * d); return Value(200 * d);
} }
// Reduction lookup tables (initialized at startup) and their access function // Reduction lookup tables (initialized at startup) and their access function
int8_t Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber] int8_t Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber]
template <bool PvNode> inline Depth reduction(bool i, Depth d, int mn) { template <bool PvNode> inline Depth reduction(bool i, Depth d, int mn) {
return (Depth) Reductions[PvNode][i][std::min(int(d), 63)][std::min(mn, 63)];
return (Depth) Reductions[PvNode][i][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)];
} }
size_t MultiPV, PVIdx; size_t PVIdx;
TimeManager TimeMgr; TimeManager TimeMgr;
double BestMoveChanges; double BestMoveChanges;
Value DrawValue[COLOR_NB]; Value DrawValue[COLOR_NB];
@@ -98,18 +99,21 @@ namespace {
string uci_pv(const Position& pos, int depth, Value alpha, Value beta); string uci_pv(const Position& pos, int depth, Value alpha, Value beta);
struct Skill { struct Skill {
Skill(int l) : level(l), best(MOVE_NONE) {} Skill(int l, size_t rootSize) : level(l),
candidates(l < 20 ? std::min(4, (int)rootSize) : 0),
best(MOVE_NONE) {}
~Skill() { ~Skill() {
if (enabled()) // Swap best PV line with the sub-optimal one if (candidates) // Swap best PV line with the sub-optimal one
std::swap(RootMoves[0], *std::find(RootMoves.begin(), std::swap(RootMoves[0], *std::find(RootMoves.begin(),
RootMoves.end(), best ? best : pick_move())); RootMoves.end(), best ? best : pick_move()));
} }
bool enabled() const { return level < 20; } size_t candidates_size() const { return candidates; }
bool time_to_pick(int depth) const { return depth == 1 + level; } bool time_to_pick(int depth) const { return depth == 1 + level; }
Move pick_move(); Move pick_move();
int level; int level;
size_t candidates;
Move best; Move best;
}; };
@@ -129,50 +133,55 @@ void Search::init() {
{ {
double pvRed = 0.00 + log(double(hd)) * log(double(mc)) / 3.00; double pvRed = 0.00 + log(double(hd)) * log(double(mc)) / 3.00;
double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25; double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25;
Reductions[1][1][hd][mc] = int8_t( pvRed >= 1.0 ? pvRed * int(ONE_PLY) : 0);
Reductions[0][1][hd][mc] = int8_t(nonPVRed >= 1.0 ? nonPVRed * int(ONE_PLY) : 0); Reductions[1][1][hd][mc] = int8_t( pvRed >= 1.0 ? pvRed + 0.5: 0);
Reductions[0][1][hd][mc] = int8_t(nonPVRed >= 1.0 ? nonPVRed + 0.5: 0);
Reductions[1][0][hd][mc] = Reductions[1][1][hd][mc]; Reductions[1][0][hd][mc] = Reductions[1][1][hd][mc];
Reductions[0][0][hd][mc] = Reductions[0][1][hd][mc]; Reductions[0][0][hd][mc] = Reductions[0][1][hd][mc];
if (Reductions[0][0][hd][mc] > 2 * ONE_PLY) if (Reductions[0][0][hd][mc] >= 2)
Reductions[0][0][hd][mc] += ONE_PLY; Reductions[0][0][hd][mc] += 1;
else if (Reductions[0][0][hd][mc] > 1 * ONE_PLY)
Reductions[0][0][hd][mc] += ONE_PLY / 2;
} }
// Init futility move count array // Init futility move count array
for (d = 0; d < 32; ++d) for (d = 0; d < 32; ++d)
{ {
FutilityMoveCounts[0][d] = int(2.4 + 0.222 * pow(d + 0.00, 1.8)); FutilityMoveCounts[0][d] = int(2.4 + 0.773 * pow(d + 0.00, 1.8));
FutilityMoveCounts[1][d] = int(3.0 + 0.300 * pow(d + 0.98, 1.8)); FutilityMoveCounts[1][d] = int(2.9 + 1.045 * pow(d + 0.49, 1.8));
} }
} }
/// Search::perft() is our utility to verify move generation. All the leaf nodes /// Search::perft() is our utility to verify move generation. All the leaf nodes
/// up to the given depth are generated and counted and the sum returned. /// up to the given depth are generated and counted and the sum returned.
template<bool Root>
static uint64_t perft(Position& pos, Depth depth) { uint64_t Search::perft(Position& pos, Depth depth) {
StateInfo st; StateInfo st;
uint64_t cnt = 0; uint64_t cnt, nodes = 0;
CheckInfo ci(pos); CheckInfo ci(pos);
const bool leaf = depth == 2 * ONE_PLY; const bool leaf = (depth == 2 * ONE_PLY);
for (MoveList<LEGAL> it(pos); *it; ++it) for (MoveList<LEGAL> it(pos); *it; ++it)
{ {
pos.do_move(*it, st, ci, pos.gives_check(*it, ci)); if (Root && depth <= ONE_PLY)
cnt += leaf ? MoveList<LEGAL>(pos).size() : ::perft(pos, depth - ONE_PLY); cnt = 1, nodes++;
pos.undo_move(*it); else
{
pos.do_move(*it, st, ci, pos.gives_check(*it, ci));
cnt = leaf ? MoveList<LEGAL>(pos).size() : perft<false>(pos, depth - ONE_PLY);
nodes += cnt;
pos.undo_move(*it);
}
if (Root)
sync_cout << move_to_uci(*it, pos.is_chess960()) << ": " << cnt << sync_endl;
} }
return cnt; return nodes;
} }
uint64_t Search::perft(Position& pos, Depth depth) { template uint64_t Search::perft<true>(Position& pos, Depth depth);
return depth > ONE_PLY ? ::perft(pos, depth) : MoveList<LEGAL>(pos).size();
}
/// Search::think() is the external interface to Stockfish's search, and is /// Search::think() is the external interface to Stockfish's search, and is
/// called by the main thread when the program receives the UCI 'go' command. It /// called by the main thread when the program receives the UCI 'go' command. It
@@ -180,14 +189,14 @@ uint64_t Search::perft(Position& pos, Depth depth) {
void Search::think() { void Search::think() {
static PolyglotBook book; // Defined static to initialize the PRNG only once TimeMgr.init(Limits, RootPos.game_ply(), RootPos.side_to_move());
int piecesCnt;
TBHits = TBCardinality = 0;
RootInTB = false;
RootColor = RootPos.side_to_move(); int cf = Options["Contempt"] * PawnValueEg / 100; // From centipawns
TimeMgr.init(Limits, RootPos.game_ply(), RootColor); DrawValue[ RootPos.side_to_move()] = VALUE_DRAW - Value(cf);
DrawValue[~RootPos.side_to_move()] = VALUE_DRAW + Value(cf);
int cf = Options["Contempt Factor"] * PawnValueEg / 100; // From centipawns
DrawValue[ RootColor] = VALUE_DRAW - Value(cf);
DrawValue[~RootColor] = VALUE_DRAW + Value(cf);
if (RootMoves.empty()) if (RootMoves.empty())
{ {
@@ -199,27 +208,55 @@ void Search::think() {
goto finalize; goto finalize;
} }
if (Options["OwnBook"] && !Limits.infinite && !Limits.mate) piecesCnt = RootPos.total_piece_count();
TBCardinality = Options["SyzygyProbeLimit"];
TBProbeDepth = Options["SyzygyProbeDepth"] * ONE_PLY;
if (TBCardinality > Tablebases::TBLargest)
{ {
Move bookMove = book.probe(RootPos, Options["Book File"], Options["Best Book Move"]); TBCardinality = Tablebases::TBLargest;
TBProbeDepth = 0 * ONE_PLY;
if (bookMove && std::count(RootMoves.begin(), RootMoves.end(), bookMove))
{
std::swap(RootMoves[0], *std::find(RootMoves.begin(), RootMoves.end(), bookMove));
goto finalize;
}
} }
TB50MoveRule = Options["Syzygy50MoveRule"];
if (Options["Write Search Log"]) if (piecesCnt <= TBCardinality)
{ {
Log log(Options["Search Log Filename"]); TBHits = RootMoves.size();
log << "\nSearching: " << RootPos.fen()
<< "\ninfinite: " << Limits.infinite // If the current root position is in the tablebases then RootMoves
<< " ponder: " << Limits.ponder // contains only moves that preserve the draw or win.
<< " time: " << Limits.time[RootColor] RootInTB = Tablebases::root_probe(RootPos, TBScore);
<< " increment: " << Limits.inc[RootColor]
<< " moves to go: " << Limits.movestogo if (RootInTB)
<< "\n" << std::endl; {
TBCardinality = 0; // Do not probe tablebases during the search
// It might be a good idea to mangle the hash key (xor it
// with a fixed value) in order to "clear" the hash table of
// the results of previous probes. However, that would have to
// be done from within the Position class, so we skip it for now.
// Optional: decrease target time.
}
else // If DTZ tables are missing, use WDL tables as a fallback
{
// Filter out moves that do not preserve a draw or win
RootInTB = Tablebases::root_probe_wdl(RootPos, TBScore);
// Only probe during search if winning
if (TBScore <= VALUE_DRAW)
TBCardinality = 0;
}
if (!RootInTB)
{
TBHits = 0;
}
else if (!TB50MoveRule)
{
TBScore = TBScore > VALUE_DRAW ? VALUE_MATE - MAX_PLY - 1
: TBScore < VALUE_DRAW ? -VALUE_MATE + MAX_PLY + 1
: TBScore;
}
} }
// Reset the threads, still sleeping: will wake up at split time // Reset the threads, still sleeping: will wake up at split time
@@ -233,25 +270,16 @@ void Search::think() {
Threads.timer->run = false; // Stop the timer Threads.timer->run = false; // Stop the timer
if (Options["Write Search Log"]) if (RootInTB)
{ {
Time::point elapsed = Time::now() - SearchTime + 1; // If we mangled the hash key, unmangle it here
Log log(Options["Search Log Filename"]);
log << "Nodes: " << RootPos.nodes_searched()
<< "\nNodes/second: " << RootPos.nodes_searched() * 1000 / elapsed
<< "\nBest move: " << move_to_san(RootPos, RootMoves[0].pv[0]);
StateInfo st;
RootPos.do_move(RootMoves[0].pv[0], st);
log << "\nPonder move: " << move_to_san(RootPos, RootMoves[0].pv[1]) << std::endl;
RootPos.undo_move(RootMoves[0].pv[0]);
} }
finalize: finalize:
// When search is stopped this info is not printed // When search is stopped this info is not printed
sync_cout << "info nodes " << RootPos.nodes_searched() sync_cout << "info nodes " << RootPos.nodes_searched()
<< " tbhits " << TBHits
<< " time " << Time::now() - SearchTime + 1 << sync_endl; << " time " << Time::now() - SearchTime + 1 << sync_endl;
// When we reach the maximum depth, we can arrive here without a raise of // When we reach the maximum depth, we can arrive here without a raise of
@@ -285,7 +313,6 @@ namespace {
Value bestValue, alpha, beta, delta; Value bestValue, alpha, beta, delta;
std::memset(ss-2, 0, 5 * sizeof(Stack)); std::memset(ss-2, 0, 5 * sizeof(Stack));
(ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains
depth = 0; depth = 0;
BestMoveChanges = 0; BestMoveChanges = 0;
@@ -298,15 +325,12 @@ namespace {
Countermoves.clear(); Countermoves.clear();
Followupmoves.clear(); Followupmoves.clear();
MultiPV = Options["MultiPV"]; size_t multiPV = Options["MultiPV"];
Skill skill(Options["Skill Level"]); Skill skill(Options["Skill Level"], RootMoves.size());
// Do we have to play with skill handicap? In this case enable MultiPV search // Do we have to play with skill handicap? In this case enable MultiPV search
// that we will use behind the scenes to retrieve a set of possible moves. // that we will use behind the scenes to retrieve a set of possible moves.
if (skill.enabled() && MultiPV < 4) multiPV = std::max(multiPV, skill.candidates_size());
MultiPV = 4;
MultiPV = std::min(MultiPV, RootMoves.size());
// Iterative deepening loop until requested to stop or target depth reached // Iterative deepening loop until requested to stop or target depth reached
while (++depth <= MAX_PLY && !Signals.stop && (!Limits.depth || depth <= Limits.depth)) while (++depth <= MAX_PLY && !Signals.stop && (!Limits.depth || depth <= Limits.depth))
@@ -320,7 +344,7 @@ namespace {
RootMoves[i].prevScore = RootMoves[i].score; RootMoves[i].prevScore = RootMoves[i].score;
// MultiPV loop. We perform a full root search for each PV line // MultiPV loop. We perform a full root search for each PV line
for (PVIdx = 0; PVIdx < MultiPV && !Signals.stop; ++PVIdx) for (PVIdx = 0; PVIdx < std::min(multiPV, RootMoves.size()) && !Signals.stop; ++PVIdx)
{ {
// Reset aspiration window starting size // Reset aspiration window starting size
if (depth >= 5) if (depth >= 5)
@@ -377,7 +401,7 @@ namespace {
else else
break; break;
delta += delta / 2; delta += 3 * delta / 8;
assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
} }
@@ -385,25 +409,14 @@ namespace {
// Sort the PV lines searched so far and update the GUI // Sort the PV lines searched so far and update the GUI
std::stable_sort(RootMoves.begin(), RootMoves.begin() + PVIdx + 1); std::stable_sort(RootMoves.begin(), RootMoves.begin() + PVIdx + 1);
if (PVIdx + 1 == MultiPV || Time::now() - SearchTime > 3000) if (PVIdx + 1 == std::min(multiPV, RootMoves.size()) || Time::now() - SearchTime > 3000)
sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl;
} }
// If skill levels are enabled and time is up, pick a sub-optimal best move // If skill levels are enabled and time is up, pick a sub-optimal best move
if (skill.enabled() && skill.time_to_pick(depth)) if (skill.candidates_size() && skill.time_to_pick(depth))
skill.pick_move(); skill.pick_move();
if (Options["Write Search Log"])
{
RootMove& rm = RootMoves[0];
if (skill.best != MOVE_NONE)
rm = *std::find(RootMoves.begin(), RootMoves.end(), skill.best);
Log log(Options["Search Log Filename"]);
log << pretty_pv(pos, depth, rm.score, Time::now() - SearchTime, &rm.pv[0])
<< std::endl;
}
// Have we found a "mate in x"? // Have we found a "mate in x"?
if ( Limits.mate if ( Limits.mate
&& bestValue >= VALUE_MATE_IN_MAX_PLY && bestValue >= VALUE_MATE_IN_MAX_PLY
@@ -414,7 +427,7 @@ namespace {
if (Limits.use_time_management() && !Signals.stop && !Signals.stopOnPonderhit) if (Limits.use_time_management() && !Signals.stop && !Signals.stopOnPonderhit)
{ {
// Take some extra time if the best move has changed // Take some extra time if the best move has changed
if (depth > 4 && depth < 50 && MultiPV == 1) if (depth > 4 && multiPV == 1)
TimeMgr.pv_instability(BestMoveChanges); TimeMgr.pv_instability(BestMoveChanges);
// Stop the search if only one legal move is available or all // Stop the search if only one legal move is available or all
@@ -540,6 +553,37 @@ namespace {
return ttValue; return ttValue;
} }
// Step 4a. Tablebase probe
if ( !RootNode
&& pos.total_piece_count() <= TBCardinality
&& ( pos.total_piece_count() < TBCardinality || depth >= TBProbeDepth )
&& pos.rule50_count() == 0)
{
int found, v = Tablebases::probe_wdl(pos, &found);
if (found)
{
TBHits++;
if (TB50MoveRule) {
value = v < -1 ? -VALUE_MATE + MAX_PLY + ss->ply
: v > 1 ? VALUE_MATE - MAX_PLY - ss->ply
: VALUE_DRAW + 2 * v;
}
else
{
value = v < 0 ? -VALUE_MATE + MAX_PLY + ss->ply
: v > 0 ? VALUE_MATE - MAX_PLY - ss->ply
: VALUE_DRAW;
}
TT.store(posKey, value_to_tt(value, ss->ply), BOUND_EXACT,
depth + 6 * ONE_PLY, MOVE_NONE, VALUE_NONE);
return value;
}
}
// Step 5. Evaluate the position statically and update parent's gain statistics // Step 5. Evaluate the position statically and update parent's gain statistics
if (inCheck) if (inCheck)
{ {
@@ -560,7 +604,9 @@ namespace {
} }
else else
{ {
eval = ss->staticEval = evaluate(pos); eval = ss->staticEval =
(ss-1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss-1)->staticEval + 2 * Eval::Tempo;
TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, ss->staticEval); TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, ss->staticEval);
} }
@@ -568,6 +614,7 @@ namespace {
&& ss->staticEval != VALUE_NONE && ss->staticEval != VALUE_NONE
&& (ss-1)->staticEval != VALUE_NONE && (ss-1)->staticEval != VALUE_NONE
&& (move = (ss-1)->currentMove) != MOVE_NULL && (move = (ss-1)->currentMove) != MOVE_NULL
&& move != MOVE_NONE
&& type_of(move) == NORMAL) && type_of(move) == NORMAL)
{ {
Square to = to_sq(move); Square to = to_sq(move);
@@ -579,7 +626,6 @@ namespace {
&& depth < 4 * ONE_PLY && depth < 4 * ONE_PLY
&& eval + razor_margin(depth) <= alpha && eval + razor_margin(depth) <= alpha
&& ttMove == MOVE_NONE && ttMove == MOVE_NONE
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
&& !pos.pawn_on_7th(pos.side_to_move())) && !pos.pawn_on_7th(pos.side_to_move()))
{ {
if ( depth <= ONE_PLY if ( depth <= ONE_PLY
@@ -597,8 +643,7 @@ namespace {
&& !ss->skipNullMove && !ss->skipNullMove
&& depth < 7 * ONE_PLY && depth < 7 * ONE_PLY
&& eval - futility_margin(depth) >= beta && eval - futility_margin(depth) >= beta
&& abs(beta) < VALUE_MATE_IN_MAX_PLY && eval < VALUE_KNOWN_WIN // Do not return unproven wins
&& abs(eval) < VALUE_KNOWN_WIN
&& pos.non_pawn_material(pos.side_to_move())) && pos.non_pawn_material(pos.side_to_move()))
return eval - futility_margin(depth); return eval - futility_margin(depth);
@@ -607,7 +652,6 @@ namespace {
&& !ss->skipNullMove && !ss->skipNullMove
&& depth >= 2 * ONE_PLY && depth >= 2 * ONE_PLY
&& eval >= beta && eval >= beta
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
&& pos.non_pawn_material(pos.side_to_move())) && pos.non_pawn_material(pos.side_to_move()))
{ {
ss->currentMove = MOVE_NULL; ss->currentMove = MOVE_NULL;
@@ -615,9 +659,7 @@ namespace {
assert(eval - beta >= 0); assert(eval - beta >= 0);
// Null move dynamic reduction based on depth and value // Null move dynamic reduction based on depth and value
Depth R = 3 * ONE_PLY Depth R = (3 + depth / 4 + std::min(int(eval - beta) / PawnValueMg, 3)) * ONE_PLY;
+ depth / 4
+ int(eval - beta) / PawnValueMg * ONE_PLY;
pos.do_null_move(st); pos.do_null_move(st);
(ss+1)->skipNullMove = true; (ss+1)->skipNullMove = true;
@@ -632,7 +674,7 @@ namespace {
if (nullValue >= VALUE_MATE_IN_MAX_PLY) if (nullValue >= VALUE_MATE_IN_MAX_PLY)
nullValue = beta; nullValue = beta;
if (depth < 12 * ONE_PLY) if (depth < 12 * ONE_PLY && abs(beta) < VALUE_KNOWN_WIN)
return nullValue; return nullValue;
// Do verification search at high depths // Do verification search at high depths
@@ -682,10 +724,9 @@ namespace {
&& !ttMove && !ttMove
&& (PvNode || ss->staticEval + 256 >= beta)) && (PvNode || ss->staticEval + 256 >= beta))
{ {
Depth d = depth - 2 * ONE_PLY - (PvNode ? DEPTH_ZERO : depth / 4); Depth d = 2 * (depth - 2 * ONE_PLY) - (PvNode ? DEPTH_ZERO : depth / 2);
ss->skipNullMove = true; ss->skipNullMove = true;
search<PvNode ? PV : NonPV, false>(pos, ss, alpha, beta, d, true); search<PvNode ? PV : NonPV, false>(pos, ss, alpha, beta, d / 2, true);
ss->skipNullMove = false; ss->skipNullMove = false;
tte = TT.probe(posKey); tte = TT.probe(posKey);
@@ -713,6 +754,8 @@ moves_loop: // When in check and at SpNode search starts from here
&& !SpNode && !SpNode
&& depth >= 8 * ONE_PLY && depth >= 8 * ONE_PLY
&& ttMove != MOVE_NONE && ttMove != MOVE_NONE
/* && ttValue != VALUE_NONE Already implicit in the next condition */
&& abs(ttValue) < VALUE_KNOWN_WIN
&& !excludedMove // Recursive singular search is not allowed && !excludedMove // Recursive singular search is not allowed
&& (tte->bound() & BOUND_LOWER) && (tte->bound() & BOUND_LOWER)
&& tte->depth() >= depth - 3 * ONE_PLY; && tte->depth() >= depth - 3 * ONE_PLY;
@@ -749,7 +792,7 @@ moves_loop: // When in check and at SpNode search starts from here
Signals.firstRootMove = (moveCount == 1); Signals.firstRootMove = (moveCount == 1);
if (thisThread == Threads.main() && Time::now() - SearchTime > 3000) if (thisThread == Threads.main() && Time::now() - SearchTime > 3000)
sync_cout << "info depth " << depth / ONE_PLY sync_cout << "info depth " << depth
<< " currmove " << move_to_uci(move, pos.is_chess960()) << " currmove " << move_to_uci(move, pos.is_chess960())
<< " currmovenumber " << moveCount + PVIdx << sync_endl; << " currmovenumber " << moveCount + PVIdx << sync_endl;
} }
@@ -777,12 +820,9 @@ moves_loop: // When in check and at SpNode search starts from here
if ( singularExtensionNode if ( singularExtensionNode
&& move == ttMove && move == ttMove
&& !ext && !ext
&& pos.legal(move, ci.pinned) && pos.legal(move, ci.pinned))
&& abs(ttValue) < VALUE_KNOWN_WIN)
{ {
assert(ttValue != VALUE_NONE); Value rBeta = ttValue - int(2 * depth);
Value rBeta = ttValue - int(depth);
ss->excludedMove = move; ss->excludedMove = move;
ss->skipNullMove = true; ss->skipNullMove = true;
value = search<NonPV, false>(pos, ss, rBeta - 1, rBeta, depth / 2, cutNode); value = search<NonPV, false>(pos, ss, rBeta - 1, rBeta, depth / 2, cutNode);
@@ -806,7 +846,7 @@ moves_loop: // When in check and at SpNode search starts from here
{ {
// Move count based pruning // Move count based pruning
if ( depth < 16 * ONE_PLY if ( depth < 16 * ONE_PLY
&& moveCount >= FutilityMoveCounts[improving][depth] ) && moveCount >= FutilityMoveCounts[improving][depth])
{ {
if (SpNode) if (SpNode)
splitPoint->mutex.lock(); splitPoint->mutex.lock();
@@ -819,8 +859,8 @@ moves_loop: // When in check and at SpNode search starts from here
// Futility pruning: parent node // Futility pruning: parent node
if (predictedDepth < 7 * ONE_PLY) if (predictedDepth < 7 * ONE_PLY)
{ {
futilityValue = ss->staticEval + futility_margin(predictedDepth) futilityValue = ss->staticEval + futility_margin(predictedDepth)
+ 128 + Gains[pos.moved_piece(move)][to_sq(move)]; + 128 + Gains[pos.moved_piece(move)][to_sq(move)];
if (futilityValue <= alpha) if (futilityValue <= alpha)
{ {
@@ -846,6 +886,9 @@ moves_loop: // When in check and at SpNode search starts from here
} }
} }
// Speculative prefetch as early as possible
prefetch((char*)TT.first_entry(pos.key_after(move)));
// Check for legality just before making the move // Check for legality just before making the move
if (!RootNode && !SpNode && !pos.legal(move, ci.pinned)) if (!RootNode && !SpNode && !pos.legal(move, ci.pinned))
{ {
@@ -872,15 +915,20 @@ moves_loop: // When in check and at SpNode search starts from here
{ {
ss->reduction = reduction<PvNode>(improving, depth, moveCount); ss->reduction = reduction<PvNode>(improving, depth, moveCount);
if (!PvNode && cutNode) if ( (!PvNode && cutNode)
|| History[pos.piece_on(to_sq(move))][to_sq(move)] < 0)
ss->reduction += ONE_PLY; ss->reduction += ONE_PLY;
else if (History[pos.piece_on(to_sq(move))][to_sq(move)] < 0)
ss->reduction += ONE_PLY / 2;
if (move == countermoves[0] || move == countermoves[1]) if (move == countermoves[0] || move == countermoves[1])
ss->reduction = std::max(DEPTH_ZERO, ss->reduction - ONE_PLY); ss->reduction = std::max(DEPTH_ZERO, ss->reduction - ONE_PLY);
// Decrease reduction for moves that escape a capture
if ( ss->reduction
&& type_of(move) == NORMAL
&& type_of(pos.piece_on(to_sq(move))) != PAWN
&& pos.see(make_move(to_sq(move), from_sq(move))) < 0)
ss->reduction = std::max(DEPTH_ZERO, ss->reduction - ONE_PLY);
Depth d = std::max(newDepth - ss->reduction, ONE_PLY); Depth d = std::max(newDepth - ss->reduction, ONE_PLY);
if (SpNode) if (SpNode)
alpha = splitPoint->alpha; alpha = splitPoint->alpha;
@@ -906,20 +954,20 @@ moves_loop: // When in check and at SpNode search starts from here
if (SpNode) if (SpNode)
alpha = splitPoint->alpha; alpha = splitPoint->alpha;
value = newDepth < ONE_PLY ? value = newDepth < ONE_PLY ?
givesCheck ? -qsearch<NonPV, true>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) givesCheck ? -qsearch<NonPV, true>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
: -qsearch<NonPV, false>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) : -qsearch<NonPV, false>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
: - search<NonPV, false>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); : - search<NonPV, false>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode);
} }
// For PV nodes only, do a full PV search on the first move or after a fail // For PV nodes only, do a full PV search on the first move or after a fail
// high (in the latter case search only if value < beta), otherwise let the // high (in the latter case search only if value < beta), otherwise let the
// parent node fail low with value <= alpha and to try another move. // parent node fail low with value <= alpha and to try another move.
if (PvNode && (pvMove || (value > alpha && (RootNode || value < beta)))) if (PvNode && (pvMove || (value > alpha && (RootNode || value < beta))))
value = newDepth < ONE_PLY ? value = newDepth < ONE_PLY ?
givesCheck ? -qsearch<PV, true>(pos, ss+1, -beta, -alpha, DEPTH_ZERO) givesCheck ? -qsearch<PV, true>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: -qsearch<PV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO) : -qsearch<PV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: - search<PV, false>(pos, ss+1, -beta, -alpha, newDepth, false); : - search<PV, false>(pos, ss+1, -beta, -alpha, newDepth, false);
// Step 17. Undo move // Step 17. Undo move
pos.undo_move(move); pos.undo_move(move);
@@ -994,8 +1042,8 @@ moves_loop: // When in check and at SpNode search starts from here
{ {
assert(bestValue > -VALUE_INFINITE && bestValue < beta); assert(bestValue > -VALUE_INFINITE && bestValue < beta);
thisThread->split<FakeSplit>(pos, ss, alpha, beta, &bestValue, &bestMove, thisThread->split(pos, ss, alpha, beta, &bestValue, &bestMove,
depth, moveCount, &mp, NT, cutNode); depth, moveCount, &mp, NT, cutNode);
if (Signals.stop || thisThread->cutoff_occurred()) if (Signals.stop || thisThread->cutoff_occurred())
return VALUE_ZERO; return VALUE_ZERO;
@@ -1116,7 +1164,8 @@ moves_loop: // When in check and at SpNode search starts from here
bestValue = ttValue; bestValue = ttValue;
} }
else else
ss->staticEval = bestValue = evaluate(pos); ss->staticEval = bestValue =
(ss-1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss-1)->staticEval + 2 * Eval::Tempo;
// Stand pat. Return immediately if static value is at least beta // Stand pat. Return immediately if static value is at least beta
if (bestValue >= beta) if (bestValue >= beta)
@@ -1189,6 +1238,9 @@ moves_loop: // When in check and at SpNode search starts from here
&& pos.see_sign(move) < VALUE_ZERO) && pos.see_sign(move) < VALUE_ZERO)
continue; continue;
// Speculative prefetch as early as possible
prefetch((char*)TT.first_entry(pos.key_after(move)));
// Check for legality just before making the move // Check for legality just before making the move
if (!pos.legal(move, ci.pinned)) if (!pos.legal(move, ci.pinned))
continue; continue;
@@ -1279,7 +1331,7 @@ moves_loop: // When in check and at SpNode search starts from here
// Increase history value of the cut-off move and decrease all the other // Increase history value of the cut-off move and decrease all the other
// played quiet moves. // played quiet moves.
Value bonus = Value(int(depth) * int(depth)); Value bonus = Value(4 * int(depth) * int(depth));
History.update(pos.moved_piece(move), to_sq(move), bonus); History.update(pos.moved_piece(move), to_sq(move), bonus);
for (int i = 0; i < quietsCnt; ++i) for (int i = 0; i < quietsCnt; ++i)
{ {
@@ -1301,8 +1353,8 @@ moves_loop: // When in check and at SpNode search starts from here
} }
// When playing with a strength handicap, choose best move among the MultiPV // When playing with a strength handicap, choose best move among the first 'candidates'
// set using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. // RootMoves using a statistical rule dependent on 'level'. Idea by Heinz van Saanen.
Move Skill::pick_move() { Move Skill::pick_move() {
@@ -1313,7 +1365,7 @@ moves_loop: // When in check and at SpNode search starts from here
rk.rand<unsigned>(); rk.rand<unsigned>();
// RootMoves are already sorted by score in descending order // RootMoves are already sorted by score in descending order
int variance = std::min(RootMoves[0].score - RootMoves[MultiPV - 1].score, PawnValueMg); int variance = std::min(RootMoves[0].score - RootMoves[candidates - 1].score, PawnValueMg);
int weakness = 120 - 2 * level; int weakness = 120 - 2 * level;
int max_s = -VALUE_INFINITE; int max_s = -VALUE_INFINITE;
best = MOVE_NONE; best = MOVE_NONE;
@@ -1321,12 +1373,12 @@ moves_loop: // When in check and at SpNode search starts from here
// Choose best move. For each move score we add two terms both dependent on // Choose best move. For each move score we add two terms both dependent on
// weakness. One deterministic and bigger for weaker moves, and one random, // weakness. One deterministic and bigger for weaker moves, and one random,
// then we choose the move with the resulting highest score. // then we choose the move with the resulting highest score.
for (size_t i = 0; i < MultiPV; ++i) for (size_t i = 0; i < candidates; ++i)
{ {
int s = RootMoves[i].score; int s = RootMoves[i].score;
// Don't allow crazy blunders even at very low skills // Don't allow crazy blunders even at very low skills
if (i > 0 && RootMoves[i-1].score > s + 2 * PawnValueMg) if (i > 0 && RootMoves[i - 1].score > s + 2 * PawnValueMg)
break; break;
// This is our magic formula // This is our magic formula
@@ -1368,14 +1420,24 @@ moves_loop: // When in check and at SpNode search starts from here
int d = updated ? depth : depth - 1; int d = updated ? depth : depth - 1;
Value v = updated ? RootMoves[i].score : RootMoves[i].prevScore; Value v = updated ? RootMoves[i].score : RootMoves[i].prevScore;
bool tb = RootInTB;
if (tb)
{
if (abs(v) >= VALUE_MATE - MAX_PLY)
tb = false;
else
v = TBScore;
}
if (ss.rdbuf()->in_avail()) // Not at first line if (ss.rdbuf()->in_avail()) // Not at first line
ss << "\n"; ss << "\n";
ss << "info depth " << d ss << "info depth " << d
<< " seldepth " << selDepth << " seldepth " << selDepth
<< " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v)) << " score " << ((!tb && i == PVIdx) ? score_to_uci(v, alpha, beta) : score_to_uci(v))
<< " nodes " << pos.nodes_searched() << " nodes " << pos.nodes_searched()
<< " nps " << pos.nodes_searched() * 1000 / elapsed << " nps " << pos.nodes_searched() * 1000 / elapsed
<< " tbhits " << TBHits
<< " time " << elapsed << " time " << elapsed
<< " multipv " << i + 1 << " multipv " << i + 1
<< " pv"; << " pv";
@@ -1463,46 +1525,13 @@ void Thread::idle_loop() {
assert(!this_sp || (this_sp->masterThread == this && searching)); assert(!this_sp || (this_sp->masterThread == this && searching));
while (true) while (!exit)
{ {
// If we are not searching, wait for a condition to be signaled instead of
// wasting CPU time polling for work.
while (!searching || exit)
{
if (exit)
{
assert(!this_sp);
return;
}
// Grab the lock to avoid races with Thread::notify_one()
mutex.lock();
// If we are master and all slaves have finished then exit idle_loop
if (this_sp && this_sp->slavesMask.none())
{
mutex.unlock();
break;
}
// Do sleep after retesting sleep conditions under lock protection. In
// particular we need to avoid a deadlock in case a master thread has,
// in the meanwhile, allocated us and sent the notify_one() call before
// we had the chance to grab the lock.
if (!searching && !exit)
sleepCondition.wait(mutex);
mutex.unlock();
}
// If this thread has been assigned work, launch a search // If this thread has been assigned work, launch a search
if (searching) while (searching)
{ {
assert(!exit);
Threads.mutex.lock(); Threads.mutex.lock();
assert(searching);
assert(activeSplitPoint); assert(activeSplitPoint);
SplitPoint* sp = activeSplitPoint; SplitPoint* sp = activeSplitPoint;
@@ -1559,7 +1588,7 @@ void Thread::idle_loop() {
if (Threads.size() > 2) if (Threads.size() > 2)
for (size_t i = 0; i < Threads.size(); ++i) for (size_t i = 0; i < Threads.size(); ++i)
{ {
int size = Threads[i]->splitPointsSize; // Local copy const int size = Threads[i]->splitPointsSize; // Local copy
sp = size ? &Threads[i]->splitPoints[size - 1] : NULL; sp = size ? &Threads[i]->splitPoints[size - 1] : NULL;
if ( sp if ( sp
@@ -1586,16 +1615,23 @@ void Thread::idle_loop() {
} }
} }
// If this thread is the master of a split point and all slaves have finished // Grab the lock to avoid races with Thread::notify_one()
// their work at this split point, return from the idle loop. mutex.lock();
// If we are master and all slaves have finished then exit idle_loop
if (this_sp && this_sp->slavesMask.none()) if (this_sp && this_sp->slavesMask.none())
{ {
this_sp->mutex.lock(); assert(!searching);
bool finished = this_sp->slavesMask.none(); // Retest under lock protection mutex.unlock();
this_sp->mutex.unlock(); break;
if (finished)
return;
} }
// If we are not searching, wait for a condition to be signaled instead of
// wasting CPU time polling for work.
if (!searching && !exit)
sleepCondition.wait(mutex);
mutex.unlock();
} }
} }

View File

@@ -45,7 +45,7 @@ struct Stack {
Move killers[2]; Move killers[2];
Depth reduction; Depth reduction;
Value staticEval; Value staticEval;
int skipNullMove; bool skipNullMove;
}; };
@@ -101,13 +101,12 @@ extern volatile SignalsType Signals;
extern LimitsType Limits; extern LimitsType Limits;
extern std::vector<RootMove> RootMoves; extern std::vector<RootMove> RootMoves;
extern Position RootPos; extern Position RootPos;
extern Color RootColor;
extern Time::point SearchTime; extern Time::point SearchTime;
extern StateStackPtr SetupStates; extern StateStackPtr SetupStates;
extern void init(); extern void init();
extern uint64_t perft(Position& pos, Depth depth);
extern void think(); extern void think();
template<bool Root> uint64_t perft(Position& pos, Depth depth);
} // namespace Search } // namespace Search

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,142 @@
/*
Copyright (c) 2011-2013 Ronald de Man
*/
#ifndef TBCORE_H
#define TBCORE_H
#ifndef __WIN32__
#define SEP_CHAR ':'
#define FD int
#define FD_ERR -1
#else
#include <windows.h>
#define SEP_CHAR ';'
#define FD HANDLE
#define FD_ERR INVALID_HANDLE_VALUE
#endif
#include <stdint.h>
#include <atomic>
#define WDLSUFFIX ".rtbw"
#define DTZSUFFIX ".rtbz"
#define TBPIECES 6
#define WDL_MAGIC 0x5d23e871
#define DTZ_MAGIC 0xa50c66d7
#define TBHASHBITS 11
typedef unsigned char ubyte;
typedef unsigned short ushort;
struct TBHashEntry;
typedef uint64_t base_t;
struct PairsData {
char *indextable;
ushort *sizetable;
ubyte *data;
ushort *offset;
ubyte *symlen;
ubyte *sympat;
int blocksize;
int idxbits;
int min_len;
base_t base[1]; // C++ complains about base[]...
};
struct TBEntry {
char *data;
uint64_t key;
uint64_t mapping;
std::atomic<ubyte> ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
} __attribute__((__may_alias__));
struct TBEntry_piece {
char *data;
uint64_t key;
uint64_t mapping;
std::atomic<ubyte> ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
ubyte enc_type;
struct PairsData *precomp[2];
int factor[2][TBPIECES];
ubyte pieces[2][TBPIECES];
ubyte norm[2][TBPIECES];
};
struct TBEntry_pawn {
char *data;
uint64_t key;
uint64_t mapping;
std::atomic<ubyte> ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
ubyte pawns[2];
struct {
struct PairsData *precomp[2];
int factor[2][TBPIECES];
ubyte pieces[2][TBPIECES];
ubyte norm[2][TBPIECES];
} file[4];
};
struct DTZEntry_piece {
char *data;
uint64_t key;
uint64_t mapping;
std::atomic<ubyte> ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
ubyte enc_type;
struct PairsData *precomp;
int factor[TBPIECES];
ubyte pieces[TBPIECES];
ubyte norm[TBPIECES];
ubyte flags; // accurate, mapped, side
ushort map_idx[4];
ubyte *map;
};
struct DTZEntry_pawn {
char *data;
uint64_t key;
uint64_t mapping;
std::atomic<ubyte> ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
ubyte pawns[2];
struct {
struct PairsData *precomp;
int factor[TBPIECES];
ubyte pieces[TBPIECES];
ubyte norm[TBPIECES];
} file[4];
ubyte flags[4];
ushort map_idx[4][4];
ubyte *map;
};
struct TBHashEntry {
uint64_t key;
struct TBEntry *ptr;
};
struct DTZTableEntry {
uint64_t key1;
uint64_t key2;
std::atomic<TBEntry*> entry;
};
#endif

View File

@@ -0,0 +1,819 @@
/*
Copyright (c) 2013 Ronald de Man
This file may be redistributed and/or modified without restrictions.
tbprobe.cpp contains the Stockfish-specific routines of the
tablebase probing code. It should be relatively easy to adapt
this code to other chess engines.
*/
#include "position.h"
#include "movegen.h"
#include "bitboard.h"
#include "search.h"
#include "bitcount.h"
#include "tbprobe.h"
#include "tbcore.h"
#include "tbcore.cpp"
namespace Zobrist {
extern Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
}
int Tablebases::TBLargest = 0;
// Given a position with 6 or fewer pieces, produce a text string
// of the form KQPvKRP, where "KQP" represents the white pieces if
// mirror == false and the black pieces if mirror == true.
static void prt_str(Position& pos, char *str, bool mirror)
{
Color color;
PieceType pt;
int i;
color = !mirror ? WHITE : BLACK;
for (pt = KING; pt >= PAWN; --pt)
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
*str++ = pchr[6 - pt];
*str++ = 'v';
color = ~color;
for (pt = KING; pt >= PAWN; --pt)
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
*str++ = pchr[6 - pt];
*str++ = 0;
}
// Given a position, produce a 64-bit material signature key.
// If the engine supports such a key, it should equal the engine's key.
static uint64_t calc_key(const Position& pos, bool mirror)
{
Color color;
PieceType pt;
int i;
uint64_t key = 0;
color = !mirror ? WHITE : BLACK;
for (pt = PAWN; pt <= KING; ++pt)
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
key ^= Zobrist::psq[WHITE][pt][i - 1];
color = ~color;
for (pt = PAWN; pt <= KING; ++pt)
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
key ^= Zobrist::psq[BLACK][pt][i - 1];
return key;
}
// Produce a 64-bit material key corresponding to the material combination
// defined by pcs[16], where pcs[1], ..., pcs[6] is the number of white
// pawns, ..., kings and pcs[9], ..., pcs[14] is the number of black
// pawns, ..., kings.
static uint64_t calc_key_from_pcs(const int *pcs, bool mirror)
{
int color;
PieceType pt;
int i;
uint64_t key = 0;
color = !mirror ? 0 : 8;
for (pt = PAWN; pt <= KING; ++pt)
for (i = 0; i < pcs[color + pt]; i++)
key ^= Zobrist::psq[WHITE][pt][i];
color ^= 8;
for (pt = PAWN; pt <= KING; ++pt)
for (i = 0; i < pcs[color + pt]; i++)
key ^= Zobrist::psq[BLACK][pt][i];
return key;
}
// probe_wdl_table and probe_dtz_table require similar adaptations.
static int probe_wdl_table(Position& pos, int *success)
{
struct TBEntry *ptr;
struct TBHashEntry *ptr2;
uint64_t idx;
uint64_t key;
int i;
ubyte res;
int p[TBPIECES];
// Obtain the position's material signature key.
key = pos.material_key();
// Test for KvK.
if (key == (Zobrist::psq[WHITE][KING][0] ^ Zobrist::psq[BLACK][KING][0]))
return 0;
ptr2 = WDL_hash[key >> (64 - TBHASHBITS)];
for (i = 0; i < HSHMAX; i++)
if (ptr2[i].key == key) break;
if (i == HSHMAX) {
*success = 0;
return 0;
}
ptr = ptr2[i].ptr;
ubyte ready = ptr->ready.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
if (!ready) {
std::lock_guard<std::mutex> L(TB_mutex);
ready = ptr->ready.load(std::memory_order_relaxed);
if (!ready) {
char str[16];
prt_str(pos, str, ptr->key != key);
if (!init_table_wdl(ptr, str)) {
ptr2[i].key = 0ULL;
*success = 0;
return 0;
}
std::atomic_thread_fence(std::memory_order_release);
ptr->ready.store(1, std::memory_order_relaxed);
}
}
int bside, mirror, cmirror;
if (!ptr->symmetric) {
if (key != ptr->key) {
cmirror = 8;
mirror = 0x38;
bside = (pos.side_to_move() == WHITE);
} else {
cmirror = mirror = 0;
bside = !(pos.side_to_move() == WHITE);
}
} else {
cmirror = pos.side_to_move() == WHITE ? 0 : 8;
mirror = pos.side_to_move() == WHITE ? 0 : 0x38;
bside = 0;
}
// p[i] is to contain the square 0-63 (A1-H8) for a piece of type
// pc[i] ^ cmirror, where 1 = white pawn, ..., 14 = black king.
// Pieces of the same type are guaranteed to be consecutive.
if (!ptr->has_pawns) {
struct TBEntry_piece *entry = (struct TBEntry_piece *)ptr;
ubyte *pc = entry->pieces[bside];
for (i = 0; i < entry->num;) {
Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
(PieceType)(pc[i] & 0x07));
do {
p[i++] = pop_lsb(&bb);
} while (bb);
}
idx = encode_piece(entry, entry->norm[bside], p, entry->factor[bside]);
res = decompress_pairs(entry->precomp[bside], idx);
} else {
struct TBEntry_pawn *entry = (struct TBEntry_pawn *)ptr;
int k = entry->file[0].pieces[0][0] ^ cmirror;
Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07));
i = 0;
do {
p[i++] = pop_lsb(&bb) ^ mirror;
} while (bb);
int f = pawn_file(entry, p);
ubyte *pc = entry->file[f].pieces[bside];
for (; i < entry->num;) {
bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
(PieceType)(pc[i] & 0x07));
do {
p[i++] = pop_lsb(&bb) ^ mirror;
} while (bb);
}
idx = encode_pawn(entry, entry->file[f].norm[bside], p, entry->file[f].factor[bside]);
res = decompress_pairs(entry->file[f].precomp[bside], idx);
}
return ((int)res) - 2;
}
static int probe_dtz_table(Position& pos, int wdl, int *success)
{
uint64_t idx;
int i, res;
int p[TBPIECES];
// Obtain the position's material signature key.
uint64_t key = calc_key(pos, false);
DTZTableEntry* dtzTabEnt;
{
dtzTabEnt = DTZ_hash[key >> (64 - TBHASHBITS)];
for (i = 0; i < HSHMAX; i++)
if (dtzTabEnt[i].key1 == key) break;
if (i == HSHMAX) {
uint64_t key2 = calc_key(pos, true);
dtzTabEnt = DTZ_hash[key2 >> (64 - TBHASHBITS)];
for (i = 0; i < HSHMAX; i++)
if (dtzTabEnt[i].key2 == key) break;
}
if (i == HSHMAX) {
*success = 0;
return 0;
}
dtzTabEnt += i;
}
TBEntry* ptr = dtzTabEnt->entry.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
if (!ptr) {
std::lock_guard<std::mutex> L(TB_mutex);
ptr = dtzTabEnt->entry.load(std::memory_order_relaxed);
if (!ptr) {
struct TBHashEntry *ptr2 = WDL_hash[key >> (64 - TBHASHBITS)];
for (i = 0; i < HSHMAX; i++)
if (ptr2[i].key == key) break;
if (i == HSHMAX) {
*success = 0;
return 0;
}
char str[16];
bool mirror = (ptr2[i].ptr->key != key);
prt_str(pos, str, mirror);
ptr = load_dtz_table(str, calc_key(pos, mirror), calc_key(pos, !mirror));
std::atomic_thread_fence(std::memory_order_release);
dtzTabEnt->entry.store(ptr, std::memory_order_relaxed);
}
}
if (!ptr) {
*success = 0;
return 0;
}
int bside, mirror, cmirror;
if (!ptr->symmetric) {
if (key != ptr->key) {
cmirror = 8;
mirror = 0x38;
bside = (pos.side_to_move() == WHITE);
} else {
cmirror = mirror = 0;
bside = !(pos.side_to_move() == WHITE);
}
} else {
cmirror = pos.side_to_move() == WHITE ? 0 : 8;
mirror = pos.side_to_move() == WHITE ? 0 : 0x38;
bside = 0;
}
if (!ptr->has_pawns) {
struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr;
if ((entry->flags & 1) != bside && !entry->symmetric) {
*success = -1;
return 0;
}
ubyte *pc = entry->pieces;
for (i = 0; i < entry->num;) {
Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
(PieceType)(pc[i] & 0x07));
do {
p[i++] = pop_lsb(&bb);
} while (bb);
}
idx = encode_piece((struct TBEntry_piece *)entry, entry->norm, p, entry->factor);
res = decompress_pairs(entry->precomp, idx);
if (entry->flags & 2)
res = entry->map[entry->map_idx[wdl_to_map[wdl + 2]] + res];
if (!(entry->flags & pa_flags[wdl + 2]) || (wdl & 1))
res *= 2;
} else {
struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr;
int k = entry->file[0].pieces[0] ^ cmirror;
Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07));
i = 0;
do {
p[i++] = pop_lsb(&bb) ^ mirror;
} while (bb);
int f = pawn_file((struct TBEntry_pawn *)entry, p);
if ((entry->flags[f] & 1) != bside) {
*success = -1;
return 0;
}
ubyte *pc = entry->file[f].pieces;
for (; i < entry->num;) {
bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
(PieceType)(pc[i] & 0x07));
do {
p[i++] = pop_lsb(&bb) ^ mirror;
} while (bb);
}
idx = encode_pawn((struct TBEntry_pawn *)entry, entry->file[f].norm, p, entry->file[f].factor);
res = decompress_pairs(entry->file[f].precomp, idx);
if (entry->flags[f] & 2)
res = entry->map[entry->map_idx[f][wdl_to_map[wdl + 2]] + res];
if (!(entry->flags[f] & pa_flags[wdl + 2]) || (wdl & 1))
res *= 2;
}
return res;
}
// Add underpromotion captures to list of captures.
static ExtMove *add_underprom_caps(Position& pos, ExtMove *stack, ExtMove *end)
{
ExtMove *moves, *extra = end;
for (moves = stack; moves < end; moves++) {
Move move = moves->move;
if (type_of(move) == PROMOTION && !pos.empty(to_sq(move))) {
(*extra++).move = (Move)(move - (1 << 12));
(*extra++).move = (Move)(move - (2 << 12));
(*extra++).move = (Move)(move - (3 << 12));
}
}
return extra;
}
static int probe_ab(Position& pos, int alpha, int beta, int *success)
{
int v;
ExtMove stack[64];
ExtMove *moves, *end;
StateInfo st;
// Generate (at least) all legal non-ep captures including (under)promotions.
// It is OK to generate more, as long as they are filtered out below.
if (!pos.checkers()) {
end = generate<CAPTURES>(pos, stack);
// Since underpromotion captures are not included, we need to add them.
end = add_underprom_caps(pos, stack, end);
} else
end = generate<EVASIONS>(pos, stack);
CheckInfo ci(pos);
for (moves = stack; moves < end; moves++) {
Move capture = moves->move;
if (!pos.capture(capture) || type_of(capture) == ENPASSANT
|| !pos.legal(capture, ci.pinned))
continue;
pos.do_move(capture, st, ci, pos.gives_check(capture, ci));
v = -probe_ab(pos, -beta, -alpha, success);
pos.undo_move(capture);
if (*success == 0) return 0;
if (v > alpha) {
if (v >= beta) {
*success = 2;
return v;
}
alpha = v;
}
}
v = probe_wdl_table(pos, success);
if (*success == 0) return 0;
if (alpha >= v) {
*success = 1 + (alpha > 0);
return alpha;
} else {
*success = 1;
return v;
}
}
// Probe the WDL table for a particular position.
// If *success != 0, the probe was successful.
// The return value is from the point of view of the side to move:
// -2 : loss
// -1 : loss, but draw under 50-move rule
// 0 : draw
// 1 : win, but draw under 50-move rule
// 2 : win
int Tablebases::probe_wdl(Position& pos, int *success)
{
int v;
*success = 1;
v = probe_ab(pos, -2, 2, success);
// If en passant is not possible, we are done.
if (pos.ep_square() == SQ_NONE)
return v;
if (!(*success)) return 0;
// Now handle en passant.
int v1 = -3;
// Generate (at least) all legal en passant captures.
ExtMove stack[192];
ExtMove *moves, *end;
StateInfo st;
if (!pos.checkers())
end = generate<CAPTURES>(pos, stack);
else
end = generate<EVASIONS>(pos, stack);
CheckInfo ci(pos);
for (moves = stack; moves < end; moves++) {
Move capture = moves->move;
if (type_of(capture) != ENPASSANT
|| !pos.legal(capture, ci.pinned))
continue;
pos.do_move(capture, st, ci, pos.gives_check(capture, ci));
int v0 = -probe_ab(pos, -2, 2, success);
pos.undo_move(capture);
if (*success == 0) return 0;
if (v0 > v1) v1 = v0;
}
if (v1 > -3) {
if (v1 >= v) v = v1;
else if (v == 0) {
// Check whether there is at least one legal non-ep move.
for (moves = stack; moves < end; moves++) {
Move capture = moves->move;
if (type_of(capture) == ENPASSANT) continue;
if (pos.legal(capture, ci.pinned)) break;
}
if (moves == end && !pos.checkers()) {
end = generate<QUIETS>(pos, end);
for (; moves < end; moves++) {
Move move = moves->move;
if (pos.legal(move, ci.pinned))
break;
}
}
// If not, then we are forced to play the losing ep capture.
if (moves == end)
v = v1;
}
}
return v;
}
// This routine treats a position with en passant captures as one without.
static int probe_dtz_no_ep(Position& pos, int *success)
{
int wdl, dtz;
wdl = probe_ab(pos, -2, 2, success);
if (*success == 0) return 0;
if (wdl == 0) return 0;
if (*success == 2)
return wdl == 2 ? 1 : 101;
ExtMove stack[192];
ExtMove *moves, *end = NULL;
StateInfo st;
CheckInfo ci(pos);
if (wdl > 0) {
// Generate at least all legal non-capturing pawn moves
// including non-capturing promotions.
if (!pos.checkers())
end = generate<NON_EVASIONS>(pos, stack);
else
end = generate<EVASIONS>(pos, stack);
for (moves = stack; moves < end; moves++) {
Move move = moves->move;
if (type_of(pos.moved_piece(move)) != PAWN || pos.capture(move)
|| !pos.legal(move, ci.pinned))
continue;
pos.do_move(move, st, ci, pos.gives_check(move, ci));
int v = -probe_ab(pos, -2, -wdl + 1, success);
pos.undo_move(move);
if (*success == 0) return 0;
if (v == wdl)
return v == 2 ? 1 : 101;
}
}
dtz = 1 + probe_dtz_table(pos, wdl, success);
if (*success >= 0) {
if (wdl & 1) dtz += 100;
return wdl >= 0 ? dtz : -dtz;
}
if (wdl > 0) {
int best = 0xffff;
for (moves = stack; moves < end; moves++) {
Move move = moves->move;
if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN
|| !pos.legal(move, ci.pinned))
continue;
pos.do_move(move, st, ci, pos.gives_check(move, ci));
int v = -Tablebases::probe_dtz(pos, success);
pos.undo_move(move);
if (*success == 0) return 0;
if (v > 0 && v + 1 < best)
best = v + 1;
}
return best;
} else {
int best = -1;
if (!pos.checkers())
end = generate<NON_EVASIONS>(pos, stack);
else
end = generate<EVASIONS>(pos, stack);
for (moves = stack; moves < end; moves++) {
int v;
Move move = moves->move;
if (!pos.legal(move, ci.pinned))
continue;
pos.do_move(move, st, ci, pos.gives_check(move, ci));
if (st.rule50 == 0) {
if (wdl == -2) v = -1;
else {
v = probe_ab(pos, 1, 2, success);
v = (v == 2) ? 0 : -101;
}
} else {
v = -Tablebases::probe_dtz(pos, success) - 1;
}
pos.undo_move(move);
if (*success == 0) return 0;
if (v < best)
best = v;
}
return best;
}
}
static int wdl_to_dtz[] = {
-1, -101, 0, 101, 1
};
// Probe the DTZ table for a particular position.
// If *success != 0, the probe was successful.
// The return value is from the point of view of the side to move:
// n < -100 : loss, but draw under 50-move rule
// -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0)
// 0 : draw
// 1 < n <= 100 : win in n ply (assuming 50-move counter == 0)
// 100 < n : win, but draw under 50-move rule
//
// The return value n can be off by 1: a return value -n can mean a loss
// in n+1 ply and a return value +n can mean a win in n+1 ply. This
// cannot happen for tables with positions exactly on the "edge" of
// the 50-move rule.
//
// This implies that if dtz > 0 is returned, the position is certainly
// a win if dtz + 50-move-counter <= 99. Care must be taken that the engine
// picks moves that preserve dtz + 50-move-counter <= 99.
//
// If n = 100 immediately after a capture or pawn move, then the position
// is also certainly a win, and during the whole phase until the next
// capture or pawn move, the inequality to be preserved is
// dtz + 50-movecounter <= 100.
//
// In short, if a move is available resulting in dtz + 50-move-counter <= 99,
// then do not accept moves leading to dtz + 50-move-counter == 100.
//
int Tablebases::probe_dtz(Position& pos, int *success)
{
*success = 1;
int v = probe_dtz_no_ep(pos, success);
if (pos.ep_square() == SQ_NONE)
return v;
if (*success == 0) return 0;
// Now handle en passant.
int v1 = -3;
ExtMove stack[192];
ExtMove *moves, *end;
StateInfo st;
if (!pos.checkers())
end = generate<CAPTURES>(pos, stack);
else
end = generate<EVASIONS>(pos, stack);
CheckInfo ci(pos);
for (moves = stack; moves < end; moves++) {
Move capture = moves->move;
if (type_of(capture) != ENPASSANT
|| !pos.legal(capture, ci.pinned))
continue;
pos.do_move(capture, st, ci, pos.gives_check(capture, ci));
int v0 = -probe_ab(pos, -2, 2, success);
pos.undo_move(capture);
if (*success == 0) return 0;
if (v0 > v1) v1 = v0;
}
if (v1 > -3) {
v1 = wdl_to_dtz[v1 + 2];
if (v < -100) {
if (v1 >= 0)
v = v1;
} else if (v < 0) {
if (v1 >= 0 || v1 < 100)
v = v1;
} else if (v > 100) {
if (v1 > 0)
v = v1;
} else if (v > 0) {
if (v1 == 1)
v = v1;
} else if (v1 >= 0) {
v = v1;
} else {
for (moves = stack; moves < end; moves++) {
Move move = moves->move;
if (type_of(move) == ENPASSANT) continue;
if (pos.legal(move, ci.pinned)) break;
}
if (moves == end && !pos.checkers()) {
end = generate<QUIETS>(pos, end);
for (; moves < end; moves++) {
Move move = moves->move;
if (pos.legal(move, ci.pinned))
break;
}
}
if (moves == end)
v = v1;
}
}
return v;
}
// Check whether there has been at least one repetition of positions
// since the last capture or pawn move.
static int has_repeated(StateInfo *st)
{
while (1) {
int i = 4, e = std::min(st->rule50, st->pliesFromNull);
if (e < i)
return 0;
StateInfo *stp = st->previous->previous;
do {
stp = stp->previous->previous;
if (stp->key == st->key)
return 1;
i += 2;
} while (i <= e);
st = st->previous;
}
}
static Value wdl_to_Value[5] = {
-VALUE_MATE + MAX_PLY + 1,
VALUE_DRAW - 2,
VALUE_DRAW,
VALUE_DRAW + 2,
VALUE_MATE - MAX_PLY - 1
};
// Use the DTZ tables to filter out moves that don't preserve the win or draw.
// If the position is lost, but DTZ is fairly high, only keep moves that
// maximise DTZ.
//
// A return value false indicates that not all probes were successful and that
// no moves were filtered out.
bool Tablebases::root_probe(Position& pos, Value& TBScore)
{
int success;
int dtz = probe_dtz(pos, &success);
if (!success) return false;
StateInfo st;
CheckInfo ci(pos);
// Probe each move.
for (size_t i = 0; i < Search::RootMoves.size(); i++) {
Move move = Search::RootMoves[i].pv[0];
pos.do_move(move, st, ci, pos.gives_check(move, ci));
int v = 0;
if (pos.checkers() && dtz > 0) {
ExtMove s[192];
if (generate<LEGAL>(pos, s) == s)
v = 1;
}
if (!v) {
if (st.rule50 != 0) {
v = -Tablebases::probe_dtz(pos, &success);
if (v > 0) v++;
else if (v < 0) v--;
} else {
v = -Tablebases::probe_wdl(pos, &success);
v = wdl_to_dtz[v + 2];
}
}
pos.undo_move(move);
if (!success) return false;
Search::RootMoves[i].score = (Value)v;
}
// Obtain 50-move counter for the root position.
// In Stockfish there seems to be no clean way, so we do it like this:
int cnt50 = st.previous->rule50;
// Use 50-move counter to determine whether the root position is
// won, lost or drawn.
int wdl = 0;
if (dtz > 0)
wdl = (dtz + cnt50 <= 100) ? 2 : 1;
else if (dtz < 0)
wdl = (-dtz + cnt50 <= 100) ? -2 : -1;
// Determine the score to report to the user.
TBScore = wdl_to_Value[wdl + 2];
// If the position is winning or losing, but too few moves left, adjust the
// score to show how close it is to winning or losing.
// NOTE: int(PawnValueEg) is used as scaling factor in score_to_uci().
if (wdl == 1 && dtz <= 100)
TBScore = (Value)(((200 - dtz - cnt50) * int(PawnValueEg)) / 200);
else if (wdl == -1 && dtz >= -100)
TBScore = -(Value)(((200 + dtz - cnt50) * int(PawnValueEg)) / 200);
// Now be a bit smart about filtering out moves.
size_t j = 0;
if (dtz > 0) { // winning (or 50-move rule draw)
int best = 0xffff;
for (size_t i = 0; i < Search::RootMoves.size(); i++) {
int v = Search::RootMoves[i].score;
if (v > 0 && v < best)
best = v;
}
int max = best;
// If the current phase has not seen repetitions, then try all moves
// that stay safely within the 50-move budget, if there are any.
if (!has_repeated(st.previous) && best + cnt50 <= 99)
max = 99 - cnt50;
for (size_t i = 0; i < Search::RootMoves.size(); i++) {
int v = Search::RootMoves[i].score;
if (v > 0 && v <= max)
Search::RootMoves[j++] = Search::RootMoves[i];
}
} else if (dtz < 0) { // losing (or 50-move rule draw)
int best = 0;
for (size_t i = 0; i < Search::RootMoves.size(); i++) {
int v = Search::RootMoves[i].score;
if (v < best)
best = v;
}
// Try all moves, unless we approach or have a 50-move rule draw.
if (-best * 2 + cnt50 < 100)
return true;
for (size_t i = 0; i < Search::RootMoves.size(); i++) {
if (Search::RootMoves[i].score == best)
Search::RootMoves[j++] = Search::RootMoves[i];
}
} else { // drawing
// Try all moves that preserve the draw.
for (size_t i = 0; i < Search::RootMoves.size(); i++) {
if (Search::RootMoves[i].score == 0)
Search::RootMoves[j++] = Search::RootMoves[i];
}
}
Search::RootMoves.resize(j, Search::RootMove(MOVE_NONE));
return true;
}
// Use the WDL tables to filter out moves that don't preserve the win or draw.
// This is a fallback for the case that some or all DTZ tables are missing.
//
// A return value false indicates that not all probes were successful and that
// no moves were filtered out.
bool Tablebases::root_probe_wdl(Position& pos, Value& TBScore)
{
int success;
int wdl = Tablebases::probe_wdl(pos, &success);
if (!success) return false;
TBScore = wdl_to_Value[wdl + 2];
StateInfo st;
CheckInfo ci(pos);
int best = -2;
// Probe each move.
for (size_t i = 0; i < Search::RootMoves.size(); i++) {
Move move = Search::RootMoves[i].pv[0];
pos.do_move(move, st, ci, pos.gives_check(move, ci));
int v = -Tablebases::probe_wdl(pos, &success);
pos.undo_move(move);
if (!success) return false;
Search::RootMoves[i].score = (Value)v;
if (v > best)
best = v;
}
size_t j = 0;
for (size_t i = 0; i < Search::RootMoves.size(); i++) {
if (Search::RootMoves[i].score == best)
Search::RootMoves[j++] = Search::RootMoves[i];
}
Search::RootMoves.resize(j, Search::RootMove(MOVE_NONE));
return true;
}

View File

@@ -0,0 +1,16 @@
#ifndef TBPROBE_H
#define TBPROBE_H
namespace Tablebases {
extern int TBLargest;
void init(const std::string& path);
int probe_wdl(Position& pos, int *success);
int probe_dtz(Position& pos, int *success);
bool root_probe(Position& pos, Value& TBScore);
bool root_probe_wdl(Position& pos, Value& TBScore);
}
#endif

View File

@@ -119,7 +119,7 @@ bool Thread::available_to(const Thread* master) const {
// Make a local copy to be sure it doesn't become zero under our feet while // Make a local copy to be sure it doesn't become zero under our feet while
// testing next condition and so leading to an out of bounds access. // testing next condition and so leading to an out of bounds access.
int size = splitPointsSize; const int size = splitPointsSize;
// No 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.
@@ -255,7 +255,6 @@ Thread* ThreadPool::available_slave(const Thread* master) const {
// leave their idle loops and call search(). When all threads have returned from // leave their idle loops and call search(). When all threads have returned from
// search() then split() returns. // search() then split() returns.
template <bool Fake>
void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Value* bestValue, void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Value* bestValue,
Move* bestMove, Depth depth, int moveCount, Move* bestMove, Depth depth, int moveCount,
MovePicker* movePicker, int nodeType, bool cutNode) { MovePicker* movePicker, int nodeType, bool cutNode) {
@@ -297,14 +296,13 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
activeSplitPoint = &sp; activeSplitPoint = &sp;
activePosition = NULL; activePosition = NULL;
if (!Fake) for (Thread* slave; (slave = Threads.available_slave(this)) != NULL; )
for (Thread* slave; (slave = Threads.available_slave(this)) != NULL; ) {
{ sp.slavesMask.set(slave->idx);
sp.slavesMask.set(slave->idx); slave->activeSplitPoint = &sp;
slave->activeSplitPoint = &sp; slave->searching = true; // Slave leaves idle_loop()
slave->searching = true; // Slave leaves idle_loop() slave->notify_one(); // Could be sleeping
slave->notify_one(); // Could be sleeping }
}
// 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 'searching' flag is set. // it will instantly launch a search, because its 'searching' flag is set.
@@ -339,11 +337,6 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
Threads.mutex.unlock(); Threads.mutex.unlock();
} }
// Explicit template instantiations
template void Thread::split<false>(Position&, const Stack*, Value, Value, Value*, Move*, Depth, int, MovePicker*, int, bool);
template void Thread::split< true>(Position&, const Stack*, Value, Value, Value*, Move*, Depth, int, MovePicker*, int, bool);
// wait_for_think_finished() waits for main thread to go to sleep then returns // wait_for_think_finished() waits for main thread to go to sleep then returns
void ThreadPool::wait_for_think_finished() { void ThreadPool::wait_for_think_finished() {

View File

@@ -117,7 +117,6 @@ struct Thread : public ThreadBase {
bool cutoff_occurred() const; bool cutoff_occurred() const;
bool available_to(const Thread* master) const; bool available_to(const Thread* master) const;
template <bool Fake>
void split(Position& pos, const Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove, void split(Position& pos, const Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove,
Depth depth, int moveCount, MovePicker* movePicker, int nodeType, bool cutNode); Depth depth, int moveCount, MovePicker* movePicker, int nodeType, bool cutNode);

View File

@@ -78,7 +78,7 @@ void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color u
increment > 0 && movesToGo == 0 means: x basetime + z increment increment > 0 && movesToGo == 0 means: x basetime + z increment
increment > 0 && movesToGo != 0 means: x moves in y minutes + z increment increment > 0 && movesToGo != 0 means: x moves in y minutes + z increment
Time management is adjusted by following UCI parameters: Time management is adjusted by following parameters:
emergencyMoveHorizon: Be prepared to always play at least this many moves emergencyMoveHorizon: Be prepared to always play at least this many moves
emergencyBaseTime : Always attempt to keep at least this much time (in ms) at clock emergencyBaseTime : Always attempt to keep at least this much time (in ms) at clock
@@ -89,11 +89,9 @@ void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color u
int hypMTG, hypMyTime, t1, t2; int hypMTG, hypMyTime, t1, t2;
// Read uci parameters // Read uci parameters
int emergencyMoveHorizon = Options["Emergency Move Horizon"]; int moveOverhead = Options["Move Overhead"];
int emergencyBaseTime = Options["Emergency Base Time"]; int minThinkingTime = Options["Minimum Thinking Time"];
int emergencyMoveTime = Options["Emergency Move Time"]; int slowMover = Options["Slow Mover"];
int minThinkingTime = Options["Minimum Thinking Time"];
int slowMover = Options["Slow Mover"];
// Initialize unstablePvFactor to 1 and search times to maximum values // Initialize unstablePvFactor to 1 and search times to maximum values
unstablePvFactor = 1; unstablePvFactor = 1;
@@ -106,8 +104,7 @@ void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color u
// Calculate thinking time for hypothetical "moves to go"-value // Calculate thinking time for hypothetical "moves to go"-value
hypMyTime = limits.time[us] hypMyTime = limits.time[us]
+ limits.inc[us] * (hypMTG - 1) + limits.inc[us] * (hypMTG - 1)
- emergencyBaseTime - moveOverhead * (2 + std::min(hypMTG, 40));
- emergencyMoveTime * std::min(hypMTG, emergencyMoveHorizon);
hypMyTime = std::max(hypMyTime, 0); hypMyTime = std::max(hypMyTime, 0);

View File

@@ -28,20 +28,19 @@ TranspositionTable TT; // Our global transposition table
/// TranspositionTable::resize() sets the size of the transposition table, /// TranspositionTable::resize() sets the size of the transposition table,
/// measured in megabytes. Transposition table consists of a power of 2 number /// measured in megabytes. Transposition table consists of a power of 2 number
/// of clusters and each cluster consists of ClusterSize number of TTEntry. /// of clusters and each cluster consists of TTClusterSize number of TTEntry.
void TranspositionTable::resize(uint64_t mbSize) { void TranspositionTable::resize(size_t mbSize) {
assert(msb((mbSize << 20) / sizeof(TTEntry)) < 32); size_t newClusterCount = size_t(1) << msb((mbSize * 1024 * 1024) / sizeof(TTCluster));
uint32_t size = ClusterSize << msb((mbSize << 20) / sizeof(TTEntry[ClusterSize])); if (newClusterCount == clusterCount)
if (hashMask == size - ClusterSize)
return; return;
hashMask = size - ClusterSize; clusterCount = newClusterCount;
free(mem); free(mem);
mem = calloc(size * sizeof(TTEntry) + CACHE_LINE_SIZE - 1, 1); mem = calloc(clusterCount * sizeof(TTCluster) + CACHE_LINE_SIZE - 1, 1);
if (!mem) if (!mem)
{ {
@@ -50,7 +49,7 @@ void TranspositionTable::resize(uint64_t mbSize) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
table = (TTEntry*)((uintptr_t(mem) + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1)); table = (TTCluster*)((uintptr_t(mem) + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1));
} }
@@ -60,7 +59,7 @@ void TranspositionTable::resize(uint64_t mbSize) {
void TranspositionTable::clear() { void TranspositionTable::clear() {
std::memset(table, 0, (hashMask + ClusterSize) * sizeof(TTEntry)); std::memset(table, 0, clusterCount * sizeof(TTCluster));
} }
@@ -71,12 +70,12 @@ void TranspositionTable::clear() {
const TTEntry* TranspositionTable::probe(const Key key) const { const TTEntry* TranspositionTable::probe(const Key key) const {
TTEntry* tte = first_entry(key); TTEntry* tte = first_entry(key);
uint32_t key32 = key >> 32; uint16_t key16 = key >> 48;
for (unsigned i = 0; i < ClusterSize; ++i, ++tte) for (unsigned i = 0; i < TTClusterSize; ++i, ++tte)
if (tte->key32 == key32) if (tte->key16 == key16)
{ {
tte->generation8 = generation; // Refresh tte->genBound8 = generation | tte->bound(); // Refresh
return tte; return tte;
} }
@@ -95,13 +94,13 @@ const TTEntry* TranspositionTable::probe(const Key key) const {
void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV) { void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV) {
TTEntry *tte, *replace; TTEntry *tte, *replace;
uint32_t key32 = key >> 32; // Use the high 32 bits as key inside the cluster uint16_t key16 = key >> 48; // Use the high 16 bits as key inside the cluster
tte = replace = first_entry(key); tte = replace = first_entry(key);
for (unsigned i = 0; i < ClusterSize; ++i, ++tte) for (unsigned i = 0; i < TTClusterSize; ++i, ++tte)
{ {
if (!tte->key32 || tte->key32 == key32) // Empty or overwrite old if (!tte->key16 || tte->key16 == key16) // Empty or overwrite old
{ {
if (!m) if (!m)
m = tte->move(); // Preserve any existing ttMove m = tte->move(); // Preserve any existing ttMove
@@ -111,11 +110,11 @@ void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m,
} }
// Implement replace strategy // Implement replace strategy
if ( ( tte->generation8 == generation || tte->bound() == BOUND_EXACT) if ( (( tte->genBound8 & 0xFC) == generation || tte->bound() == BOUND_EXACT)
- (replace->generation8 == generation) - ((replace->genBound8 & 0xFC) == generation)
- (tte->depth16 < replace->depth16) < 0) - (tte->depth8 < replace->depth8) < 0)
replace = tte; replace = tte;
} }
replace->save(key32, v, b, d, m, generation, statV); replace->save(key16, v, b, d, m, generation, statV);
} }

View File

@@ -23,70 +23,80 @@
#include "misc.h" #include "misc.h"
#include "types.h" #include "types.h"
/// The TTEntry is the 14 bytes transposition table entry, defined as below: /// The TTEntry is the 10 bytes transposition table entry, defined as below:
/// ///
/// key 32 bit /// key 16 bit
/// move 16 bit /// move 16 bit
/// bound type 8 bit
/// generation 8 bit
/// value 16 bit /// value 16 bit
/// depth 16 bit
/// eval value 16 bit /// eval value 16 bit
/// generation 6 bit
/// bound type 2 bit
/// depth 8 bit
struct TTEntry { struct TTEntry {
Move move() const { return (Move )move16; } Move move() const { return (Move )move16; }
Bound bound() const { return (Bound)bound8; }
Value value() const { return (Value)value16; } Value value() const { return (Value)value16; }
Depth depth() const { return (Depth)depth16; }
Value eval_value() const { return (Value)evalValue; } Value eval_value() const { return (Value)evalValue; }
Depth depth() const { return (Depth)depth8; }
Bound bound() const { return (Bound)(genBound8 & 0x3); }
private: private:
friend class TranspositionTable; friend class TranspositionTable;
void save(uint32_t k, Value v, Bound b, Depth d, Move m, uint8_t g, Value ev) { void save(uint16_t k, Value v, Bound b, Depth d, Move m, uint8_t g, Value ev) {
key32 = (uint32_t)k; key16 = (uint16_t)k;
move16 = (uint16_t)m; move16 = (uint16_t)m;
bound8 = (uint8_t)b; value16 = (int16_t)v;
generation8 = (uint8_t)g; evalValue = (int16_t)ev;
value16 = (int16_t)v; genBound8 = (uint8_t)(g | b);
depth16 = (int16_t)d; depth8 = (int8_t)d;
evalValue = (int16_t)ev;
} }
uint32_t key32; uint16_t key16;
uint16_t move16; uint16_t move16;
uint8_t bound8, generation8; int16_t value16;
int16_t value16, depth16, evalValue; int16_t evalValue;
uint8_t genBound8;
int8_t depth8;
}; };
/// TTCluster is a 32 bytes cluster of TT entries consisting of:
///
/// 3 x TTEntry (3 x 10 bytes)
/// padding (2 bytes)
const unsigned TTClusterSize = 3;
struct TTCluster {
TTEntry entry[TTClusterSize];
char padding[2];
};
/// A TranspositionTable consists of a power of 2 number of clusters and each /// A TranspositionTable consists of a power of 2 number of clusters and each
/// cluster consists of ClusterSize number of TTEntry. Each non-empty entry /// cluster consists of TTClusterSize number of TTEntry. Each non-empty entry
/// contains information of exactly one position. The size of a cluster should /// contains information of exactly one position. The size of a cluster should
/// not be bigger than a cache line size. In case it is less, it should be padded /// not be bigger than a cache line size. In case it is less, it should be padded
/// to guarantee always aligned accesses. /// to guarantee always aligned accesses.
class TranspositionTable { class TranspositionTable {
static const unsigned ClusterSize = 4;
public: public:
~TranspositionTable() { free(mem); } ~TranspositionTable() { free(mem); }
void new_search() { ++generation; } void new_search() { generation += 4; } // Lower 2 bits are used by Bound
const TTEntry* probe(const Key key) const; const TTEntry* probe(const Key key) const;
TTEntry* first_entry(const Key key) const; TTEntry* first_entry(const Key key) const;
void resize(uint64_t mbSize); void resize(size_t mbSize);
void clear(); void clear();
void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV); void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV);
private: private:
uint32_t hashMask; size_t clusterCount;
TTEntry* table; TTCluster* table;
void* mem; void* mem;
uint8_t generation; // Size must be not bigger than TTEntry::generation8 uint8_t generation; // Size must be not bigger than TTEntry::genBound8
}; };
extern TranspositionTable TT; extern TranspositionTable TT;
@@ -94,11 +104,11 @@ extern TranspositionTable TT;
/// TranspositionTable::first_entry() returns a pointer to the first entry of /// TranspositionTable::first_entry() returns a pointer to the first entry of
/// 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 inside the table.
inline TTEntry* TranspositionTable::first_entry(const Key key) const { inline TTEntry* TranspositionTable::first_entry(const Key key) const {
return table + ((uint32_t)key & hashMask); return &table[(size_t)key & (clusterCount - 1)].entry[0];
} }
#endif // #ifndef TT_H_INCLUDED #endif // #ifndef TT_H_INCLUDED

View File

@@ -136,7 +136,7 @@ enum CastlingSide {
KING_SIDE, QUEEN_SIDE, CASTLING_SIDE_NB = 2 KING_SIDE, QUEEN_SIDE, CASTLING_SIDE_NB = 2
}; };
enum CastlingRight { // Defined as in PolyGlot book hash key enum CastlingRight {
NO_CASTLING, NO_CASTLING,
WHITE_OO, WHITE_OO,
WHITE_OOO = WHITE_OO << 1, WHITE_OOO = WHITE_OO << 1,
@@ -181,8 +181,8 @@ enum Value {
VALUE_INFINITE = 32001, VALUE_INFINITE = 32001,
VALUE_NONE = 32002, VALUE_NONE = 32002,
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY,
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY,
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,
@@ -211,14 +211,14 @@ enum Piece {
enum Depth { enum Depth {
ONE_PLY = 2, ONE_PLY = 1,
DEPTH_ZERO = 0 * ONE_PLY, DEPTH_ZERO = 0,
DEPTH_QS_CHECKS = 0 * ONE_PLY, DEPTH_QS_CHECKS = 0,
DEPTH_QS_NO_CHECKS = -1 * ONE_PLY, DEPTH_QS_NO_CHECKS = -1,
DEPTH_QS_RECAPTURES = -5 * ONE_PLY, DEPTH_QS_RECAPTURES = -5,
DEPTH_NONE = -127 * ONE_PLY DEPTH_NONE = -6
}; };
enum Square { enum Square {
@@ -267,28 +267,17 @@ enum Score {
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
}; };
typedef union { inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
uint32_t full;
struct { int16_t eg, mg; } half;
} ScoreView;
inline Score make_score(int mg, int eg) {
ScoreView v;
v.half.mg = (int16_t)(mg - (uint16_t(eg) >> 15));
v.half.eg = (int16_t)eg;
return Score(v.full);
}
/// Extracting the signed lower and upper 16 bits is not so trivial because
/// according to the standard a simple cast to short is implementation defined
/// and so is a right shift of a signed integer.
inline Value mg_value(Score s) { inline Value mg_value(Score s) {
ScoreView v; return Value(((s + 0x8000) & ~0xffff) / 0x10000);
v.full = s;
return Value(v.half.mg + (uint16_t(v.half.eg) >> 15));
} }
inline Value eg_value(Score s) { inline Value eg_value(Score s) {
ScoreView v; return Value((int)(unsigned(s) & 0x7FFFU) - (int)(unsigned(s) & 0x8000U));
v.full = s;
return Value(v.half.eg);
} }
#define ENABLE_BASE_OPERATORS_ON(T) \ #define ENABLE_BASE_OPERATORS_ON(T) \
@@ -337,6 +326,8 @@ inline Score operator/(Score s, int i) {
return make_score(mg_value(s) / i, eg_value(s) / i); return make_score(mg_value(s) / i, eg_value(s) / i);
} }
CACHE_LINE_ALIGNMENT
extern Value PieceValue[PHASE_NB][PIECE_NB]; extern Value PieceValue[PHASE_NB][PIECE_NB];
struct ExtMove { struct ExtMove {
@@ -414,14 +405,6 @@ inline bool opposite_colors(Square s1, Square s2) {
return ((s >> 3) ^ s) & 1; return ((s >> 3) ^ s) & 1;
} }
inline char to_char(File f, bool tolower = true) {
return char(f - FILE_A + (tolower ? 'a' : 'A'));
}
inline char to_char(Rank r) {
return char(r - RANK_1 + '1');
}
inline Square pawn_push(Color c) { inline Square pawn_push(Color c) {
return c == WHITE ? DELTA_N : DELTA_S; return c == WHITE ? DELTA_N : DELTA_S;
} }
@@ -455,11 +438,4 @@ inline bool is_ok(Move m) {
return from_sq(m) != to_sq(m); // Catches also MOVE_NULL and MOVE_NONE return from_sq(m) != to_sq(m); // Catches also MOVE_NULL and MOVE_NONE
} }
#include <string>
inline const std::string to_string(Square s) {
char ch[] = { to_char(file_of(s)), to_char(rank_of(s)), 0 };
return ch;
}
#endif // #ifndef TYPES_H_INCLUDED #endif // #ifndef TYPES_H_INCLUDED

View File

@@ -174,7 +174,7 @@ void UCI::loop(int argc, char* argv[]) {
else else
Search::Limits.ponder = false; Search::Limits.ponder = false;
} }
else if (token == "perft" || token == "divide") else if (token == "perft")
{ {
int depth; int depth;
stringstream ss; stringstream ss;
@@ -197,12 +197,7 @@ void UCI::loop(int argc, char* argv[]) {
<< "\n" << Options << "\n" << Options
<< "\nuciok" << sync_endl; << "\nuciok" << sync_endl;
else if (token == "eval") else if (token == "ucinewgame") TT.clear();
{
Search::RootColor = pos.side_to_move(); // Ensure it is set
sync_cout << Eval::trace(pos) << sync_endl;
}
else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ }
else if (token == "go") go(pos, is); else if (token == "go") go(pos, is);
else if (token == "position") position(pos, is); else if (token == "position") position(pos, is);
else if (token == "setoption") setoption(is); else if (token == "setoption") setoption(is);
@@ -210,6 +205,7 @@ void UCI::loop(int argc, char* argv[]) {
else if (token == "bench") benchmark(pos, is); else if (token == "bench") benchmark(pos, is);
else if (token == "d") sync_cout << pos.pretty() << sync_endl; else if (token == "d") sync_cout << pos.pretty() << sync_endl;
else if (token == "isready") sync_cout << "readyok" << 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;

View File

@@ -27,6 +27,7 @@
#include "thread.h" #include "thread.h"
#include "tt.h" #include "tt.h"
#include "ucioption.h" #include "ucioption.h"
#include "tbprobe.h"
using std::string; using std::string;
@@ -40,6 +41,7 @@ void on_eval(const Option&) { Eval::init(); }
void on_threads(const Option&) { Threads.read_uci_options(); } void on_threads(const Option&) { Threads.read_uci_options(); }
void on_hash_size(const Option& o) { TT.resize(o); } void on_hash_size(const Option& o) { TT.resize(o); }
void on_clear_hash(const Option&) { TT.clear(); } void on_clear_hash(const Option&) { TT.clear(); }
void on_tb_path(const Option& o) { Tablebases::init(o); }
/// Our case insensitive less() function as required by UCI protocol /// Our case insensitive less() function as required by UCI protocol
@@ -54,35 +56,23 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const
void init(OptionsMap& o) { void init(OptionsMap& o) {
o["Write Debug Log"] << Option(false, on_logger); o["Write Debug Log"] << Option(false, on_logger);
o["Write Search Log"] << Option(false); o["Contempt"] << Option(0, -100, 100);
o["Search Log Filename"] << Option("SearchLog.txt"); o["Min Split Depth"] << Option(0, 0, 12, on_threads);
o["Book File"] << Option("book.bin"); o["Threads"] << Option(1, 1, MAX_THREADS, on_threads);
o["Best Book Move"] << Option(false); o["Hash"] << Option(16, 1, 1024 * 1024, on_hash_size);
o["Contempt Factor"] << Option(0, -50, 50); o["Clear Hash"] << Option(on_clear_hash);
o["Mobility (Midgame)"] << Option(100, 0, 200, on_eval); o["Ponder"] << Option(true);
o["Mobility (Endgame)"] << Option(100, 0, 200, on_eval); o["MultiPV"] << Option(1, 1, 500);
o["Pawn Structure (Midgame)"] << Option(100, 0, 200, on_eval); o["Skill Level"] << Option(20, 0, 20);
o["Pawn Structure (Endgame)"] << Option(100, 0, 200, on_eval); o["Move Overhead"] << Option(30, 0, 5000);
o["Passed Pawns (Midgame)"] << Option(100, 0, 200, on_eval); o["Minimum Thinking Time"] << Option(20, 0, 5000);
o["Passed Pawns (Endgame)"] << Option(100, 0, 200, on_eval); o["Slow Mover"] << Option(80, 10, 1000);
o["Space"] << Option(100, 0, 200, on_eval); o["UCI_Chess960"] << Option(false);
o["Aggressiveness"] << Option(100, 0, 200, on_eval); o["SyzygyPath"] << Option("<empty>", on_tb_path);
o["Cowardice"] << Option(100, 0, 200, on_eval); o["SyzygyProbeDepth"] << Option(1, 1, 100);
o["Min Split Depth"] << Option(0, 0, 12, on_threads); o["Syzygy50MoveRule"] << Option(true);
o["Threads"] << Option(1, 1, MAX_THREADS, on_threads); o["SyzygyProbeLimit"] << Option(6, 0, 6);
o["Hash"] << Option(32, 1, 16384, on_hash_size);
o["Clear Hash"] << Option(on_clear_hash);
o["Ponder"] << Option(true);
o["OwnBook"] << Option(false);
o["MultiPV"] << Option(1, 1, 500);
o["Skill Level"] << Option(20, 0, 20);
o["Emergency Move Horizon"] << Option(40, 0, 50);
o["Emergency Base Time"] << Option(60, 0, 30000);
o["Emergency Move Time"] << Option(30, 0, 5000);
o["Minimum Thinking Time"] << Option(20, 0, 5000);
o["Slow Mover"] << Option(80, 10, 1000);
o["UCI_Chess960"] << Option(false);
} }